diff --git a/indra/llcharacter/llkeyframemotion.h b/indra/llcharacter/llkeyframemotion.h
index 4ea938d6955c60664d1a339d352d98eeaa6ea139..d517fe4b04ced25e06f7f2360c68b479f6463c3f 100644
--- a/indra/llcharacter/llkeyframemotion.h
+++ b/indra/llcharacter/llkeyframemotion.h
@@ -436,6 +436,10 @@ class LLKeyframeMotion :
 
 public:
 	void setCharacter(LLCharacter* character) { mCharacter = character; }
+
+	//BD - Poser
+	JointMotionList* getJointMotionList() const { return mJointMotionList; }
+	void setJointMotionList(JointMotionList* list) { mJointMotionList = list; }
 };
 
 class LLKeyframeDataCache
diff --git a/indra/llcharacter/llmotion.cpp b/indra/llcharacter/llmotion.cpp
index f7eb26c7c593659e8a1ebaa2a9b017795fec6fc7..7e8e55693392e34e8fe4523cc7ed710dcf32bbbb 100644
--- a/indra/llcharacter/llmotion.cpp
+++ b/indra/llcharacter/llmotion.cpp
@@ -52,7 +52,9 @@ LLMotion::LLMotion( const LLUUID &id ) :
 	mResidualWeight(0.f),
 	mFadeWeight(1.f),
 	mDeactivateCallback(NULL),
-	mDeactivateCallbackUserData(NULL)
+	mDeactivateCallbackUserData(NULL),
+	//BD - Eternal
+	mEternal(false)
 {
 	for (S32 i=0; i<3; ++i)
 		memset(&mJointSignature[i][0], 0, sizeof(U8) * LL_CHARACTER_MAX_ANIMATED_JOINTS);
diff --git a/indra/llcharacter/llmotion.h b/indra/llcharacter/llmotion.h
index 22ac98009df09af33cbf0bc66a2338c717dc84b7..e7d083ba244d1a2608d1d36409cbfd618f4c944b 100644
--- a/indra/llcharacter/llmotion.h
+++ b/indra/llcharacter/llmotion.h
@@ -100,6 +100,11 @@ class LLMotion
 
 	BOOL isBlending();
 
+	//BD - Eternal animations are never deactivated, never deprecated and never removed,
+	//     released or destroyed, they stay around until we allow them to be deprecated.
+	void setEternal(bool eternal) { mEternal = eternal; }
+	bool getEternal() const { return mEternal; }
+
 	// Activation functions.
 	// It is OK for other classes to activate a motion,
 	// but only the controller can deactivate it.
@@ -212,6 +217,9 @@ class LLMotion
 	S32					mInterpolationType;
 	F32					mInterpolationTime;
 	LLFrameTimer		mInterpolationTimer;
+
+	//BD - Eternal
+	bool				mEternal;
 	
 	F32 mActivationTimestamp;	// time when motion was activated
 	F32 mStopTimestamp;			// time when motion was told to stop
diff --git a/indra/llcharacter/llmotioncontroller.cpp b/indra/llcharacter/llmotioncontroller.cpp
index 87d038f924874ea5cc42054980cfac039de77955..b376301b352b8e86478f514054950bc2f0d3dcbf 100644
--- a/indra/llcharacter/llmotioncontroller.cpp
+++ b/indra/llcharacter/llmotioncontroller.cpp
@@ -186,7 +186,8 @@ void LLMotionController::purgeExcessMotions()
 		{
 			motion_set_t::iterator cur_iter = deprecated_motion_it++;
 			LLMotion* cur_motionp = *cur_iter;
-			if (!isMotionActive(cur_motionp))
+			//BD - Do not kill eternal animations.
+			if (!isMotionActive(cur_motionp) && !cur_motionp->getEternal())
 			{
 				// Motion is deprecated so we know it's not cannonical,
 				//  we can safely remove the instance
@@ -206,7 +207,8 @@ void LLMotionController::purgeExcessMotions()
 		for (LLMotion* cur_motionp : mLoadedMotions)
 		{
 			// motion isn't playing, delete it
-			if (!isMotionActive(cur_motionp))
+			//BD - Do not kill eternal animations.
+			if (!isMotionActive(cur_motionp) && !cur_motionp->getEternal())
 			{
 				motions_to_kill.push_back(cur_motionp->getID());
 			}
@@ -219,7 +221,8 @@ void LLMotionController::purgeExcessMotions()
 		// look up the motion again by ID to get canonical instance
 		// and kill it only if that one is inactive
 		LLMotion* motionp = findMotion(motion_id);
-		if (motionp && !isMotionActive(motionp))
+		//BD - Do not kill eternal animations.
+		if (motionp && !isMotionActive(motionp) && !motionp->getEternal())
 		{
 			removeMotion(motion_id);
 		}
@@ -858,7 +861,8 @@ void LLMotionController::updateMotions(bool force_update)
 		}
 		else
 		{
-			mAnimTime = llmax(mAnimTime, update_time);
+			//BD - Keep this, otherwise playing animations backwards doesn't work.
+			mAnimTime = update_time;
 		}
 	}
 
diff --git a/indra/llfilesystem/lldir.cpp b/indra/llfilesystem/lldir.cpp
index 0ef3d20513fabb09af7fdd17c8546b650999aeab..be38f1da81661dd7559b7bd4c5b46b7e31ead698 100644
--- a/indra/llfilesystem/lldir.cpp
+++ b/indra/llfilesystem/lldir.cpp
@@ -594,6 +594,11 @@ std::string LLDir::getExpandedFilename(ELLPath location, const std::string& subd
 	case LL_PATH_FONTS:
 		prefix = add(getAppRODataDir(), "fonts");
 		break;
+
+	case LL_PATH_POSES:
+		prefix = add(getOSUserAppDir(), "user_settings", "poses");
+		break;
+
 		
 	default:
 		llassert(0);
diff --git a/indra/llfilesystem/lldir.h b/indra/llfilesystem/lldir.h
index 8f58fdc12d91ca39652931eb807fb084de333e0d..25335645c6d08cddea41e61e011b7f22ee02e86c 100644
--- a/indra/llfilesystem/lldir.h
+++ b/indra/llfilesystem/lldir.h
@@ -49,6 +49,7 @@ typedef enum ELLPath
 	LL_PATH_DEFAULT_SKIN = 17,
 	LL_PATH_FONTS = 18,
     LL_PATH_DUMP = 19,
+	LL_PATH_POSES = 20,
 	LL_PATH_LAST
 } ELLPath;
 
diff --git a/indra/llui/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp
index daba536f979d75c2e6b6f9054bbf3369c0990a6a..5723e7781fbb1e6ab36b2d33bd08f3128d7d9b00 100644
--- a/indra/llui/llscrolllistctrl.cpp
+++ b/indra/llui/llscrolllistctrl.cpp
@@ -2128,7 +2128,7 @@ BOOL LLScrollListCtrl::handleRightMouseDown(S32 x, S32 y, MASK mask)
 		// check to see if we have a UUID for this row
 		std::string id = item->getValue().asString();
 		LLUUID uuid(id);
-		if (! uuid.isNull() && mContextMenuType != MENU_NONE)
+		if (! uuid.isNull() && mContextMenuType != MENU_NONE && mContextMenuType != MENU_EXTERNAL)
 		{
 			// set up the callbacks for all of the avatar/group menu items
 			// (N.B. callbacks don't take const refs as id is local scope)
@@ -2176,6 +2176,24 @@ BOOL LLScrollListCtrl::handleRightMouseDown(S32 x, S32 y, MASK mask)
 				return TRUE;
 			}
 		}
+		//BD - Right Click Context Menu
+		else if (mContextMenuType == MENU_EXTERNAL)
+		{
+			deselectAllItems(TRUE);
+			selectItem(item, getColumnIndexFromOffset(x));
+
+			auto menu = mPopupMenuHandle.get();
+			if (menu)
+			{
+				menu->show(x, y);
+				LLMenuGL::showPopup(this, menu, x, y);
+				return TRUE;
+			}
+			else
+			{
+				LL_WARNS() << "External menu is null!" << LL_ENDL;
+			}
+		}
 		return LLUICtrl::handleRightMouseDown(x, y, mask);
 	}
 	return FALSE;
@@ -3678,4 +3696,20 @@ bool LLScrollListCtrl::isFiltered(const LLScrollListItem* item) const
 		}
 	}
 	return false;
+}
+
+void LLScrollListCtrl::setContextMenu(const ContextMenuType& menu, LLContextMenu* new_menup/* = nullptr*/) 
+{
+	mContextMenuType = menu;
+	LLContextMenu* menup = static_cast<LLContextMenu*>(mPopupMenuHandle.get());
+	if (menup)
+	{
+		menup->die();
+		mPopupMenuHandle.markDead();
+	}
+
+	if (new_menup)
+	{
+		mPopupMenuHandle = new_menup->getHandle();
+	}
 }
\ No newline at end of file
diff --git a/indra/llui/llscrolllistctrl.h b/indra/llui/llscrolllistctrl.h
index de828a17ab6542b8e8a371d79a5b7e9267b41c04..8116f73b514192ebd2674eb45a72e51bb8656e94 100644
--- a/indra/llui/llscrolllistctrl.h
+++ b/indra/llui/llscrolllistctrl.h
@@ -353,8 +353,8 @@ class LLScrollListCtrl : public LLUICtrl, public LLEditMenuHandler,
 	bool			isFiltered(const LLScrollListItem* item) const;
 
 	// support right-click context menus for avatar/group lists
-	enum ContextMenuType { MENU_NONE, MENU_AVATAR, MENU_GROUP };
-	void setContextMenu(const ContextMenuType &menu) { mContextMenuType = menu; }
+	enum ContextMenuType { MENU_NONE, MENU_AVATAR, MENU_GROUP, MENU_EXTERNAL };
+	void setContextMenu(const ContextMenuType& menu, LLContextMenu* new_menup = nullptr);
     ContextMenuType getContextMenuType() { return mContextMenuType; }
 
 	// Overridden from LLView
diff --git a/indra/llui/lltabcontainer.cpp b/indra/llui/lltabcontainer.cpp
index 397f6108baa2208139437d22b81fc2bbf739e3c6..ab5a0f5ec5c804afd842077adc7cd2f7322be6d9 100644
--- a/indra/llui/lltabcontainer.cpp
+++ b/indra/llui/lltabcontainer.cpp
@@ -1341,6 +1341,16 @@ void LLTabContainer::enableTabButton(S32 which, BOOL enable)
 	mDragAndDropDelayTimer.stop();
 }
 
+//BD
+BOOL LLTabContainer::getTabButtonEnabled(S32 which)
+{
+	if (which >= 0 && which < (S32)mTabList.size())
+	{
+		return mTabList[which]->mButton->getEnabled();
+	}
+	return false;
+}
+
 void LLTabContainer::deleteAllTabs()
 {
 	// Remove all the tab buttons and delete them.  Also, unlink all the child panels.
diff --git a/indra/llui/lltabcontainer.h b/indra/llui/lltabcontainer.h
index 765b70401a326a3c8eb7068a7c131fdf639ef90c..d40c4331d6068ec3641f18fd84b878a92ed88976 100644
--- a/indra/llui/lltabcontainer.h
+++ b/indra/llui/lltabcontainer.h
@@ -193,6 +193,9 @@ class LLTabContainer : public LLPanel
     S32         getTotalTabWidth() const;
 	void		setCurrentTabName(const std::string& name);
 
+	//BD
+	BOOL 		getTabButtonEnabled(S32 which);
+
 	void		selectFirstTab();
 	void		selectLastTab();
 	void		selectNextTab();
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index 2fafdb63479911366aaff80f8b20d6faafbda7ea..2452432d7328e91c6a693a331ce548e946a7054c 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -110,6 +110,7 @@ set(viewer_SOURCE_FILES
     alviewermenu.cpp
     bdanimator.cpp
     bdfloaterposer.cpp
+    #bdfloaterposecreator.cpp
     bdposingmotion.cpp
     fslslpreproc.cpp
     fslslpreprocviewer.cpp
@@ -844,6 +845,7 @@ set(viewer_HEADER_FILES
     alviewermenu.h
     bdanimator.h
     bdfloaterposer.h
+    #bdfloaterposecreator.h
     bdposingmotion.h
     fslslpreproc.h
     fslslpreprocviewer.h
diff --git a/indra/newview/bdanimator.cpp b/indra/newview/bdanimator.cpp
index cb96480f3a09a43cba896236fc9484c0e0d861cc..f5c48a119424d25dd9b1b435b9028e368c408607 100644
--- a/indra/newview/bdanimator.cpp
+++ b/indra/newview/bdanimator.cpp
@@ -16,6 +16,8 @@
 
 #include "llviewerprecompiledheaders.h"
 
+#include "bdanimator.h"
+
 #include "lluictrlfactory.h"
 #include "llagent.h"
 #include "lldiriterator.h"
@@ -25,13 +27,25 @@
 #include "llviewerjointattachment.h"
 #include "llviewerjoint.h"
 #include "llvoavatarself.h"
+#include "llviewercontrol.h"
 
 #include "bdfloaterposer.h"
-#include "bdanimator.h"
 #include "bdposingmotion.h"
 
+#include "llagentcamera.h"
+
 BDAnimator gDragonAnimator;
 
+
+BDAnimator::BDAnimator() :
+			mPlaying(false)
+{
+}
+
+BDAnimator::~BDAnimator()
+{
+}
+
 void BDAnimator::update()
 {
 	//BD - Don't do anything if the animator is not activated.
@@ -238,7 +252,7 @@ BOOL BDAnimator::loadPose(const LLSD& name, S32 load_type)
 	std::string filename;
 	if (!name.asString().empty())
 	{
-		filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "poses", LLDir::escapePathString(name.asString()) + ".xml");
+		filename = gDirUtilp->getExpandedFilename(LL_PATH_POSES, LLDir::escapePathString(name.asString()) + ".xml");
 	}
 
 	LLSD pose;
@@ -259,84 +273,162 @@ BOOL BDAnimator::loadPose(const LLSD& name, S32 load_type)
 			return FALSE;
 		}
 
-		//BD - Not sure how to read the exact line out of a XML file, so we're just going
-		//     by the amount of tags here, since the header has only 3 it's a good indicator
-		//     if it's the correct line we're in.
-		BDPosingMotion* motion = (BDPosingMotion*)mTargetAvatar->findMotion(ANIM_BD_POSING_MOTION);
-		if (count == 3)
+		if (pose.has("version"))
 		{
-			if (motion)
+			for (LLSD::map_const_iterator itr = pose.beginMap(); itr != pose.endMap(); ++itr)
 			{
-				F32 time = pose["time"].asReal();
-				S32 type = pose["type"].asInteger();
-				motion->setInterpolationType(type);
-				motion->setInterpolationTime(time);
-				motion->startInterpolationTimer();
-			}
-		}
+				std::string const & name = itr->first;
+				LLSD const & control_map = itr->second;
+
+				//BD - Not sure how to read the exact line out of a XML file, so we're just going
+				//     by the amount of tags here, since the header has only 3 it's a good indicator
+				//     if it's the correct line we're in.
+				BDPosingMotion* motion = (BDPosingMotion*)mTargetAvatar->findMotion(ANIM_BD_POSING_MOTION);
+				if (motion)
+				{
+					F32 time = 0.f;
+					S32 type = 0;
+					if (control_map.has("time"))
+						time = control_map["time"].asReal();
+					if (control_map.has("type"))
+						type = control_map["type"].asInteger();
+					motion->setInterpolationType(type);
+					motion->setInterpolationTime(time);
+					motion->startInterpolationTimer();
+				}
 
-		LLJoint* joint = mTargetAvatar->getJoint(pose["bone"].asString());
-		if (joint)
-		{
-			//BD - Don't try to add/remove joint states for anything but our default bones.
-			if (motion && joint->getJointNum() < 134)
-			{
-				LLPose* mpose = motion->getPose();
-				if (mpose)
+				LLJoint* joint = mTargetAvatar->getJoint(name);
+				if (joint)
 				{
-					//BD - Fail safe, assume that a bone is always enabled in case we
-					//     load a pose that was created prior to including the enabled
-					//     state or for whatever reason end up not having an enabled state
-					//     written into the file.
-					bool state_enabled = true;
-
-					//BD - Check whether the joint state of the current joint has any enabled
-					//     status saved into the pose file or not.
-					if (pose["enabled"].isDefined())
+					//BD - Don't try to add/remove joint states for anything but our default bones.
+					if (motion && joint->getJointNum() < 134)
 					{
-						state_enabled = pose["enabled"].asBoolean();
+						LLPose* mpose = motion->getPose();
+						if (mpose)
+						{
+							//BD - Fail safe, assume that a bone is always enabled in case we
+							//     load a pose that was created prior to including the enabled
+							//     state or for whatever reason end up not having an enabled state
+							//     written into the file.
+							bool state_enabled = true;
+
+							//BD - Check whether the joint state of the current joint has any enabled
+							//     status saved into the pose file or not.
+							if (control_map.has("enabled"))
+							{
+								state_enabled = control_map["enabled"].asBoolean();
+							}
+
+							//BD - Add the joint state but only if it's not active yet.
+							//     Same goes for removing it, don't remove it if it doesn't exist.
+							LLPointer<LLJointState> joint_state = mpose->findJointState(joint);
+							if (!joint_state && state_enabled)
+							{
+								motion->addJointToState(joint);
+							}
+							else if (joint_state && !state_enabled)
+							{
+								motion->removeJointState(joint_state);
+							}
+						}
 					}
 
-					//BD - Add the joint state but only if it's not active yet.
-					//     Same goes for removing it, don't remove it if it doesn't exist.
-					LLPointer<LLJointState> joint_state = mpose->findJointState(joint);
-					if (!joint_state && state_enabled)
+					LLVector3 vec3;
+					if (load_type & ROTATIONS && control_map.has("rotation"))
 					{
-						motion->addJointToState(joint);
+						LLQuaternion quat;
+						LLQuaternion new_quat = joint->getRotation();
+
+						joint->setLastRotation(new_quat);
+						vec3.setValue(control_map["rotation"]);
+						quat.setEulerAngles(vec3.mV[VX], vec3.mV[VZ], vec3.mV[VY]);
+						joint->setTargetRotation(quat);
 					}
-					else if (joint_state && !state_enabled)
+
+					//BD - Position information is only ever written when it is actually safe to do.
+					//     It's safe to assume that IF information is available it's safe to apply.
+					if (load_type & POSITIONS && control_map.has("position"))
 					{
-						motion->removeJointState(joint_state);
+						vec3.setValue(control_map["position"]);
+						joint->setLastPosition(joint->getPosition());
+						joint->setTargetPosition(vec3);
+					}
+
+					//BD - Bone Scales
+					if (load_type & SCALES && control_map.has("scale"))
+					{
+						vec3.setValue(control_map["scale"]);
+						joint->setScale(vec3);
 					}
 				}
 			}
-
-			LLVector3 vec3;
-			if (load_type & ROTATIONS && pose["rotation"].isDefined())
+		}
+		else
+		{
+			LLJoint* joint = mTargetAvatar->getJoint(pose["bone"].asString());
+			if (joint)
 			{
-				LLQuaternion quat;
-				LLQuaternion new_quat = joint->getRotation();
+				BDPosingMotion* motion = (BDPosingMotion*)mTargetAvatar->findMotion(ANIM_BD_POSING_MOTION);
+				//BD - Don't try to add/remove joint states for anything but our default bones.
+				if (motion && joint->getJointNum() < 134)
+				{
+					LLPose* mpose = motion->getPose();
+					if (mpose)
+					{
+						//BD - Fail safe, assume that a bone is always enabled in case we
+						//     load a pose that was created prior to including the enabled
+						//     state or for whatever reason end up not having an enabled state
+						//     written into the file.
+						bool state_enabled = true;
+
+						//BD - Check whether the joint state of the current joint has any enabled
+						//     status saved into the pose file or not.
+						if (pose["enabled"].isDefined())
+						{
+							state_enabled = pose["enabled"].asBoolean();
+						}
+
+						//BD - Add the joint state but only if it's not active yet.
+						//     Same goes for removing it, don't remove it if it doesn't exist.
+						LLPointer<LLJointState> joint_state = mpose->findJointState(joint);
+						if (!joint_state && state_enabled)
+						{
+							motion->addJointToState(joint);
+						}
+						else if (joint_state && !state_enabled)
+						{
+							motion->removeJointState(joint_state);
+						}
+					}
+				}
 
-				joint->setLastRotation(new_quat);
-				vec3.setValue(pose["rotation"]);
-				quat.setEulerAngles(vec3.mV[VX], vec3.mV[VZ], vec3.mV[VY]);
-				joint->setTargetRotation(quat);
-			}
+				LLVector3 vec3;
+				if (load_type & ROTATIONS && pose["rotation"].isDefined())
+				{
+					LLQuaternion quat;
+					LLQuaternion new_quat = joint->getRotation();
 
-			//BD - Position information is only ever written when it is actually safe to do.
-			//     It's safe to assume that IF information is available it's safe to apply.
-			if (load_type & POSITIONS && pose["position"].isDefined())
-			{
-				vec3.setValue(pose["position"]);
-				joint->setLastPosition(joint->getPosition());
-				joint->setTargetPosition(vec3);
-			}
+					joint->setLastRotation(new_quat);
+					vec3.setValue(pose["rotation"]);
+					quat.setEulerAngles(vec3.mV[VX], vec3.mV[VZ], vec3.mV[VY]);
+					joint->setTargetRotation(quat);
+				}
 
-			//BD - Bone Scales
-			if (load_type & SCALES && pose["scale"].isDefined())
-			{
-				vec3.setValue(pose["scale"]);
-				joint->setScale(vec3);
+				//BD - Position information is only ever written when it is actually safe to do.
+				//     It's safe to assume that IF information is available it's safe to apply.
+				if (load_type & POSITIONS && pose["position"].isDefined())
+				{
+					vec3.setValue(pose["position"]);
+					joint->setLastPosition(joint->getPosition());
+					joint->setTargetPosition(vec3);
+				}
+
+				//BD - Bone Scales
+				if (load_type & SCALES && pose["scale"].isDefined())
+				{
+					vec3.setValue(pose["scale"]);
+					joint->setScale(vec3);
+				}
 			}
 		}
 	}
@@ -349,7 +441,7 @@ LLSD BDAnimator::returnPose(const LLSD& name)
 	std::string filename;
 	if (!name.asString().empty())
 	{
-		filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "poses", LLDir::escapePathString(name.asString()) + ".xml");
+		filename = gDirUtilp->getExpandedFilename(LL_PATH_POSES, LLDir::escapePathString(name.asString()) + ".xml");
 	}
 
 	LLSD pose;
@@ -358,17 +450,14 @@ LLSD BDAnimator::returnPose(const LLSD& name)
 	if (!infile.is_open())
 	{
 		LL_WARNS("Posing") << "Cannot find file in: " << filename << LL_ENDL;
-		//return;
 	}
 
 	for (S32 i = 0; !infile.eof(); ++i)
-	//while (!infile.eof())
 	{
 		S32 count = LLSDSerialize::fromXML(pose[i], infile);
 		if (count == LLSDParser::PARSE_FAILURE)
 		{
 			LL_WARNS("Posing") << "Failed to parse file: " << filename << LL_ENDL;
-			//return;
 		}
 	}
 	infile.close();
diff --git a/indra/newview/bdanimator.h b/indra/newview/bdanimator.h
index 4b6ce5fa24bd85758aa84f9d7ca64f18f33272b5..5e8b3519d9ddc539bea0309114c0dc15e53a334c 100644
--- a/indra/newview/bdanimator.h
+++ b/indra/newview/bdanimator.h
@@ -18,12 +18,11 @@
 #ifndef BD_ANIMATOR_H
 #define BD_ANIMATOR_H
 
-#include "llfloater.h"
-#include "llscrolllistctrl.h"
-#include "llsliderctrl.h"
-#include "llmultisliderctrl.h"
-#include "lltimectrl.h"
-#include "llkeyframemotion.h"
+#include "llsd.h"
+
+class LLKeyframeMotion;
+class LLScrollListItem;
+class LLVOAvatar;
 
 enum BD_EActionType
 {
@@ -43,37 +42,40 @@ enum BD_ELoadType
 class Action
 {
 public:
-	std::string		mPoseName;
-	BD_EActionType	mType = BD_EActionType::WAIT;
-	F32				mTime = 1.f;
+	std::string			mPoseName;
+	BD_EActionType		mType;
+	F32					mTime;
 };
 
 class BDAnimator
 {
 public:
 
-	BDAnimator() = default;
-	~BDAnimator() = default;
+	BDAnimator();
+	/*virtual*/	~BDAnimator();
 
-	void			onAddAction(LLVOAvatar* avatar, LLScrollListItem* item, S32 location);
-	void			onAddAction(LLVOAvatar* avatar, std::string name, BD_EActionType type, F32 time, S32 location);
-	void			onAddAction(LLVOAvatar* avatar, Action action, S32 location);
-	void			onDeleteAction(LLVOAvatar* avatar, S32 i);
+	void				onAddAction(LLVOAvatar* avatar, LLScrollListItem* item, S32 location);
+	void				onAddAction(LLVOAvatar* avatar, std::string name, BD_EActionType type, F32 time, S32 location);
+	void				onAddAction(LLVOAvatar* avatar, Action action, S32 location);
+	void				onDeleteAction(LLVOAvatar* avatar, S32 i);
 
-	BOOL			loadPose(const LLSD& name, S32 load_type = 3);
-	LLSD			returnPose(const LLSD& name);
+	BOOL				loadPose(const LLSD& name, S32 load_type = 3);
+	LLSD				returnPose(const LLSD& name);
 
-	void			update();
-	void			startPlayback();
-	void			stopPlayback();
+	void				update();
+	void				startPlayback();
+	void				stopPlayback();
 
 	//BD - Animesh Support
-	LLVOAvatar*						mTargetAvatar = nullptr;
+	LLVOAvatar*						mTargetAvatar;
 
 	std::vector<LLVOAvatar*>		mAvatarsList;
 
-	bool			getIsPlaying() { return mPlaying; }
-	bool			mPlaying = false;
+	bool				getIsPlaying() { return mPlaying; }
+	bool				mPlaying;
+
+	//BD - Animator
+	LLKeyframeMotion*	mPoseCreatorMotion;
 };
 
 extern BDAnimator gDragonAnimator;
diff --git a/indra/newview/bdfloaterposecreator.cpp b/indra/newview/bdfloaterposecreator.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..da88b21154a7c3754f74de88dad7f5361e85309b
--- /dev/null
+++ b/indra/newview/bdfloaterposecreator.cpp
@@ -0,0 +1,3128 @@
+/**
+*
+* Copyright (C) 2018, NiranV Dean
+*
+* 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.
+*
+*/
+
+#include "llviewerprecompiledheaders.h"
+
+#include "bdfloaterposecreator.h"
+#include "lluictrlfactory.h"
+#include "llagent.h"
+#include "llavatarname.h"
+#include "llavatarnamecache.h"
+#include "llclipboard.h"
+#include "lldatapacker.h"
+#include "lldiriterator.h"
+#include "llfilepicker.h"
+#include "llfilesystem.h"
+#include "llkeyframemotion.h"
+#include "llnotificationsutil.h"
+#include "llmenugl.h"
+#include "llmenubutton.h"
+#include "lltoggleablemenu.h"
+#include "llviewermenu.h"
+#include "llsdserialize.h"
+#include "llsdutil.h"
+#include "llviewerjointattachment.h"
+#include "llviewerjoint.h"
+#include "llvoavatarself.h"
+#include "llwindowwin32.h"
+#include "pipeline.h"
+
+#include "llviewerobjectlist.h"
+#include "lldrawpoolavatar.h"
+
+//BD - Animesh Support
+#include "llcontrolavatar.h"
+
+//BD - Black Dragon specifics
+#include "bdanimator.h"
+#include "bdfunctions.h"
+#include "bdposingmotion.h"
+#include "bdstatus.h"
+#include "bdfloaterposer.h"
+
+//BD - 1 frame is always 1/60 of a second, we assume animations run at 60 FPS by default.
+//     I might add an option to change it but right now its all manual work.
+const F32 FRAMETIME = 1.f / 60.f;
+
+
+BDFloaterPoseCreator::BDFloaterPoseCreator(const LLSD& key)
+	:	LLFloater(key),
+	mJointScrolls()
+{
+	//BD - Save our current pose as XML or ANIM file to be used or uploaded later.
+	mCommitCallbackRegistrar.add("Pose.Save", boost::bind(&BDFloaterPoseCreator::onClickPoseSave, this, _2));
+	//BD - Start our custom pose.
+	mCommitCallbackRegistrar.add("Pose.Start", boost::bind(&BDFloaterPoseCreator::onPoseStart, this));
+	//BD - Import ANIM file to the poser.
+	mCommitCallbackRegistrar.add("Pose.Import", boost::bind(&BDFloaterPoseCreator::onPoseImport, this));
+	//BD - Start/Stop the pose to preview it.
+	mCommitCallbackRegistrar.add("Pose.StartStop", boost::bind(&BDFloaterPoseCreator::onPoseStartStop, this));
+	//BD - Multipurpose function to edit several basic parameters in the animation.
+	mCommitCallbackRegistrar.add("Pose.EditInfo", boost::bind(&BDFloaterPoseCreator::onEditAnimationInfo, this, _2));
+	//BD - Multipurpose function to edit several basic parameters in the animation.
+	mCommitCallbackRegistrar.add("Pose.Interpolation", boost::bind(&BDFloaterPoseCreator::onInterpolationChange, this, _1));
+
+	//BD - Add a keyframe to the motion curve.
+	mCommitCallbackRegistrar.add("Keyframe.Add", boost::bind(&BDFloaterPoseCreator::onKeyframeAdd, this));
+	//BD - Remove a keyframe from the motion curve.
+	mCommitCallbackRegistrar.add("Keyframe.Remove", boost::bind(&BDFloaterPoseCreator::onKeyframeRemove, this));
+	//BD - Change a keyframe's time value.
+	mCommitCallbackRegistrar.add("Keyframe.Time", boost::bind(&BDFloaterPoseCreator::onKeyframeTime, this));
+	//BD - Refresh the keyframe scroll list and fill it with all relevant keys.
+	mCommitCallbackRegistrar.add("Keyframe.Refresh", boost::bind(&BDFloaterPoseCreator::onKeyframeRefresh, this));
+
+	//BD - Change a bone's rotation.
+	mCommitCallbackRegistrar.add("Joint.Set", boost::bind(&BDFloaterPoseCreator::onJointSet, this, _1, _2));
+	//BD - Change a bone's position.
+	mCommitCallbackRegistrar.add("Joint.PosSet", boost::bind(&BDFloaterPoseCreator::onJointPosSet, this, _1, _2));
+	//BD - Change a bone's scale.
+	mCommitCallbackRegistrar.add("Joint.SetScale", boost::bind(&BDFloaterPoseCreator::onJointScaleSet, this, _1, _2));
+	//BD - Add or remove a joint state to or from the pose (enable/disable our overrides).
+	mCommitCallbackRegistrar.add("Joint.ChangeState", boost::bind(&BDFloaterPoseCreator::onJointChangeState, this));
+	//BD - Reset all selected bone rotations and positions.
+	mCommitCallbackRegistrar.add("Joint.ResetJointFull", boost::bind(&BDFloaterPoseCreator::onJointRotPosScaleReset, this));
+	//BD - Reset all selected bone rotations back to 0,0,0.
+	mCommitCallbackRegistrar.add("Joint.ResetJointRotation", boost::bind(&BDFloaterPoseCreator::onJointRotationReset, this));
+	//BD - Reset all selected bones positions back to their default.
+	mCommitCallbackRegistrar.add("Joint.ResetJointPosition", boost::bind(&BDFloaterPoseCreator::onJointPositionReset, this));
+	//BD - Reset all selected bones scales back to their default.
+	mCommitCallbackRegistrar.add("Joint.ResetJointScale", boost::bind(&BDFloaterPoseCreator::onJointScaleReset, this));
+	//BD - Reset all selected bone rotations back to the initial rotation.
+	mCommitCallbackRegistrar.add("Joint.RevertJointRotation", boost::bind(&BDFloaterPoseCreator::onJointRotationRevert, this));
+	//BD - Mirror the current bone's rotation to match what the other body side's rotation should be.
+	mCommitCallbackRegistrar.add("Joint.Mirror", boost::bind(&BDFloaterPoseCreator::onJointMirror, this));
+	//BD - Copy and mirror the other body side's bone rotation.
+	mCommitCallbackRegistrar.add("Joint.Symmetrize", boost::bind(&BDFloaterPoseCreator::onJointSymmetrize, this));
+
+	//BD - Toggle Mirror Mode on/off.
+	mCommitCallbackRegistrar.add("Joint.ToggleMirror", boost::bind(&BDFloaterPoseCreator::toggleMirrorMode, this, _1));
+	//BD - Toggle Easy Rotation on/off.
+	mCommitCallbackRegistrar.add("Joint.EasyRotations", boost::bind(&BDFloaterPoseCreator::toggleEasyRotations, this, _1));
+	//BD - Flip pose (mirror).
+	mCommitCallbackRegistrar.add("Joint.FlipPose", boost::bind(&BDFloaterPoseCreator::onFlipPose, this));
+}
+
+BDFloaterPoseCreator::~BDFloaterPoseCreator()
+{
+}
+
+BOOL BDFloaterPoseCreator::postBuild()
+{
+	//BD - Posing
+	mJointScrolls = { { this->getChild<LLScrollListCtrl>("joints_scroll", true),
+						this->getChild<LLScrollListCtrl>("cv_scroll", true),
+						this->getChild<LLScrollListCtrl>("attach_scroll", true) } };
+
+	mJointScrolls[JOINTS]->setCommitOnSelectionChange(TRUE);
+	mJointScrolls[JOINTS]->setCommitCallback(boost::bind(&BDFloaterPoseCreator::onKeyframeRefresh, this));
+	mJointScrolls[JOINTS]->setDoubleClickCallback(boost::bind(&BDFloaterPoseCreator::onJointChangeState, this));
+
+	//BD - Collision Volumes
+	mJointScrolls[COLLISION_VOLUMES]->setCommitOnSelectionChange(TRUE);
+	mJointScrolls[COLLISION_VOLUMES]->setCommitCallback(boost::bind(&BDFloaterPoseCreator::onKeyframeRefresh, this));
+
+	//BD - Attachment Bones
+	mJointScrolls[ATTACHMENT_BONES]->setCommitOnSelectionChange(TRUE);
+	mJointScrolls[ATTACHMENT_BONES]->setCommitCallback(boost::bind(&BDFloaterPoseCreator::onKeyframeRefresh, this));
+
+	mKeyframeScroll = this->getChild<LLScrollListCtrl>("keyframe_scroll", true);
+	mKeyframeScroll->setCommitOnSelectionChange(TRUE);
+	mKeyframeScroll->setCommitCallback(boost::bind(&BDFloaterPoseCreator::onKeyframeSelect, this));
+
+	mTimelineScroll = this->getChild<LLScrollListCtrl>("timeframe_scroll", true);
+
+	mRotationSliders = { { getChild<LLUICtrl>("Rotation_X"), getChild<LLUICtrl>("Rotation_Y"), getChild<LLUICtrl>("Rotation_Z") } };
+	mPositionSliders = { { getChild<LLSliderCtrl>("Position_X"), getChild<LLSliderCtrl>("Position_Y"), getChild<LLSliderCtrl>("Position_Z") } };
+	mScaleSliders = { { getChild<LLSliderCtrl>("Scale_X"), getChild<LLSliderCtrl>("Scale_Y"), getChild<LLSliderCtrl>("Scale_Z") } };
+
+	mJointTabs = getChild<LLTabContainer>("joints_tabs");
+	mJointTabs->setCommitCallback(boost::bind(&BDFloaterPoseCreator::onJointControlsRefresh, this));
+
+	mModifierTabs = getChild<LLTabContainer>("modifier_tabs");
+	mModifierTabs->setCommitCallback(boost::bind(&BDFloaterPoseCreator::onKeyframeRefresh, this));
+
+	//BD - Misc
+	mDelayRefresh = false;
+
+	mMirrorMode = false;
+	mEasyRotations = true;
+
+	mAutoDuration = true;
+
+	mStartPosingBtn = getChild<LLButton>("activate");
+
+	//BD - Poser Menu
+	LLUICtrl::CommitCallbackRegistry::ScopedRegistrar pose_reg;
+	LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar;
+
+	//BD - Poser Right Click Menu
+	pose_reg.add("Joints.Menu", boost::bind(&BDFloaterPoseCreator::onJointContextMenuAction, this, _2));
+	enable_registrar.add("Joints.OnEnable", boost::bind(&BDFloaterPoseCreator::onJointContextMenuEnable, this, _2));
+	LLContextMenu* joint_menu = LLUICtrlFactory::getInstance()->createFromFile<LLContextMenu>("menu_poser_joints.xml",
+		gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
+	//mJointScrolls[JOINTS]->setContextMenu(joint_menu);
+
+	return TRUE;
+}
+
+void BDFloaterPoseCreator::draw()
+{
+	LLFloater::draw();
+}
+
+void BDFloaterPoseCreator::onOpen(const LLSD& key)
+{
+	//BD - Check whether we should delay the default value collection or fire it immediately.
+	mDelayRefresh = !gAgentAvatarp->isFullyLoaded();
+	if (!mDelayRefresh)
+	{
+		onCollectDefaults();
+	}
+
+	onJointRotPosScaleReset();
+	onJointRefresh();
+}
+
+void BDFloaterPoseCreator::onClose(bool app_quitting)
+{
+	//BD - Doesn't matter because we destroy the window and rebuild it every time we open it anyway.
+	mJointScrolls[JOINTS]->clearRows();
+	mJointScrolls[COLLISION_VOLUMES]->clearRows();
+	mJointScrolls[ATTACHMENT_BONES]->clearRows();
+}
+
+////////////////////////////////
+//BD - Keyframes
+////////////////////////////////
+void BDFloaterPoseCreator::onKeyframesRebuild()
+{
+	if (!gDragonAnimator.mPoseCreatorMotion)
+		return;
+
+	//BD - Now write an entry with all given information into our list so we can use it.
+	LLKeyframeMotion::JointMotionList* joint_list = gDragonAnimator.mPoseCreatorMotion->getJointMotionList();
+	if (!joint_list)
+		return;
+
+	mKeyframeScroll->clearRows();
+	mTimelineScroll->clearRows();
+
+	for(S32 joint_idx = 0; joint_idx < joint_list->getNumJointMotions(); joint_idx++)
+	{
+		LLKeyframeMotion::JointMotion* joint_motion = joint_list->getJointMotion(joint_idx);
+		if (!joint_motion)
+			return;
+
+		LLKeyframeMotion::RotationCurve rot_curve = joint_motion->mRotationCurve;
+		LLKeyframeMotion::PositionCurve pos_curve = joint_motion->mPositionCurve;
+		LLKeyframeMotion::ScaleCurve scale_curve = joint_motion->mScaleCurve;
+
+		LLScrollListMultiSlider* col_slider = nullptr;
+		if (!rot_curve.mKeys.empty()
+			|| !pos_curve.mKeys.empty()
+			|| !scale_curve.mKeys.empty())
+		{
+			LLSD slider;
+			const std::string joint_name = joint_motion->mJointName;
+			slider["columns"][0]["column"] = "joint";
+			slider["columns"][0]["value"] = joint_name;
+			slider["columns"][1]["column"] = "multislider";
+			slider["columns"][1]["type"] = "multislider";
+			slider["columns"][1]["min_value"] = 0;
+			slider["columns"][1]["max_value"] = 3600;
+			slider["columns"][1]["increment"] = 1.f;
+			LLScrollListItem* element = mTimelineScroll->addElement(slider);
+			col_slider = (LLScrollListMultiSlider*)element->getColumn(1);
+		}
+
+		for (auto& rot_key : rot_curve.mKeys)
+		{
+			F32 roll, pitch, yaw;
+			LLQuaternion rot_quat = rot_key.second.mRotation;
+			rot_quat.getEulerAngles(&roll, &pitch, &yaw);
+
+			S32 time_frames = (S32)ll_round(rot_key.second.mTime / FRAMETIME, 1.f);
+
+			if (col_slider)
+			{
+				//F32 time_key = time_frames / col_slider->getMaxValue();
+				col_slider->addKeyframe(time_frames, (LLSD)rot_key.first);
+			}
+		}
+
+		for (auto& pos_key : pos_curve.mKeys)
+		{
+			LLVector3 pos = pos_key.second.mPosition;
+			S32 time_frames = (S32)ll_round(pos_key.second.mTime / FRAMETIME, 1.f);
+
+			if (col_slider)
+			{
+				//F32 time_key = time_frames / col_slider->getMaxValue();
+				col_slider->addKeyframe(time_frames, (LLSD)pos_key.first);
+			}
+		}
+
+		for (auto& scale_key : scale_curve.mKeys)
+		{
+			LLVector3 scale = scale_key.second.mScale;
+			S32 time_frames = (S32)ll_round(scale_key.second.mTime / FRAMETIME, 1.f);
+
+			if (col_slider)
+			{
+				//F32 time_key = time_frames / col_slider->getMaxValue();
+				col_slider->addKeyframe(time_frames, (LLSD)scale_key.first);
+			}
+		}
+	}
+}
+
+void BDFloaterPoseCreator::onKeyframeRefresh()
+{
+	S32 idx = mJointTabs->getCurrentPanelIndex();
+	LLScrollListItem* item = mJointScrolls[idx]->getFirstSelected();
+	if (!item)
+		return;
+
+	if (!item->getEnabled())
+		return;
+
+	LLJoint* joint = (LLJoint*)item->getUserdata();
+	if (!joint)
+		return;
+
+	if (!gDragonAnimator.mPoseCreatorMotion)
+		return;
+
+	//BD - Now write an entry with all given information into our list so we can use it.
+	LLKeyframeMotion::JointMotionList* joint_list = gDragonAnimator.mPoseCreatorMotion->getJointMotionList();
+	if (!joint_list)
+		return;
+
+	LLKeyframeMotion::JointMotion* joint_motion = joint_list->getJointMotion(joint->getJointNum());
+	if (!joint_motion)
+		return;
+
+	mKeyframeScroll->clearRows();
+
+	LLKeyframeMotion::RotationCurve rot_curve = joint_motion->mRotationCurve;
+	LLKeyframeMotion::PositionCurve pos_curve = joint_motion->mPositionCurve;
+	LLKeyframeMotion::ScaleCurve scale_curve = joint_motion->mScaleCurve;
+
+	LLScrollListMultiSlider* col_slider = nullptr;
+	if (!rot_curve.mKeys.empty() 
+		|| !pos_curve.mKeys.empty() 
+		|| !scale_curve.mKeys.empty())
+	{
+		//BD - Find and delete the current entry.
+		for (auto timeline_item : mTimelineScroll->getAllData())
+		{
+			std::string name = timeline_item->getColumn(0)->getValue();
+			if (name == joint->getName())
+			{
+				timeline_item->setFlagged(true);
+				break;
+			}
+		}
+		mTimelineScroll->deleteFlaggedItems();
+
+		//BD - Now rewrite the entry.
+		LLSD slider;
+		const std::string joint_name = joint->getName();
+		slider["columns"][0]["column"] = "joint";
+		slider["columns"][0]["value"] = joint_name;
+		slider["columns"][1]["column"] = "multislider";
+		slider["columns"][1]["type"] = "multislider";
+		slider["columns"][1]["min_value"] = 0;
+		slider["columns"][1]["max_value"] = 3600;
+		slider["columns"][1]["increment"] = 1.f;
+		LLScrollListItem* element = mTimelineScroll->addElement(slider);
+		col_slider = (LLScrollListMultiSlider*)element->getColumn(1);
+	}
+
+	S32 modidx = mModifierTabs->getCurrentPanelIndex();
+	if (modidx == 0)
+	{
+		for (auto& rot_key : rot_curve.mKeys)
+		{
+			F32 roll, pitch, yaw;
+			LLQuaternion rot_quat = rot_key.second.mRotation;
+			rot_quat.getEulerAngles(&roll, &pitch, &yaw);
+
+			S32 time_frames = (S32)ll_round(rot_key.second.mTime / FRAMETIME, 1.f);
+
+			LLSD row;
+			row["columns"][0]["column"] = "time";
+			row["columns"][0]["value"] = ll_round(time_frames, 1.f);
+			row["columns"][1]["column"] = "value";
+			row["columns"][1]["value"] = rot_key.first;
+			row["columns"][2]["column"] = "x";
+			row["columns"][2]["value"] = ll_round(roll, 0.001f);
+			row["columns"][3]["column"] = "y";
+			row["columns"][3]["value"] = ll_round(pitch, 0.001f);
+			row["columns"][4]["column"] = "z";
+			row["columns"][4]["value"] = ll_round(yaw, 0.001f);
+			mKeyframeScroll->addElement(row);
+
+			if (col_slider)
+			{
+				//F32 time_key = time_frames / col_slider->getMaxValue();
+				col_slider->addKeyframe(time_frames, (LLSD)rot_key.first);
+			}
+		}
+	}
+	else if (modidx == 1)
+	{
+		for (auto& pos_key : pos_curve.mKeys)
+		{
+			LLVector3 pos = pos_key.second.mPosition;
+			S32 time_frames = (S32)ll_round(pos_key.second.mTime / FRAMETIME, 1.f);
+			LLSD row;
+			row["columns"][0]["column"] = "time";
+			row["columns"][0]["value"] = ll_round(time_frames, 1.f);
+			row["columns"][1]["column"] = "value";
+			row["columns"][1]["value"] = pos_key.first;
+			row["columns"][2]["column"] = "x";
+			row["columns"][2]["value"] = ll_round(pos.mV[VX], 0.001f);
+			row["columns"][3]["column"] = "y";
+			row["columns"][3]["value"] = ll_round(pos.mV[VY], 0.001f);
+			row["columns"][4]["column"] = "z";
+			row["columns"][4]["value"] = ll_round(pos.mV[VZ], 0.001f);
+			mKeyframeScroll->addElement(row);
+
+			if (col_slider)
+			{
+				//F32 time_key = time_frames / col_slider->getMaxValue();
+				col_slider->addKeyframe(time_frames, (LLSD)pos_key.first);
+			}
+		}
+	}
+	else if (modidx == 2)
+	{
+		for (auto& scale_key : scale_curve.mKeys)
+		{
+			LLVector3 scale = scale_key.second.mScale;
+			S32 time_frames = (S32)ll_round(scale_key.second.mTime / FRAMETIME, 1.f);
+			LLSD row;
+			row["columns"][0]["column"] = "time";
+			row["columns"][0]["value"] = ll_round(time_frames, 1.f);
+			row["columns"][1]["column"] = "value";
+			row["columns"][1]["value"] = scale_key.first;
+			row["columns"][2]["column"] = "x";
+			row["columns"][2]["value"] = ll_round(scale.mV[VX], 0.001f);
+			row["columns"][3]["column"] = "y";
+			row["columns"][3]["value"] = ll_round(scale.mV[VY], 0.001f);
+			row["columns"][4]["column"] = "z";
+			row["columns"][4]["value"] = ll_round(scale.mV[VZ], 0.001f);
+			mKeyframeScroll->addElement(row);
+
+			if (col_slider)
+			{
+				//F32 time_key = time_frames / col_slider->getMaxValue();
+				col_slider->addKeyframe(time_frames, (LLSD)scale_key.first);
+			}
+		}
+	}
+
+	//BD - Always select the last entry whenever we switch bones to allow quickly making
+	//     changes or adding new keyframes.
+	mKeyframeScroll->selectNthItem(mKeyframeScroll->getItemCount() - 1);
+
+	onJointControlsRefresh();
+	onKeyframeSelect();
+}
+
+void BDFloaterPoseCreator::onKeyframeSelect()
+{
+	LLScrollListItem* item = mKeyframeScroll->getFirstSelected();
+	if (!item)
+		return;
+
+	LLScrollListItem* joint_item = mJointScrolls[JOINTS]->getFirstSelected();
+	if (!joint_item)
+		return;
+
+	LLJoint* joint = (LLJoint*)joint_item->getUserdata();
+	if (!joint)
+		return;
+
+	if (!gDragonAnimator.mPoseCreatorMotion)
+		return;
+
+	LLKeyframeMotion::JointMotionList* joint_list = gDragonAnimator.mPoseCreatorMotion->getJointMotionList();
+	LLKeyframeMotion::JointMotion* joint_motion = joint_list->getJointMotion(joint->getJointNum());
+	S32 si = item->getColumn(1)->getValue().asInteger();
+	S32 idx = mModifierTabs->getCurrentPanelIndex();
+
+	if (idx == 0)
+	{
+		//BD - Find the key we are looking for and remove it.
+		for (auto& it : joint_motion->mRotationCurve.mKeys)
+		{
+			if ((S32)it.first == si)
+			{
+				LLQuaternion rot_quat = it.second.mRotation;
+				LLVector3 rot_vec;
+				rot_quat.getEulerAngles(&rot_vec.mV[VX], &rot_vec.mV[VY], &rot_vec.mV[VZ]);
+				mRotationSliders[VX]->setValue(rot_vec.mV[VX]);
+				mRotationSliders[VY]->setValue(rot_vec.mV[VY]);
+				mRotationSliders[VZ]->setValue(rot_vec.mV[VZ]);
+				joint->setTargetRotation(rot_quat);
+			}
+		}
+	}
+	else if (idx == 1)
+	{
+		for (auto& it : joint_motion->mPositionCurve.mKeys)
+		{
+			if ((S32)it.first == si)
+			{
+				LLVector3 pos = it.second.mPosition;
+				mPositionSliders[VX]->setValue(pos.mV[VX]);
+				mPositionSliders[VY]->setValue(pos.mV[VY]);
+				mPositionSliders[VZ]->setValue(pos.mV[VZ]);
+				joint->setTargetPosition(pos);
+			}
+		}
+	}
+	else if (idx == 2)
+	{
+		for (auto& it : joint_motion->mScaleCurve.mKeys)
+		{
+			if ((S32)it.first == si)
+			{
+				LLVector3 scale = it.second.mScale;
+				mScaleSliders[VX]->setValue(scale.mV[VX]);
+				mScaleSliders[VY]->setValue(scale.mV[VY]);
+				mScaleSliders[VZ]->setValue(scale.mV[VZ]);
+				joint->setScale(scale);
+			}
+		}
+	}
+}
+
+void BDFloaterPoseCreator::onKeyframeAdd(F32 time, LLJoint* joint)
+{
+	LLKeyframeMotion::JointMotionList* joint_list = gDragonAnimator.mPoseCreatorMotion->getJointMotionList();
+	S32 modifier_idx = mModifierTabs->getCurrentPanelIndex();
+
+	if (joint)
+	{
+		LLKeyframeMotion::JointMotion* joint_motion = joint_list->getJointMotion(joint->getJointNum());
+
+		if (modifier_idx == 0)
+		{
+			//BD - Create a rotation key and put it into the rotation curve.
+			LLKeyframeMotion::RotationKey rotation_key = LLKeyframeMotion::RotationKey(ll_round(time, 0.001f), joint->getTargetRotation());
+
+			//BD - Increase the key number since we are adding another key.
+			joint_motion->mRotationCurve.mNumKeys++;
+
+			joint_motion->mRotationCurve.mKeys[joint_motion->mRotationCurve.mNumKeys] = rotation_key;
+
+			LLJointState* joint_state = gDragonAnimator.mPoseCreatorMotion->findJointState(joint);
+			if (joint_state->getUsage() ^ LLJointState::ROT)
+			{
+				joint_state->setUsage(joint_state->getUsage() | LLJointState::ROT);
+			}
+		}
+		else if (modifier_idx == 1)
+		{
+			//BD - Create a position key and put it into the position curve.
+			LLKeyframeMotion::PositionKey position_key = LLKeyframeMotion::PositionKey(ll_round(time, 0.001f), joint->getTargetPosition());
+
+			//BD - Increase the key number since we are adding another key.
+			joint_motion->mPositionCurve.mNumKeys++;
+
+			joint_motion->mPositionCurve.mKeys[joint_motion->mPositionCurve.mNumKeys] = position_key;
+
+			LLJointState* joint_state = gDragonAnimator.mPoseCreatorMotion->findJointState(joint);
+			if (joint_state->getUsage() ^ LLJointState::POS)
+			{
+				joint_state->setUsage(joint_state->getUsage() | LLJointState::POS);
+			}
+		}
+		else if (modifier_idx == 2)
+		{
+			//BD - Create a scale key and put it into the scale curve.
+			LLKeyframeMotion::ScaleKey scale_key = LLKeyframeMotion::ScaleKey(ll_round(time, 0.001f), joint->getScale());
+
+			//BD - Increase the key number since we are adding another key.
+			joint_motion->mScaleCurve.mNumKeys++;
+
+			joint_motion->mScaleCurve.mKeys[joint_motion->mScaleCurve.mNumKeys] = scale_key;
+
+			LLJointState* joint_state = gDragonAnimator.mPoseCreatorMotion->findJointState(joint);
+			if (joint_state->getUsage() ^ LLJointState::SCALE)
+			{
+				joint_state->setUsage(joint_state->getUsage() | LLJointState::SCALE);
+			}
+		}
+	}
+
+	//BD - Check if our animation duration needs changing.
+	onAnimationDurationCheck();
+}
+
+void BDFloaterPoseCreator::onKeyframeAdd()
+{
+	std::vector<LLScrollListItem*> items = mJointScrolls[JOINTS]->getAllSelected();
+	LLKeyframeMotion::JointMotionList* joint_list = gDragonAnimator.mPoseCreatorMotion->getJointMotionList();
+	LLScrollListItem* item = mKeyframeScroll->getFirstSelected();
+	MASK mask = gKeyboard->currentMask(TRUE);
+	bool multiple = (items.size() > 1);
+	S32 new_selected_idx = (mask == MASK_SHIFT || multiple) ? mKeyframeScroll->getChildCount() - 1 : mKeyframeScroll->getFirstSelectedIndex() + 1;
+	S32 modifier_idx = mModifierTabs->getCurrentPanelIndex();
+
+
+	for (auto joint_item : items)
+	{
+		LLJoint* joint = (LLJoint*)joint_item->getUserdata();
+		if (!joint)
+			continue;
+
+		LLKeyframeMotion::JointMotion* joint_motion = joint_list->getJointMotion(joint->getJointNum());
+		S32 selected_idx = 0;
+		S32 count = 1;
+		S32 keys = 1;
+		F32 time = FRAMETIME;
+		if (item)
+			selected_idx = item->getColumn(1)->getValue().asInteger();
+
+		if (modifier_idx == 0)
+		{
+			//BD - Put the key at the end of the list if SHIFT is held while pressing the add key button.
+			if (multiple || joint_motion->mRotationCurve.mNumKeys == 0 || gKeyboard->currentMask(TRUE) == MASK_SHIFT)
+			{
+				//BD - Add a bit of time to differentiate the keys.
+				//     This also kinda determines if this is the first keyframe or not. If we can't find a previous
+				//     entry we assume its the first keyframe, so simply use 1.f.
+				if (joint_motion->mRotationCurve.mNumKeys > 0)
+					time = joint_motion->mRotationCurve.mKeys[joint_motion->mRotationCurve.mNumKeys].mTime + FRAMETIME;
+
+				//BD - Create a rotation key and put it into the rotation curve.
+				LLKeyframeMotion::RotationKey rotation_key = LLKeyframeMotion::RotationKey(ll_round(time, 0.001f), joint->getTargetRotation());
+
+				//BD - Increase the key number since we are adding another key.
+				joint_motion->mRotationCurve.mNumKeys++;
+
+				joint_motion->mRotationCurve.mKeys[joint_motion->mRotationCurve.mNumKeys] = rotation_key;
+			}
+			//BD - Conversely put the key at the start of the list if CTRL is held while adding a key.
+			else if (gKeyboard->currentMask(TRUE) == MASK_CONTROL)
+			{
+				LLKeyframeMotion::RotationCurve rot_curve;
+
+				//BD - Create a rotation key and put it into the rotation curve.
+				LLKeyframeMotion::RotationKey rotation_key = LLKeyframeMotion::RotationKey(ll_round(time, 0.001f), joint->getTargetRotation());
+
+				rot_curve.mNumKeys++;
+				rot_curve.mKeys[count] = rotation_key;
+				count++;
+
+				//BD - We have to rewrite the entire rotation curve in order to push a key to the beginning.
+				for (auto& it : joint_motion->mRotationCurve.mKeys)
+				{
+					rot_curve.mNumKeys++;
+					rot_curve.mKeys[count] = it.second;
+					count++;
+				}
+
+				joint_motion->mRotationCurve = rot_curve;
+			}
+			//BD - Put the key after whatever is currently selected.
+			else
+			{
+				LLKeyframeMotion::RotationCurve rot_curve;
+
+				//BD - We have to rewrite the entire rotation curve in order to push a key inbetween others.
+				for (auto& it : joint_motion->mRotationCurve.mKeys)
+				{
+					rot_curve.mNumKeys++;
+					rot_curve.mKeys[count] = it.second;
+					count++;
+
+					if ((S32)it.first == selected_idx)
+					{
+						//BD - Add a bit of time to differentiate the keys.
+						//     This also kinda determines if this is the first keyframe or not. If we can't find a previous
+						//     entry we assume its the first keyframe, so simply use 1.f.
+						time = it.second.mTime + FRAMETIME;
+
+						//BD - Create a rotation key and put it into the rotation curve.
+						LLKeyframeMotion::RotationKey rotation_key = LLKeyframeMotion::RotationKey(ll_round(time, 0.001f), joint->getTargetRotation());
+
+						rot_curve.mNumKeys++;
+						rot_curve.mKeys[count] = rotation_key;
+						count++;
+					}
+				}
+
+				joint_motion->mRotationCurve = rot_curve;
+			}
+
+			keys = joint_motion->mRotationCurve.mNumKeys;
+
+			LLJointState* joint_state = gDragonAnimator.mPoseCreatorMotion->findJointState(joint);
+			if (joint_state->getUsage() ^ LLJointState::ROT)
+			{
+				joint_state->setUsage(joint_state->getUsage() | LLJointState::ROT);
+			}
+		}
+		else if (modifier_idx == 1)
+		{
+			//BD - Put the key at the end of the list if SHIFT is held while pressing the add key button.
+			if (multiple || joint_motion->mPositionCurve.mNumKeys == 0 || gKeyboard->currentMask(TRUE) == MASK_SHIFT)
+			{
+				//BD - Add a bit of time to differentiate the keys.
+				//     This also kinda determines if this is the first keyframe or not. If we can't find a previous
+				//     entry we assume its the first keyframe, so simply use 1.f.
+				if (joint_motion->mPositionCurve.mNumKeys > 0)
+					time = joint_motion->mPositionCurve.mKeys[joint_motion->mPositionCurve.mNumKeys].mTime + FRAMETIME;
+
+				//BD - Create a position key and put it into the position curve.
+				LLKeyframeMotion::PositionKey position_key = LLKeyframeMotion::PositionKey(ll_round(time, 0.001f), joint->getTargetPosition());
+
+				//BD - Increase the key number since we are adding another key.
+				joint_motion->mPositionCurve.mNumKeys++;
+
+				joint_motion->mPositionCurve.mKeys[joint_motion->mPositionCurve.mNumKeys] = position_key;
+			}
+			//BD - Conversely put the key at the start of the list if CTRL is held while adding a key.
+			else if (gKeyboard->currentMask(TRUE) == MASK_CONTROL)
+			{
+				LLKeyframeMotion::PositionCurve pos_curve;
+
+				//BD - Create a position key and put it into the position curve.
+				LLKeyframeMotion::PositionKey position_key = LLKeyframeMotion::PositionKey(ll_round(time, 0.001f), joint->getTargetPosition());
+
+				pos_curve.mNumKeys++;
+				pos_curve.mKeys[count] = position_key;
+				count++;
+
+				//BD - We have to rewrite the entire rotation curve in order to push a key to the beginning.
+				for (auto& it : joint_motion->mPositionCurve.mKeys)
+				{
+					pos_curve.mNumKeys++;
+					pos_curve.mKeys[count] = it.second;
+					count++;
+				}
+
+				joint_motion->mPositionCurve = pos_curve;
+			}
+			//BD - Put the key after whatever is currently selected.
+			else
+			{
+				LLKeyframeMotion::PositionCurve pos_curve;
+
+				//BD - We have to rewrite the entire rotation curve in order to push a key inbetween others.
+				for (auto& it : joint_motion->mPositionCurve.mKeys)
+				{
+					pos_curve.mNumKeys++;
+					pos_curve.mKeys[count] = it.second;
+					count++;
+
+					if ((S32)it.first == selected_idx)
+					{
+						//BD - Add a bit of time to differentiate the keys.
+						//     This also kinda determines if this is the first keyframe or not. If we can't find a previous
+						//     entry we assume its the first keyframe, so simply use 1.f.
+						time = it.second.mTime + FRAMETIME;
+
+						//BD - Create a position key and put it into the position curve.
+						LLKeyframeMotion::PositionKey position_key = LLKeyframeMotion::PositionKey(ll_round(time, 0.001f), joint->getTargetPosition());
+
+						pos_curve.mNumKeys++;
+						pos_curve.mKeys[count] = position_key;
+						count++;
+					}
+				}
+
+				joint_motion->mPositionCurve = pos_curve;
+			}
+
+			keys = joint_motion->mPositionCurve.mNumKeys;
+
+			LLJointState* joint_state = gDragonAnimator.mPoseCreatorMotion->findJointState(joint);
+			if (joint_state->getUsage() ^ LLJointState::POS)
+			{
+				joint_state->setUsage(joint_state->getUsage() | LLJointState::POS);
+			}
+		}
+		else if (modifier_idx == 2)
+		{
+			//BD - Create a scale key and put it into the scale curve.
+			//LLKeyframeMotion::ScaleKey scale_key = LLKeyframeMotion::ScaleKey(ll_round(time, 0.001f), joint->getScale());
+
+			//BD - Put the key at the end of the list if SHIFT is held while pressing the add key button.
+			if (multiple || joint_motion->mScaleCurve.mNumKeys == 0 || gKeyboard->currentMask(TRUE) == MASK_SHIFT)
+			{
+				//BD - Add a bit of time to differentiate the keys.
+				//     This also kinda determines if this is the first keyframe or not. If we can't find a previous
+				//     entry we assume its the first keyframe, so simply use 1.f.
+				if (joint_motion->mScaleCurve.mNumKeys > 0)
+					time = joint_motion->mScaleCurve.mKeys[joint_motion->mScaleCurve.mNumKeys].mTime + FRAMETIME;
+
+				//BD - Create a scale key and put it into the scale curve.
+				LLKeyframeMotion::ScaleKey scale_key = LLKeyframeMotion::ScaleKey(ll_round(time, 0.001f), joint->getScale());
+
+				//BD - Increase the key number since we are adding another key.
+				joint_motion->mScaleCurve.mNumKeys++;
+
+				joint_motion->mScaleCurve.mKeys[joint_motion->mScaleCurve.mNumKeys] = scale_key;
+			}
+			//BD - Conversely put the key at the start of the list if CTRL is held while adding a key.
+			else if (gKeyboard->currentMask(TRUE) == MASK_CONTROL)
+			{
+				LLKeyframeMotion::ScaleCurve scale_curve;
+
+				//BD - Create a scale key and put it into the scale curve.
+				LLKeyframeMotion::ScaleKey scale_key = LLKeyframeMotion::ScaleKey(ll_round(time, 0.001f), joint->getScale());
+
+				scale_curve.mNumKeys++;
+				scale_curve.mKeys[count] = scale_key;
+				count++;
+
+				//BD - We have to rewrite the entire rotation curve in order to push a key to the beginning.
+				for (auto& it : joint_motion->mScaleCurve.mKeys)
+				{
+					scale_curve.mNumKeys++;
+					scale_curve.mKeys[count] = it.second;
+					count++;
+				}
+
+				joint_motion->mScaleCurve = scale_curve;
+			}
+			//BD - Put the key after whatever is currently selected.
+			else
+			{
+				LLKeyframeMotion::ScaleCurve scale_curve;
+
+				//BD - We have to rewrite the entire rotation curve in order to push a key inbetween others.
+				for (auto& it : joint_motion->mScaleCurve.mKeys)
+				{
+					scale_curve.mNumKeys++;
+					scale_curve.mKeys[count] = it.second;
+					count++;
+
+					if ((S32)it.first == selected_idx)
+					{
+						//BD - Add a bit of time to differentiate the keys.
+						//     This also kinda determines if this is the first keyframe or not. If we can't find a previous
+						//     entry we assume its the first keyframe, so simply use 1.f.
+						time = it.second.mTime + FRAMETIME;
+
+						//BD - Create a scale key and put it into the scale curve.
+						LLKeyframeMotion::ScaleKey scale_key = LLKeyframeMotion::ScaleKey(ll_round(time, 0.001f), joint->getScale());
+
+						scale_curve.mNumKeys++;
+						scale_curve.mKeys[count] = scale_key;
+						count++;
+					}
+				}
+
+				joint_motion->mScaleCurve = scale_curve;
+			}
+
+			keys = joint_motion->mScaleCurve.mNumKeys;
+
+			LLJointState* joint_state = gDragonAnimator.mPoseCreatorMotion->findJointState(joint);
+			if (joint_state->getUsage() ^ LLJointState::SCALE)
+			{
+				joint_state->setUsage(joint_state->getUsage() | LLJointState::SCALE);
+			}
+		}
+	}
+
+	//BD - Refresh the keyframes.
+	onKeyframeRefresh();
+
+	//BD - Check if our animation duration needs changing.
+	onAnimationDurationCheck();
+
+	//BD - Always select the last entry whenever we switch bones to allow quickly making
+	//     changes or adding new keyframes.
+	mKeyframeScroll->selectNthItem(new_selected_idx);
+}
+
+void BDFloaterPoseCreator::onKeyframeRemove()
+{
+	LLScrollListItem* item = mKeyframeScroll->getFirstSelected();
+	if (!item)
+		return;
+
+	LLScrollListItem* joint_item = mJointScrolls[JOINTS]->getFirstSelected();
+	if (!joint_item)
+		return;
+
+	LLJoint* joint = (LLJoint*)joint_item->getUserdata();
+	if (!joint)
+		return;
+
+	if (!gDragonAnimator.mPoseCreatorMotion)
+		return;
+
+	LLKeyframeMotion::JointMotionList* joint_list = gDragonAnimator.mPoseCreatorMotion->getJointMotionList();
+	LLKeyframeMotion::JointMotion* joint_motion = joint_list->getJointMotion(joint->getJointNum());
+	LLJointState* joint_state = gDragonAnimator.mPoseCreatorMotion->findJointState(joint);
+	S32 idx = mModifierTabs->getCurrentPanelIndex();
+	S32 si = mKeyframeScroll->getFirstSelected()->getColumn(1)->getValue().asInteger();
+	S32 usage = joint_state->getUsage();
+	S32 count = 1;
+	S32 last_selected = mKeyframeScroll->getFirstSelectedIndex();
+
+	if (idx == 0)
+	{
+		LLKeyframeMotion::RotationCurve rot_curve;
+		//BD - Rather than removing the key, rewriting all index numbers or shoving keys around
+		//     we simply opt to create a new curve, this automatically reorganizes and counts all
+		//     keys putting them in their proper place making this a simple solution. Not exactly
+		//     efficient but it hardly matters here.
+		for (auto& it : joint_motion->mRotationCurve.mKeys)
+		{
+			if((S32)it.first != si)
+			{
+				rot_curve.mKeys[count] = it.second;
+				rot_curve.mNumKeys = count;
+				count++;
+			}
+		}
+
+		joint_motion->mRotationCurve = rot_curve;
+
+		if (joint_motion->mRotationCurve.mNumKeys == 0)
+		{
+			if (usage & LLJointState::ROT)
+			{
+				usage &= !LLJointState::ROT;
+				joint_state->setUsage(usage);
+			}
+		}
+	}
+	else if (idx == 1)
+	{
+		LLKeyframeMotion::PositionCurve pos_curve;
+		for (auto& it : joint_motion->mPositionCurve.mKeys)
+		{
+			if ((S32)it.first != si)
+			{
+				pos_curve.mKeys[count] = it.second;
+				pos_curve.mNumKeys = count;
+				count++;
+			}
+		}
+
+		joint_motion->mPositionCurve = pos_curve;
+
+		if (joint_motion->mPositionCurve.mNumKeys == 0)
+		{
+			if (usage & LLJointState::POS)
+			{
+				usage &= !LLJointState::POS;
+				joint_state->setUsage(usage);
+			}
+		}
+	}
+	else if (idx == 2)
+	{
+		LLKeyframeMotion::ScaleCurve scale_curve;
+		for (auto& it : joint_motion->mScaleCurve.mKeys)
+		{
+			if ((S32)it.first != si)
+			{
+				scale_curve.mKeys[count] = it.second;
+				scale_curve.mNumKeys = count;
+				count++;
+			}
+		}
+
+		joint_motion->mScaleCurve = scale_curve;
+
+		if (joint_motion->mScaleCurve.mNumKeys == 0)
+		{
+			if (usage & LLJointState::SCALE)
+			{
+				usage &= !LLJointState::SCALE;
+				joint_state->setUsage(usage);
+			}
+		}
+	}
+
+	//BD - Refresh the keyframes.
+	onKeyframeRefresh();
+
+	//BD - Check if our animation duration needs changing.
+	onAnimationDurationCheck();
+
+	//BD - Select the next entry in line after deleting one.
+	mKeyframeScroll->selectNthItem(last_selected);
+}
+
+void BDFloaterPoseCreator::onKeyframeTime()
+{
+	LLScrollListItem* item = mKeyframeScroll->getFirstSelected();
+	if (!item)
+		return;
+
+	LLScrollListItem* joint_item = mJointScrolls[JOINTS]->getFirstSelected();
+	if (!joint_item)
+		return;
+
+	LLJoint* joint = (LLJoint*)joint_item->getUserdata();
+	if (!joint)
+		return;
+
+	if (!gDragonAnimator.mPoseCreatorMotion)
+		return;
+
+	LLKeyframeMotion::JointMotionList* joint_list = gDragonAnimator.mPoseCreatorMotion->getJointMotionList();
+	LLKeyframeMotion::JointMotion* joint_motion = joint_list->getJointMotion(joint->getJointNum());
+	S32 si = mKeyframeScroll->getFirstSelectedIndex() + 1;
+	S32 idx = mModifierTabs->getCurrentPanelIndex();
+	//BD - Set times in frames rather than direct time, it's easier for the user.
+	//     We assume 60 FPS for an animation by default.
+	S32 time_frames = getChild<LLUICtrl>("key_time")->getValue().asInteger();
+	F32 time_float = FRAMETIME * time_frames;
+
+	if (idx == 0)
+	{
+		//BD - Find the key we are looking for and remove it.
+		for (auto& it : joint_motion->mRotationCurve.mKeys)
+		{
+			if ((S32)it.first == si)
+			{
+				it.second.mTime = ll_round(time_float, 0.001f);
+			}
+		}
+	}
+	else if (idx == 1)
+	{
+		for (auto& it : joint_motion->mPositionCurve.mKeys)
+		{
+			if((S32)it.first == si)
+			{
+				it.second.mTime = ll_round(time_float, 0.001f);
+			}
+		}
+	}
+	else if (idx == 2)
+	{
+		for (auto& it : joint_motion->mScaleCurve.mKeys)
+		{
+			if ((S32)it.first == si)
+			{
+				it.second.mTime = ll_round(time_float, 0.001f);
+			}
+		}
+	}
+
+	onKeyframeRefresh();
+	mKeyframeScroll->selectNthItem(si - 1);
+	//item->getColumn(0)->setValue(time_frames);
+	onAnimationDurationCheck();
+}
+
+void BDFloaterPoseCreator::onEditAnimationInfo(const LLSD& param)
+{
+	if (!gDragonAnimator.mPoseCreatorMotion)
+		return;
+
+	LLKeyframeMotion::JointMotionList* joint_list = gDragonAnimator.mPoseCreatorMotion->getJointMotionList();
+	std::string msg = param.asString();
+	if (msg == "ease_in")
+	{
+		joint_list->mEaseInDuration = getChild<LLUICtrl>("ease_in")->getValue().asReal();
+	}
+	else if (msg == "ease_out")
+	{
+		joint_list->mEaseOutDuration = getChild<LLUICtrl>("ease_out")->getValue().asReal();
+	}
+	else if (msg == "duration_auto")
+	{
+		mAutoDuration = getChild<LLUICtrl>("AutoDuration")->getValue().asBoolean();
+		onAnimationDurationCheck();
+	}
+	else if (msg == "duration")
+	{
+		joint_list->mDuration = getChild<LLUICtrl>("keyframe_duration")->getValue().asReal();
+	}
+	else if (msg == "loop")
+	{
+		joint_list->mLoop = getChild<LLUICtrl>("LoopAnimation")->getValue().asBoolean();
+	}
+	else if (msg == "loop_in")
+	{
+		joint_list->mLoopInPoint = getChild<LLUICtrl>("loop_in")->getValue().asReal();
+	}
+	else if (msg == "loop_out")
+	{
+		joint_list->mLoopOutPoint = getChild<LLUICtrl>("loop_out")->getValue().asReal();
+	}
+	else if (msg == "priority")
+	{
+		joint_list->mBasePriority = (LLJoint::JointPriority)getChild<LLUICtrl>("base_priority")->getValue().asInteger();
+	}
+	else if (msg == "hand")
+	{
+		joint_list->mHandPose = (LLHandMotion::eHandPose)getChild<LLUICtrl>("hand_poses")->getValue().asInteger();
+	}
+}
+
+void BDFloaterPoseCreator::onInterpolationChange(LLUICtrl* ctrl)
+{
+	LLScrollListItem* joint_item = mJointScrolls[JOINTS]->getFirstSelected();
+	if (!joint_item)
+		return;
+
+	LLJoint* joint = (LLJoint*)joint_item->getUserdata();
+	if (!joint)
+		return;
+
+	if (!gDragonAnimator.mPoseCreatorMotion)
+		return;
+
+	LLKeyframeMotion::JointMotionList* joint_list = gDragonAnimator.mPoseCreatorMotion->getJointMotionList();
+	LLKeyframeMotion::JointMotion* joint_motion = joint_list->getJointMotion(joint->getJointNum());
+	S32 interp_idx = ctrl->getValue().asInteger();
+	S32 idx = mModifierTabs->getCurrentPanelIndex();
+
+	if (idx == 0)
+	{
+		joint_motion->mRotationCurve.mInterpolationType = (LLKeyframeMotion::InterpolationType)interp_idx;
+	}
+	else if (idx == 1)
+	{
+		joint_motion->mPositionCurve.mInterpolationType = (LLKeyframeMotion::InterpolationType)interp_idx;
+	}
+	else if (idx == 2)
+	{
+		joint_motion->mScaleCurve.mInterpolationType = (LLKeyframeMotion::InterpolationType)interp_idx;
+	}
+}
+
+void BDFloaterPoseCreator::onAnimationDurationCheck()
+{
+	if (!mAutoDuration)
+		return;
+
+	if (!gDragonAnimator.mPoseCreatorMotion)
+		return;
+
+	LLScrollListItem* joint_item = mJointScrolls[JOINTS]->getFirstSelected();
+	if (!joint_item)
+		return;
+
+	LLJoint* joint = (LLJoint*)joint_item->getUserdata();
+	if (!joint)
+		return;
+
+	LLKeyframeMotion::JointMotionList* joint_list = gDragonAnimator.mPoseCreatorMotion->getJointMotionList();
+	F32 new_time = 0.0f;
+
+	//BD - Go through all motions and all keys and determine the largest keyframe time.
+	for (auto& joint_motion : joint_list->mJointMotionArray)
+	{
+		for (auto& it : joint_motion->mRotationCurve.mKeys)
+		{
+			new_time = ll_round(llmax(new_time, it.second.mTime), 0.001f);
+		}
+
+		for (auto& it : joint_motion->mPositionCurve.mKeys)
+		{
+			new_time = ll_round(llmax(new_time, it.second.mTime), 0.001f);
+		}
+
+		for (auto& it : joint_motion->mScaleCurve.mKeys)
+		{
+			new_time = ll_round(llmax(new_time, it.second.mTime), 0.001f);
+		}
+	}
+
+	joint_list->mDuration = new_time;
+	joint_list->mLoopOutPoint = new_time;
+	getChild<LLUICtrl>("keyframe_duration")->setValue(new_time);
+	getChild<LLSpinCtrl>("loop_out")->setMaxValue(new_time);
+	getChild<LLUICtrl>("loop_out")->setValue(new_time);
+}
+
+////////////////////////////////
+//BD - Poses
+////////////////////////////////
+
+void BDFloaterPoseCreator::onPoseStartStop()
+{
+	BDPosingMotion* pmotion = (BDPosingMotion*)gAgentAvatarp->findMotion(ANIM_BD_POSING_MOTION);
+	LLKeyframeMotion* tmotion = (LLKeyframeMotion*)gAgentAvatarp->findMotion(mTempMotionID);
+	if(gDragonAnimator.mPoseCreatorMotion)
+	{
+		//BD - The reason we create a new preview motion here is because LLMotionController can deprecate and
+		//     destroy our preview animation if we start and stop it and leave it stopped for a while. This
+		//     would destroy all our work. Leaving the actual internal temp motion alive without ever running
+		//     it prevents it from ever being put into the active list, thus never dropping it into the deactivated
+		//     list when we stop it preventing it from being destroyed at some point.
+		if (!tmotion)
+		{
+			gDragonAnimator.mPoseCreatorMotion->dumpToFile("_poser_temp.anim", false);
+
+			LLKeyframeMotion* motion = onReadyTempMotion();
+			if (motion)
+			{
+				//BD - Start the newly created preview animation and set it to be eternal as
+				//     we are going to continue editing it unless we start the preview again.
+				gAgentAvatarp->startMotion(motion->getID());
+				mTempMotionID = motion->getID();
+			}
+		}
+		else
+		{
+			if (tmotion->isStopped())
+			{
+				//BD - Save the animation temporarily.
+				gDragonAnimator.mPoseCreatorMotion->dumpToFile("_poser_temp.anim", false);
+				//BD - Reload the animation data back into our already existing animation.
+				//     To refresh it and make sure its always up to date with all our changes.
+				LLKeyframeMotion* tmotion = onReadyTempMotion();
+				if (tmotion)
+				{
+					//BD - Start the newly created preview animation and set it to be eternal as
+					//     we are going to continue editing it unless we start the preview again.
+					gAgentAvatarp->startMotion(tmotion->getID());
+					mTempMotionID = tmotion->getID();
+				}
+			}
+			else
+			{
+				gAgentAvatarp->stopMotion(tmotion->getID());
+			}
+		}
+
+		//BD - Stop our Poser motion if its active, start it again if we are still posing after disabling
+		//     the preview.
+		if (gAgentAvatarp->mIsPosing)
+		{
+			if (!pmotion || pmotion->isStopped())
+			{
+				gAgentAvatarp->startMotion(ANIM_BD_POSING_MOTION);
+				//BD - We need to reset the skeleton here and reapply all the latest keyframes.
+				onJointRotPosScaleReset();
+				onPoseReapply();
+			}
+			else
+			{
+				gAgentAvatarp->stopMotion(ANIM_BD_POSING_MOTION);
+			}
+		}
+	}
+}
+
+bool BDFloaterPoseCreator::onClickPoseSave(const LLSD& param)
+{
+	bool ret = onPoseExport();
+	if (ret)
+		LLNotificationsUtil::add("PoserExportANIMSuccess");
+	return ret;
+}
+
+void BDFloaterPoseCreator::onPoseReapply()
+{
+	if(!gDragonAnimator.mPoseCreatorMotion)
+		return;
+
+	LLKeyframeMotion::JointMotionList* joint_list = gDragonAnimator.mPoseCreatorMotion->getJointMotionList();
+	if (!joint_list)
+		return;
+
+	for (auto& joint_motion : joint_list->mJointMotionArray)
+	{
+		LLJoint* joint = gAgentAvatarp->getJoint(joint_motion->mJointName);
+		if (joint)
+		{
+			LLKeyframeMotion::RotationCurve rot_curve = joint_motion->mRotationCurve;
+			LLKeyframeMotion::PositionCurve pos_curve = joint_motion->mPositionCurve;
+			LLKeyframeMotion::ScaleCurve scale_curve = joint_motion->mScaleCurve;
+
+			//BD - Load in the last keyframe of every bone and apply it to give us a
+			//     start.
+			for (auto& rot_key : rot_curve.mKeys)
+			{
+				LLQuaternion rot = rot_key.second.mRotation;
+				joint->setTargetRotation(rot);
+			}
+
+			for (auto& pos_key : pos_curve.mKeys)
+			{
+				LLVector3 pos = pos_key.second.mPosition;
+				joint->setTargetPosition(pos);
+			}
+
+			for (auto& scale_key : scale_curve.mKeys)
+			{
+				LLVector3 scale = scale_key.second.mScale;
+				joint->setScale(scale);
+			}
+
+			//BD - We support per bone priority. Question is does SL?
+			//LLJoint::JointPriority priority = joint_motion->mPriority;
+		}
+	}
+}
+
+void BDFloaterPoseCreator::onPoseStart()
+{
+	BDPosingMotion* motion = (BDPosingMotion*)gAgentAvatarp->findMotion(ANIM_BD_POSING_MOTION);
+	if (!motion || motion->isStopped())
+	{
+		gAgentAvatarp->setPosing();
+
+		//BD - Grab our current defaults to revert to them when stopping the Poser.
+		if(gAgentAvatarp->isFullyLoaded())
+			onCollectDefaults();
+
+		gAgent.stopFidget();
+		gDragonStatus->setPosing(true);
+
+		gAgentAvatarp->startMotion(ANIM_BD_POSING_MOTION);
+		onJointRotPosScaleReset();
+
+		//BD - Do not recreate the pose, keep the animation so we can return to it.
+		if(!gDragonAnimator.mPoseCreatorMotion)
+		{
+			//BD - Create the temp posing motion that we will use for the animation.
+			onCreateTempMotion();
+			gDragonAnimator.mPoseCreatorMotion = onReadyTempMotion("_poser_temp.anim" ,true);
+		}
+	}
+	else
+	{
+		//BD - Reset everything, all rotations, positions and scales of all bones.
+		onJointRotPosScaleReset();
+
+		//BD - Clear posing when we're done now that we've safely endangered getting spaghetified.
+		gAgentAvatarp->clearPosing();
+		gAgentAvatarp->stopMotion(ANIM_BD_POSING_MOTION);
+		gDragonStatus->setPosing(false);
+	}
+
+	//BD - Wipe the joint list.
+	onJointRefresh();
+}
+
+bool BDFloaterPoseCreator::onPoseExport()
+{
+	BDPosingMotion* motion = (BDPosingMotion*)gAgentAvatarp->findMotion(ANIM_BD_POSING_MOTION);
+	if (!motion)
+		return false;
+
+	if (!gDragonAnimator.mPoseCreatorMotion)
+		return false;
+
+	LLPose* pose = motion->getPose();
+	if (!pose)
+		return false;
+
+	std::string motion_name = getChild<LLUICtrl>("export_name")->getValue().asString();
+	if (motion_name.empty())
+		return false;
+
+	return gDragonAnimator.mPoseCreatorMotion->dumpToFile(motion_name, false);
+
+	LLKeyframeMotion* temp_motion = NULL;
+	LLAssetID mMotionID;
+	LLTransactionID	mTransactionID;
+
+	//BD - To make this work we'll first need a unique UUID for this animation.
+	mTransactionID.generate();
+	mMotionID = mTransactionID.makeAssetID(gAgent.getSecureSessionID());
+
+	temp_motion = (LLKeyframeMotion*)gAgentAvatarp->createMotion(mMotionID);
+
+	if (!temp_motion)
+		return false;
+
+	LLKeyframeMotion::JointMotionList* joint_motion_list = new LLKeyframeMotion::JointMotionList;
+	std::vector<LLPointer<LLJointState>> joint_states;
+
+	if (joint_motion_list == NULL)
+		return false;
+
+	if (!joint_motion_list->mJointMotionArray.empty())
+		joint_motion_list->mJointMotionArray.clear();
+	joint_motion_list->mJointMotionArray.reserve(134);
+	joint_states.clear();
+	joint_states.reserve(134);
+
+	joint_motion_list->mBasePriority = (LLJoint::JointPriority)getChild<LLUICtrl>("base_priority")->getValue().asInteger();
+	joint_motion_list->mDuration = 1.f;
+	joint_motion_list->mEaseInDuration = getChild<LLUICtrl>("ease_in")->getValue().asReal();
+	joint_motion_list->mEaseOutDuration = getChild<LLUICtrl>("ease_out")->getValue().asReal();
+	joint_motion_list->mEmoteName = "";
+	joint_motion_list->mHandPose = (LLHandMotion::eHandPose)getChild<LLUICtrl>("hand_poses")->getValue().asInteger();
+	joint_motion_list->mLoop = true;
+	joint_motion_list->mLoopInPoint = 0.0f;
+	joint_motion_list->mLoopOutPoint = 1.0f;
+	joint_motion_list->mMaxPriority = LLJoint::ADDITIVE_PRIORITY;
+
+	for (S32 i = 0; i <= ATTACHMENT_BONES; i++)
+	{
+		for (auto item : mJointScrolls[i]->getAllData())
+		{
+			LLJoint* joint = (LLJoint*)item->getUserdata();
+			if (joint)
+			{
+				//BD - Skip this bone if its not enabled. Allows us to selectively include bones.
+				LLPointer<LLJointState> state = pose->findJointState(joint);
+				if (!state)
+					continue;
+
+				//BD - Create a new joint motion and add it to the pile.
+				LLKeyframeMotion::JointMotion* joint_motion = new LLKeyframeMotion::JointMotion;
+				joint_motion_list->mJointMotionArray.push_back(joint_motion);
+
+				//BD - Fill out joint motion with relevant basic data.
+				joint_motion->mJointName = joint->getName();
+				joint_motion->mPriority = LLJoint::HIGHEST_PRIORITY;
+
+				//BD - Create the basic joint state for this joint and add it to the joint states.
+				LLPointer<LLJointState> joint_state = new LLJointState;
+				joint_states.push_back(joint_state);
+				joint_state->setJoint(joint); // note: can accept NULL
+				joint_state->setUsage(0);
+
+				//BD - Start with filling general rotation data in.
+				joint_motion->mRotationCurve.mNumKeys = 1;
+				joint_motion->mRotationCurve.mInterpolationType = LLKeyframeMotion::IT_LINEAR;
+
+				//BD - Create a rotation key and put it into the rotation curve.
+				LLKeyframeMotion::RotationKey rotation_key = LLKeyframeMotion::RotationKey(1.f, joint->getTargetRotation());
+				joint_motion->mRotationCurve.mKeys[0] = rotation_key;
+				joint_state->setUsage(joint_state->getUsage() | LLJointState::ROT);
+
+				//BD - Fill general position data in.
+				joint_motion->mPositionCurve.mNumKeys = 1;
+				joint_motion->mPositionCurve.mInterpolationType = LLKeyframeMotion::IT_LINEAR;
+
+				//BD - Create a position key and put it into the position curve.
+				LLKeyframeMotion::PositionKey position_key = LLKeyframeMotion::PositionKey(1.f, joint->getTargetPosition());
+				joint_motion->mPositionCurve.mKeys[0] = position_key;
+				joint_state->setUsage(joint_state->getUsage() | LLJointState::POS);
+
+				temp_motion->addJointState(joint_state);
+
+				//BD - Start with filling general scale data in.
+				joint_motion->mScaleCurve.mNumKeys = 1;
+				joint_motion->mScaleCurve.mInterpolationType = LLKeyframeMotion::IT_LINEAR;
+
+				//BD - Create a scale key and put it into the scale curve.
+				LLKeyframeMotion::ScaleKey scale_key = LLKeyframeMotion::ScaleKey(1.f, joint->getScale());
+				joint_motion->mScaleCurve.mKeys[0] = scale_key;
+
+				joint_motion->mUsage = joint_state->getUsage();
+
+				//BD - We do not use constraints so we just leave them out here.
+				//     Should we ever add them we'd do so here.
+				//joint_motion_list->mConstraints.push_front(constraint);
+
+				//BD - Get the pelvis's bounding box and add it.
+				if (joint->getJointNum() == 0)
+				{
+					joint_motion_list->mPelvisBBox.addPoint(position_key.mPosition);
+				}
+			}
+		}
+	}
+
+	temp_motion->setJointMotionList(joint_motion_list);
+	return temp_motion->dumpToFile(motion_name, false);
+}
+
+void BDFloaterPoseCreator::onPoseImport()
+{
+	BDPosingMotion* motion = (BDPosingMotion*)gAgentAvatarp->findMotion(ANIM_BD_POSING_MOTION);
+	if (!motion)
+		return;
+
+	//BD - Put us back in T pose before importing a new animation. We want to apply
+	//     the changes to a clean pose.
+	onJointRotPosScaleReset();
+
+	//BD - Create a new motion from an anim file from disk.
+	//     Let us pick the file we want to preview with the filepicker.
+	LLFilePicker& picker = LLFilePicker::instance();
+	if (picker.getOpenFile(LLFilePicker::FFLOAD_ANIM))
+	{
+		std::string outfilename = picker.getFirstFile().c_str();
+		gDragonAnimator.mPoseCreatorMotion = onReadyTempMotion(gDirUtilp->getBaseFileName(outfilename), true);
+
+		onJointRefresh();
+		onPoseReapply();
+		onKeyframesRebuild();
+		//onKeyframeRefresh();
+
+		//BD - Always select the last entry whenever we switch bones to allow quickly making
+		//     changes or adding new keyframes.
+		//mKeyframeScroll->selectNthItem(mKeyframeScroll->getItemCount() - 1);
+	}
+}
+
+////////////////////////////////
+//BD - Joints
+////////////////////////////////
+void BDFloaterPoseCreator::onJointRefresh()
+{
+	//BD - Getting collision volumes and attachment points.
+	std::vector<std::string> cv_names, attach_names;
+	gAgentAvatarp->getSortedJointNames(1, cv_names);
+	gAgentAvatarp->getSortedJointNames(2, attach_names);
+
+	bool is_posing = gAgentAvatarp->getPosing();
+	mJointScrolls[JOINTS]->clearRows();
+	mJointScrolls[COLLISION_VOLUMES]->clearRows();
+	mJointScrolls[ATTACHMENT_BONES]->clearRows();
+
+	LLVector3 rot;
+	LLVector3 pos;
+	LLVector3 scale;
+	LLJoint* joint;
+	for (S32 i = 0; (joint = gAgentAvatarp->getCharacterJoint(i)); ++i)
+	{
+		//BD - Nothing? Invalid? Skip, when we hit the end we'll break out anyway.
+		if (!joint)	continue;
+
+		LLSD row;
+		const std::string joint_name = joint->getName();
+		//BD - Show some categories to make it a bit easier finding out which
+		//     bone belongs where and what they might be for those who can't use
+		//     bone names.
+		if (joint->mJointNum == 0 ||	//mPelvis
+			joint->mJointNum == 8 ||	//mHead
+			joint->mJointNum == 58 ||	//mCollarLeft
+			joint->mJointNum == 77 ||	//mCollarRight
+			joint->mJointNum == 96 ||	//mWingsRoot
+			joint->mJointNum == 107 ||	//mHipRight
+			joint->mJointNum == 112 ||	//mHipLeft
+			joint->mJointNum == 117 ||	//mTail1
+			joint->mJointNum == 123)	//mGroin
+		{
+			row["columns"][COL_ICON]["column"] = "icon";
+			row["columns"][COL_ICON]["type"] = "icon";
+			row["columns"][COL_ICON]["value"] = getString("icon_category");
+			row["columns"][COL_NAME]["column"] = "joint";
+			row["columns"][COL_NAME]["value"] = getString("title_" + joint_name);
+			LLScrollListItem* element = mJointScrolls[JOINTS]->addElement(row);
+			element->setEnabled(FALSE);
+		}
+
+		row["columns"][COL_ICON]["column"] = "icon";
+		row["columns"][COL_ICON]["type"] = "icon";
+		row["columns"][COL_ICON]["value"] = getString("icon_bone");
+		row["columns"][COL_NAME]["column"] = "joint";
+		row["columns"][COL_NAME]["value"] = joint_name;
+
+		if (is_posing)
+		{
+			//BD - Bone Rotations
+			joint->getTargetRotation().getEulerAngles(&rot.mV[VX], &rot.mV[VY], &rot.mV[VZ]);
+			row["columns"][COL_ROT_X]["column"] = "x";
+			row["columns"][COL_ROT_X]["value"] = ll_round(rot.mV[VX], 0.001f);
+			row["columns"][COL_ROT_Y]["column"] = "y";
+			row["columns"][COL_ROT_Y]["value"] = ll_round(rot.mV[VY], 0.001f);
+			row["columns"][COL_ROT_Z]["column"] = "z";
+			row["columns"][COL_ROT_Z]["value"] = ll_round(rot.mV[VZ], 0.001f);
+
+			//BD - Bone Positions
+			pos = joint->getTargetPosition();
+			row["columns"][COL_POS_X]["column"] = "pos_x";
+			row["columns"][COL_POS_X]["value"] = ll_round(pos.mV[VX], 0.001f);
+			row["columns"][COL_POS_Y]["column"] = "pos_y";
+			row["columns"][COL_POS_Y]["value"] = ll_round(pos.mV[VY], 0.001f);
+			row["columns"][COL_POS_Z]["column"] = "pos_z";
+			row["columns"][COL_POS_Z]["value"] = ll_round(pos.mV[VZ], 0.001f);
+
+			//BD - Bone Scales
+			scale = joint->getScale();
+			row["columns"][COL_SCALE_X]["column"] = "scale_x";
+			row["columns"][COL_SCALE_X]["value"] = ll_round(scale.mV[VX], 0.001f);
+			row["columns"][COL_SCALE_Y]["column"] = "scale_y";
+			row["columns"][COL_SCALE_Y]["value"] = ll_round(scale.mV[VY], 0.001f);
+			row["columns"][COL_SCALE_Z]["column"] = "scale_z";
+			row["columns"][COL_SCALE_Z]["value"] = ll_round(scale.mV[VZ], 0.001f);
+		}
+
+		LLScrollListItem* item = mJointScrolls[JOINTS]->addElement(row);
+		item->setUserdata(joint);
+
+		//BD - We need to check if we are posing or not, simply set all bones to deactivated
+		//     when we are not posed otherwise they will remain on "enabled" state. This behavior
+		//     could be confusing to the user, this is due to how animations work.
+		if (is_posing)
+		{
+			BDPosingMotion* motion = (BDPosingMotion*)gAgentAvatarp->findMotion(ANIM_BD_POSING_MOTION);
+			if (motion)
+			{
+				LLPose* pose = motion->getPose();
+				if (pose)
+				{
+					// BD - We do check here for the joint_state because otherwise we end up with the toggle
+					//      state not appearing properly toggled/untoggled when we first refresh after firing
+					//      up the poser. At the same time this is used to refresh all bone states when we
+					//      load a pose.
+					LLPointer<LLJointState> joint_state = pose->findJointState(joint);
+					if (joint_state)
+					{
+						((LLScrollListText*)item->getColumn(COL_NAME))->setFontStyle(LLFontGL::BOLD);
+					}
+				}
+			}
+		}
+		else
+		{
+			((LLScrollListText*)item->getColumn(COL_NAME))->setFontStyle(LLFontGL::NORMAL);
+		}
+	}
+
+	//BD - We add an empty line into both lists and include an icon to automatically resize
+	//     the list row heights to sync up with the height in our joint list. We remove it
+	//     immediately after anyway.
+	LLSD test_row;
+	test_row["columns"][COL_ICON]["column"] = "icon";
+	test_row["columns"][COL_ICON]["type"] = "icon";
+	test_row["columns"][COL_ICON]["value"] = getString("icon_category");
+	mJointScrolls[COLLISION_VOLUMES]->addElement(test_row);
+	mJointScrolls[COLLISION_VOLUMES]->deleteSingleItem(0);
+	mJointScrolls[ATTACHMENT_BONES]->addElement(test_row);
+	mJointScrolls[ATTACHMENT_BONES]->deleteSingleItem(0);
+
+	//BD - Collision Volumes
+	for (auto name : cv_names)
+	{
+		joint = gAgentAvatarp->getJoint(name);
+		//BD - Nothing? Invalid? Skip, when we hit the end we'll break out anyway.
+		if (!joint) continue;
+
+		LLSD row;
+		row["columns"][COL_ICON]["column"] = "icon";
+		row["columns"][COL_ICON]["type"] = "icon";
+		row["columns"][COL_ICON]["value"] = getString("icon_bone");
+		row["columns"][COL_NAME]["column"] = "joint";
+		row["columns"][COL_NAME]["value"] = name;
+
+		if (is_posing)
+		{
+			//BD - Bone Rotations
+			//     It's stupid but we have to define the empty columns here and fill them with
+			//     nothing otherwise the addRow() function is assuming that the sometimes missing
+			//     rotation columns have an "empty" name and thus creates faulty extra columns.
+			row["columns"][COL_ROT_X]["column"] = "x";
+			row["columns"][COL_ROT_X]["value"] = "";
+			row["columns"][COL_ROT_Y]["column"] = "y";
+			row["columns"][COL_ROT_Y]["value"] = "";
+			row["columns"][COL_ROT_Z]["column"] = "z";
+			row["columns"][COL_ROT_Z]["value"] = "";
+
+			//BD - Bone Positions
+			pos = joint->getPosition();
+			row["columns"][COL_POS_X]["column"] = "pos_x";
+			row["columns"][COL_POS_X]["value"] = ll_round(pos.mV[VX], 0.001f);
+			row["columns"][COL_POS_Y]["column"] = "pos_y";
+			row["columns"][COL_POS_Y]["value"] = ll_round(pos.mV[VY], 0.001f);
+			row["columns"][COL_POS_Z]["column"] = "pos_z";
+			row["columns"][COL_POS_Z]["value"] = ll_round(pos.mV[VZ], 0.001f);
+
+			//BD - Bone Scales
+			scale = joint->getScale();
+			row["columns"][COL_SCALE_X]["column"] = "scale_x";
+			row["columns"][COL_SCALE_X]["value"] = ll_round(scale.mV[VX], 0.001f);
+			row["columns"][COL_SCALE_Y]["column"] = "scale_y";
+			row["columns"][COL_SCALE_Y]["value"] = ll_round(scale.mV[VY], 0.001f);
+			row["columns"][COL_SCALE_Z]["column"] = "scale_z";
+			row["columns"][COL_SCALE_Z]["value"] = ll_round(scale.mV[VZ], 0.001f);
+		}
+
+		LLScrollListItem* item = mJointScrolls[COLLISION_VOLUMES]->addElement(row);
+		item->setUserdata(joint);
+	}
+
+	//BD - Attachment Bones
+	for (auto name : attach_names)
+	{
+		joint = gAgentAvatarp->getJoint(name);
+		//BD - Nothing? Invalid? Skip, when we hit the end we'll break out anyway.
+		if (!joint) continue;
+
+		LLSD row;
+		row["columns"][COL_ICON]["column"] = "icon";
+		row["columns"][COL_ICON]["type"] = "icon";
+		row["columns"][COL_ICON]["value"] = getString("icon_bone");
+		row["columns"][COL_NAME]["column"] = "joint";
+		row["columns"][COL_NAME]["value"] = name;
+
+		if (is_posing)
+		{
+			//BD - Bone Rotations
+			//     It's stupid but we have to define the empty columns here and fill them with
+			//     nothing otherwise the addRow() function is assuming that the sometimes missing
+			//     rotation columns have an "empty" name and thus creates faulty extra columns.
+			row["columns"][COL_ROT_X]["column"] = "x";
+			row["columns"][COL_ROT_X]["value"] = "";
+			row["columns"][COL_ROT_Y]["column"] = "y";
+			row["columns"][COL_ROT_Y]["value"] = "";
+			row["columns"][COL_ROT_Z]["column"] = "z";
+			row["columns"][COL_ROT_Z]["value"] = "";
+
+			//BD - Bone Positions
+			pos = joint->getPosition();
+			row["columns"][COL_POS_X]["column"] = "pos_x";
+			row["columns"][COL_POS_X]["value"] = ll_round(pos.mV[VX], 0.001f);
+			row["columns"][COL_POS_Y]["column"] = "pos_y";
+			row["columns"][COL_POS_Y]["value"] = ll_round(pos.mV[VY], 0.001f);
+			row["columns"][COL_POS_Z]["column"] = "pos_z";
+			row["columns"][COL_POS_Z]["value"] = ll_round(pos.mV[VZ], 0.001f);
+
+			//BD - Bone Scales
+			scale = joint->getScale();
+			row["columns"][COL_SCALE_X]["column"] = "scale_x";
+			row["columns"][COL_SCALE_X]["value"] = ll_round(scale.mV[VX], 0.001f);
+			row["columns"][COL_SCALE_Y]["column"] = "scale_y";
+			row["columns"][COL_SCALE_Y]["value"] = ll_round(scale.mV[VY], 0.001f);
+			row["columns"][COL_SCALE_Z]["column"] = "scale_z";
+			row["columns"][COL_SCALE_Z]["value"] = ll_round(scale.mV[VZ], 0.001f);
+		}
+
+		LLScrollListItem* item = mJointScrolls[ATTACHMENT_BONES]->addElement(row);
+		item->setUserdata(joint);
+	}
+
+	onJointControlsRefresh();
+}
+
+void BDFloaterPoseCreator::onJointControlsRefresh()
+{
+	bool is_pelvis = false;
+	bool is_posing = (gAgentAvatarp->isFullyLoaded() && gAgentAvatarp->getPosing());
+	bool is_previewing = false;
+	S32 index = mJointTabs->getCurrentPanelIndex();
+	LLScrollListItem* item = mJointScrolls[index]->getFirstSelected();
+	is_previewing = gDragonAnimator.mPoseCreatorMotion && !gDragonAnimator.mPoseCreatorMotion->isStopped();
+
+	if (item)
+	{
+		LLJoint* joint = (LLJoint*)item->getUserdata();
+		if (joint)
+		{
+			if (index == 0)
+			{
+				mRotationSliders[VX]->setValue(item->getColumn(COL_ROT_X)->getValue());
+				mRotationSliders[VY]->setValue(item->getColumn(COL_ROT_Y)->getValue());
+				mRotationSliders[VZ]->setValue(item->getColumn(COL_ROT_Z)->getValue());
+			}
+
+			is_pelvis = (joint->mJointNum == 0);
+
+			mPositionSliders[VX]->setValue(item->getColumn(COL_POS_X)->getValue());
+			mPositionSliders[VY]->setValue(item->getColumn(COL_POS_Y)->getValue());
+			mPositionSliders[VZ]->setValue(item->getColumn(COL_POS_Z)->getValue());
+
+			//BD - Bone Scales
+			mScaleSliders[VX]->setValue(item->getColumn(COL_SCALE_X)->getValue());
+			mScaleSliders[VY]->setValue(item->getColumn(COL_SCALE_Y)->getValue());
+			mScaleSliders[VZ]->setValue(item->getColumn(COL_SCALE_Z)->getValue());
+
+			BDPosingMotion* motion = (BDPosingMotion*)gAgentAvatarp->findMotion(ANIM_BD_POSING_MOTION);
+			if (motion)
+			{
+				//BD - If we don't use our default spherical interpolation, set it once.
+				motion->setInterpolationTime(0.25f);
+				motion->setInterpolationType(2);
+
+				LLPose* pose = motion->getPose();
+				if (pose)
+				{
+					LLPointer<LLJointState> joint_state = pose->findJointState(joint);
+					getChild<LLButton>("toggle_bone")->setValue(joint_state.notNull());
+				}
+			}
+		}
+	}
+
+	getChild<LLButton>("toggle_bone")->setEnabled(item && is_posing && index == JOINTS);
+	mStartPosingBtn->setValue(is_posing);
+	getChild<LLUICtrl>("export_poses")->setEnabled(is_posing);
+	getChild<LLUICtrl>("import_poses")->setEnabled(is_posing);
+	getChild<LLUICtrl>("joints_tabs")->setEnabled(is_posing);
+
+	//BD - Enable position tabs whenever positions are available, scales are always enabled
+	//     unless we are editing attachment bones, rotations on the other hand are only
+	//     enabled when editing joints.
+	S32 curr_idx = mModifierTabs->getCurrentPanelIndex();
+	mModifierTabs->setEnabled(item && is_posing && !is_previewing);
+	mModifierTabs->enableTabButton(0, (item && is_posing && index == JOINTS));
+	mModifierTabs->enableTabButton(1, (item && is_posing));
+	mModifierTabs->enableTabButton(2, (item && is_posing && index == COLLISION_VOLUMES));
+	//BD - Swap out of "Position" tab when it's not available.
+	if (curr_idx == 1 && !mModifierTabs->getTabButtonEnabled(1))
+	{
+		mModifierTabs->selectTab(0);
+	}
+	//BD - Swap out of "Scale" and "Rotation" tabs when they are not available.
+	if (curr_idx == 2 && !mModifierTabs->getTabButtonEnabled(2)
+		|| curr_idx == 0 && !mModifierTabs->getTabButtonEnabled(0))
+	{
+		mModifierTabs->selectTab(1);
+	}
+
+	F32 max_val = is_pelvis ? 20.f : 1.0f;
+	mPositionSliders[VX]->setMaxValue(max_val);
+	mPositionSliders[VY]->setMaxValue(max_val);
+	mPositionSliders[VZ]->setMaxValue(max_val);
+	mPositionSliders[VX]->setMinValue(-max_val);
+	mPositionSliders[VY]->setMinValue(-max_val);
+	mPositionSliders[VZ]->setMinValue(-max_val);
+
+	//BD - Change our animator's target, make sure it is always up-to-date.
+	gDragonAnimator.mTargetAvatar = gAgentAvatarp;
+
+	getChild<LLUICtrl>("add_keyframe")->setEnabled(is_posing && !is_previewing);
+	getChild<LLUICtrl>("remove_keyframe")->setEnabled(is_posing && !is_previewing);
+	getChild<LLUICtrl>("interpolation")->setEnabled(is_posing && !is_previewing);
+	getChild<LLUICtrl>("key_time")->setEnabled(is_posing && !is_previewing);
+	mStartPosingBtn->setEnabled(!is_previewing);
+}
+
+void BDFloaterPoseCreator::onJointSet(LLUICtrl* ctrl, const LLSD& param)
+{
+	//BD - Rotations are only supported by joints so far, everything
+	//     else snaps back instantly.
+	LLScrollListItem* item = mJointScrolls[JOINTS]->getFirstSelected();
+	if (!item)
+		return;
+
+	LLJoint* joint = (LLJoint*)item->getUserdata();
+	if (!joint)
+		return;
+
+	if (!gDragonAnimator.mPoseCreatorMotion)
+		return;
+
+	LLScrollListItem* key_item = mKeyframeScroll->getFirstSelected();
+
+	//BD - Neat yet quick and direct way of rotating our bones.
+	//     No more need to include bone rotation orders.
+	F32 val = ctrl->getValue().asReal();
+	S32 axis = param.asInteger();
+	LLScrollListCell* cell[3] = { item->getColumn(COL_ROT_X), item->getColumn(COL_ROT_Y), item->getColumn(COL_ROT_Z) };
+	LLQuaternion rot_quat = joint->getTargetRotation();
+	LLMatrix3 rot_mat;
+	F32 old_value = cell[axis]->getValue().asReal();
+	F32 new_value = val - old_value;
+	LLVector3 vec3;
+	S32 time = key_item ? key_item->getColumn(0)->getValue().asInteger() : 1;
+
+	cell[axis]->setValue(ll_round(val, 0.001f));
+	vec3.mV[axis] = new_value;
+	rot_mat = LLMatrix3(vec3.mV[VX], vec3.mV[VY], vec3.mV[VZ]);
+	rot_quat = LLQuaternion(rot_mat)*rot_quat;
+	joint->setTargetRotation(rot_quat);
+	if (!mEasyRotations)
+	{
+		rot_quat.getEulerAngles(&vec3.mV[VX], &vec3.mV[VY], &vec3.mV[VZ]);
+		S32 i = 0;
+		while (i < 3)
+		{
+			if (i != axis)
+			{
+				cell[i]->setValue(ll_round(vec3.mV[i], 0.001f));
+				mRotationSliders[i]->setValue(item->getColumn(i + 2)->getValue());
+			}
+			++i;
+		}
+	}
+
+	//BD - After a lot of different approaches this seemed to be the most feasible and functional.
+	LLKeyframeMotion::JointMotionList* joint_list = gDragonAnimator.mPoseCreatorMotion->getJointMotionList();
+	LLKeyframeMotion::JointMotion* joint_motion = joint_list->getJointMotion(joint->getJointNum());
+	LLKeyframeMotion::RotationCurve rot_curve = joint_motion->mRotationCurve;
+
+	//BD - Attempt to find the keyframe first.
+	bool found = false;
+	S32 si = mKeyframeScroll->getFirstSelectedIndex() + 1;
+	for (auto it = joint_motion->mRotationCurve.mKeys.begin();
+		it != joint_motion->mRotationCurve.mKeys.end(); )
+	{
+		auto curr_it = it;
+		//BD - Previously we were rounding the keyframe time and comparing it to the time set
+		//     in the keyframe list entry, this was ugly and had a big downside, having two
+		//     or more entries with the same time resulting in all of them getting changed.
+		//     Lucky for us, adding new keyframes into any transformation curve automatically
+		//     counts a float value (used here as S32 since its whole numbers anyway) up by 1
+		//     for every keyframe added, this allows us to easily "compare" the what seems to be
+		//     the index number of the keyframe, against the index number in the list.
+		//     This should work for now until we decide to allow reordering keyframes.
+		//if (ll_round(curr_it->second.mTime, 0.1f) == ll_round(time, 0.1f))
+		if ((S32)curr_it->first == si)
+		{
+			found = true;
+			curr_it->second.mValue = joint->getTargetRotation();
+
+			F32 roll, pitch, yaw;
+			LLQuaternion rot_quat = joint->getTargetRotation();
+			rot_quat.getEulerAngles(&roll, &pitch, &yaw);
+			//BD - Should we really be able to get here? The comparison above should already
+			//     prevent ever getting here since a missing selection means we will never
+			//     find a keyframe. I don't trust anything.
+			if (key_item)
+			{
+				key_item->getColumn(2)->setValue(ll_round(roll, 0.001f));
+				key_item->getColumn(3)->setValue(ll_round(pitch, 0.001f));
+				key_item->getColumn(4)->setValue(ll_round(yaw, 0.001f));
+			}
+			break;
+		}
+		++it;
+	}
+
+	//BD - We couldn't find a keyframe, create one automatically.
+	if (!found)
+	{
+		onKeyframeAdd((time * FRAMETIME), joint);
+
+		//BD - Refresh the keyframes.
+		onKeyframeRefresh();
+
+		//BD - Always select the last entry whenever we switch bones to allow quickly making
+		//     changes or adding new keyframes.
+		mKeyframeScroll->selectNthItem(mKeyframeScroll->getChildCount() - 1);
+	}
+	
+	//BD - If we are in Mirror mode, try to find the opposite bone of our currently
+	//     selected one, for now this simply means we take the name and replace "Left"
+	//     with "Right" and vise versa since all bones are conveniently that way.
+	//     TODO: Do this when creating the joint list so we don't try to find the joint
+	//     over and over again.
+	if (mMirrorMode)
+	{
+		LLJoint* mirror_joint = nullptr;
+		std::string mirror_joint_name = joint->getName();
+		S32 idx = joint->getName().find("Left");
+		if (idx != -1)
+			mirror_joint_name.replace(idx, mirror_joint_name.length(), "Right");
+
+		idx = joint->getName().find("Right");
+		if (idx != -1)
+			mirror_joint_name.replace(idx, mirror_joint_name.length(), "Left");
+
+		if (mirror_joint_name != joint->getName())
+		{
+			mirror_joint = gDragonAnimator.mTargetAvatar->mRoot->findJoint(mirror_joint_name);
+		}
+
+		if (mirror_joint)
+		{
+			//BD - For the opposite joint we invert X and Z axis, everything else is directly applied
+			//     exactly like we do it in our currently selected joint.
+			if (axis != 1)
+				val = -val;
+
+			LLQuaternion inv_quat = LLQuaternion(-rot_quat.mQ[VX], rot_quat.mQ[VY], -rot_quat.mQ[VZ], rot_quat.mQ[VW]);
+			mirror_joint->setTargetRotation(inv_quat);
+
+			LLKeyframeMotion::JointMotion* mirror_joint_motion = joint_list->getJointMotion(mirror_joint->getJointNum());
+			LLKeyframeMotion::RotationCurve mirror_rot_curve = joint_motion->mRotationCurve;
+
+			//BD - Attempt to find the keyframe first.
+			bool found = false;
+
+			LLScrollListItem* key_item = mKeyframeScroll->getItemByLabel(mirror_joint_name);
+			if (key_item)
+			{
+				for (std::map<F32, LLKeyframeMotion::RotationKey>::iterator it = mirror_joint_motion->mRotationCurve.mKeys.begin();
+					it != mirror_joint_motion->mRotationCurve.mKeys.end(); )
+				{
+					std::map<F32, LLKeyframeMotion::RotationKey>::iterator curr_it = it;
+					//BD - Previously we were rounding the keyframe time and comparing it to the time set
+					//     in the keyframe list entry, this was ugly and had a big downside, having two
+					//     or more entries with the same time resulting in all of them getting changed.
+					//     Lucky for us, adding new keyframes into any transformation curve automatically
+					//     counts a float value (used here as S32 since its whole numbers anyway) up by 1
+					//     for every keyframe added, this allows us to easily "compare" the what seems to be
+					//     the index number of the keyframe, against the index number in the list.
+					//     This should work for now until we decide to allow reordering keyframes.
+					//if (ll_round(curr_it->second.mTime, 0.1f) == ll_round(time, 0.1f))
+					if ((S32)curr_it->first == si)
+					{
+						found = true;
+						curr_it->second.mRotation = mirror_joint->getTargetRotation();
+
+						F32 roll, pitch, yaw;
+						LLQuaternion rot_quat = mirror_joint->getTargetRotation();
+						rot_quat.getEulerAngles(&roll, &pitch, &yaw);
+						//BD - Should we really be able to get here? The comparison above should already
+						//     prevent ever getting here since a missing selection means we will never
+						//     find a keyframe. I don't trust anything.
+						if (key_item)
+						{
+							key_item->getColumn(2)->setValue(ll_round(roll, 0.001f));
+							key_item->getColumn(3)->setValue(ll_round(pitch, 0.001f));
+							key_item->getColumn(4)->setValue(ll_round(yaw, 0.001f));
+						}
+						break;
+					}
+					++it;
+				}
+			}
+
+			//BD - We couldn't find a keyframe, create one automatically.
+			if (!found)
+			{
+				onKeyframeAdd((time * FRAMETIME), mirror_joint);
+
+				//BD - Refresh the keyframes.
+				onKeyframeRefresh();
+
+				//BD - Always select the last entry whenever we switch bones to allow quickly making
+				//     changes or adding new keyframes.
+				//mKeyframeScroll->selectNthItem(mKeyframeScroll->getChildCount() - 1);
+			}
+		}
+	}
+}
+
+void BDFloaterPoseCreator::onJointPosSet(LLUICtrl* ctrl, const LLSD& param)
+{
+	S32 index = mJointTabs->getCurrentPanelIndex();
+	LLScrollListItem* item = mJointScrolls[index]->getFirstSelected();
+	if (!item)
+		return;
+
+	LLJoint* joint = (LLJoint*)item->getUserdata();
+	if (!joint)
+		return;
+
+	if (!gDragonAnimator.mPoseCreatorMotion)
+		return;
+
+	LLScrollListItem* key_item = mKeyframeScroll->getFirstSelected();
+
+	//BD - We could just check whether position information is available since only joints
+	//     which can have their position changed will have position information but we
+	//     want this to be a minefield for crashes.
+	//     Bones that can support position
+	//     0, 9-37, 39-43, 45-59, 77, 97-107, 110, 112, 115, 117-121, 125, 128-129, 132
+	//     as well as all attachment bones and collision volumes.
+	F32 val = ctrl->getValue().asReal();
+	LLScrollListCell* cell[3] = { item->getColumn(COL_POS_X), item->getColumn(COL_POS_Y), item->getColumn(COL_POS_Z) };
+	LLVector3 vec3 = { F32(cell[VX]->getValue().asReal()),
+						F32(cell[VY]->getValue().asReal()),
+						F32(cell[VZ]->getValue().asReal()) };
+	S32 time = key_item ? key_item->getColumn(0)->getValue().asInteger() : 1;
+
+	S32 dir = param.asInteger();
+	vec3.mV[dir] = val;
+	cell[dir]->setValue(ll_round(vec3.mV[dir], 0.001f));
+	joint->setTargetPosition(vec3);
+
+	LLKeyframeMotion::JointMotionList* joint_list = gDragonAnimator.mPoseCreatorMotion->getJointMotionList();
+	LLKeyframeMotion::JointMotion* joint_motion = joint_list->getJointMotion(joint->getJointNum());
+	LLKeyframeMotion::RotationCurve rot_curve = joint_motion->mRotationCurve;
+
+	bool found = false;
+	S32 si = mKeyframeScroll->getFirstSelectedIndex() + 1;
+	for (std::map<F32, LLKeyframeMotion::PositionKey>::iterator it = joint_motion->mPositionCurve.mKeys.begin();
+		it != joint_motion->mPositionCurve.mKeys.end(); )
+	{
+		std::map<F32, LLKeyframeMotion::PositionKey>::iterator curr_it = it;
+		//BD - Previously we were rounding the keyframe time and comparing it to the time set
+		//     in the keyframe list entry, this was ugly and had a big downside, having two
+		//     or more entries with the same time resulting in all of them getting changed.
+		//     Lucky for us, adding new keyframes into any transformation curve automatically
+		//     counts a float value (used here as S32 since its whole numbers anyway) up by 1
+		//     for every keyframe added, this allows us to easily "compare" the what seems to be
+		//     the index number of the keyframe, against the index number in the list.
+		//     This should work for now until we decide to allow reordering keyframes.
+		if ((S32)curr_it->first == si)
+		{
+			found = true;
+			curr_it->second.mPosition = joint->getTargetPosition();
+
+			LLVector3 pos = joint->getTargetPosition();
+			//BD - Should we really be able to get here? The comparison above should already
+			//     prevent ever getting here since a missing selection means we will never
+			//     find a keyframe. I don't trust anything.
+			if (key_item)
+			{
+				key_item->getColumn(2)->setValue(ll_round(pos.mV[VX], 0.001f));
+				key_item->getColumn(3)->setValue(ll_round(pos.mV[VY], 0.001f));
+				key_item->getColumn(4)->setValue(ll_round(pos.mV[VZ], 0.001f));
+			}
+		}
+		++it;
+	}
+
+	//BD - We couldn't find a keyframe, create one automatically.
+	if (!found)
+	{
+		onKeyframeAdd((time * FRAMETIME), joint);
+
+		//BD - Refresh the keyframes.
+		onKeyframeRefresh();
+
+		//BD - Always select the last entry whenever we switch bones to allow quickly making
+		//     changes or adding new keyframes.
+		mKeyframeScroll->selectNthItem(mKeyframeScroll->getChildCount() - 1);
+	}
+}
+
+void BDFloaterPoseCreator::onJointScaleSet(LLUICtrl* ctrl, const LLSD& param)
+{
+	S32 index = mJointTabs->getCurrentPanelIndex();
+	LLScrollListItem* item = mJointScrolls[index]->getFirstSelected();
+	if (!item)
+		return;
+
+	LLJoint* joint = (LLJoint*)item->getUserdata();
+	if (!joint)
+		return;
+
+	if (!gDragonAnimator.mPoseCreatorMotion)
+		return;
+
+	LLScrollListItem* key_item = mKeyframeScroll->getFirstSelected();
+	if (!key_item)
+		return;
+
+	F32 val = ctrl->getValue().asReal();
+	LLScrollListCell* cell[3] = { item->getColumn(COL_SCALE_X), item->getColumn(COL_SCALE_Y), item->getColumn(COL_SCALE_Z) };
+	LLVector3 vec3 = { F32(cell[VX]->getValue().asReal()),
+						F32(cell[VY]->getValue().asReal()),
+						F32(cell[VZ]->getValue().asReal()) };
+
+	S32 dir = param.asInteger();
+	vec3.mV[dir] = val;
+	cell[dir]->setValue(ll_round(vec3.mV[dir], 0.001f));
+	joint->setScale(vec3);
+
+	LLKeyframeMotion::JointMotionList* joint_list = gDragonAnimator.mPoseCreatorMotion->getJointMotionList();
+	LLKeyframeMotion::JointMotion* joint_motion = joint_list->getJointMotion(joint->getJointNum());
+	LLKeyframeMotion::RotationCurve rot_curve = joint_motion->mRotationCurve;
+
+	S32 si = mKeyframeScroll->getFirstSelectedIndex() + 1;
+	for (std::map<F32, LLKeyframeMotion::ScaleKey>::iterator it = joint_motion->mScaleCurve.mKeys.begin();
+		it != joint_motion->mScaleCurve.mKeys.end(); )
+	{
+		std::map<F32, LLKeyframeMotion::ScaleKey>::iterator curr_it = it;
+		//BD - Previously we were rounding the keyframe time and comparing it to the time set
+		//     in the keyframe list entry, this was ugly and had a big downside, having two
+		//     or more entries with the same time resulting in all of them getting changed.
+		//     Lucky for us, adding new keyframes into any transformation curve automatically
+		//     counts a float value (used here as S32 since its whole numbers anyway) up by 1
+		//     for every keyframe added, this allows us to easily "compare" the what seems to be
+		//     the index number of the keyframe, against the index number in the list.
+		//     This should work for now until we decide to allow reordering keyframes.
+		if ((S32)curr_it->first == si)
+		{
+			curr_it->second.mScale = joint->getScale();
+
+			LLVector3 scale = joint->getScale();
+			key_item->getColumn(2)->setValue(ll_round(scale.mV[VX], 0.001f));
+			key_item->getColumn(3)->setValue(ll_round(scale.mV[VY], 0.001f));
+			key_item->getColumn(4)->setValue(ll_round(scale.mV[VZ], 0.001f));
+		}
+		++it;
+	}
+}
+
+void BDFloaterPoseCreator::onJointChangeState()
+{
+	BDPosingMotion* motion = (BDPosingMotion*)gAgentAvatarp->findMotion(ANIM_BD_POSING_MOTION);
+	if (motion)
+	{
+		for (auto item : mJointScrolls[JOINTS]->getAllSelected())
+		{
+			LLJoint* joint = (LLJoint*)item->getUserdata();
+			if (joint)
+			{
+				LLPose* pose = motion->getPose();
+				if (pose)
+				{
+					LLPointer<LLJointState> joint_state = pose->findJointState(joint);
+					if (joint_state)
+					{
+						motion->removeJointState(joint_state);
+						((LLScrollListText*)item->getColumn(COL_NAME))->setFontStyle(LLFontGL::NORMAL);
+					}
+					else
+					{
+						motion->addJointToState(joint);
+						((LLScrollListText*)item->getColumn(COL_NAME))->setFontStyle(LLFontGL::BOLD);
+					}
+				}
+			}
+		}
+	}
+	onJointControlsRefresh();
+}
+
+//BD - We use this to reset everything at once.
+void BDFloaterPoseCreator::onJointRotPosScaleReset()
+{
+	//BD - While editing rotations, make sure we use a bit of spherical linear interpolation 
+	//     to make movements smoother.
+	BDPosingMotion* motion = (BDPosingMotion*)gAgentAvatarp->findMotion(ANIM_BD_POSING_MOTION);
+	if (motion)
+	{
+		//BD - If we don't use our default spherical interpolation, set it once.
+		motion->setInterpolationTime(0.25f);
+		motion->setInterpolationType(2);
+	}
+
+	for (S32 it = 0; it < 3; ++it)
+	{
+		//BD - We use this bool to determine whether or not we'll be in need for a full skeleton
+		//     reset and to prevent checking for it every single time.
+		for (auto item : mJointScrolls[it]->getAllData())
+		{
+			if (item)
+			{
+				LLJoint* joint = (LLJoint*)item->getUserdata();
+				if (joint)
+				{
+					//BD - Resetting rotations first if there are any.
+					if (it == JOINTS)
+					{
+						LLQuaternion quat;
+						LLScrollListCell* col_rot_x = item->getColumn(COL_ROT_X);
+						LLScrollListCell* col_rot_y = item->getColumn(COL_ROT_Y);
+						LLScrollListCell* col_rot_z = item->getColumn(COL_ROT_Z);
+
+						col_rot_x->setValue(0.000f);
+						col_rot_y->setValue(0.000f);
+						col_rot_z->setValue(0.000f);
+
+						quat.setEulerAngles(0, 0, 0);
+						joint->setTargetRotation(quat);
+
+						//BD - If we are in Mirror mode, try to find the opposite bone of our currently
+						//     selected one, for now this simply means we take the name and replace "Left"
+						//     with "Right" and vise versa since all bones are conveniently that way.
+						//     TODO: Do this when creating the joint list so we don't try to find the joint
+						//     over and over again.
+						if (mMirrorMode)
+						{
+							LLJoint* mirror_joint = nullptr;
+							std::string mirror_joint_name = joint->getName();
+							S32 idx = joint->getName().find("Left");
+							if (idx != -1)
+								mirror_joint_name.replace(idx, mirror_joint_name.length(), "Right");
+
+							idx = joint->getName().find("Right");
+							if (idx != -1)
+								mirror_joint_name.replace(idx, mirror_joint_name.length(), "Left");
+
+							if (mirror_joint_name != joint->getName())
+							{
+								mirror_joint = gDragonAnimator.mTargetAvatar->mRoot->findJoint(mirror_joint_name);
+							}
+
+							if (mirror_joint)
+							{
+								//BD - We also need to find the opposite joint's list entry and change its values to reflect
+								//     the new ones, doing this here is still better than causing a complete refresh.
+								LLScrollListItem* item2 = mJointScrolls[JOINTS]->getItemByLabel(mirror_joint_name, FALSE, COL_NAME);
+								if (item2)
+								{
+									col_rot_x = item2->getColumn(COL_ROT_X);
+									col_rot_y = item2->getColumn(COL_ROT_Y);
+									col_rot_z = item2->getColumn(COL_ROT_Z);
+
+									col_rot_x->setValue(0.000f);
+									col_rot_y->setValue(0.000f);
+									col_rot_z->setValue(0.000f);
+
+									mirror_joint->setTargetRotation(quat);
+								}
+							}
+						}
+					}
+
+					//BD - Resetting positions next.
+					LLScrollListCell* col_pos_x = item->getColumn(COL_POS_X);
+					LLScrollListCell* col_pos_y = item->getColumn(COL_POS_Y);
+					LLScrollListCell* col_pos_z = item->getColumn(COL_POS_Z);
+					LLVector3 pos = mDefaultPositions[joint->getName()];
+
+					col_pos_x->setValue(ll_round(pos.mV[VX], 0.001f));
+					col_pos_y->setValue(ll_round(pos.mV[VY], 0.001f));
+					col_pos_z->setValue(ll_round(pos.mV[VZ], 0.001f));
+					joint->setTargetPosition(pos);
+
+					//BD - Resetting scales last.
+					LLScrollListCell* col_scale_x = item->getColumn(COL_SCALE_X);
+					LLScrollListCell* col_scale_y = item->getColumn(COL_SCALE_Y);
+					LLScrollListCell* col_scale_z = item->getColumn(COL_SCALE_Z);
+					LLVector3 scale = mDefaultScales[joint->getName()];
+
+					col_scale_x->setValue(ll_round(scale.mV[VX], 0.001f));
+					col_scale_y->setValue(ll_round(scale.mV[VY], 0.001f));
+					col_scale_z->setValue(ll_round(scale.mV[VZ], 0.001f));
+					joint->setScale(scale);
+				}
+			}
+		}
+	}
+
+	onJointControlsRefresh();
+}
+
+//BD - Used to reset rotations only.
+void BDFloaterPoseCreator::onJointRotationReset()
+{
+	//BD - While editing rotations, make sure we use a bit of spherical linear interpolation 
+	//     to make movements smoother.
+	BDPosingMotion* motion = (BDPosingMotion*)gAgentAvatarp->findMotion(ANIM_BD_POSING_MOTION);
+	if (motion)
+	{
+		//BD - If we don't use our default spherical interpolation, set it once.
+		motion->setInterpolationTime(0.25f);
+		motion->setInterpolationType(2);
+	}
+
+	for (auto item : mJointScrolls[JOINTS]->getAllSelected())
+	{
+		if (item)
+		{
+			LLJoint* joint = (LLJoint*)item->getUserdata();
+			if (joint)
+			{
+				LLQuaternion quat;
+				LLScrollListCell* col_x = item->getColumn(COL_ROT_X);
+				LLScrollListCell* col_y = item->getColumn(COL_ROT_Y);
+				LLScrollListCell* col_z = item->getColumn(COL_ROT_Z);
+
+				col_x->setValue(0.000f);
+				col_y->setValue(0.000f);
+				col_z->setValue(0.000f);
+
+				quat.setEulerAngles(0, 0, 0);
+				joint->setTargetRotation(quat);
+
+				//BD - If we are in Mirror mode, try to find the opposite bone of our currently
+				//     selected one, for now this simply means we take the name and replace "Left"
+				//     with "Right" and vise versa since all bones are conveniently that way.
+				//     TODO: Do this when creating the joint list so we don't try to find the joint
+				//     over and over again.
+				if (mMirrorMode)
+				{
+					LLJoint* mirror_joint = nullptr;
+					std::string mirror_joint_name = joint->getName();
+					S32 idx = joint->getName().find("Left");
+					if (idx != -1)
+						mirror_joint_name.replace(idx, mirror_joint_name.length(), "Right");
+
+					idx = joint->getName().find("Right");
+					if (idx != -1)
+						mirror_joint_name.replace(idx, mirror_joint_name.length(), "Left");
+
+					if (mirror_joint_name != joint->getName())
+					{
+						mirror_joint = gDragonAnimator.mTargetAvatar->mRoot->findJoint(mirror_joint_name);
+					}
+
+					if (mirror_joint)
+					{
+						//BD - We also need to find the opposite joint's list entry and change its values to reflect
+						//     the new ones, doing this here is still better than causing a complete refresh.
+						LLScrollListItem* item2 = mJointScrolls[JOINTS]->getItemByLabel(mirror_joint_name, FALSE, COL_NAME);
+						if (item2)
+						{
+							col_x = item2->getColumn(COL_ROT_X);
+							col_y = item2->getColumn(COL_ROT_Y);
+							col_z = item2->getColumn(COL_ROT_Z);
+
+							col_x->setValue(0.000f);
+							col_y->setValue(0.000f);
+							col_z->setValue(0.000f);
+
+							mirror_joint->setTargetRotation(quat);
+						}
+					}
+				}
+			}
+		}
+	}
+
+	onJointControlsRefresh();
+}
+
+//BD - Used to reset positions, this is very tricky hence why it was separated.
+//     It causes the avatar to flinch for a second which doesn't look as nice as resetting
+//     rotations does.
+void BDFloaterPoseCreator::onJointPositionReset()
+{
+	S32 index = mJointTabs->getCurrentPanelIndex();
+
+	//BD - When resetting positions, we don't use interpolation for now, it looks stupid.
+	BDPosingMotion* motion = (BDPosingMotion*)gAgentAvatarp->findMotion(ANIM_BD_POSING_MOTION);
+	if (motion)
+	{
+		motion->setInterpolationTime(0.25f);
+		motion->setInterpolationType(2);
+	}
+
+	//BD - We use this bool to prevent going through attachment override reset every single time.
+	//bool has_reset = false;
+	for (auto item : mJointScrolls[index]->getAllSelected())
+	{
+		if (item)
+		{
+			LLJoint* joint = (LLJoint*)item->getUserdata();
+			if (joint)
+			{
+				LLScrollListCell* col_pos_x = item->getColumn(COL_POS_X);
+				LLScrollListCell* col_pos_y = item->getColumn(COL_POS_Y);
+				LLScrollListCell* col_pos_z = item->getColumn(COL_POS_Z);
+				LLVector3 pos = mDefaultPositions[joint->getName()];
+
+				col_pos_x->setValue(ll_round(pos.mV[VX], 0.001f));
+				col_pos_y->setValue(ll_round(pos.mV[VY], 0.001f));
+				col_pos_z->setValue(ll_round(pos.mV[VZ], 0.001f));
+				joint->setTargetPosition(pos);
+			}
+		}
+	}
+
+	onJointControlsRefresh();
+}
+
+//BD - Used to reset scales only.
+void BDFloaterPoseCreator::onJointScaleReset()
+{
+	S32 index = mJointTabs->getCurrentPanelIndex();
+
+	//BD - Clear all attachment bone scale changes we've done, they are not automatically
+	//     reverted.
+	for (auto item : mJointScrolls[index]->getAllSelected())
+	{
+		if (item)
+		{
+			LLJoint* joint = (LLJoint*)item->getUserdata();
+			if (joint)
+			{
+				LLScrollListCell* col_scale_x = item->getColumn(COL_SCALE_X);
+				LLScrollListCell* col_scale_y = item->getColumn(COL_SCALE_Y);
+				LLScrollListCell* col_scale_z = item->getColumn(COL_SCALE_Z);
+				LLVector3 scale = mDefaultScales[joint->getName()];
+
+				col_scale_x->setValue(ll_round(scale.mV[VX], 0.001f));
+				col_scale_y->setValue(ll_round(scale.mV[VY], 0.001f));
+				col_scale_z->setValue(ll_round(scale.mV[VZ], 0.001f));
+				joint->setScale(scale);
+			}
+		}
+	}
+	onJointControlsRefresh();
+}
+
+//BD - Used to revert rotations only.
+void BDFloaterPoseCreator::onJointRotationRevert()
+{
+	//BD - While editing rotations, make sure we use a bit of spherical linear interpolation 
+	//     to make movements smoother.
+	BDPosingMotion* motion = (BDPosingMotion*)gAgentAvatarp->findMotion(ANIM_BD_POSING_MOTION);
+	if (motion)
+	{
+		//BD - If we don't use our default spherical interpolation, set it once.
+		motion->setInterpolationTime(0.25f);
+		motion->setInterpolationType(2);
+	}
+
+	for (auto item : mJointScrolls[JOINTS]->getAllSelected())
+	{
+		if (item)
+		{
+			LLJoint* joint = (LLJoint*)item->getUserdata();
+			if (joint)
+			{
+				//BD - Reverting rotations first if there are any.
+				LLQuaternion quat = mDefaultRotations[joint->getName()];
+				LLVector3 rot;
+				quat.getEulerAngles(&rot.mV[VX], &rot.mV[VY], &rot.mV[VZ]);
+				LLScrollListCell* col_rot_x = item->getColumn(COL_ROT_X);
+				LLScrollListCell* col_rot_y = item->getColumn(COL_ROT_Y);
+				LLScrollListCell* col_rot_z = item->getColumn(COL_ROT_Z);
+
+				col_rot_x->setValue(rot.mV[VX]);
+				col_rot_y->setValue(rot.mV[VY]);
+				col_rot_z->setValue(rot.mV[VZ]);
+
+				joint->setTargetRotation(quat);
+
+				//BD - If we are in Mirror mode, try to find the opposite bone of our currently
+				//     selected one, for now this simply means we take the name and replace "Left"
+				//     with "Right" and vise versa since all bones are conveniently that way.
+				//     TODO: Do this when creating the joint list so we don't try to find the joint
+				//     over and over again.
+				if (mMirrorMode)
+				{
+					LLJoint* mirror_joint = nullptr;
+					std::string mirror_joint_name = joint->getName();
+					S32 idx = joint->getName().find("Left");
+					if (idx != -1)
+						mirror_joint_name.replace(idx, mirror_joint_name.length(), "Right");
+
+					idx = joint->getName().find("Right");
+					if (idx != -1)
+						mirror_joint_name.replace(idx, mirror_joint_name.length(), "Left");
+
+					if (mirror_joint_name != joint->getName())
+					{
+						mirror_joint = gDragonAnimator.mTargetAvatar->mRoot->findJoint(mirror_joint_name);
+					}
+
+					if (mirror_joint)
+					{
+						//BD - We also need to find the opposite joint's list entry and change its values to reflect
+						//     the new ones, doing this here is still better than causing a complete refresh.
+						LLScrollListItem* item2 = mJointScrolls[JOINTS]->getItemByLabel(mirror_joint_name, FALSE, COL_NAME);
+						if (item2)
+						{
+							col_rot_x = item2->getColumn(COL_ROT_X);
+							col_rot_y = item2->getColumn(COL_ROT_Y);
+							col_rot_z = item2->getColumn(COL_ROT_Z);
+
+							col_rot_x->setValue(rot.mV[VX]);
+							col_rot_y->setValue(rot.mV[VY]);
+							col_rot_z->setValue(rot.mV[VZ]);
+
+							mirror_joint->setTargetRotation(quat);
+						}
+					}
+				}
+			}
+		}
+	}
+
+	onJointControlsRefresh();
+}
+
+//BD - Flip our pose (mirror it)
+void BDFloaterPoseCreator::onFlipPose()
+{
+	LLVOAvatar* avatar = gDragonAnimator.mTargetAvatar;
+	if (!avatar || avatar->isDead()) return;
+
+	if (!(avatar->getRegion() == gAgent.getRegion())) return;
+
+	LLJoint* joint = nullptr;
+	bool flipped[134] = { false };
+
+	for (S32 i = 0; (joint = avatar->getCharacterJoint(i)); ++i)
+	{
+		//BD - Skip if we already flipped this bone.
+		if (flipped[i]) continue;
+
+		//BD - Nothing? Invalid? Skip, when we hit the end we'll break out anyway.
+		if (!joint)	continue;
+
+		LLVector3 rot, mirror_rot;
+		LLQuaternion rot_quat, mirror_rot_quat;
+		std::string joint_name = joint->getName();
+		std::string mirror_joint_name = joint->getName();
+		//BD - Attempt to find the "right" version of this bone first, we assume we always
+		//     end up with the "left" version of a bone first.
+		S32 idx = joint->getName().find("Left");
+		if (idx != -1)
+			mirror_joint_name.replace(idx, mirror_joint_name.length(), "Right");
+		//BD - Attempt to find the "right" version of this bone first, this is necessary
+		//     because there are a couple bones starting with the "right" bone.
+		idx = joint->getName().find("Right");
+		if (idx != -1)
+			mirror_joint_name.replace(idx, mirror_joint_name.length(), "Left");
+
+		LLJoint* mirror_joint = nullptr;
+		if (mirror_joint_name != joint->getName())
+			mirror_joint = gDragonAnimator.mTargetAvatar->mRoot->findJoint(mirror_joint_name);
+
+		//BD - Collect the joint and mirror joint entries and their cells, we need them later.
+		LLScrollListItem* item1 = mJointScrolls[JOINTS]->getItemByLabel(joint_name, FALSE, COL_NAME);
+		LLScrollListItem* item2 = nullptr;
+
+		//BD - Get the rotation of our current bone and that of the mirror bone (if available).
+		//     Flip our current bone's rotation and apply it to the mirror bone (if available).
+		//     Flip the mirror bone's rotation (if available) and apply it to our current bone.
+		//     If the mirror bone does not exist, flip the current bone rotation and use that.
+		rot_quat = joint->getTargetRotation();
+		LLQuaternion inv_rot_quat = LLQuaternion(-rot_quat.mQ[VX], rot_quat.mQ[VY], -rot_quat.mQ[VZ], rot_quat.mQ[VW]);
+		inv_rot_quat.getEulerAngles(&rot[VX], &rot[VY], &rot[VZ]);
+
+		if (mirror_joint)
+		{
+			mirror_rot_quat = mirror_joint->getTargetRotation();
+			LLQuaternion inv_mirror_rot_quat = LLQuaternion(-mirror_rot_quat.mQ[VX], mirror_rot_quat.mQ[VY], -mirror_rot_quat.mQ[VZ], mirror_rot_quat.mQ[VW]);
+			inv_mirror_rot_quat.getEulerAngles(&mirror_rot[VX], &mirror_rot[VY], &mirror_rot[VZ]);
+			mirror_joint->setTargetRotation(inv_rot_quat);
+			joint->setTargetRotation(inv_mirror_rot_quat);
+
+			item2 = mJointScrolls[JOINTS]->getItemByLabel(mirror_joint_name, FALSE, COL_NAME);
+
+			//BD - Make sure we flag this bone as flipped so we skip it next time we iterate over it.
+			flipped[mirror_joint->getJointNum()] = true;
+		}
+		else
+		{
+			joint->setTargetRotation(inv_rot_quat);
+		}
+
+		S32 axis = 0;
+		while (axis <= 2)
+		{
+			//BD - Now flip the list entry values.
+			if (item1)
+			{
+				if (mirror_joint)
+					item1->getColumn(axis + 2)->setValue(ll_round(mirror_rot[axis], 0.001f));
+				else
+					item1->getColumn(axis + 2)->setValue(ll_round(rot[axis], 0.001f));
+			}
+
+			//BD - Now flip the mirror joint list entry values.
+			if (item2)
+				item2->getColumn(axis + 2)->setValue(ll_round(rot[axis], 0.001f));
+
+			++axis;
+		}
+		flipped[i] = true;
+	}
+}
+
+////////////////////////////////
+//BD - Utility Functions
+////////////////////////////////
+//BD - Poser Utility Functions
+void BDFloaterPoseCreator::onJointPasteRotation()
+{
+	for (auto item : mJointScrolls[JOINTS]->getAllSelected())
+	{
+		LLJoint* joint = (LLJoint*)item->getUserdata();
+		if (joint)
+		{
+			LLScrollListCell* col_rot_x = item->getColumn(COL_ROT_X);
+			LLScrollListCell* col_rot_y = item->getColumn(COL_ROT_Y);
+			LLScrollListCell* col_rot_z = item->getColumn(COL_ROT_Z);
+
+			LLVector3 euler_rot;
+			LLQuaternion rot = (LLQuaternion)mClipboard["rot"];
+
+			joint->setTargetRotation(rot);
+
+			rot.getEulerAngles(&euler_rot.mV[VX], &euler_rot.mV[VY], &euler_rot.mV[VZ]);
+			col_rot_x->setValue(ll_round(euler_rot.mV[VX], 0.001f));
+			col_rot_y->setValue(ll_round(euler_rot.mV[VY], 0.001f));
+			col_rot_z->setValue(ll_round(euler_rot.mV[VZ], 0.001f));
+		}
+	}
+}
+
+void BDFloaterPoseCreator::onJointPastePosition()
+{
+	for (auto item : mJointScrolls[JOINTS]->getAllSelected())
+	{
+		LLJoint* joint = (LLJoint*)item->getUserdata();
+		if (joint)
+		{
+			LLScrollListCell* col_pos_x = item->getColumn(COL_POS_X);
+			LLScrollListCell* col_pos_y = item->getColumn(COL_POS_Y);
+			LLScrollListCell* col_pos_z = item->getColumn(COL_POS_Z);
+
+			LLVector3 pos = (LLVector3)mClipboard["pos"];
+
+			joint->setTargetPosition(pos);
+
+			col_pos_x->setValue(ll_round(pos.mV[VX], 0.001f));
+			col_pos_y->setValue(ll_round(pos.mV[VY], 0.001f));
+			col_pos_z->setValue(ll_round(pos.mV[VZ], 0.001f));
+		}
+	}
+}
+
+void BDFloaterPoseCreator::onJointPasteScale()
+{
+	for (auto item : mJointScrolls[JOINTS]->getAllSelected())
+	{
+		LLJoint* joint = (LLJoint*)item->getUserdata();
+		if (joint)
+		{
+			LLScrollListCell* col_scale_x = item->getColumn(COL_SCALE_X);
+			LLScrollListCell* col_scale_y = item->getColumn(COL_SCALE_Y);
+			LLScrollListCell* col_scale_z = item->getColumn(COL_SCALE_Z);
+			LLVector3 scale = (LLVector3)mClipboard["scale"];
+
+			joint->setScale(scale);
+
+			col_scale_x->setValue(ll_round(scale.mV[VX], 0.001f));
+			col_scale_y->setValue(ll_round(scale.mV[VY], 0.001f));
+			col_scale_z->setValue(ll_round(scale.mV[VZ], 0.001f));
+		}
+	}
+}
+
+void BDFloaterPoseCreator::onJointMirror()
+{
+	for (auto item : mJointScrolls[JOINTS]->getAllSelected())
+	{
+		LLJoint* joint = (LLJoint*)item->getUserdata();
+		if (joint)
+		{
+			LLVector3 euler_rot;
+			LLQuaternion rot_quat = joint->getTargetRotation();
+
+			//BD - Simply mirror the current bone's rotation like we'd do if we pressed the mirror
+			//     button without a mirror bone available.
+			LLQuaternion inv_rot_quat = LLQuaternion(-rot_quat.mQ[VX], rot_quat.mQ[VY], -rot_quat.mQ[VZ], rot_quat.mQ[VW]);
+			inv_rot_quat.getEulerAngles(&euler_rot[VX], &euler_rot[VY], &euler_rot[VZ]);
+			joint->setTargetRotation(inv_rot_quat);
+
+			LLScrollListCell* col_rot_x = item->getColumn(COL_ROT_X);
+			LLScrollListCell* col_rot_y = item->getColumn(COL_ROT_Y);
+			LLScrollListCell* col_rot_z = item->getColumn(COL_ROT_Z);
+
+			col_rot_x->setValue(ll_round(euler_rot.mV[VX], 0.001f));
+			col_rot_y->setValue(ll_round(euler_rot.mV[VY], 0.001f));
+			col_rot_z->setValue(ll_round(euler_rot.mV[VZ], 0.001f));
+		}
+	}
+}
+
+void BDFloaterPoseCreator::onJointSymmetrize()
+{
+	for (auto item : mJointScrolls[JOINTS]->getAllSelected())
+	{
+		LLJoint* joint = (LLJoint*)item->getUserdata();
+		if (joint)
+		{
+			std::string joint_name = joint->getName();
+			std::string mirror_joint_name = joint->getName();
+			//BD - Attempt to find the "right" version of this bone, if we can't find it try
+			//     the left version.
+			S32 idx = joint->getName().find("Left");
+			if (idx != -1)
+				mirror_joint_name.replace(idx, mirror_joint_name.length(), "Right");
+			idx = joint->getName().find("Right");
+			if (idx != -1)
+				mirror_joint_name.replace(idx, mirror_joint_name.length(), "Left");
+
+			LLJoint* mirror_joint = nullptr;
+			if (mirror_joint_name != joint->getName())
+				mirror_joint = gDragonAnimator.mTargetAvatar->mRoot->findJoint(mirror_joint_name);
+
+			//BD - Get the rotation of the mirror bone (if available).
+			//     Flip the mirror bone's rotation (if available) and apply it to our current bone.
+			if (mirror_joint)
+			{
+				LLVector3 mirror_rot;
+				LLQuaternion mirror_rot_quat;
+				mirror_rot_quat = mirror_joint->getTargetRotation();
+				LLQuaternion inv_mirror_rot_quat = LLQuaternion(-mirror_rot_quat.mQ[VX], mirror_rot_quat.mQ[VY], -mirror_rot_quat.mQ[VZ], mirror_rot_quat.mQ[VW]);
+				inv_mirror_rot_quat.getEulerAngles(&mirror_rot[VX], &mirror_rot[VY], &mirror_rot[VZ]);
+				joint->setTargetRotation(inv_mirror_rot_quat);
+
+				LLScrollListCell* col_rot_x = item->getColumn(COL_ROT_X);
+				LLScrollListCell* col_rot_y = item->getColumn(COL_ROT_Y);
+				LLScrollListCell* col_rot_z = item->getColumn(COL_ROT_Z);
+
+				col_rot_x->setValue(ll_round(mirror_rot.mV[VX], 0.001f));
+				col_rot_y->setValue(ll_round(mirror_rot.mV[VY], 0.001f));
+				col_rot_z->setValue(ll_round(mirror_rot.mV[VZ], 0.001f));
+			}
+		}
+	}
+}
+
+void BDFloaterPoseCreator::onJointCopyTransforms()
+{
+	LLScrollListItem* item = mJointScrolls[JOINTS]->getFirstSelected();
+	LLJoint* joint = (LLJoint*)item->getUserdata();
+	if (joint)
+	{
+		mClipboard["rot"] = joint->getTargetRotation().getValue();
+		mClipboard["pos"] = joint->getTargetPosition().getValue();
+		mClipboard["scale"] = joint->getScale().getValue();
+		LL_INFOS("Posing") << "Copied all transforms " << LL_ENDL;
+	}
+}
+
+bool BDFloaterPoseCreator::onJointContextMenuEnable(const LLSD& param)
+{
+	std::string action = param.asString();
+	if (action == "clipboard")
+	{
+		return mClipboard.has("rot");
+	}
+	if (action == "enable_bone")
+	{
+		LLScrollListItem* item = mJointScrolls[JOINTS]->getFirstSelected();
+		if (item)
+		{
+			LLJoint* joint = (LLJoint*)item->getUserdata();
+			if (joint)
+			{
+				BDPosingMotion* motion = (BDPosingMotion*)gAgentAvatarp->findMotion(ANIM_BD_POSING_MOTION);
+				LLPose* pose = motion->getPose();
+				if (pose)
+				{
+					LLPointer<LLJointState> joint_state = pose->findJointState(joint);
+					return joint_state;
+				}
+			}
+		}
+	}
+	return false;
+}
+
+void BDFloaterPoseCreator::onJointContextMenuAction(const LLSD& param)
+{
+	std::string action = param.asString();
+	if (action == "copy_transforms")
+	{
+		onJointCopyTransforms();
+	}
+	else if (action == "paste_rot")
+	{
+		onJointPasteRotation();
+	}
+	else if (action == "paste_pos")
+	{
+		onJointPastePosition();
+	}
+	else if (action == "paste_scale")
+	{
+		onJointPasteScale();
+	}
+	else if (action == "paste_rot_pos")
+	{
+		onJointPasteRotation();
+		onJointPastePosition();
+	}
+	else if (action == "paste_rot_scale")
+	{
+		onJointPasteRotation();
+		onJointPasteScale();
+	}
+	else if (action == "paste_pos_scale")
+	{
+		onJointPastePosition();
+		onJointPasteScale();
+	}
+	else if (action == "paste_all")
+	{
+		onJointPasteRotation();
+		onJointPastePosition();
+		onJointPasteScale();
+	}
+	else if (action == "symmetrize")
+	{
+		onJointSymmetrize();
+	}
+	else if (action == "mirror")
+	{
+		onJointMirror();
+	}
+	else if (action == "enable_bone")
+	{
+		onJointChangeState();
+	}
+	else if (action == "enable_override")
+	{
+
+	}
+	else if (action == "enable_offset")
+	{
+
+	}
+	else if (action == "reset_rot")
+	{
+		onJointRotationReset();
+	}
+	else if (action == "reset_pos")
+	{
+		onJointPositionReset();
+	}
+	else if (action == "reset_scale")
+	{
+		onJointScaleReset();
+	}
+	else if (action == "reset_all")
+	{
+		//BD - We do all 3 here because the combined function resets all bones regardless of
+		//     our selection, these only reset the selected ones.
+		onJointRotationReset();
+		onJointPositionReset();
+		onJointScaleReset();
+	}
+}
+
+////////////////////////////////
+//BD - Misc Functions
+////////////////////////////////
+LLKeyframeMotion* BDFloaterPoseCreator::onReadyTempMotion(std::string filename, bool eternal)
+{
+	std::string outfilename = gDirUtilp->getExpandedFilename(LL_PATH_ANIMATIONS, filename);
+	S32 file_size;
+	BOOL success = FALSE;
+	LLAPRFile infile;
+	LLKeyframeMotion* mTempMotion = NULL;
+	LLAssetID mMotionID;
+	LLTransactionID	mTransactionID;
+
+	//BD - To make this work we'll first need a unique UUID for this animation.
+	mTransactionID.generate();
+	mMotionID = mTransactionID.makeAssetID(gAgent.getSecureSessionID());
+	mTempMotion = (LLKeyframeMotion*)gAgentAvatarp->createMotion(mMotionID);
+
+	LL_INFOS("Posing") << mMotionID << LL_ENDL;
+
+	//BD - Find and open the file, we'll need to write it temporarily into the VFS pool.
+	infile.open(outfilename, LL_APR_RB, NULL, &file_size);
+	if (infile.getFileHandle())
+	{
+		U8 *anim_data;
+		S32 anim_file_size;
+
+		LLFileSystem file(mMotionID, LLAssetType::AT_ANIMATION, LLFileSystem::WRITE);
+		const S32 buf_size = 65536;
+		U8 copy_buf[buf_size];
+		while ((file_size = infile.read(copy_buf, buf_size)))
+		{
+			file.write(copy_buf, file_size);
+		}
+
+		//BD - Now that we wrote the temporary file, find it and use it to set the size
+		//     and buffer into which we will unpack the .anim file into.
+		LLFileSystem* anim_file = new LLFileSystem(mMotionID, LLAssetType::AT_ANIMATION);
+		anim_file_size = anim_file->getSize();
+		anim_data = new U8[anim_file_size];
+		anim_file->read(anim_data, anim_file_size);
+
+		//BD - Cleanup everything we don't need anymore.
+		delete anim_file;
+		anim_file = NULL;
+
+		//BD - Use the datapacker now to actually deserialize and unpack the animation
+		//     into our temporary motion so we can use it after we added it into the list.
+		LLDataPackerBinaryBuffer dp(anim_data, anim_file_size);
+		success = mTempMotion && mTempMotion->deserialize(dp, mMotionID);
+
+		if (success && eternal)
+		{
+			mTempMotion->setEternal(true);
+		}
+
+		//BD - Cleanup the rest.
+		delete[]anim_data;
+	}
+	infile.close();
+
+	//return success ? mMotionID : LLUUID::null;
+	return mTempMotion;
+}
+
+void BDFloaterPoseCreator::onCreateTempMotion()
+{
+	LLAssetID mMotionID;
+	LLTransactionID	mTransactionID;
+
+	//BD - To make this work we'll first need a unique UUID for this animation.
+	mTransactionID.generate();
+	mMotionID = mTransactionID.makeAssetID(gAgent.getSecureSessionID());
+
+	LLKeyframeMotion* temp_motion = (LLKeyframeMotion*)gAgentAvatarp->createMotion(mMotionID);
+
+	if (!temp_motion)
+		return;
+
+	LLKeyframeMotion::JointMotionList* mTempMotionList = new LLKeyframeMotion::JointMotionList;
+	std::vector<LLPointer<LLJointState>> joint_states;
+
+	if (!mTempMotionList)
+		return;
+
+	if (!mTempMotionList->mJointMotionArray.empty())
+		mTempMotionList->mJointMotionArray.clear();
+	mTempMotionList->mJointMotionArray.reserve(134);
+	joint_states.clear();
+	joint_states.reserve(134);
+
+	mTempMotionList->mBasePriority = (LLJoint::JointPriority)getChild<LLUICtrl>("base_priority")->getValue().asInteger();
+	mTempMotionList->mDuration = getChild<LLUICtrl>("keyframe_duration")->getValue().asReal();
+	mTempMotionList->mEaseInDuration = getChild<LLUICtrl>("ease_in")->getValue().asReal();
+	mTempMotionList->mEaseOutDuration = getChild<LLUICtrl>("ease_out")->getValue().asReal();
+	mTempMotionList->mEmoteName = "";
+	mTempMotionList->mHandPose = (LLHandMotion::eHandPose)getChild<LLUICtrl>("hand_poses")->getValue().asInteger();
+	mTempMotionList->mLoop = true;
+	mTempMotionList->mLoopInPoint = 0.0f;
+	mTempMotionList->mLoopOutPoint = getChild<LLUICtrl>("keyframe_duration")->getValue().asReal();
+	mTempMotionList->mMaxPriority = LLJoint::HIGHEST_PRIORITY;
+
+	for (S32 i = 0; i <= ATTACHMENT_BONES; i++)
+	{
+		for (auto item : mJointScrolls[i]->getAllData())
+		{
+			LLJoint* joint = (LLJoint*)item->getUserdata();
+			if (joint)
+			{
+				//BD - Create a new joint motion and add it to the pile.
+				LLKeyframeMotion::JointMotion* joint_motion = new LLKeyframeMotion::JointMotion;
+				mTempMotionList->mJointMotionArray.push_back(joint_motion);
+
+				//BD - Fill out joint motion with relevant basic data.
+				joint_motion->mJointName = joint->getName();
+				joint_motion->mPriority = LLJoint::HIGHEST_PRIORITY;
+
+				//BD - Create the basic joint state for this joint and add it to the joint states.
+				LLPointer<LLJointState> joint_state = new LLJointState;
+				joint_states.push_back(joint_state);
+				joint_state->setJoint(joint); // note: can accept NULL
+				joint_state->setUsage(0);
+
+				//BD - Start with filling general rotation data in.
+				joint_motion->mRotationCurve.mNumKeys = 0;
+				joint_motion->mRotationCurve.mInterpolationType = LLKeyframeMotion::IT_LINEAR;
+
+				//BD - Create a rotation key and put it into the rotation curve.
+				/*LLKeyframeMotion::RotationKey rotation_key = LLKeyframeMotion::RotationKey(1.0f, joint->getTargetRotation());
+				joint_motion->mRotationCurve.mKeys[0] = rotation_key;
+				joint_state->setUsage(joint_state->getUsage() | LLJointState::ROT);*/
+
+				//BD - Fill general position data in.
+				joint_motion->mPositionCurve.mNumKeys = 0;
+				joint_motion->mPositionCurve.mInterpolationType = LLKeyframeMotion::IT_LINEAR;
+
+				/*LLKeyframeMotion::PositionKey position_key;
+				if (joint->mHasPosition)
+				{
+					//BD - Create a position key and put it into the position curve.
+					LLKeyframeMotion::PositionKey position_key = LLKeyframeMotion::PositionKey(1.f, joint->getTargetPosition());
+					joint_motion->mPositionCurve.mKeys[0] = position_key;
+					joint_state->setUsage(joint_state->getUsage() | LLJointState::POS);
+				}*/
+
+				temp_motion->addJointState(joint_state);
+
+				//BD - Start with filling general scale data in.
+				joint_motion->mScaleCurve.mNumKeys = 0;
+				joint_motion->mScaleCurve.mInterpolationType = LLKeyframeMotion::IT_LINEAR;
+
+				//BD - Create a scale key and put it into the scale curve.
+				/*LLKeyframeMotion::ScaleKey scale_key = LLKeyframeMotion::ScaleKey(1.f, joint->getScale());
+				joint_motion->mScaleCurve.mKeys[0] = scale_key;*/
+
+				joint_motion->mUsage = joint_state->getUsage();
+
+				//BD - We do not use constraints so we just leave them out here.
+				//     Should we ever add them we'd do so here.
+				//joint_motion_list->mConstraints.push_front(constraint);
+
+				//BD - Get the pelvis's bounding box and add it.
+				if (joint->getJointNum() == 0)
+				{
+					//mTempMotionList->mPelvisBBox.addPoint(position_key.mPosition);
+					mTempMotionList->mPelvisBBox.addPoint(joint->getTargetPosition());
+				}
+			}
+		}
+	}
+
+	temp_motion->setJointMotionList(mTempMotionList);
+	//BD - We export this file immediately so we can import it later.
+	//     This is going to be the animation we write everything we do into
+	//     and it also neatly acts as an "autosave" thing that we can just
+	//     load back in case we quit out of the pose creator.
+	//     The main reason we do this however is because we are going to import
+	//     this animation locally which will create a fully setup LLKeyframeAnimation
+	//     that doesn't have some weird things going on causing crashes, it is also
+	//     a lot easier to do.
+	temp_motion->dumpToFile("_poser_temp", false);
+	//return temp_motion;
+}
+
+//BD - This is used to collect all default values at the beginning to revert to later on.
+void BDFloaterPoseCreator::onCollectDefaults()
+{
+	LLQuaternion rot;
+	LLVector3 pos;
+	LLVector3 scale;
+	LLJoint* joint;
+
+	//BD - Getting collision volumes and attachment points.
+	std::vector<std::string> joint_names, cv_names, attach_names;
+	gAgentAvatarp->getSortedJointNames(0, joint_names);
+	gAgentAvatarp->getSortedJointNames(1, cv_names);
+	gAgentAvatarp->getSortedJointNames(2, attach_names);
+
+	mDefaultRotations.clear();
+	mDefaultScales.clear();
+	mDefaultPositions.clear();
+
+	for (auto name : joint_names)
+	{
+		joint = gAgentAvatarp->getJoint(name);
+		//BD - Nothing? Invalid? Skip, when we hit the end we'll break out anyway.
+		if (!joint)	continue;
+
+		LLSD row;
+
+		rot = joint->getTargetRotation();
+		mDefaultRotations.insert(std::pair<std::string, LLQuaternion>(name, rot));
+
+		//BD - We always get the values but we don't write them out as they are not relevant for the
+		//     user yet but we need them to establish default values we revert to later on.
+		scale = joint->getScale();
+		mDefaultScales.insert(std::pair<std::string, LLVector3>(name, scale));
+
+		//BD - We always get the values but we don't write them out as they are not relevant for the
+		//     user yet but we need them to establish default values we revert to later on.
+		pos = joint->getPosition();
+		mDefaultPositions.insert(std::pair<std::string, LLVector3>(name, pos));
+	}
+
+	//BD - Collision Volumes
+	for (auto name : cv_names)
+	{
+		LLJoint* joint = gAgentAvatarp->getJoint(name);
+		//BD - Nothing? Invalid? Skip, when we hit the end we'll break out anyway.
+		if (!joint)	continue;
+
+		//BD - We always get the values but we don't write them out as they are not relevant for the
+		//     user yet but we need them to establish default values we revert to later on.
+		pos = joint->getPosition();
+		scale = joint->getScale();
+		mDefaultPositions.insert(std::pair<std::string, LLVector3>(name, pos));
+		mDefaultScales.insert(std::pair<std::string, LLVector3>(name, scale));
+	}
+
+	//BD - Attachment Bones
+	for (auto name : attach_names)
+	{
+		LLJoint* joint = gAgentAvatarp->getJoint(name);
+		//BD - Nothing? Invalid? Skip, when we hit the end we'll break out anyway.
+		if (!joint)	continue;
+
+		//BD - We always get the values but we don't write them out as they are not relevant for the
+		//     user yet but we need them to establish default values we revert to later on.
+		pos = joint->getPosition();
+		scale = joint->getScale();
+
+		mDefaultPositions.insert(std::pair<std::string, LLVector3>(name, pos));
+		mDefaultScales.insert(std::pair<std::string, LLVector3>(name, scale));
+	}
+}
\ No newline at end of file
diff --git a/indra/newview/bdfloaterposecreator.h b/indra/newview/bdfloaterposecreator.h
new file mode 100644
index 0000000000000000000000000000000000000000..c66eb4f638f3e28a6f2a1f170557d5ec24517b4e
--- /dev/null
+++ b/indra/newview/bdfloaterposecreator.h
@@ -0,0 +1,132 @@
+/**
+*
+* Copyright (C) 2018, NiranV Dean
+*
+* 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.
+*
+*/
+
+
+#ifndef BD_FLOATER_POSE_CREATOR_H
+#define BD_FLOATER_POSE_CREATOR_H
+
+#include "llfloater.h"
+#include "llscrolllistctrl.h"
+#include "llsliderctrl.h"
+#include "lltabcontainer.h"
+#include "llkeyframemotion.h"
+#include "lltoggleablemenu.h"
+#include "llmenubutton.h"
+#include "llspinctrl.h"
+#include "bdposingmotion.h"
+
+#include "llviewerobject.h"
+
+class BDFloaterPoseCreator :
+	public LLFloater
+{
+	friend class LLFloaterReg;
+private:
+	BDFloaterPoseCreator(const LLSD& key);
+	/*virtual*/	~BDFloaterPoseCreator();
+	/*virtual*/	BOOL postBuild();
+	/*virtual*/ void draw();
+	/*virtual*/ void onOpen(const LLSD& key);
+	/*virtual*/	void onClose(bool app_quitting);
+
+	//BD - Posing
+	bool onClickPoseSave(const LLSD& param);
+	void onPoseStart();
+	bool onPoseExport();
+	void onPoseImport();
+	void onPoseStartStop();
+	void onPoseReapply();
+	void onEditAnimationInfo(const LLSD& param);
+	void onInterpolationChange(LLUICtrl* ctrl);
+	void onAnimationDurationCheck();
+
+	//BD - Joints
+	void onJointRefresh();
+	void onJointSet(LLUICtrl* ctrl, const LLSD& param);
+	void onJointPosSet(LLUICtrl* ctrl, const LLSD& param);
+	void onJointScaleSet(LLUICtrl* ctrl, const LLSD& param);
+	void onJointChangeState();
+	void onJointControlsRefresh();
+	void onJointRotPosScaleReset();
+	void onJointRotationReset();
+	void onJointPositionReset();
+	void onJointScaleReset();
+	void onJointRotationRevert();
+	void onCollectDefaults();
+	void onJointContextMenuAction(const LLSD& param);
+	bool onJointContextMenuEnable(const LLSD& param);
+	//BD - Joints - Utilities
+	void onJointPasteRotation();
+	void onJointPastePosition();
+	void onJointPasteScale();
+	void onJointMirror();
+	void onJointSymmetrize();
+	void onJointCopyTransforms();
+	//BD - Keyframes
+	void onKeyframeSelect();
+	void onKeyframeAdd();
+	void onKeyframeAdd(F32 time, LLJoint* joint);
+	void onKeyframeRemove();
+	void onKeyframeTime();
+	void onKeyframeRefresh();
+	void onKeyframesRebuild();
+
+	//BD - Misc
+	LLKeyframeMotion* onReadyTempMotion(std::string filename = "_poser_temp.anim", bool eternal = false);
+	void onCreateTempMotion();
+
+	//BD - Mirror Bone
+	void toggleMirrorMode(LLUICtrl* ctrl) { mMirrorMode = ctrl->getValue().asBoolean(); }
+	void toggleEasyRotations(LLUICtrl* ctrl) { mEasyRotations = ctrl->getValue().asBoolean(); }
+
+	//BD - Flip Poses
+	void onFlipPose();
+
+private:
+	//BD - Posing
+	LLTabContainer*								mJointTabs;
+	LLTabContainer*								mModifierTabs;
+	LLScrollListCtrl*							mKeyframeScroll;
+	LLScrollListCtrl*							mTimelineScroll;
+	LLHandle<LLToggleableMenu>					mSaveMenuHandle;
+
+	std::array<LLUICtrl*, 3>					mRotationSliders;
+	std::array<LLSliderCtrl*, 3>				mPositionSliders;
+	std::array<LLSliderCtrl*, 3>				mScaleSliders;
+	std::array<LLScrollListCtrl*, 3>			mJointScrolls;
+
+	//BD - I really didn't want to do this this way but we have to.
+	//     It's the easiest way doing this.
+	std::map<const std::string, LLQuaternion>	mDefaultRotations;
+	std::map<const std::string, LLVector3>		mDefaultScales;
+	std::map<const std::string, LLVector3>		mDefaultPositions;
+
+	//BD - Misc
+	bool										mDelayRefresh;
+	bool										mEasyRotations;
+	bool										mAutoDuration;
+
+	LLUUID										mTempMotionID;
+	
+	//BD - Mirror Bone
+	bool										mMirrorMode;
+
+	LLButton*									mStartPosingBtn;
+
+	LLSD										mClipboard;
+};
+
+#endif
diff --git a/indra/newview/bdfloaterposer.cpp b/indra/newview/bdfloaterposer.cpp
index 8685a72f8e1508dd39aac8968ffdea2971143a99..24139cf7285ed8f6e038bc79a83daa9bb14490a0 100644
--- a/indra/newview/bdfloaterposer.cpp
+++ b/indra/newview/bdfloaterposer.cpp
@@ -21,8 +21,13 @@
 #include "llagent.h"
 #include "llavatarname.h"
 #include "llavatarnamecache.h"
+#include "llclipboard.h"
+#include "lldatapacker.h"
 #include "lldiriterator.h"
+#include "llfilepicker.h"
+#include "llfilesystem.h"
 #include "llkeyframemotion.h"
+#include "llnotificationsutil.h"
 #include "llmenugl.h"
 #include "llmenubutton.h"
 #include "lltoggleablemenu.h"
@@ -32,6 +37,11 @@
 #include "llviewerjointattachment.h"
 #include "llviewerjoint.h"
 #include "llvoavatarself.h"
+#include "llwindowwin32.h"
+#include "pipeline.h"
+
+#include "llviewerobjectlist.h"
+#include "lldrawpoolavatar.h"
 
 //BD - Animesh Support
 #include "llcontrolavatar.h"
@@ -40,24 +50,19 @@
 #include "bdanimator.h"
 #include "bdposingmotion.h"
 
-
 BDFloaterPoser::BDFloaterPoser(const LLSD& key)
 	:	LLFloater(key)
 {
-	//BD - Save our current pose into a XML file to import it later or use it for creating an animation.
-	mCommitCallbackRegistrar.add("Pose.Save", boost::bind(&BDFloaterPoser::onClickPoseSave, this));
+	//BD - Save our current pose as XML or ANIM file to be used or uploaded later.
+	mCommitCallbackRegistrar.add("Pose.Save", boost::bind(&BDFloaterPoser::onClickPoseSave, this, _2));
 	//BD - Start our custom pose.
 	mCommitCallbackRegistrar.add("Pose.Start", boost::bind(&BDFloaterPoser::onPoseStart, this));
 	//BD - Load the current pose and export all its values into the UI so we can alter them.
 	mCommitCallbackRegistrar.add("Pose.Load", boost::bind(&BDFloaterPoser::onPoseLoad, this));
 	//BD - Delete the currently selected Pose.
 	mCommitCallbackRegistrar.add("Pose.Delete", boost::bind(&BDFloaterPoser::onPoseDelete, this));
-	//BD - Change a pose's blend type and time.
-	mCommitCallbackRegistrar.add("Pose.Set", boost::bind(&BDFloaterPoser::onPoseSet, this, _1, _2));
 	//BD - Extend or collapse the floater's pose list.
 	mCommitCallbackRegistrar.add("Pose.Layout", boost::bind(&BDFloaterPoser::onUpdateLayout, this));
-	//BD - Set the desired pose interpolation type.
-	mCommitCallbackRegistrar.add("Pose.Interpolation", boost::bind(&BDFloaterPoser::onJointControlsRefresh, this));
 	//BD - Include some menu interactions. Sadly necessary.
 	mCommitCallbackRegistrar.add("Pose.Menu", boost::bind(&BDFloaterPoser::onPoseMenuAction, this, _2));
 
@@ -77,6 +82,14 @@ BDFloaterPoser::BDFloaterPoser(const LLSD& key)
 	mCommitCallbackRegistrar.add("Joint.ResetJointPosition", boost::bind(&BDFloaterPoser::onJointPositionReset, this));
 	//BD - Reset all selected bones scales back to their default.
 	mCommitCallbackRegistrar.add("Joint.ResetJointScale", boost::bind(&BDFloaterPoser::onJointScaleReset, this));
+	//BD - Reset all selected bone rotations back to the initial rotation.
+	mCommitCallbackRegistrar.add("Joint.RevertJointRotation", boost::bind(&BDFloaterPoser::onJointRotationRevert, this));
+	//BD - Recapture all bones either all or just disabled ones.
+	mCommitCallbackRegistrar.add("Joint.Recapture", boost::bind(&BDFloaterPoser::onJointRecapture, this));
+	//BD - Mirror the current bone's rotation to match what the other body side's rotation should be.
+	mCommitCallbackRegistrar.add("Joint.Mirror", boost::bind(&BDFloaterPoser::onJointMirror, this));
+	//BD - Copy and mirror the other body side's bone rotation.
+	mCommitCallbackRegistrar.add("Joint.Symmetrize", boost::bind(&BDFloaterPoser::onJointSymmetrize, this));
 
 	//BD - Toggle Mirror Mode on/off.
 	mCommitCallbackRegistrar.add("Joint.ToggleMirror", boost::bind(&BDFloaterPoser::toggleMirrorMode, this, _1));
@@ -87,25 +100,6 @@ BDFloaterPoser::BDFloaterPoser(const LLSD& key)
 
 	//BD - Refresh the avatar list.
 	mCommitCallbackRegistrar.add("Poser.RefreshAvatars", boost::bind(&BDFloaterPoser::onAvatarsRefresh, this));
-
-	//BD - Add a new entry to the animation creator.
-	mCommitCallbackRegistrar.add("Anim.Add", boost::bind(&BDFloaterPoser::onAnimAdd, this, _2));
-	//BD - Move the selected entry one row up.
-	mCommitCallbackRegistrar.add("Anim.Move", boost::bind(&BDFloaterPoser::onAnimMove, this, _2));
-	//BD - Remove an entry in the animation creator.
-	mCommitCallbackRegistrar.add("Anim.Delete", boost::bind(&BDFloaterPoser::onAnimDelete, this));
-	//BD - Save the currently build list as animation.
-	mCommitCallbackRegistrar.add("Anim.Save", boost::bind(&BDFloaterPoser::onAnimSave, this));
-	//BD - Play the current animator queue.
-	mCommitCallbackRegistrar.add("Anim.Play", boost::bind(&BDFloaterPoser::onAnimPlay, this));
-	//BD - Stop the current animator queue.
-	mCommitCallbackRegistrar.add("Anim.Stop", boost::bind(&BDFloaterPoser::onAnimStop, this));
-	//BD - Change the value for a wait entry.
-	mCommitCallbackRegistrar.add("Anim.Set", boost::bind(&BDFloaterPoser::onAnimSet, this));
-
-	//BD - Test.
-	//mCommitCallbackRegistrar.add("Anim.Edit", boost::bind(&BDFloaterPoser::onAnimEdit, this, _1, _2));
-	//mCommitCallbackRegistrar.add("Anim.SetValue", boost::bind(&BDFloaterPoser::onAnimSetValue, this, _1, _2));
 }
 
 BDFloaterPoser::~BDFloaterPoser()
@@ -135,7 +129,6 @@ BOOL BDFloaterPoser::postBuild()
 	mPoseScroll->setCommitOnSelectionChange(TRUE);
 	mPoseScroll->setCommitCallback(boost::bind(&BDFloaterPoser::onPoseControlsRefresh, this));
 	mPoseScroll->setDoubleClickCallback(boost::bind(&BDFloaterPoser::onPoseLoad, this));
-	mPoseScroll->setRightMouseDownCallback(boost::bind(&BDFloaterPoser::onPoseScrollRightMouse, this, _1, _2, _3));
 
 	mRotationSliders = { { getChild<LLUICtrl>("Rotation_X"), getChild<LLUICtrl>("Rotation_Y"), getChild<LLUICtrl>("Rotation_Z") } };
 	mPositionSliders = { { getChild<LLSliderCtrl>("Position_X"), getChild<LLSliderCtrl>("Position_Y"), getChild<LLSliderCtrl>("Position_Z") } };
@@ -144,14 +137,12 @@ BOOL BDFloaterPoser::postBuild()
 	mJointTabs = getChild<LLTabContainer>("joints_tabs");
 	mJointTabs->setCommitCallback(boost::bind(&BDFloaterPoser::onJointControlsRefresh, this));
 
+	mModifierTabs = getChild<LLTabContainer>("modifier_tabs");
+
 	//BD - Animesh
 	mAvatarScroll = this->getChild<LLScrollListCtrl>("avatar_scroll", true);
 	mAvatarScroll->setCommitCallback(boost::bind(&BDFloaterPoser::onAvatarsSelect, this));
 
-	//BD - Animations
-	mAnimEditorScroll = this->getChild<LLScrollListCtrl>("anim_editor_scroll", true);
-	mAnimEditorScroll->setCommitCallback(boost::bind(&BDFloaterPoser::onAnimControlsRefresh, this));
-
 	//BD - Misc
 	mDelayRefresh = false;
 
@@ -160,17 +151,31 @@ BOOL BDFloaterPoser::postBuild()
 
 	mStartPosingBtn = getChild<LLButton>("activate");
 	mLoadPosesBtn = getChild<LLMenuButton>("load_poses");
+	mSavePosesBtn = getChild<LLButton>("save_poses");
 
 	//BD - Poser Menu
-	LLUICtrl::CommitCallbackRegistry::ScopedRegistrar registrar; registrar.add("Pose.Menu", boost::bind(&BDFloaterPoser::onPoseLoadSelective, this, _2));
-	LLToggleableMenu* context_menu = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>("menu_poser_poses.xml",
+	LLUICtrl::CommitCallbackRegistry::ScopedRegistrar pose_reg;
+	LLUICtrl::EnableCallbackRegistry::ScopedRegistrar enable_registrar;
+	pose_reg.add("Pose.Menu", boost::bind(&BDFloaterPoser::onPoseLoadSelective, this, _2));
+	LLToggleableMenu* btn_menu = LLUICtrlFactory::getInstance()->createFromFile<LLToggleableMenu>("menu_poser_poses_btn.xml",
+		gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
+	if(btn_menu)
+		mLoadPosesBtn->setMenu(btn_menu, LLMenuButton::MP_BOTTOM_LEFT);
+
+	LLContextMenu *context_menu = LLUICtrlFactory::getInstance()->createFromFile<LLContextMenu>("menu_poser_poses.xml",
 		gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
 	if (context_menu)
 	{
-		mPosesMenuHandle = context_menu->getHandle();
-		mLoadPosesBtn->setMenu(context_menu, LLMenuButton::MP_BOTTOM_LEFT);
+		mPoseScroll->setContextMenu(LLScrollListCtrl::MENU_EXTERNAL, context_menu);
 	}
 
+	//BD - Poser Right Click Menu
+	pose_reg.add("Joints.Menu", boost::bind(&BDFloaterPoser::onJointContextMenuAction, this, _2));
+	enable_registrar.add("Joints.OnEnable", boost::bind(&BDFloaterPoser::onJointContextMenuEnable, this, _2));
+	LLContextMenu* joint_menu = LLUICtrlFactory::getInstance()->createFromFile<LLContextMenu>("menu_poser_joints.xml",
+		gMenuHolder, LLViewerMenuHolderGL::child_registry_t::instance());
+	mJointScrolls[JOINTS]->setContextMenu(LLScrollListCtrl::MENU_EXTERNAL, joint_menu);
+
 	//BD - Experimental
 	/*mTimeSlider = getChild<LLMultiSliderCtrl>("time_slider");
 	mKeySlider = getChild<LLMultiSliderCtrl>("key_slider");
@@ -187,23 +192,6 @@ BOOL BDFloaterPoser::postBuild()
 
 void BDFloaterPoser::draw()
 {
-	if (gDragonAnimator.getIsPlaying())
-	{
-		LLScrollListItem* item = mAvatarScroll->getFirstSelected();
-		if (item)
-		{
-			LLVOAvatar* avatar = (LLVOAvatar*)item->getUserdata();
-			if (avatar || !avatar->isDead())
-			{
-				S32 current_index = avatar->getCurrentActionIndex();
-				if (mAnimEditorScroll->getItemCount() != 0)
-				{
-					mAnimEditorScroll->selectNthItem(current_index);
-				}
-			}
-		}
-	}
-
 	LLFloater::draw();
 }
 
@@ -216,6 +204,7 @@ void BDFloaterPoser::onOpen(const LLSD& key)
 		onCollectDefaults();
 	}
 
+	//BD - We first fill the avatar list because the creation controls require it.
 	onAvatarsRefresh();
 	onJointRefresh();
 	onPoseRefresh();
@@ -237,7 +226,7 @@ void BDFloaterPoser::onClose(bool app_quitting)
 void BDFloaterPoser::onPoseRefresh()
 {
 	mPoseScroll->clearRows();
-	std::string dir = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "poses", "");
+	std::string dir = gDirUtilp->getExpandedFilename(LL_PATH_POSES, "");
 	std::string file;
 	LLDirIterator dir_iter(dir, "*.xml");
 	while (dir_iter.next(file))
@@ -263,42 +252,36 @@ void BDFloaterPoser::onPoseRefresh()
 			LL_WARNS("Posing") << "Skipping: Failed to parse pose file: " << path << LL_ENDL;
 			continue;
 		}
-
-		if (!infile.eof())
-		{
-			row["columns"][1]["column"] = "time";
-			row["columns"][1]["value"] = data["time"];
-			row["columns"][2]["column"] = "type";
-			row["columns"][2]["value"] = data["type"];
-		}
 		mPoseScroll->addElement(row);
 	}
 	onJointControlsRefresh();
 }
 
-void BDFloaterPoser::onClickPoseSave()
+bool BDFloaterPoser::onClickPoseSave(const LLSD& param)
 {
 	//BD - Values don't matter when not editing.
-	onPoseSave(2, 0.1f, false);
-
-	//BD - Flash the poses button to give the user a visual cue where it went.
-	getChild<LLButton>("extend")->setFlashing(true, true);
+	if (onPoseSave())
+	{
+		LLNotificationsUtil::add("PoserExportXMLSuccess");
+		return true;
+	}
+	return false;
 }
 
-void BDFloaterPoser::onPoseSave(S32 type, F32 time, bool editing)
+bool BDFloaterPoser::onPoseSave()
 {
 	LLScrollListItem* av_item = mAvatarScroll->getFirstSelected();
 	if (!av_item)
 	{
 		LL_WARNS("Posing") << "No avatar selected." << LL_ENDL;
-		return;
+		return false;
 	}
 
 	LLVOAvatar* avatar = (LLVOAvatar*)av_item->getUserdata();
 	if (!avatar || avatar->isDead())
 	{
 		LL_WARNS("Posing") << "Couldn't find avatar, dead?" << LL_ENDL;
-		return;
+		return false;
 	}
 
 	//BD - First and foremost before we do anything, check if the folder exists.
@@ -309,141 +292,58 @@ void BDFloaterPoser::onPoseSave(S32 type, F32 time, bool editing)
 		LLFile::mkdir(pathname);
 	}
 
-	std::string filename;
-	if (editing)
-	{
-		LLScrollListItem* item = mPoseScroll->getFirstSelected();
-		if (item)
-		{
-			filename = item->getColumn(0)->getValue().asString();
-		}
-	}
-	else
-	{
-		filename = getChild<LLUICtrl>("pose_name")->getValue().asString();
-	}
-
+	std::string filename = getChild<LLUICtrl>("pose_name")->getValue().asString();
 	if (filename.empty())
-	{
-		return;
-	}
+		return false;
 
-	std::string full_path = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "poses", LLDir::escapePathString(filename) + ".xml");
+	std::string full_path = gDirUtilp->getExpandedFilename(LL_PATH_POSES, LLDir::escapePathString(filename) + ".xml");
 	LLSD record;
-	S32 line = 0;
-
-	if (editing)
-	{
-		llifstream infile;
 
-		infile.open(full_path);
-		if (!infile.is_open())
-		{
-			LL_WARNS("Posing") << "Cannot find file in: " << filename << LL_ENDL;
-			return;
-		}
+	//BD - Create the header first.
+	S32 version = 3;
+	record["version"]["value"] = version;
 
-		LLSD old_record;
-		//BD - Read the pose and save it into an LLSD so we can rewrite it later.
-		while (!infile.eof())
+	//BD - Now create the rest.
+	for (S32 it = 0; it < 3; ++it)
+	{
+		for (auto element : mJointScrolls[it]->getAllData())
 		{
-			if (LLSDParser::PARSE_FAILURE == LLSDSerialize::fromXML(old_record, infile))
-			{
-				LL_WARNS("Posing") << "Failed to parse while rewrtiting file: " << filename << LL_ENDL;
-				return;
-			}
-
-			if (line != 0)
+			LLVector3 vec3;
+			LLJoint* joint = (LLJoint*)element->getUserdata();
+			if (joint)
 			{
-				record[line] = old_record;
-			}
-			++line;
-		}
-
-		//BD - Change the header here.
-		record[0]["type"] = type;
-		//BD - If we are using spherical linear interpolation we need to clamp the values 
-		//     between 0.001f and 1.f otherwise unexpected things might happen.
-		if (type == 2)
-		{
-			time = llclamp(time, 0.001f, 1.0f);
-		}
-		record[0]["time"] = time;
+				std::string bone_name = joint->getName();
+				record[bone_name] = joint->getName();
+				joint->getTargetRotation().getEulerAngles(&vec3.mV[VX], &vec3.mV[VZ], &vec3.mV[VY]);
+				record[bone_name]["rotation"] = vec3.getValue();
 
-		infile.close();
+				//BD - All bones support positions now.
+				vec3 = it > JOINTS ? joint->getPosition() : joint->getTargetPosition();
+				record[bone_name]["position"] = vec3.getValue();
 
-		LLScrollListItem* item = mPoseScroll->getFirstSelected();
-		if (item)
-		{
-			item->getColumn(1)->setValue(time);
-			item->getColumn(2)->setValue(type);
-		}
-	}
-	else
-	{
-		//BD - Create the header first.
-		S32 type = getChild<LLUICtrl>("interpolation_type")->getValue();
-		F32 time = getChild<LLUICtrl>("interpolation_time")->getValue().asReal();
-		//BD - If we are using spherical linear interpolation we need to clamp the values 
-		//     between 0.001f and 1.f otherwise unexpected things might happen.
-		if (type == 2)
-		{
-			time = llclamp(time, 0.001f, 1.0f);
-		}
-		record[line]["time"] = time;
-		record[line]["type"] = type;
-		++line;
+				vec3 = joint->getScale();
+				record[bone_name]["scale"] = vec3.getValue();
 
-		//BD - Now create the rest.
-		for (S32 it = 0; it < 3; ++it)
-		{
-			for (auto element : mJointScrolls[it]->getAllData())
-			{
-				LLVector3 vec3;
-				LLJoint* joint = (LLJoint*)element->getUserdata();
-				if (joint)
+				if (it == JOINTS)
 				{
-					record[line]["bone"] = joint->getName();
-					joint->getTargetRotation().getEulerAngles(&vec3.mV[VX], &vec3.mV[VZ], &vec3.mV[VY]);
-					record[line]["rotation"] = vec3.getValue();
-
-					//BD - We could just check whether position information is available since only joints
-					//     which can have their position changed will have position information but we
-					//     want this to be a minefield for crashes.
-					//     Bones that can support position
-					//     0, 9-37, 39-43, 45-59, 77, 97-107, 110, 112, 115, 117-121, 125, 128-129, 132
-					//     as well as all attachment bones and collision volumes.
-					if (joint->mHasPosition || it > JOINTS)
+					//BD - Save the enabled state per preset so we can switch bones on and off
+					//     on demand inbetween poses additionally to globally.
+					BDPosingMotion* motion = (BDPosingMotion*)gAgentAvatarp->findMotion(ANIM_BD_POSING_MOTION);
+					if (motion)
 					{
-						vec3 = it > JOINTS ? joint->getPosition() : joint->getTargetPosition();
-						record[line]["position"] = vec3.getValue();
-					}
-
-					vec3 = joint->getScale();
-					record[line]["scale"] = vec3.getValue();
-
-					if (it == JOINTS)
-					{
-						//BD - Save the enabled state per preset so we can switch bones on and off
-						//     on demand inbetween poses additionally to globally.
-						BDPosingMotion* motion = (BDPosingMotion*)gAgentAvatarp->findMotion(ANIM_BD_POSING_MOTION);
-						if (motion)
+						LLPose* pose = motion->getPose();
+						if (pose)
 						{
-							LLPose* pose = motion->getPose();
-							if (pose)
+							if (pose->findJointState(joint))
+							{
+								record[bone_name]["enabled"] = true;
+							}
+							else
 							{
-								if (pose->findJointState(joint))
-								{
-									record[line]["enabled"] = true;
-								}
-								else
-								{
-									record[line]["enabled"] = false;
-								}
+								record[bone_name]["enabled"] = false;
 							}
 						}
 					}
-					++line;
 				}
 			}
 		}
@@ -453,16 +353,14 @@ void BDFloaterPoser::onPoseSave(S32 type, F32 time, bool editing)
 	file.open(full_path.c_str());
 	//BD - Now lets actually write the file, whether it is writing a new one
 	//     or just rewriting the previous one with a new header.
-	for (LLSD cur_line : llsd::inArray(record))
-	{
-		LLSDSerialize::toXML(cur_line, file);
-	}
+	LLSDSerialize::toPrettyXML(record, file);
 	file.close();
 
-	if (!editing)
-	{
-		onPoseRefresh();
-	}
+	onPoseRefresh();
+
+	//BD - Flash the poses button to give the user a visual cue where it went.
+	getChild<LLButton>("extend")->setFlashing(true, true);
+	return true;
 }
 
 void BDFloaterPoser::onPoseLoad()
@@ -522,8 +420,8 @@ void BDFloaterPoser::onPoseStart()
 				onCollectDefaults();
 
 			gAgent.stopFidget();
-			//gDragonStatus->setPosing(true);
 		}
+		avatar->startDefaultMotions();
 		avatar->startMotion(ANIM_BD_POSING_MOTION);
 	}
 	else
@@ -534,11 +432,6 @@ void BDFloaterPoser::onPoseStart()
 		//BD - Clear posing when we're done now that we've safely endangered getting spaghetified.
 		avatar->clearPosing();
 		avatar->stopMotion(ANIM_BD_POSING_MOTION);
-
-		if (avatar->isSelf())
-		{
-			//gDragonStatus->setPosing(false);
-		}
 	}
 	//BD - Wipe the joint list.
 	onJointRefresh();
@@ -561,48 +454,13 @@ void BDFloaterPoser::onPoseDelete()
 	onPoseRefresh();
 }
 
-//BD - We use this function to edit an already saved pose's interpolation time or type.
-//     Both are combined into one function.
-void BDFloaterPoser::onPoseSet(LLUICtrl* ctrl, const LLSD& param)
-{
-	LLScrollListItem* item = mPoseScroll->getFirstSelected();
-	if (item)
-	{
-		S32 type;
-		F32 time;
-		//BD - We either edit the interpolation time here or if we don't we automatically assume 
-		//     we are trying to edit the interpolation type.
-		if (param.asString() == "time")
-		{
-			//BD - Get the new interpolation time from the time widget and read the type from
-			//     the list as we are not going to change it anyway.
-			time = getChild<LLUICtrl>("interp_time")->getValue().asReal();
-			type = item->getColumn(2)->getValue().asInteger();
-		}
-		else
-		{
-			//BD - Do the opposite here.
-			time = item->getColumn(1)->getValue().asReal();
-			type = getChild<LLUICtrl>("interp_type")->getValue().asInteger();
-		}
-		onPoseSave(type, time, true);
-	}
-}
-
 void BDFloaterPoser::onPoseControlsRefresh()
 {
 	bool is_playing = gDragonAnimator.getIsPlaying();
 	LLScrollListItem* item = mPoseScroll->getFirstSelected();
-	if (item)
-	{
-		getChild<LLUICtrl>("interp_time")->setValue(item->getColumn(1)->getValue());
-		getChild<LLUICtrl>("interp_type")->setValue(item->getColumn(2)->getValue());
-	}
-	getChild<LLUICtrl>("interp_time")->setEnabled(bool(item));
-	getChild<LLUICtrl>("interp_type")->setEnabled(bool(item));
 	getChild<LLUICtrl>("delete_poses")->setEnabled(bool(item));
 	getChild<LLUICtrl>("add_entry")->setEnabled(!is_playing && item);
-	getChild<LLUICtrl>("load_poses")->setEnabled(bool(item));
+	mLoadPosesBtn->setEnabled(bool(item));
 }
 
 void BDFloaterPoser::onPoseMenuAction(const LLSD& param)
@@ -610,21 +468,6 @@ void BDFloaterPoser::onPoseMenuAction(const LLSD& param)
 	onPoseLoadSelective(param);
 }
 
-void BDFloaterPoser::onPoseScrollRightMouse(LLUICtrl* ctrl, S32 x, S32 y)
-{
-	mPoseScroll->selectItemAt(x, y, MASK_NONE);
-	if (mPoseScroll->getFirstSelected())
-	{
-		LLToggleableMenu* poses_menu = mPosesMenuHandle.get();
-		if (poses_menu)
-		{
-			poses_menu->buildDrawLabels();
-			poses_menu->updateParent(LLMenuGL::sMenuContainer);
-			LLMenuGL::showPopup(mPoseScroll, poses_menu, x, y);
-		}
-	}
-}
-
 ////////////////////////////////
 //BD - Joints
 ////////////////////////////////
@@ -698,6 +541,15 @@ void BDFloaterPoser::onJointRefresh()
 			row["columns"][COL_ROT_Z]["column"] = "z";
 			row["columns"][COL_ROT_Z]["value"] = ll_round(rot.mV[VZ], 0.001f);
 
+			//BD - All bones support positions now.
+			pos = joint->getTargetPosition();
+			row["columns"][COL_POS_X]["column"] = "pos_x";
+			row["columns"][COL_POS_X]["value"] = ll_round(pos.mV[VX], 0.001f);
+			row["columns"][COL_POS_Y]["column"] = "pos_y";
+			row["columns"][COL_POS_Y]["value"] = ll_round(pos.mV[VY], 0.001f);
+			row["columns"][COL_POS_Z]["column"] = "pos_z";
+			row["columns"][COL_POS_Z]["value"] = ll_round(pos.mV[VZ], 0.001f);
+
 			//BD - Bone Scales
 			scale = joint->getScale();
 			row["columns"][COL_SCALE_X]["column"] = "scale_x";
@@ -708,25 +560,6 @@ void BDFloaterPoser::onJointRefresh()
 			row["columns"][COL_SCALE_Z]["value"] = ll_round(scale.mV[VZ], 0.001f);
 		}
 
-		//BD - We could just check whether position information is available since only joints
-		//     which can have their position changed will have position information but we
-		//     want this to be a minefield for crashes.
-		//     Bones that can support position
-		//     0, 9-37, 39-43, 45-59, 77, 97-107, 110, 112, 115, 117-121, 125, 128-129, 132
-		if (joint->mHasPosition)
-		{
-			if (is_posing)
-			{
-				pos = joint->getTargetPosition();
-				row["columns"][COL_POS_X]["column"] = "pos_x";
-				row["columns"][COL_POS_X]["value"] = ll_round(pos.mV[VX], 0.001f);
-				row["columns"][COL_POS_Y]["column"] = "pos_y";
-				row["columns"][COL_POS_Y]["value"] = ll_round(pos.mV[VY], 0.001f);
-				row["columns"][COL_POS_Z]["column"] = "pos_z";
-				row["columns"][COL_POS_Z]["value"] = ll_round(pos.mV[VZ], 0.001f);
-			}
-		}
-
 		LLScrollListItem* item = mJointScrolls[JOINTS]->addElement(row);
 		item->setUserdata(joint);
 
@@ -787,6 +620,17 @@ void BDFloaterPoser::onJointRefresh()
 
 		if (is_posing)
 		{
+			//BD - Bone Rotations
+			//     It's stupid but we have to define the empty columns here and fill them with
+			//     nothing otherwise the addRow() function is assuming that the sometimes missing
+			//     rotation columns have an "empty" name and thus creates faulty extra columns.
+			row["columns"][COL_ROT_X]["column"] = "x";
+			row["columns"][COL_ROT_X]["value"] = "";
+			row["columns"][COL_ROT_Y]["column"] = "y";
+			row["columns"][COL_ROT_Y]["value"] = "";
+			row["columns"][COL_ROT_Z]["column"] = "z";
+			row["columns"][COL_ROT_Z]["value"] = "";
+
 			//BD - Bone Positions
 			pos = joint->getPosition();
 			row["columns"][COL_POS_X]["column"] = "pos_x";
@@ -806,8 +650,8 @@ void BDFloaterPoser::onJointRefresh()
 			row["columns"][COL_SCALE_Z]["value"] = ll_round(scale.mV[VZ], 0.001f);
 		}
 
-		LLScrollListItem* item = mJointScrolls[COLLISION_VOLUMES]->addElement(row);
-		item->setUserdata(joint);
+		LLScrollListItem* new_item = mJointScrolls[COLLISION_VOLUMES]->addElement(row);
+		new_item->setUserdata(joint);
 	}
 
 	//BD - Attachment Bones
@@ -826,6 +670,17 @@ void BDFloaterPoser::onJointRefresh()
 
 		if (is_posing)
 		{
+			//BD - Bone Rotations
+			//     It's stupid but we have to define the empty columns here and fill them with
+			//     nothing otherwise the addRow() function is assuming that the sometimes missing
+			//     rotation columns have an "empty" name and thus creates faulty extra columns.
+			row["columns"][COL_ROT_X]["column"] = "x";
+			row["columns"][COL_ROT_X]["value"] = "";
+			row["columns"][COL_ROT_Y]["column"] = "y";
+			row["columns"][COL_ROT_Y]["value"] = "";
+			row["columns"][COL_ROT_Z]["column"] = "z";
+			row["columns"][COL_ROT_Z]["value"] = "";
+
 			//BD - Bone Positions
 			pos = joint->getPosition();
 			row["columns"][COL_POS_X]["column"] = "pos_x";
@@ -860,7 +715,6 @@ void BDFloaterPoser::onJointControlsRefresh()
 	LLVOAvatar* avatar = (LLVOAvatar*)av_item->getUserdata();
 	if (!avatar || avatar->isDead()) return;
 
-	bool can_position = false;
 	bool is_pelvis = false;
 	bool is_posing = (avatar->isFullyLoaded() && avatar->getPosing());
 	S32 index = mJointTabs->getCurrentPanelIndex();
@@ -878,30 +732,12 @@ void BDFloaterPoser::onJointControlsRefresh()
 				mRotationSliders[VZ]->setValue(item->getColumn(COL_ROT_Z)->getValue());
 			}
 
-			//BD - We could just check whether position information is available since only joints
-			//     which can have their position changed will have position information but we
-			//     want this to be a minefield for crashes.
-			//     Bones that can support position
-			//     0, 9-37, 39-43, 45-59, 77, 97-107, 110, 112, 115, 117-121, 125, 128-129, 132
-			//     as well as all attachment bones and collision volumes.
-			if (joint->mHasPosition || index > JOINTS)
-			{
-				can_position = true;
-				is_pelvis = (joint->mJointNum == 0);
+			//BD - All bones support positions now.
+			is_pelvis = (joint->mJointNum == 0);
 
-				mPositionSliders[VX]->setValue(item->getColumn(COL_POS_X)->getValue());
-				mPositionSliders[VY]->setValue(item->getColumn(COL_POS_Y)->getValue());
-				mPositionSliders[VZ]->setValue(item->getColumn(COL_POS_Z)->getValue());
-			}
-			else
-			{
-				//BD - If we didn't select a positionable bone kill all values. We might
-				//     end up with numbers that are too big for the min/max values when
-				//     changed below.
-				mPositionSliders[VX]->setValue(0.000f);
-				mPositionSliders[VY]->setValue(0.000f);
-				mPositionSliders[VZ]->setValue(0.000f);
-			}
+			mPositionSliders[VX]->setValue(item->getColumn(COL_POS_X)->getValue());
+			mPositionSliders[VY]->setValue(item->getColumn(COL_POS_Y)->getValue());
+			mPositionSliders[VZ]->setValue(item->getColumn(COL_POS_Z)->getValue());
 
 			//BD - Bone Scales
 			mScaleSliders[VX]->setValue(item->getColumn(COL_SCALE_X)->getValue());
@@ -926,30 +762,30 @@ void BDFloaterPoser::onJointControlsRefresh()
 	}
 
 	getChild<LLButton>("toggle_bone")->setEnabled(item && is_posing && index == JOINTS);
-	getChild<LLButton>("activate")->setValue(is_posing);
+	mStartPosingBtn->setValue(is_posing);
 	getChild<LLUICtrl>("pose_name")->setEnabled(is_posing);
 	getChild<LLUICtrl>("save_poses")->setEnabled(is_posing);
 	getChild<LLUICtrl>("joints_tabs")->setEnabled(is_posing);
 
-	//BD - Depending on which interpolation type the user selects we want the time editor to show
-	//     a different label, since spherical and linear both have different min/max numbers and
-	//     work differently with them.
-	//     Spherical: 0.001 - 1.0.
-	//     Linear: 0.0 - infinite.
-	getChild<LLUICtrl>("interpolation_type")->setEnabled(is_posing);
-	S32 interp_type = getChild<LLUICtrl>("interpolation_type")->getValue();
-	getChild<LLUICtrl>("interpolation_time")->setEnabled(is_posing && interp_type != 0);
-	const std::string label_time = interp_type == 1 ? "linear_time" : "spherical_time";
-	getChild<LLLineEditor>("interpolation_time")->setLabel(getString(label_time));
-
 	//BD - Enable position tabs whenever positions are available, scales are always enabled
 	//     unless we are editing attachment bones, rotations on the other hand are only
 	//     enabled when editing joints.
-	LLTabContainer* modifier_tabs = getChild<LLTabContainer>("modifier_tabs");
-	modifier_tabs->setEnabled(item && is_posing);
-	modifier_tabs->enableTabButton(0, (item && is_posing && index == JOINTS));
-	modifier_tabs->enableTabButton(1, (item && is_posing && can_position));
-	modifier_tabs->enableTabButton(2, (item && is_posing && index != ATTACHMENT_BONES));
+	S32 curr_idx = mModifierTabs->getCurrentPanelIndex();
+	mModifierTabs->setEnabled(item && is_posing);
+	mModifierTabs->enableTabButton(0, (item && is_posing && index == JOINTS));
+	mModifierTabs->enableTabButton(1, (item && is_posing));
+	mModifierTabs->enableTabButton(2, (item && is_posing && index != ATTACHMENT_BONES));
+	//BD - Swap out of "Position" tab when it's not available.
+	if (curr_idx == 1 && !mModifierTabs->getTabButtonEnabled(1))
+	{
+		mModifierTabs->selectTab(0);
+	}
+	//BD - Swap out of "Scale" and "Rotation" tabs when they are not available.
+	if (curr_idx == 2 && !mModifierTabs->getTabButtonEnabled(2)
+		|| curr_idx == 0 && !mModifierTabs->getTabButtonEnabled(0))
+	{
+		mModifierTabs->selectTab(1);
+	}
 
 	F32 max_val = is_pelvis ? 20.f : 1.0f;
 	mPositionSliders[VX]->setMaxValue(max_val);
@@ -1049,7 +885,7 @@ void BDFloaterPoser::onJointSet(LLUICtrl* ctrl, const LLSD& param)
 				S32 i = 0;
 				while (i < 3)
 				{
-					cell2[i]->setValue(ll_round(static_cast<F32>(item->getColumn(i + 2)->getValue().asReal()), 0.001f));
+					cell2[i]->setValue(ll_round(item->getColumn(i + 2)->getValue(), 0.001f));
 					++i;
 				}
 			}
@@ -1067,25 +903,17 @@ void BDFloaterPoser::onJointPosSet(LLUICtrl* ctrl, const LLSD& param)
 		LLJoint* joint = (LLJoint*)item->getUserdata();
 		if (joint)
 		{
-			//BD - We could just check whether position information is available since only joints
-			//     which can have their position changed will have position information but we
-			//     want this to be a minefield for crashes.
-			//     Bones that can support position
-			//     0, 9-37, 39-43, 45-59, 77, 97-107, 110, 112, 115, 117-121, 125, 128-129, 132
-			//     as well as all attachment bones and collision volumes.
-			if (joint->mHasPosition || index > JOINTS)
-			{
-				F32 val = ctrl->getValue().asReal();
-				LLScrollListCell* cell[3] = { item->getColumn(COL_POS_X), item->getColumn(COL_POS_Y), item->getColumn(COL_POS_Z) };
-				LLVector3 vec3 = { F32(cell[VX]->getValue().asReal()),
-								   F32(cell[VY]->getValue().asReal()),
-								   F32(cell[VZ]->getValue().asReal()) };
-
-				S32 dir = param.asInteger();
-				vec3.mV[dir] = val;
-				cell[dir]->setValue(ll_round(vec3.mV[dir], 0.001f));
-				joint->setTargetPosition(vec3);
-			}
+			//BD - All bones support positions now.
+			F32 val = ctrl->getValue().asReal();
+			LLScrollListCell* cell[3] = { item->getColumn(COL_POS_X), item->getColumn(COL_POS_Y), item->getColumn(COL_POS_Z) };
+			LLVector3 vec3 = { F32(cell[VX]->getValue().asReal()),
+								F32(cell[VY]->getValue().asReal()),
+								F32(cell[VZ]->getValue().asReal()) };
+
+			S32 dir = param.asInteger();
+			vec3.mV[dir] = val;
+			cell[dir]->setValue(ll_round(vec3.mV[dir], 0.001f));
+			joint->setTargetPosition(vec3);
 		}
 	}
 }
@@ -1148,11 +976,11 @@ void BDFloaterPoser::onJointChangeState()
 //BD - We use this to reset everything at once.
 void BDFloaterPoser::onJointRotPosScaleReset()
 {
-	LLScrollListItem* item = mAvatarScroll->getFirstSelected();
-	if (!item) return;
+	LLScrollListItem* av_item = mAvatarScroll->getFirstSelected();
+	if (!av_item) return;
 
 	//BD - We don't support resetting bones for anyone else yet.
-	LLVOAvatar* avatar = (LLVOAvatar*)item->getUserdata();
+	LLVOAvatar* avatar = (LLVOAvatar*)av_item->getUserdata();
 	if (!avatar || avatar->isDead() || !avatar->isSelf()) return;
 
 	//BD - While editing rotations, make sure we use a bit of spherical linear interpolation 
@@ -1167,8 +995,6 @@ void BDFloaterPoser::onJointRotPosScaleReset()
 
 	for (S32 it = 0; it < 3; ++it)
 	{
-		//BD - We use this bool to determine whether or not we'll be in need for a full skeleton
-		//     reset and to prevent checking for it every single time.
 		for (auto item : mJointScrolls[it]->getAllData())
 		{
 			if (item)
@@ -1181,8 +1007,8 @@ void BDFloaterPoser::onJointRotPosScaleReset()
 					{
 						LLQuaternion quat;
 						LLScrollListCell* col_rot_x = item->getColumn(COL_ROT_X);
-						LLScrollListCell* col_rot_y = item->getColumn(COL_ROT_X);
-						LLScrollListCell* col_rot_z = item->getColumn(COL_ROT_X);
+						LLScrollListCell* col_rot_y = item->getColumn(COL_ROT_Y);
+						LLScrollListCell* col_rot_z = item->getColumn(COL_ROT_Z);
 
 						col_rot_x->setValue(0.000f);
 						col_rot_y->setValue(0.000f);
@@ -1190,69 +1016,19 @@ void BDFloaterPoser::onJointRotPosScaleReset()
 
 						quat.setEulerAngles(0, 0, 0);
 						joint->setTargetRotation(quat);
-
-						//BD - If we are in Mirror mode, try to find the opposite bone of our currently
-						//     selected one, for now this simply means we take the name and replace "Left"
-						//     with "Right" and vise versa since all bones are conveniently that way.
-						//     TODO: Do this when creating the joint list so we don't try to find the joint
-						//     over and over again.
-						if (mMirrorMode)
-						{
-							LLJoint* mirror_joint = nullptr;
-							std::string mirror_joint_name = joint->getName();
-							S32 idx = joint->getName().find("Left");
-							if (idx != -1)
-								mirror_joint_name.replace(idx, mirror_joint_name.length(), "Right");
-
-							idx = joint->getName().find("Right");
-							if (idx != -1)
-								mirror_joint_name.replace(idx, mirror_joint_name.length(), "Left");
-
-							if (mirror_joint_name != joint->getName())
-							{
-								mirror_joint = gDragonAnimator.mTargetAvatar->mRoot->findJoint(mirror_joint_name);
-							}
-
-							if (mirror_joint)
-							{
-								//BD - We also need to find the opposite joint's list entry and change its values to reflect
-								//     the new ones, doing this here is still better than causing a complete refresh.
-								LLScrollListItem* item2 = mJointScrolls[JOINTS]->getItemByLabel(mirror_joint_name, FALSE, COL_NAME);
-								if (item2)
-								{
-									col_rot_x = item2->getColumn(COL_ROT_X);
-									col_rot_y = item2->getColumn(COL_ROT_Y);
-									col_rot_z = item2->getColumn(COL_ROT_Z);
-
-									col_rot_x->setValue(0.000f);
-									col_rot_y->setValue(0.000f);
-									col_rot_z->setValue(0.000f);
-
-									mirror_joint->setTargetRotation(quat);
-								}
-							}
-						}
 					}
 
 					//BD - Resetting positions next.
-					//BD - We could just check whether position information is available since only joints
-					//     which can have their position changed will have position information but we
-					//     want this to be a minefield for crashes.
-					//     Bones that can support position
-					//     0, 9-37, 39-43, 45-59, 77, 97-107, 110, 112, 115, 117-121, 125, 128-129, 132
-					//     as well as all attachment bones and collision volumes.
-					if (joint->mHasPosition || it > JOINTS)
-					{
-						LLScrollListCell* col_pos_x = item->getColumn(COL_POS_X);
-						LLScrollListCell* col_pos_y = item->getColumn(COL_POS_Y);
-						LLScrollListCell* col_pos_z = item->getColumn(COL_POS_Z);
-						LLVector3 pos = mDefaultPositions[joint->getName()];
-
-						col_pos_x->setValue(ll_round(pos.mV[VX], 0.001f));
-						col_pos_y->setValue(ll_round(pos.mV[VY], 0.001f));
-						col_pos_z->setValue(ll_round(pos.mV[VZ], 0.001f));
-						joint->setTargetPosition(pos);
-					}
+					//     All bones support positions now.
+					LLScrollListCell* col_pos_x = item->getColumn(COL_POS_X);
+					LLScrollListCell* col_pos_y = item->getColumn(COL_POS_Y);
+					LLScrollListCell* col_pos_z = item->getColumn(COL_POS_Z);
+					LLVector3 pos = mDefaultPositions[joint->getName()];
+
+					col_pos_x->setValue(ll_round(pos.mV[VX], 0.001f));
+					col_pos_y->setValue(ll_round(pos.mV[VY], 0.001f));
+					col_pos_z->setValue(ll_round(pos.mV[VZ], 0.001f));
+					joint->setTargetPosition(pos);
 
 					//BD - Resetting scales last.
 					LLScrollListCell* col_scale_x = item->getColumn(COL_SCALE_X);
@@ -1292,17 +1068,17 @@ void BDFloaterPoser::onJointRotationReset()
 		motion->setInterpolationType(2);
 	}
 
-	for (auto item : mJointScrolls[JOINTS]->getAllSelected())
+	for (auto new_item : mJointScrolls[JOINTS]->getAllSelected())
 	{
-		if (item)
+		if (new_item)
 		{
-			LLJoint* joint = (LLJoint*)item->getUserdata();
+			LLJoint* joint = (LLJoint*)new_item->getUserdata();
 			if (joint)
 			{
 				LLQuaternion quat;
-				LLScrollListCell* col_x = item->getColumn(COL_ROT_X);
-				LLScrollListCell* col_y = item->getColumn(COL_ROT_Y);
-				LLScrollListCell* col_z = item->getColumn(COL_ROT_Z);
+				LLScrollListCell* col_x = new_item->getColumn(COL_ROT_X);
+				LLScrollListCell* col_y = new_item->getColumn(COL_ROT_Y);
+				LLScrollListCell* col_z = new_item->getColumn(COL_ROT_Z);
 
 				col_x->setValue(0.000f);
 				col_y->setValue(0.000f);
@@ -1451,6 +1227,95 @@ void BDFloaterPoser::onJointScaleReset()
 	onJointControlsRefresh();
 }
 
+//BD - Used to revert rotations only.
+void BDFloaterPoser::onJointRotationRevert()
+{
+	LLScrollListItem* item = mAvatarScroll->getFirstSelected();
+	if (!item) return;
+
+	//BD - We do support reverting bone rotations for everyone however.
+	LLVOAvatar* avatar = (LLVOAvatar*)item->getUserdata();
+	if (!avatar || avatar->isDead()) return;
+
+	//BD - While editing rotations, make sure we use a bit of spherical linear interpolation 
+	//     to make movements smoother.
+	BDPosingMotion* motion = (BDPosingMotion*)avatar->findMotion(ANIM_BD_POSING_MOTION);
+	if (motion)
+	{
+		//BD - If we don't use our default spherical interpolation, set it once.
+		motion->setInterpolationTime(0.25f);
+		motion->setInterpolationType(2);
+	}
+
+	for (auto item : mJointScrolls[JOINTS]->getAllSelected())
+	{
+		if (item)
+		{
+			LLJoint* joint = (LLJoint*)item->getUserdata();
+			if (joint)
+			{
+				//BD - Reverting rotations first if there are any.
+				LLQuaternion quat = mDefaultRotations[joint->getName()];
+				LLVector3 rot;
+				quat.getEulerAngles(&rot.mV[VX], &rot.mV[VY], &rot.mV[VZ]);
+				LLScrollListCell* col_rot_x = item->getColumn(COL_ROT_X);
+				LLScrollListCell* col_rot_y = item->getColumn(COL_ROT_Y);
+				LLScrollListCell* col_rot_z = item->getColumn(COL_ROT_Z);
+
+				col_rot_x->setValue(rot.mV[VX]);
+				col_rot_y->setValue(rot.mV[VY]);
+				col_rot_z->setValue(rot.mV[VZ]);
+
+				joint->setTargetRotation(quat);
+
+				//BD - If we are in Mirror mode, try to find the opposite bone of our currently
+				//     selected one, for now this simply means we take the name and replace "Left"
+				//     with "Right" and vise versa since all bones are conveniently that way.
+				//     TODO: Do this when creating the joint list so we don't try to find the joint
+				//     over and over again.
+				if (mMirrorMode)
+				{
+					LLJoint* mirror_joint = nullptr;
+					std::string mirror_joint_name = joint->getName();
+					S32 idx = joint->getName().find("Left");
+					if (idx != -1)
+						mirror_joint_name.replace(idx, mirror_joint_name.length(), "Right");
+
+					idx = joint->getName().find("Right");
+					if (idx != -1)
+						mirror_joint_name.replace(idx, mirror_joint_name.length(), "Left");
+
+					if (mirror_joint_name != joint->getName())
+					{
+						mirror_joint = gDragonAnimator.mTargetAvatar->mRoot->findJoint(mirror_joint_name);
+					}
+
+					if (mirror_joint)
+					{
+						//BD - We also need to find the opposite joint's list entry and change its values to reflect
+						//     the new ones, doing this here is still better than causing a complete refresh.
+						LLScrollListItem* item2 = mJointScrolls[JOINTS]->getItemByLabel(mirror_joint_name, FALSE, COL_NAME);
+						if (item2)
+						{
+							col_rot_x = item2->getColumn(COL_ROT_X);
+							col_rot_y = item2->getColumn(COL_ROT_Y);
+							col_rot_z = item2->getColumn(COL_ROT_Z);
+
+							col_rot_x->setValue(rot.mV[VX]);
+							col_rot_y->setValue(rot.mV[VY]);
+							col_rot_z->setValue(rot.mV[VZ]);
+
+							mirror_joint->setTargetRotation(quat);
+						}
+					}
+				}
+			}
+		}
+	}
+
+	onJointControlsRefresh();
+}
+
 //BD - Flip our pose (mirror it)
 void BDFloaterPoser::onFlipPose()
 {
@@ -1541,246 +1406,432 @@ void BDFloaterPoser::onFlipPose()
 	}
 }
 
-//BD - This is used to collect all default values at the beginning to revert to later on.
-void BDFloaterPoser::onCollectDefaults()
+//BD - Flip our pose (mirror it)
+void BDFloaterPoser::onJointRecapture()
 {
-	LLVector3 rot;
-	LLVector3 pos;
-	LLVector3 scale;
-	LLJoint* joint;
+	LLScrollListItem* item = mAvatarScroll->getFirstSelected();
+	if (!item) return;
 
-	//BD - Getting collision volumes and attachment points.
-	std::vector<std::string> joint_names, cv_names, attach_names;
-	gAgentAvatarp->getSortedJointNames(0, joint_names);
-	gAgentAvatarp->getSortedJointNames(1, cv_names);
-	gAgentAvatarp->getSortedJointNames(2, attach_names);
+	LLVOAvatar* avatar = (LLVOAvatar*)item->getUserdata();
+	if (!avatar || avatar->isDead()) return;
 
-	mDefaultScales.clear();
-	mDefaultPositions.clear();
+	if (!(avatar->getRegion() == gAgent.getRegion())) return;
 
-	for (auto name : joint_names)
+	LLQuaternion rot;
+	LLVector3 pos;
+
+	BDPosingMotion* motion = (BDPosingMotion*)avatar->findMotion(ANIM_BD_POSING_MOTION);
+	if (motion)
 	{
-		joint = gAgentAvatarp->getJoint(name);
-		//BD - Nothing? Invalid? Skip, when we hit the end we'll break out anyway.
-		if (!joint)	continue;
+		LLPose* pose = motion->getPose();
+		if (pose)
+		{
+			for (auto item : mJointScrolls[JOINTS]->getAllData())
+			{
+				if (item)
+				{
+					LLJoint* joint = (LLJoint*)item->getUserdata();
+					if (joint)
+					{
+						// BD - Check for the joint state (whether a bone is enabled or not)
+						//      If not proceed.
+						LLPointer<LLJointState> joint_state = pose->findJointState(joint);
+						if (!joint_state)
+						{
+							//BD - First gather the current rotation and position.
+							rot = joint->getRotation();
+							pos = joint->getPosition();
+
+							//BD - Now, re-add the joint state and enable changing the pose.
+							motion->addJointToState(joint);
+							((LLScrollListText*)item->getColumn(COL_NAME))->setFontStyle(LLFontGL::BOLD);
+
+							//BD - Apply the newly collected rotation and position to the pose.
+							joint->setTargetRotation(rot);
+							joint->setTargetPosition(pos);
+
+							//BD - Get all columns and fill in the new values.
+							LLScrollListCell* col_rot_x = item->getColumn(COL_ROT_X);
+							LLScrollListCell* col_rot_y = item->getColumn(COL_ROT_Y);
+							LLScrollListCell* col_rot_z = item->getColumn(COL_ROT_Z);
+
+							LLScrollListCell* col_pos_x = item->getColumn(COL_POS_X);
+							LLScrollListCell* col_pos_y = item->getColumn(COL_POS_Y);
+							LLScrollListCell* col_pos_z = item->getColumn(COL_POS_Z);
+
+							LLVector3 euler_rot;
+							rot.getEulerAngles(&euler_rot.mV[VX], &euler_rot.mV[VY], &euler_rot.mV[VZ]);
+							col_rot_x->setValue(ll_round(euler_rot.mV[VX], 0.001f));
+							col_rot_y->setValue(ll_round(euler_rot.mV[VY], 0.001f));
+							col_rot_z->setValue(ll_round(euler_rot.mV[VZ], 0.001f));
+
+							col_pos_x->setValue(ll_round(pos.mV[VX], 0.001f));
+							col_pos_y->setValue(ll_round(pos.mV[VY], 0.001f));
+							col_pos_z->setValue(ll_round(pos.mV[VZ], 0.001f));
+						}
+					}
+				}
+			}
+		}
+	}
+}
 
-		LLSD row;
+//BD - Poser Utility Functions
+void BDFloaterPoser::onJointPasteRotation()
+{
+	for (auto item : mJointScrolls[JOINTS]->getAllSelected())
+	{
+		LLJoint* joint = (LLJoint*)item->getUserdata();
+		if (joint)
+		{
+			LLScrollListCell* col_rot_x = item->getColumn(COL_ROT_X);
+			LLScrollListCell* col_rot_y = item->getColumn(COL_ROT_Y);
+			LLScrollListCell* col_rot_z = item->getColumn(COL_ROT_Z);
 
-		//BD - We always get the values but we don't write them out as they are not relevant for the
-		//     user yet but we need them to establish default values we revert to later on.
-		scale = joint->getScale();
-		mDefaultScales.insert(std::pair<std::string, LLVector3>(name, scale));
+			LLVector3 euler_rot;
+			LLQuaternion rot = (LLQuaternion)mClipboard["rot"];
 
-		//BD - We could just check whether position information is available since only joints
-		//     which can have their position changed will have position information but we
-		//     want this to be a minefield for crashes.
-		//     Bones that can support position
-		//     0, 9-37, 39-43, 45-59, 77, 97-107, 110, 112, 115, 117-121, 125, 128-129, 132
-		if (joint->mHasPosition)
-		{
-			//BD - We always get the values but we don't write them out as they are not relevant for the
-			//     user yet but we need them to establish default values we revert to later on.
-			pos = joint->getPosition();
-			mDefaultPositions.insert(std::pair<std::string, LLVector3>(name, pos));
+			joint->setTargetRotation(rot);
+
+			rot.getEulerAngles(&euler_rot.mV[VX], &euler_rot.mV[VY], &euler_rot.mV[VZ]);
+			col_rot_x->setValue(ll_round(euler_rot.mV[VX], 0.001f));
+			col_rot_y->setValue(ll_round(euler_rot.mV[VY], 0.001f));
+			col_rot_z->setValue(ll_round(euler_rot.mV[VZ], 0.001f));
 		}
 	}
+}
 
-	//BD - Collision Volumes
-	for (auto name : cv_names)
+void BDFloaterPoser::onJointPastePosition()
+{
+	for (auto item : mJointScrolls[JOINTS]->getAllSelected())
 	{
-		LLJoint* joint = gAgentAvatarp->getJoint(name);
-		//BD - Nothing? Invalid? Skip, when we hit the end we'll break out anyway.
-		if (!joint)	continue;
+		LLJoint* joint = (LLJoint*)item->getUserdata();
+		if (joint)
+		{
+			LLScrollListCell* col_pos_x = item->getColumn(COL_POS_X);
+			LLScrollListCell* col_pos_y = item->getColumn(COL_POS_Y);
+			LLScrollListCell* col_pos_z = item->getColumn(COL_POS_Z);
 
-		//BD - We always get the values but we don't write them out as they are not relevant for the
-		//     user yet but we need them to establish default values we revert to later on.
-		pos = joint->getPosition();
-		scale = joint->getScale();
-		mDefaultPositions.insert(std::pair<std::string, LLVector3>(name, pos));
-		mDefaultScales.insert(std::pair<std::string, LLVector3>(name, scale));
+			LLVector3 pos = (LLVector3)mClipboard["pos"];
+
+			joint->setTargetPosition(pos);
+
+			col_pos_x->setValue(ll_round(pos.mV[VX], 0.001f));
+			col_pos_y->setValue(ll_round(pos.mV[VY], 0.001f));
+			col_pos_z->setValue(ll_round(pos.mV[VZ], 0.001f));
+		}
 	}
+}
 
-	//BD - Attachment Bones
-	for (auto name : attach_names)
+void BDFloaterPoser::onJointPasteScale()
+{
+	for (auto item : mJointScrolls[JOINTS]->getAllSelected())
 	{
-		LLJoint* joint = gAgentAvatarp->getJoint(name);
-		//BD - Nothing? Invalid? Skip, when we hit the end we'll break out anyway.
-		if (!joint)	continue;
+		LLJoint* joint = (LLJoint*)item->getUserdata();
+		if (joint)
+		{
+			LLScrollListCell* col_scale_x = item->getColumn(COL_SCALE_X);
+			LLScrollListCell* col_scale_y = item->getColumn(COL_SCALE_Y);
+			LLScrollListCell* col_scale_z = item->getColumn(COL_SCALE_Z);
+			LLVector3 scale = (LLVector3)mClipboard["scale"];
 
-		//BD - We always get the values but we don't write them out as they are not relevant for the
-		//     user yet but we need them to establish default values we revert to later on.
-		pos = joint->getPosition();
-		scale = joint->getScale();
+			joint->setScale(scale);
 
-		mDefaultPositions.insert(std::pair<std::string, LLVector3>(name, pos));
-		mDefaultScales.insert(std::pair<std::string, LLVector3>(name, scale));
+			col_scale_x->setValue(ll_round(scale.mV[VX], 0.001f));
+			col_scale_y->setValue(ll_round(scale.mV[VY], 0.001f));
+			col_scale_z->setValue(ll_round(scale.mV[VZ], 0.001f));
+		}
 	}
 }
 
-////////////////////////////////
-//BD - Animations
-////////////////////////////////
-void BDFloaterPoser::onAnimAdd(const LLSD& param)
+void BDFloaterPoser::onJointMirror()
 {
-	//BD - Don't allow changing the list while it's currently playing, this might
-	//     cause a crash when the animator is past the null check and we delete an
-	//     entry between null checking and trying to read its values. 
-	if (gDragonAnimator.getIsPlaying()) return;
+	for (auto item : mJointScrolls[JOINTS]->getAllSelected())
+	{
+		LLJoint* joint = (LLJoint*)item->getUserdata();
+		if (joint)
+		{
+			LLVector3 euler_rot;
+			LLQuaternion rot_quat = joint->getTargetRotation();
 
-	LLScrollListItem* item = mAvatarScroll->getFirstSelected();
-	if (!item) return;
+			//BD - Simply mirror the current bone's rotation like we'd do if we pressed the mirror
+			//     button without a mirror bone available.
+			LLQuaternion inv_rot_quat = LLQuaternion(-rot_quat.mQ[VX], rot_quat.mQ[VY], -rot_quat.mQ[VZ], rot_quat.mQ[VW]);
+			inv_rot_quat.getEulerAngles(&euler_rot[VX], &euler_rot[VY], &euler_rot[VZ]);
+			joint->setTargetRotation(inv_rot_quat);
 
-	LLVOAvatar* avatar = (LLVOAvatar*)item->getUserdata();
-	if (!avatar || avatar->isDead()) return;
+			LLScrollListCell* col_rot_x = item->getColumn(COL_ROT_X);
+			LLScrollListCell* col_rot_y = item->getColumn(COL_ROT_Y);
+			LLScrollListCell* col_rot_z = item->getColumn(COL_ROT_Z);
 
-	LLScrollListItem* pose = mPoseScroll->getFirstSelected();
-	std::string name = param.asString();
-	S32 location = mAnimEditorScroll->getFirstSelectedIndex() + 1;
-	F32 time = 0.f;
-	BD_EActionType type;
-	if (param.asString() == "Repeat")
-	{
-		type = BD_EActionType::REPEAT;
+			col_rot_x->setValue(ll_round(euler_rot.mV[VX], 0.001f));
+			col_rot_y->setValue(ll_round(euler_rot.mV[VY], 0.001f));
+			col_rot_z->setValue(ll_round(euler_rot.mV[VZ], 0.001f));
+		}
 	}
-	else if (param.asString() == "Wait")
+}
+
+void BDFloaterPoser::onJointSymmetrize()
+{
+	for (auto item : mJointScrolls[JOINTS]->getAllSelected())
 	{
-		type = BD_EActionType::WAIT;
-		time = 1.f;
+		LLJoint* joint = (LLJoint*)item->getUserdata();
+		if (joint)
+		{
+			std::string joint_name = joint->getName();
+			std::string mirror_joint_name = joint->getName();
+			//BD - Attempt to find the "right" version of this bone, if we can't find it try
+			//     the left version.
+			S32 idx = joint->getName().find("Left");
+			if (idx != -1)
+				mirror_joint_name.replace(idx, mirror_joint_name.length(), "Right");
+			idx = joint->getName().find("Right");
+			if (idx != -1)
+				mirror_joint_name.replace(idx, mirror_joint_name.length(), "Left");
+
+			LLJoint* mirror_joint = nullptr;
+			if (mirror_joint_name != joint->getName())
+				mirror_joint = gDragonAnimator.mTargetAvatar->mRoot->findJoint(mirror_joint_name);
+
+			//BD - Get the rotation of the mirror bone (if available).
+			//     Flip the mirror bone's rotation (if available) and apply it to our current bone.
+			if (mirror_joint)
+			{
+				LLVector3 mirror_rot;
+				LLQuaternion mirror_rot_quat;
+				mirror_rot_quat = mirror_joint->getTargetRotation();
+				LLQuaternion inv_mirror_rot_quat = LLQuaternion(-mirror_rot_quat.mQ[VX], mirror_rot_quat.mQ[VY], -mirror_rot_quat.mQ[VZ], mirror_rot_quat.mQ[VW]);
+				inv_mirror_rot_quat.getEulerAngles(&mirror_rot[VX], &mirror_rot[VY], &mirror_rot[VZ]);
+				joint->setTargetRotation(inv_mirror_rot_quat);
+
+				LLScrollListCell* col_rot_x = item->getColumn(COL_ROT_X);
+				LLScrollListCell* col_rot_y = item->getColumn(COL_ROT_Y);
+				LLScrollListCell* col_rot_z = item->getColumn(COL_ROT_Z);
+
+				col_rot_x->setValue(ll_round(mirror_rot.mV[VX], 0.001f));
+				col_rot_y->setValue(ll_round(mirror_rot.mV[VY], 0.001f));
+				col_rot_z->setValue(ll_round(mirror_rot.mV[VZ], 0.001f));
+			}
+		}
 	}
-	else
+}
+
+void BDFloaterPoser::onJointCopyTransforms()
+{
+	LLScrollListItem* item = mJointScrolls[JOINTS]->getFirstSelected();
+	LLJoint* joint = (LLJoint*)item->getUserdata();
+	if (joint)
 	{
-		name = pose->getColumn(0)->getValue().asString();
-		type = BD_EActionType::POSE;
+		mClipboard["rot"] = joint->getTargetRotation().getValue();
+		mClipboard["pos"] = joint->getTargetPosition().getValue();
+		mClipboard["scale"] = joint->getScale().getValue();
+		LL_INFOS("Posing") << "Copied all transforms " << LL_ENDL;
 	}
-	//BD - We'll be adding the avatar into a list in here to keep track of all
-	//     avatars we've added actions for.
-	gDragonAnimator.onAddAction(avatar, name, type, time, location);
-	onAnimListWrite();
-
-	//BD - Select added entry and make it appear as nothing happened.
-	//     In case of nothing being selected yet, select the first entry.
-	mAnimEditorScroll->selectNthItem(location);
-
-	//BD - Update our controls when we add items, the move and delete buttons
-	//     should enable now that we selected something.
-	onAnimControlsRefresh();
 }
 
-void BDFloaterPoser::onAnimListWrite()
+bool BDFloaterPoser::onJointContextMenuEnable(const LLSD& param)
 {
-	LLScrollListItem* item = mAvatarScroll->getFirstSelected();
-	if (!item) return;
+	std::string action = param.asString();
+	if (action == "clipboard")
+	{
+		return mClipboard.has("rot");
+	}
+	if (action == "enable_bone")
+	{
+		LLScrollListItem* item = mAvatarScroll->getFirstSelected();
+		if (!item) return false;
 
-	LLVOAvatar* avatar = (LLVOAvatar*)item->getUserdata();
-	if (!avatar || avatar->isDead()) return;
+		LLVOAvatar* avatar = (LLVOAvatar*)item->getUserdata();
+		if (!avatar || avatar->isDead()) return false;
 
-	mAnimEditorScroll->clearRows();
-	//BD - Now go through the new list we created, read them out and add them
-	//     to our list in the new desired order.
-	for (auto action : avatar->mAnimatorActions)
-	{
-		LLSD row;
-		row["columns"][0]["column"] = "name";
-		row["columns"][0]["value"] = action.mPoseName;
-		if (action.mType == BD_EActionType::WAIT)
+		item = mJointScrolls[JOINTS]->getFirstSelected();
+		if (item)
 		{
-			row["columns"][1]["column"] = "time";
-			row["columns"][1]["value"] = action.mTime;
-			row["columns"][2]["column"] = "type";
-			row["columns"][2]["value"] = action.mType;
+			LLJoint* joint = (LLJoint*)item->getUserdata();
+			if (joint)
+			{
+				BDPosingMotion* motion = (BDPosingMotion*)avatar->findMotion(ANIM_BD_POSING_MOTION);
+				LLPose* pose = motion->getPose();
+				if (pose)
+				{
+					LLPointer<LLJointState> joint_state = pose->findJointState(joint);
+					return joint_state;
+				}
+			}
 		}
-		mAnimEditorScroll->addElement(row);
 	}
-
-	//BD - Make sure we don't have a scrollbar unless we need it.
-	mAnimEditorScroll->updateLayout();
+	return false;
 }
 
-void BDFloaterPoser::onAnimMove(const LLSD& param)
+void BDFloaterPoser::onJointContextMenuAction(const LLSD& param)
 {
-	//BD - Don't allow changing the list while it's currently playing, this might
-	//     cause a crash when the animator is past the null check and we delete an
-	//     entry between null checking and trying to read its values. 
-	if (gDragonAnimator.getIsPlaying())	return;
-
-	//BD - Don't allow moving if we don't have anything selected either. (Crashfix)
 	LLScrollListItem* item = mAvatarScroll->getFirstSelected();
 	if (!item) return;
 
 	LLVOAvatar* avatar = (LLVOAvatar*)item->getUserdata();
 	if (!avatar || avatar->isDead()) return;
 
-	S32 item_count = mAnimEditorScroll->getItemCount();
-	S32 new_index = mAnimEditorScroll->getFirstSelectedIndex();
-	S32 old_index = new_index;
-
-	//BD - Don't allow moving down when we are already at the bottom, this would
-	//     create another entry.
-	//     Don't allow going up when we are on the first entry already either, this
-	//     would select everything in the list.
-	//     Don't allow moving if there's nothing to move. (Crashfix)
-	if (((new_index + 1) >= item_count && param.asString() == "Down")
-		|| (new_index == 0 && param.asString() == "Up")
-		|| item_count == 0)
+	std::string action = param.asString();
+	if (action == "copy_transforms")
 	{
-		return;
+		onJointCopyTransforms();
 	}
-
-	//BD - Move up, otherwise move the entry down. No other option.
-	if (param.asString() == "Up")
+	else if (action == "paste_rot")
 	{
-		--new_index;
+		onJointPasteRotation();
 	}
-	else
+	else if (action == "paste_pos")
 	{
-		++new_index;
+		onJointPastePosition();
 	}
+	else if (action == "paste_scale")
+	{
+		onJointPasteScale();
+	}
+	else if (action == "paste_rot_pos")
+	{
+		onJointPasteRotation();
+		onJointPastePosition();
+	}
+	else if (action == "paste_rot_scale")
+	{
+		onJointPasteRotation();
+		onJointPasteScale();
+	}
+	else if (action == "paste_pos_scale")
+	{
+		onJointPastePosition();
+		onJointPasteScale();
+	}
+	else if (action == "paste_all")
+	{
+		onJointPasteRotation();
+		onJointPastePosition();
+		onJointPasteScale();
+	}
+	else if (action == "symmetrize")
+	{
+		onJointSymmetrize();
+	}
+	else if (action == "mirror")
+	{
+		onJointMirror();
+	}
+	else if (action == "recapture")
+	{
+		onJointRecapture();
+	}
+	else if (action == "enable_bone")
+	{
+		onJointChangeState();
+	}
+	else if (action == "enable_override")
+	{
 
-	Action action = avatar->mAnimatorActions[old_index];
-	gDragonAnimator.onDeleteAction(avatar, old_index);
-	gDragonAnimator.onAddAction(avatar, action, new_index);
-	onAnimListWrite();
-
-	//BD - Select added entry and make it appear as nothing happened.
-	//     In case of nothing being selected yet, select the first entry.
-	mAnimEditorScroll->selectNthItem(new_index);
+	}
+	else if (action == "enable_offset")
+	{
 
-	//BD - Update our controls when we move items, the move and delete buttons
-	//     should enable now that we might have selected something.
-	onAnimControlsRefresh();
+	}
+	else if (action == "reset_rot")
+	{
+		onJointRotationReset();
+	}
+	else if (action == "reset_pos")
+	{
+		onJointPositionReset();
+	}
+	else if (action == "reset_scale")
+	{
+		onJointScaleReset();
+	}
+	else if (action == "reset_all")
+	{
+		//BD - We do all 3 here because the combined function resets all bones regardless of
+		//     our selection, these only reset the selected ones.
+		onJointRotationReset();
+		onJointPositionReset();
+		onJointScaleReset();
+	}
 }
 
-void BDFloaterPoser::onAnimDelete()
+//BD - This is used to collect all default values at the beginning to revert to later on.
+void BDFloaterPoser::onCollectDefaults()
 {
-	//BD - Don't allow changing the list while it's currently playing, this might
-	//     cause a crash when the animator is past the null check and we delete an
-	//     entry between null checking and trying to read its values. 
-	if (gDragonAnimator.getIsPlaying())	return;
+	LLQuaternion rot;
+	LLVector3 pos;
+	LLVector3 scale;
+	LLJoint* joint;
 
-	LLScrollListItem* item = mAvatarScroll->getFirstSelected();
-	if (!item) return;
+	//BD - Getting collision volumes and attachment points.
+	std::vector<std::string> joint_names, cv_names, attach_names;
+	gAgentAvatarp->getSortedJointNames(0, joint_names);
+	gAgentAvatarp->getSortedJointNames(1, cv_names);
+	gAgentAvatarp->getSortedJointNames(2, attach_names);
 
-	LLVOAvatar* avatar = (LLVOAvatar*)item->getUserdata();
-	if (!avatar || avatar->isDead()) return;
+	mDefaultRotations.clear();
+	mDefaultScales.clear();
+	mDefaultPositions.clear();
 
-	std::vector<LLScrollListItem*> items = mAnimEditorScroll->getAllSelected();
-	while (!items.empty())
+	for (auto name : joint_names)
 	{
-		//BD - We'll delete the avatar from our list if necessary when no more actions
-		//     are saved in a given avatar.
-		gDragonAnimator.onDeleteAction(avatar, mAnimEditorScroll->getFirstSelectedIndex());
-		items.erase(items.begin());
+		joint = gAgentAvatarp->getJoint(name);
+		//BD - Nothing? Invalid? Skip, when we hit the end we'll break out anyway.
+		if (!joint)	continue;
+
+		LLSD row;
+
+		rot = joint->getTargetRotation();
+		mDefaultRotations.insert(std::pair<std::string, LLQuaternion>(name, rot));
+
+		//BD - We always get the values but we don't write them out as they are not relevant for the
+		//     user yet but we need them to establish default values we revert to later on.
+		scale = joint->getScale();
+		mDefaultScales.insert(std::pair<std::string, LLVector3>(name, scale));
+
+		//BD - All bones support positions now.
+		//     We always get the values but we don't write them out as they are not relevant for the
+		//     user yet but we need them to establish default values we revert to later on.
+		pos = joint->getPosition();
+		mDefaultPositions.insert(std::pair<std::string, LLVector3>(name, pos));
 	}
-	onAnimListWrite();
 
-	//BD - Make sure we don't have a scrollbar unless we need it.
-	mAnimEditorScroll->updateLayout();
+	//BD - Collision Volumes
+	for (auto name : cv_names)
+	{
+		LLJoint* joint = gAgentAvatarp->getJoint(name);
+		//BD - Nothing? Invalid? Skip, when we hit the end we'll break out anyway.
+		if (!joint)	continue;
+
+		//BD - We always get the values but we don't write them out as they are not relevant for the
+		//     user yet but we need them to establish default values we revert to later on.
+		pos = joint->getPosition();
+		scale = joint->getScale();
+		mDefaultPositions.insert(std::pair<std::string, LLVector3>(name, pos));
+		mDefaultScales.insert(std::pair<std::string, LLVector3>(name, scale));
+	}
 
-	//BD - Update our controls when we delete items. Most of them should
-	//     disable now that nothing is selected anymore.
-	onAnimControlsRefresh();
+	//BD - Attachment Bones
+	for (auto name : attach_names)
+	{
+		LLJoint* joint = gAgentAvatarp->getJoint(name);
+		//BD - Nothing? Invalid? Skip, when we hit the end we'll break out anyway.
+		if (!joint)	continue;
+
+		//BD - We always get the values but we don't write them out as they are not relevant for the
+		//     user yet but we need them to establish default values we revert to later on.
+		pos = joint->getPosition();
+		scale = joint->getScale();
+
+		mDefaultPositions.insert(std::pair<std::string, LLVector3>(name, pos));
+		mDefaultScales.insert(std::pair<std::string, LLVector3>(name, scale));
+	}
 }
 
+////////////////////////////////
+//BD - Misc Functions
+////////////////////////////////
+
 void BDFloaterPoser::loadPoseRotations(std::string name, LLVector3 *rotations)
 {
 	LLScrollListItem* item = mAvatarScroll->getFirstSelected();
@@ -1792,7 +1843,7 @@ void BDFloaterPoser::loadPoseRotations(std::string name, LLVector3 *rotations)
 	std::string filename;
 	if (!name.empty())
 	{
-		filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "poses", LLDir::escapePathString(name) + ".xml");
+		filename = gDirUtilp->getExpandedFilename(LL_PATH_POSES, LLDir::escapePathString(name) + ".xml");
 	}
 
 	LLSD pose;
@@ -1838,7 +1889,7 @@ void BDFloaterPoser::loadPosePositions(std::string name, LLVector3 *positions)
 	std::string filename;
 	if (!name.empty())
 	{
-		filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "poses", LLDir::escapePathString(name) + ".xml");
+		filename = gDirUtilp->getExpandedFilename(LL_PATH_POSES, LLDir::escapePathString(name) + ".xml");
 	}
 
 	LLSD pose;
@@ -1886,7 +1937,7 @@ void BDFloaterPoser::loadPoseScales(std::string name, LLVector3 *scales)
 	std::string filename;
 	if (!name.empty())
 	{
-		filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "poses", LLDir::escapePathString(name) + ".xml");
+		filename = gDirUtilp->getExpandedFilename(LL_PATH_POSES, LLDir::escapePathString(name) + ".xml");
 	}
 
 	LLSD pose;
@@ -1922,384 +1973,17 @@ void BDFloaterPoser::loadPoseScales(std::string name, LLVector3 *scales)
 	return;
 }
 
-void BDFloaterPoser::onAnimSave()
-{
-	LLKeyframeMotion* temp_motion = NULL;
-	LLAssetID mMotionID;
-	LLTransactionID	mTransactionID;
-
-	//BD - To make this work we'll first need a unique UUID for this animation.
-	mTransactionID.generate();
-	mMotionID = mTransactionID.makeAssetID(gAgent.getSecureSessionID());
-	temp_motion->create(mMotionID);
-
-	std::string motion_name = getChild<LLUICtrl>("set_name")->getValue().asString();
-	//U16 motion_major_version = KEYFRAME_MOTION_VERSION;
-	//U16 motion_sub_version = KEYFRAME_MOTION_SUBVERSION;
-	//S32 base_priority = getChild<LLUICtrl>("set_priority")->getValue().asInteger();
-	//LLJoint::JointPriority base_priority = LLJoint::HIGHEST_PRIORITY;
-	F32 duration = getChild<LLUICtrl>("set_duration")->getValue().asReal();
-	//std::string face_emote_name = getChild<LLUICtrl>("set_face_emote")->getValue();
-	//F32 loop_in_time = getChild<LLUICtrl>("set_loop_in")->getValue().asReal();
-	//F32 loop_out_time = getChild<LLUICtrl>("set_loop_out")->getValue().asReal();
-	//S32 loop = getChild<LLUICtrl>("set_loop")->getValue().asBoolean();
-	//F32 ease_in_time = getChild<LLUICtrl>("set_ease_in")->getValue().asReal();
-	//F32 ease_out_time = getChild<LLUICtrl>("set_ease_out")->getValue().asReal();
-	//U32 hand_pose = getChild<LLUICtrl>("set_hand_pose")->getValue().asInteger();
-	//U32 joint_motion_count = getChild<LLUICtrl>("set_active_motions")->getValue().asInteger();
-	//LLUUID emote_name = getChild<LLUICtrl>("set_emote")->getValue();
-
-	//LLKeyframeMotion* motion = (LLKeyframeMotion*)gAgentAvatarp->findMotion(ANIM_BD_POSING_MOTION);
-	temp_motion = (LLKeyframeMotion*)gAgentAvatarp->createMotion(mMotionID);
-	typedef std::map<LLUUID, class LLKeyframeMotion::JointMotionList*> keyframe_data_map_t;
-
-	LLKeyframeMotion::JointMotionList* jointmotion_list;
-	jointmotion_list = LLKeyframeDataCache::getKeyframeData(ANIM_BD_POSING_MOTION);
-	F32 time = 0.f;
-	for (auto item : mAnimEditorScroll->getAllData())
-	{
-		std::string pose_name = item->getColumn(0)->getValue();
-		LLVector3 rotations[134];
-		LLVector3 positions[134];
-		LLVector3 scales[134];
-		loadPoseRotations(pose_name, rotations);
-		loadPosePositions(pose_name, positions);
-		loadPoseScales(pose_name, scales);
-
-		if (!item->getColumn(1)->getValue().asString().empty())
-		{
-			time += item->getColumn(1)->getValue().asReal();
-		}
-
-		LLKeyframeMotion::JointMotion* joint_motion = new LLKeyframeMotion::JointMotion;
-		jointmotion_list->mJointMotionArray.push_back(joint_motion);
-
-		for (S32 joint_num = 0; joint_num < 134; joint_num++)
-		{
-			LLJoint *joint = gAgentAvatarp->getCharacterJoint(joint_num);
-			if (joint)
-			{
-				joint_motion->mJointName = joint->getName();
-				LLPointer<LLJointState> joint_state = temp_motion->findJointState(joint);
-				if (!joint_state)
-				{
-					joint_state = new LLJointState;
-					temp_motion->addJointState(joint_state);
-					joint_state->setJoint(joint); // note: can accept NULL
-					joint_state->setUsage(0);
-				}
-
-				//---------------------------------------------------------------------
-				// get joint priority
-				//---------------------------------------------------------------------
-				S32 joint_priority = LLJoint::HIGHEST_PRIORITY;
-				joint_motion->mPriority = (LLJoint::JointPriority)joint_priority;
-				joint_state->setPriority((LLJoint::JointPriority)joint_priority);
-
-				//BD - Get the number of poses (keys) of our entire animation.
-				joint_motion->mRotationCurve.mNumKeys = 1;
-				joint_motion->mRotationCurve.mInterpolationType = LLKeyframeMotion::IT_LINEAR;
-				if (joint_motion->mRotationCurve.mNumKeys != 0)
-				{
-					joint_state->setUsage(joint_state->getUsage() | LLJointState::ROT);
-				}
-
-				//---------------------------------------------------------------------
-				// scan rotation curve keys
-				//---------------------------------------------------------------------
-				LLKeyframeMotion::RotationCurve *rCurve = &joint_motion->mRotationCurve;
-
-				for (S32 k = 0; k < joint_motion->mRotationCurve.mNumKeys; k++)
-				{
-					//BD - Get the duration of our animation until this pose.
-					U16 time_short = duration;
-					time = U16_to_F32(time_short, 0.f, jointmotion_list->mDuration);
-
-					LLKeyframeMotion::RotationKey rot_key;
-					rot_key.mTime = time;
-					LLVector3 rot_vec = rotations[joint_num];
-					//BD - Rotations need to be in range from 1 to -1 to be unpacked.
-					//     FIX or translate them directly from euler to quaternions.
-					rot_key.mValue.unpackFromVector3(rot_vec);
-
-					if (!(rot_key.mValue.isFinite()))
-					{
-						LL_WARNS() << "non-finite angle in rotation key for animation " << LL_ENDL;
-					}
-
-					rCurve->mKeys.emplace_back(time, rot_key);
-				}
-
-				std::sort(rCurve->mKeys.begin(), rCurve->mKeys.end(), [](const auto& a, const auto& b) { return a.first < b.first; });
-
-				//---------------------------------------------------------------------
-				// scan position curve header
-				//---------------------------------------------------------------------
-				//BD - Get the number of poses (keys) of our entire animation.
-				joint_motion->mPositionCurve.mNumKeys = 1;
-				joint_motion->mPositionCurve.mInterpolationType = LLKeyframeMotion::IT_LINEAR;
-				if (joint_motion->mPositionCurve.mNumKeys != 0)
-				{
-					joint_state->setUsage(joint_state->getUsage() | LLJointState::POS);
-				}
-
-				//---------------------------------------------------------------------
-				// scan position curve keys
-				//---------------------------------------------------------------------
-				LLKeyframeMotion::PositionCurve *pCurve = &joint_motion->mPositionCurve;
-				BOOL is_pelvis = joint_motion->mJointName == "mPelvis";
-				for (S32 k = 0; k < joint_motion->mPositionCurve.mNumKeys; k++)
-				{
-					U16 time_short = duration;
-					LLKeyframeMotion::PositionKey pos_key;
-					pos_key.mTime = U16_to_F32(time_short, 0.f, jointmotion_list->mDuration);
-
-					pos_key.mValue.mV[VX] = U16_to_F32(positions[joint_num].mV[VX], -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET);
-					pos_key.mValue.mV[VY] = U16_to_F32(positions[joint_num].mV[VY], -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET);
-					pos_key.mValue.mV[VZ] = U16_to_F32(positions[joint_num].mV[VZ], -LL_MAX_PELVIS_OFFSET, LL_MAX_PELVIS_OFFSET);
-
-					if (!(pos_key.mValue.isFinite()))
-					{
-						LL_WARNS() << "non-finite position in key for animation "<< LL_ENDL;
-					}
-
-					pCurve->mKeys.emplace_back(pos_key.mTime, pos_key);
-
-					if (is_pelvis)
-					{
-						jointmotion_list->mPelvisBBox.addPoint(pos_key.mValue);
-					}
-				}
-
-				std::sort(pCurve->mKeys.begin(), pCurve->mKeys.end(), [](const auto& a, const auto& b) { return a.first < b.first; });
-
-				joint_motion->mUsage = joint_state->getUsage();
-			}
-		}
-	}
-
-	//BD - Work in Progress exporter.
-	/*for (S32 idx = 0; idx > jointmotion_list->getNumJointMotions(); idx++)
-	{
-		LLKeyframeMotion::JointMotion* joint_motion = jointmotion_list->getJointMotion(idx);
-		LLKeyframeMotion::RotationCurve rCurve = joint_motion->mRotationCurve;
-
-		rCurve.mKeys[time];
-	}*/
-	//LLKeyframeMotion::JointMotion* joint_motion = jointmotion_list->getJointMotion(0);
-	//LLKeyframeMotion::RotationCurve rot_courve = joint_motion->mRotationCurve;
-	//LLKeyframeMotion::RotationKey rot_key = rot_courve.mLoopInKey;
-	//LLQuaternion rotation = rot_key.mRotation;
-	//F32 time = rot_key.mTime;
-	//LLDataPackerBinaryBuffer* dp;
-	//BOOL success = TRUE;
-
-	//BD - Lets collect everything we will need.
-	/*std::string motion_name = getChild<LLUICtrl>("set_name")->getValue().asString();
-	//U16 motion_major_version = KEYFRAME_MOTION_VERSION;
-	//U16 motion_sub_version = KEYFRAME_MOTION_SUBVERSION;
-	//S32 base_priority = getChild<LLUICtrl>("set_priority")->getValue().asInteger();
-	LLJoint::JointPriority base_priority = LLJoint::HIGHEST_PRIORITY;
-	F32 duration = getChild<LLUICtrl>("set_duration")->getValue().asReal();
-	std::string face_emote_name = getChild<LLUICtrl>("set_face_emote")->getValue();
-	F32 loop_in_time = getChild<LLUICtrl>("set_loop_in")->getValue().asReal();
-	F32 loop_out_time = getChild<LLUICtrl>("set_loop_out")->getValue().asReal();
-	S32 loop = getChild<LLUICtrl>("set_loop")->getValue().asBoolean();
-	F32 ease_in_time = getChild<LLUICtrl>("set_ease_in")->getValue().asReal();
-	F32 ease_out_time = getChild<LLUICtrl>("set_ease_out")->getValue().asReal();
-	//U32 hand_pose = getChild<LLUICtrl>("set_hand_pose")->getValue().asInteger();
-	//U32 joint_motion_count = getChild<LLUICtrl>("set_active_motions")->getValue().asInteger();
-	LLUUID emote_name = getChild<LLUICtrl>("set_emote")->getValue();
-
-	LLKeyframeMotion::JointMotionList* jointmotion_list = new LLKeyframeMotion::JointMotionList;
-
-	//BD - Reserve for all 134 joints.
-	jointmotion_list->mJointMotionArray.reserve(134);
-
-	LLJoint* joint;
-	for (S32 i = 0; (joint = gAgentAvatarp->getCharacterJoint(i)); ++i)
-	{
-		if (!joint)
-			continue;
-
-		//BD - Let's build joint motions
-		LLKeyframeMotion::JointMotion* joint_motion = jointmotion_list->mJointMotionArray[i];
-
-		//BD - We start with the joint name;
-		joint_motion->mJointName = joint->getName();
-
-		//BD - Add the priority
-		joint_motion->mPriority = LLJoint::HIGHEST_PRIORITY;
-
-		//BD - Define the bone usage
-		S32 usage = 0;
-		if (joint->mHasPosition)
-		{
-			usage = usage & LLJointState::POS;
-		}
-		usage = usage & LLJointState::ROT;
-		usage = usage & LLJointState::POS;
-		joint_motion->mUsage = usage;
-
-		//BD - And finally the fun part.
-		//LLKeyframeMotion::RotationCurve* rot_courve;
-		//LLKeyframeMotion::PositionCurve* pos_courve;
-		//LLKeyframeMotion::ScaleCurve* scale_courve;
-
-		//LLKeyframeMotion::RotationKey* rot_key;
-		//rot_key->mRotation = joint->getRotation();
-		//rot_key->mTime = 0.0f;		
-		//rot_courve->mKeys[0.0f].mRotation = joint->getRotation();
-		//rot_courve->mKeys[0.0f].mTime = 0.0f;
-		joint_motion->mRotationCurve.mKeys[0.0f].mRotation = joint->getRotation();
-		joint_motion->mRotationCurve.mKeys[0.0f].mTime = 0.0f;
-
-		joint_motion->mPositionCurve.mKeys[0.0f].mPosition = joint->getPosition();
-		joint_motion->mPositionCurve.mKeys[0.0f].mTime = 0.0f;
-
-		joint_motion->mScaleCurve.mKeys[0.0f].mScale = joint->getScale();
-		joint_motion->mScaleCurve.mKeys[0.0f].mTime = 0.0f;
-
-		//BD - Add the joint motion to our array.
-		jointmotion_list->mJointMotionArray[i] = joint_motion;
-	}
-	LLKeyframeDataCache::addKeyframeData(mMotionID, jointmotion_list);
-
-	temp_motion->setPriority(base_priority);
-	temp_motion->setEaseIn(ease_in_time);
-	temp_motion->setEaseOut(ease_out_time);
-	temp_motion->setEmote(emote_name);
-	//jointmotion_list->mHandPose = hand_pose;
-	temp_motion->setLoop(loop);
-	temp_motion->setLoopIn(loop_in_time);
-	temp_motion->setLoopOut(loop_out_time);
-
-	if (!jointmotion_list)
-		return;
-
-	jointmotion_list->mBasePriority = base_priority;
-	jointmotion_list->mDuration = duration;
-	jointmotion_list->mEaseInDuration = ease_in_time;
-	jointmotion_list->mEaseOutDuration = ease_out_time;
-	jointmotion_list->mEmoteName = face_emote_name;
-	//jointmotion_list->mHandPose = hand_pose;
-	jointmotion_list->mLoop = loop;
-	jointmotion_list->mLoopInPoint = loop_in_time;
-	jointmotion_list->mLoopOutPoint = loop_out_time;
-	jointmotion_list->mMaxPriority = base_priority;
-	*/
-	temp_motion->dumpToFile(motion_name);
-}
-
-void BDFloaterPoser::onAnimSet()
-{
-	LLScrollListItem* av_item = mAvatarScroll->getFirstSelected();
-	if (!av_item) return;
-
-	LLVOAvatar* avatar = (LLVOAvatar*)av_item->getUserdata();
-	if (!avatar || avatar->isDead()) return;
-
-	F32 value = getChild<LLUICtrl>("anim_time")->getValue().asReal();
-	S32 selected_index = mAnimEditorScroll->getFirstSelectedIndex();
-	Action action = avatar->mAnimatorActions[selected_index];
-	LLScrollListItem* item = mAnimEditorScroll->getFirstSelected();
-	if (item)
-	{
-		if (action.mType == BD_EActionType::WAIT)
-		{
-			avatar->mAnimatorActions[mAnimEditorScroll->getFirstSelectedIndex()].mTime = value;
-			LLScrollListCell* column = item->getColumn(1);
-			column->setValue(value);
-		}
-	}
-}
-
-void BDFloaterPoser::onAnimPlay()
-{
-	//BD - We start the animator frametimer here and set the expiry time to 0.0
-	//     to force the animator to start immediately when hitting the "Start" button.
-	gDragonAnimator.startPlayback();
-
-	//BD - Update our controls when we start the animator. Most of them should
-	//     disable now.
-	onAnimControlsRefresh();
-	onPoseControlsRefresh();
-}
-
-void BDFloaterPoser::onAnimStop()
-{
-	//BD - We only need to stop it here because the .start function resets the timer 
-	//     automatically.
-	gDragonAnimator.stopPlayback();
-
-	//BD - Update our controls when we stop the animator. Most of them should
-	//     enable again.
-	onAnimControlsRefresh();
-	onPoseControlsRefresh();
-}
-
-void BDFloaterPoser::onAnimControlsRefresh()
-{
-	S32 item_count = mAnimEditorScroll->getItemCount();
-	S32 index = mAnimEditorScroll->getFirstSelectedIndex();
-
-	bool is_playing = gDragonAnimator.getIsPlaying();
-	bool selected = index != -1;
-	bool is_wait = false;
-
-	//BD - Don't.
-	if (!is_playing)
-	{
-		LLScrollListItem* item = mAvatarScroll->getFirstSelected();
-		if (item)
-		{
-			LLVOAvatar* avatar = (LLVOAvatar*)item->getUserdata();
-			if (avatar || !avatar->isDead())
-			{
-				//BD - Crashfix.
-				if (selected)
-				{
-					is_wait = avatar->mAnimatorActions[index].mType == BD_EActionType::WAIT;
-				}
-			}
-		}
-	}
-
-	getChild<LLUICtrl>("anim_time")->setEnabled(!is_playing && selected && is_wait);
-	getChild<LLUICtrl>("delete_entry")->setEnabled(!is_playing && selected);
-	getChild<LLUICtrl>("move_up")->setEnabled(!is_playing && selected && index != 0);
-	getChild<LLUICtrl>("move_down")->setEnabled(!is_playing && selected && !((index + 1) >= item_count));
-	getChild<LLUICtrl>("add_repeat")->setEnabled(!is_playing);
-	getChild<LLUICtrl>("add_wait")->setEnabled(!is_playing);
-	getChild<LLUICtrl>("play_anim")->setEnabled(!is_playing && (item_count > 0));
-	getChild<LLUICtrl>("stop_anim")->setEnabled(is_playing);
-}
-
-
-////////////////////////////////
-//BD - Misc Functions
-////////////////////////////////
-
 void BDFloaterPoser::onUpdateLayout()
 {
 	if (!this->isMinimized())
 	{
-		bool animator_expanded = getChild<LLButton>("animator")->getValue();
-		bool poses_expanded = getChild<LLButton>("extend")->getValue() || animator_expanded;
+		bool poses_expanded = getChild<LLButton>("extend")->getValue();
 		getChild<LLUICtrl>("poses_layout")->setVisible(poses_expanded);
-		getChild<LLUICtrl>("animator_layout")->setVisible(animator_expanded);
-		getChild<LLUICtrl>("poser_layout")->setVisible(!animator_expanded);
 
 		S32 collapsed_width = getChild<LLPanel>("min_panel")->getRect().getWidth();
 		S32 expanded_width = getChild<LLPanel>("max_panel")->getRect().getWidth();
-		S32 floater_width = (poses_expanded && !animator_expanded) ? expanded_width : collapsed_width;
+		S32 floater_width = poses_expanded ? expanded_width : collapsed_width;
 		this->reshape(floater_width, this->getRect().getHeight());
-
-		if (animator_expanded)
-		{
-			//BD - Refresh our animation list
-			onAnimListWrite();
-		}
 	}
 }
 
@@ -2321,7 +2005,6 @@ void BDFloaterPoser::onAvatarsSelect()
 	onUpdateLayout();
 
 	onPoseControlsRefresh();
-	onAnimControlsRefresh();
 
 	//BD - Disable the Start Posing button if we haven't loaded yet.
 	LLScrollListItem* item = mAvatarScroll->getFirstSelected();
@@ -2352,7 +2035,7 @@ void BDFloaterPoser::onAvatarsRefresh()
 		create_new = true;
 		LLVOAvatar* avatar = dynamic_cast<LLVOAvatar*>(character);
 		if (avatar && !avatar->isControlAvatar()
-			&& (avatar->isSelf() || gAgent.isGodlike()))
+			&& avatar->isSelf())
 		{
 			LLUUID uuid = avatar->getID();
 			for (LLScrollListItem* item : mAvatarScroll->getAllData())
@@ -2448,174 +2131,4 @@ void BDFloaterPoser::onAvatarsRefresh()
 
 	//BD - Make sure we don't have a scrollbar unless we need it.
 	mAvatarScroll->updateLayout();
-}
-
-
-////////////////////////////////
-//BD - Experimental Functions
-////////////////////////////////
-
-/*void BDFloaterPoser::onAnimEdit(LLUICtrl* ctrl, const LLSD& param)
-{
-	getChild<LLMultiSliderCtrl>("key_slider")->clear();
-	LLUUID id = LLUUID("cd9b0386-b26d-e860-0114-d879ee12a777");
-
-	//LLKeyframeMotion* motion = (LLKeyframeMotion*)gAgentAvatarp->findMotion(id);
-	typedef std::map<LLUUID, class LLKeyframeMotion::JointMotionList*> keyframe_data_map_t;
-
-	LLKeyframeMotion::JointMotionList* jointmotion_list;
-	jointmotion_list = LLKeyframeDataCache::getKeyframeData(id);
-	S32 i = 0;
-
-	if (!jointmotion_list)
-	{
-		return;
-	}
-
-	LLScrollListItem* item = mJointScrolls[JOINTS]->getLastSelectedItem();
-	if (!item)
-	{
-		return;
-	}
-
-	LLJoint* joint = (LLJoint*)item->getUserdata();
-	i = joint->mJointNum;
-
-	LLKeyframeMotion::JointMotion* joint_motion = jointmotion_list->getJointMotion(i);
-	LLKeyframeMotion::RotationCurve rot_courve = joint_motion->mRotationCurve;
-	LLKeyframeMotion::RotationKey rot_key;
-	LLQuaternion rotation;
-	F32 time;
-	LLKeyframeMotion::RotationCurve::key_map_t keys = rot_courve.mKeys;
-
-	for (LLKeyframeMotion::RotationCurve::key_map_t::const_iterator iter = keys.begin();
-		iter != keys.end(); ++iter)
-	{
-		time = iter->first;
-		rot_key = iter->second;
-
-		//const std::string& sldr_name = getChild<LLMultiSliderCtrl>("key_slider")->addSlider(time);
-		getChild<LLMultiSliderCtrl>("key_slider")->addSlider(time);
-		//getChild<LLMultiSliderCtrl>("key_slider")->getSliderValue();
-	}
-}
-
-void BDFloaterPoser::onAddKey()
-{
-	//S32 max_sliders = 60;
-
-	//if ((S32)mSliderToKey.size() >= max_sliders)
-	//{
-	//	LLSD args;
-	//	args["MAX"] = max_sliders;
-	//	//LLNotificationsUtil::add("DayCycleTooManyKeyframes", args, LLSD(), LLNotificationFunctorRegistry::instance().DONOTHING);
-	//	return;
-	//}
-
-	// add the slider key
-	std::string key_val = mPoseScroll->getFirstSelected()->getColumn(0)->getValue().asString();
-	BDPoseKey pose_params(key_val);
-
-	F32 time = mTimeSlider->getCurSliderValue();
-	addSliderKey(time, pose_params);
-}
-
-void BDFloaterPoser::addSliderKey(F32 time, BDPoseKey keyframe)
-{
-	// make a slider
-	const std::string& sldr_name = mKeySlider->addSlider(time);
-	if (sldr_name.empty())
-	{
-		return;
-	}
-
-	// set the key
-	SliderKey newKey(keyframe, mKeySlider->getCurSliderValue());
-
-	// add to map
-	mSliderToKey.insert(std::pair<std::string, SliderKey>(sldr_name, newKey));
-}
-
-void BDFloaterPoser::onTimeSliderMoved()
-{
-}
-
-void BDFloaterPoser::onKeyTimeMoved()
-{
-	if (mKeySlider->getValue().size() == 0)
-	{
-		return;
-	}
-
-	// make sure we have a slider
-	const std::string& cur_sldr = mKeySlider->getCurSlider();
-	if (cur_sldr == "")
-	{
-		return;
-	}
-
-	F32 time24 = mKeySlider->getCurSliderValue();
-
-	// check to see if a key exists
-	BDPoseKey key = mSliderToKey[cur_sldr].keyframe;
-	//// _LL_DEBUGS() << "Setting key time: " << time24 << LL_ENDL;
-	mSliderToKey[cur_sldr].time = time24;
-
-	// if it exists, turn on check box
-	//mSkyPresetsCombo->selectByValue(key.toStringVal());
-
-	//mTimeCtrl->setTime24(time24);
-}
-
-void BDFloaterPoser::onKeyTimeChanged()
-{
-	// if no keys, skipped
-	if (mSliderToKey.size() == 0)
-	{
-		return;
-	}
-
-	F32 time24 = getChild<LLTimeCtrl>("time_control")->getTime24();
-
-	const std::string& cur_sldr = mKeySlider->getCurSlider();
-	mKeySlider->setCurSliderValue(time24, TRUE);
-	F32 time = mKeySlider->getCurSliderValue() / 60;
-
-	// now set the key's time in the sliderToKey map
-	//// _LL_DEBUGS() << "Setting key time: " << time << LL_ENDL;
-	mSliderToKey[cur_sldr].time = time;
-
-	//applyTrack();
-}
-
-void BDFloaterPoser::onDeleteKey()
-{
-	if (mSliderToKey.size() == 0)
-	{
-		return;
-	}
-
-	// delete from map
-	const std::string& sldr_name = mKeySlider->getCurSlider();
-	std::map<std::string, SliderKey>::iterator mIt = mSliderToKey.find(sldr_name);
-	mSliderToKey.erase(mIt);
-
-	mKeySlider->deleteCurSlider();
-
-	if (mSliderToKey.size() == 0)
-	{
-		return;
-	}
-
-	const std::string& name = mKeySlider->getCurSlider();
-	//mSkyPresetsCombo->selectByValue(mSliderToKey[name].keyframe.toStringVal());
-	F32 time24 = mSliderToKey[name].time;
-
-	getChild<LLTimeCtrl>("time_control")->setTime24(time24);
-}
-
-void BDFloaterPoser::onAnimSetValue(LLUICtrl* ctrl, const LLSD& param)
-{
-	F32 val = ctrl->getValue().asReal();
-	mKeySlider->setValue(val);
-}*/
+}
\ No newline at end of file
diff --git a/indra/newview/bdfloaterposer.h b/indra/newview/bdfloaterposer.h
index 7f125c9d94ff181b74a391b73c183bd56ef220de..7d8393d308310e7515afbe30336fd5117824652e 100644
--- a/indra/newview/bdfloaterposer.h
+++ b/indra/newview/bdfloaterposer.h
@@ -21,86 +21,12 @@
 #include "llfloater.h"
 #include "llscrolllistctrl.h"
 #include "llsliderctrl.h"
-//#include "llmultisliderctrl.h"
-//#include "lltimectrl.h"
 #include "lltabcontainer.h"
 #include "llkeyframemotion.h"
 #include "lltoggleablemenu.h"
 #include "llmenubutton.h"
 
-/*struct BDPoseKey
-{
-public:
-	// source of a pose set
-	std::string name;
-
-	// for conversion from LLSD
-	static const int NAME_IDX = 0;
-	static const int SCOPE_IDX = 1;
-
-	inline BDPoseKey(const std::string& n)
-		: name(n)
-	{
-	}
-
-	inline BDPoseKey(LLSD llsd)
-		: name(llsd[NAME_IDX].asString())
-	{
-	}
-
-	inline BDPoseKey() // NOT really valid, just so std::maps can return a default of some sort
-		: name("")
-	{
-	}
-
-	inline BDPoseKey(std::string& stringVal)
-	{
-		size_t len = stringVal.length();
-		if (len > 0)
-		{
-			name = stringVal.substr(0, len - 1);
-		}
-	}
-
-	inline std::string toStringVal() const
-	{
-		std::stringstream str;
-		str << name;
-		return str.str();
-	}
-
-	inline LLSD toLLSD() const
-	{
-		LLSD llsd = LLSD::emptyArray();
-		llsd.append(LLSD(name));
-		return llsd;
-	}
-
-	inline void fromLLSD(const LLSD& llsd)
-	{
-		name = llsd[NAME_IDX].asString();
-	}
-
-	inline bool operator <(const BDPoseKey other) const
-	{
-		if (name < other.name)
-		{
-			return true;
-		}
-		else
-		{
-			return false;
-		}
-	}
-
-	inline bool operator ==(const BDPoseKey other) const
-	{
-		return (name == other.name);
-	}
-
-	std::string toString() const;
-};*/
-
+#include "llviewerobject.h"
 
 typedef enum E_BoneTypes
 {
@@ -137,17 +63,15 @@ class BDFloaterPoser :
 	/*virtual*/	void onClose(bool app_quitting);
 
 	//BD - Posing
-	void onClickPoseSave();
+	bool onClickPoseSave(const LLSD& param);
 	void onPoseStart();
 	void onPoseDelete();
 	void onPoseRefresh();
-	void onPoseSet(LLUICtrl* ctrl, const LLSD& param);
 	void onPoseControlsRefresh();
-	void onPoseSave(S32 type, F32 time, bool editing);
+	bool onPoseSave();
 	void onPoseLoad();
 	void onPoseLoadSelective(const LLSD& param);
 	void onPoseMenuAction(const LLSD& param);
-	void onPoseScrollRightMouse(LLUICtrl* ctrl, S32 x, S32 y);
 
 	//BD - Joints
 	void onJointRefresh();
@@ -160,18 +84,18 @@ class BDFloaterPoser :
 	void onJointRotationReset();
 	void onJointPositionReset();
 	void onJointScaleReset();
+	void onJointRotationRevert();
+	void onJointRecapture();
 	void onCollectDefaults();
-
-	//BD - Animating
-	void onAnimAdd(const LLSD& param);
-	void onAnimListWrite();
-	void onAnimMove(const LLSD& param);
-	void onAnimDelete();
-	void onAnimSave();
-	void onAnimSet();
-	void onAnimPlay();
-	void onAnimStop();
-	void onAnimControlsRefresh();
+	void onJointContextMenuAction(const LLSD& param);
+	bool onJointContextMenuEnable(const LLSD& param);
+	//BD - Joints - Utilities
+	void onJointPasteRotation();
+	void onJointPastePosition();
+	void onJointPasteScale();
+	void onJointMirror();
+	void onJointSymmetrize();
+	void onJointCopyTransforms();
 
 	//BD - Misc
 	void onUpdateLayout();
@@ -194,61 +118,36 @@ class BDFloaterPoser :
 
 private:
 	//BD - Posing
-	LLScrollListCtrl*						mPoseScroll;
-	LLTabContainer*							mJointTabs;
-	LLHandle<LLToggleableMenu>				mPosesMenuHandle;
+	LLScrollListCtrl*							mPoseScroll;
+	LLTabContainer*								mJointTabs;
+	LLTabContainer*								mModifierTabs;
 
-	std::array<LLUICtrl*, 3>				mRotationSliders;
-	std::array<LLSliderCtrl*, 3>			mPositionSliders;
-	std::array<LLSliderCtrl*, 3>			mScaleSliders;
-	std::array<LLScrollListCtrl*, 3>		mJointScrolls;
+	std::array<LLUICtrl*, 3>					mRotationSliders;
+	std::array<LLSliderCtrl*, 3>				mPositionSliders;
+	std::array<LLSliderCtrl*, 3>				mScaleSliders;
+	std::array<LLScrollListCtrl*, 3>			mJointScrolls;
 
 	//BD - I really didn't want to do this this way but we have to.
 	//     It's the easiest way doing this.
-	std::map<const std::string, LLVector3>	mDefaultScales;
-	std::map<const std::string, LLVector3>	mDefaultPositions;
-
-	//BD - Animations
-	LLScrollListCtrl*						mAnimEditorScroll;
+	std::map<const std::string, LLQuaternion>	mDefaultRotations;
+	std::map<const std::string, LLVector3>		mDefaultScales;
+	std::map<const std::string, LLVector3>		mDefaultPositions;
 
 	//BD - Misc
-	bool									mDelayRefresh;
-	bool									mEasyRotations;
+	bool										mDelayRefresh;
+	bool										mEasyRotations;
 	
 	//BD - Mirror Bone
-	bool									mMirrorMode;
+	bool										mMirrorMode;
 
 	//BD - Animesh
-	LLScrollListCtrl*						mAvatarScroll;
-
-	LLButton*								mStartPosingBtn;
-	LLMenuButton*							mLoadPosesBtn;
-
-	//BD - Experimental
-	/*void onAnimEdit(LLUICtrl* ctrl, const LLSD& param);
-	void onAddKey();
-	void onDeleteKey();
-	void addSliderKey(F32 time, BDPoseKey keyframe);
-	void onTimeSliderMoved();
-	void onKeyTimeMoved();
-	void onKeyTimeChanged();
-	void onAnimSetValue(LLUICtrl* ctrl, const LLSD& param);
-
-	/// convenience class for holding keyframes mapped to sliders
-	struct SliderKey
-	{
-	public:
-		SliderKey(BDPoseKey kf, F32 t) : keyframe(kf), time(t) {}
-		SliderKey() : keyframe(), time(0.f) {} // Don't use this default constructor
-
-		BDPoseKey keyframe;
-		F32 time;
-	};
-
-	LLSD 									mAnimJointMap[134][200][2]; // 134 bones, 200 keyframes , 2 stats (rotation | time)
-	std::map<std::string, SliderKey>		mSliderToKey;
-	LLMultiSliderCtrl*						mTimeSlider;
-	LLMultiSliderCtrl*						mKeySlider; */
+	LLScrollListCtrl*							mAvatarScroll;
+
+	LLButton*									mStartPosingBtn;
+	LLMenuButton*								mLoadPosesBtn;
+	LLButton*									mSavePosesBtn;
+
+	LLSD										mClipboard;
 };
 
 #endif
diff --git a/indra/newview/bdposingmotion.cpp b/indra/newview/bdposingmotion.cpp
index ef8a21e3b2d5d986b7d25fe19d375c71456fe907..20aa02ec97ae33bacd415393f3a78a6f6285df34 100644
--- a/indra/newview/bdposingmotion.cpp
+++ b/indra/newview/bdposingmotion.cpp
@@ -65,16 +65,8 @@ LLMotion::LLMotionInitStatus BDPosingMotion::onInitialize(LLCharacter *character
 	for (S32 i = 0; (mTargetJoint = mCharacter->getCharacterJoint(i)); ++i)
 	{
 		mJointState[i]->setJoint(mTargetJoint);
-		//BD - Bones that can support position
-		//     0, 9-37, 39-43, 45-59, 77, 97-107, 110, 112, 115, 117-121, 125, 128-129, 132
-		if (mTargetJoint->mHasPosition)
-		{
-			mJointState[i]->setUsage(LLJointState::POS | LLJointState::ROT);
-		}
-		else
-		{
-			mJointState[i]->setUsage(LLJointState::ROT);
-		}
+		//BD - All bones support positions now.
+		mJointState[i]->setUsage(LLJointState::POS | LLJointState::ROT /* | LLJointState::SCALE*/);
 		addJointState(mJointState[i]);
 	}
 
@@ -92,12 +84,8 @@ BOOL BDPosingMotion::onActivate()
 		if (joint)
 		{
 			joint->setTargetRotation(joint->getRotation());
-			//BD - Bones that can support position
-			//     0, 9-37, 39-43, 45-59, 77, 97-107, 110, 112, 115, 117-121, 125, 128-129, 132
-			if (joint->mHasPosition)
-			{
-				joint->setTargetPosition(joint->getPosition());
-			}
+			//BD - All bones support positions now.
+			joint->setTargetPosition(joint->getPosition());
 		}
 	}
 	return TRUE;
@@ -129,53 +117,49 @@ BOOL BDPosingMotion::onUpdate(F32 time, U8* joint_mask)
 
 			//BD - Merge these two together?
 			perc = llclamp(mInterpolationTimer.getElapsedTimeF32() / mInterpolationTime, 0.0f, 1.0f);
-			//BD - Bones that can support position
-			//     0, 9-37, 39-43, 45-59, 77, 97-107, 110, 112, 115, 117-121, 125, 128-129, 132
-			if (joint->mHasPosition)
+			//BD - All bones support positions now.
+			joint_pos = joint->getPosition();
+			target_pos = joint->getTargetPosition();
+			last_pos = joint->getLastPosition();
+			if (target_pos != joint_pos)
 			{
-				joint_pos = joint->getPosition();
-				target_pos = joint->getTargetPosition();
-				last_pos = joint->getLastPosition();
-				if (target_pos != joint_pos)
+				if (mInterpolationType == 2)
 				{
-					if (mInterpolationType == 2)
-					{
-						//BD - Do spherical linear interpolation.
-						//     We emulate the spherical linear interpolation here because
-						//     slerp() does not support LLVector3. mInterpolationTime is always
-						//     in a range between 0.00 and 1.00 which makes it perfect to use
-						//     as percentage directly.
-						//     We use the current joint position rather than the original like
-						//     in linear interpolation to take a fraction of the fraction, this
-						//     re-creates spherical linear interpolation's behavior.
-						joint_pos = lerp(joint_pos, target_pos, mInterpolationTime);
-					}
-					else if (mInterpolationType == 3)
+					//BD - Do spherical linear interpolation.
+					//     We emulate the spherical linear interpolation here because
+					//     slerp() does not support LLVector3. mInterpolationTime is always
+					//     in a range between 0.00 and 1.00 which makes it perfect to use
+					//     as percentage directly.
+					//     We use the current joint position rather than the original like
+					//     in linear interpolation to take a fraction of the fraction, this
+					//     re-creates spherical linear interpolation's behavior.
+					joint_pos = lerp(joint_pos, target_pos, mInterpolationTime);
+				}
+				else if (mInterpolationType == 3)
+				{
+					next_pos = joint->getNextPosition();
+					//BD - Do curve interpolation.
+					//     This is a special kind of interpolation where we interpolate towards
+					//     a "middle" pose to a given degree while on our way to the actual final
+					//     pose.
+					joint_pos = lerp(joint_pos, next_pos, perc);
+					joint_pos = lerp(joint_pos, target_pos, 0.5f - abs(0.5f - perc));
+				}
+				else
+				{
+					if (perc >= 1.0f)
 					{
-						next_pos = joint->getNextPosition();
-						//BD - Do curve interpolation.
-						//     This is a special kind of interpolation where we interpolate towards
-						//     a "middle" pose to a given degree while on our way to the actual final
-						//     pose.
-						joint_pos = lerp(joint_pos, next_pos, perc);
-						joint_pos = lerp(joint_pos, target_pos, 0.5f - abs(0.5f - perc));
+						//BD - Can be used to do no interpolation too.
+						joint_pos = target_pos;
+						last_pos = joint_pos;
 					}
 					else
 					{
-						if (perc >= 1.0f)
-						{
-							//BD - Can be used to do no interpolation too.
-							joint_pos = target_pos;
-							last_pos = joint_pos;
-						}
-						else
-						{
-							//BD - Do linear interpolation.
-							joint_pos = lerp(last_pos, target_pos, perc);
-						}
+						//BD - Do linear interpolation.
+						joint_pos = lerp(last_pos, target_pos, perc);
 					}
-					joint_state->setPosition(joint_pos);
 				}
+				joint_state->setPosition(joint_pos);
 			}
 
 			if (target_quat != joint_quat)
@@ -245,16 +229,8 @@ void BDPosingMotion::addJointToState(LLJoint *joint)
 		return;
 
 	mJointState[i]->setJoint(joint);
-	//BD - Bones that can support position
-	//     0, 9-37, 39-43, 45-59, 77, 97-107, 110, 112, 115, 117-121, 125, 128-129, 132
-	if (joint->mHasPosition)
-	{
-		mJointState[i]->setUsage(LLJointState::POS | LLJointState::ROT);
-	}
-	else
-	{
-		mJointState[i]->setUsage(LLJointState::ROT);
-	}
+	//BD - All bones support positions now.
+	mJointState[i]->setUsage(LLJointState::POS | LLJointState::ROT /* | LLJointState::SCALE*/);
 	addJointState(mJointState[i]);
 }
 // End
diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp
index 1a2d15dadac526b359a91b0a48e351ede5f63062..189612f46f9b82c904a8e11315921e55e577272c 100644
--- a/indra/newview/llviewerfloaterreg.cpp
+++ b/indra/newview/llviewerfloaterreg.cpp
@@ -200,6 +200,7 @@
 #include "llscriptfloater.h"
 #include "llsyswellwindow.h"
 #include "bdfloaterposer.h"
+//#include "bdfloaterposecreator.h"
 
 // *NOTE: Please add files in alphabetical order to keep merges easy.
 // [RLVa:KB] - Checked: 2010-03-11
@@ -569,6 +570,7 @@ void LLViewerFloaterReg::registerFloaters()
     LLFloaterReg::add("new_local_inventory", "floater_new_local_inventory.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterNewLocalInventory>);
 	LLFloaterReg::add("particle_editor", "floater_particle_editor.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<ALFloaterParticleEditor>);
 	LLFloaterReg::add("poser", "floater_poser.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<BDFloaterPoser>);
+	//LLFloaterReg::add("poser_creator", "floater_poser_creator.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<BDFloaterPoseCreator>);
 	LLFloaterReg::add("progress_view", "floater_progress_view.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterProgressView>);
 	LLFloaterReg::add("quick_settings", "floater_quick_settings.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloater>);
 	LLFloaterReg::add("region_tracker", "floater_region_tracker.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<ALFloaterRegionTracker>);
diff --git a/indra/newview/skins/default/textures/textures.xml b/indra/newview/skins/default/textures/textures.xml
index aa1f5e6a6ac6037cd4f06c9a2abd45b7af2d0f8f..43b829934c2dcc546c9489abcdfb1b77e0a355f0 100644
--- a/indra/newview/skins/default/textures/textures.xml
+++ b/indra/newview/skins/default/textures/textures.xml
@@ -277,6 +277,7 @@ with the same filename but different name
 
   <texture name="Icon_Dock_Foreground" file_name="windows/Icon_Dock_Foreground.png" preload="true" />
   <texture name="Icon_Dock_Press" file_name="windows/Icon_Dock_Press.png" preload="true" />
+  <texture name="Icon_Undock_Foreground" file_name="windows/Icon_Undock_Foreground.png" preload="true" />
 
   <texture name="Icon_File_Upload" file_name="icons/file_upload.png" preload="true" />
   
diff --git a/indra/newview/skins/default/xui/en/floater_poser.xml b/indra/newview/skins/default/xui/en/floater_poser.xml
index a9f96e9c2b6aded07e0e903958e993fe0307122d..916093baf7f5829de64738ce7b9ed11cda2a2b3f 100644
--- a/indra/newview/skins/default/xui/en/floater_poser.xml
+++ b/indra/newview/skins/default/xui/en/floater_poser.xml
@@ -4,22 +4,22 @@ positioning="centered"
 height="431"
 layout="topleft"
 name="floater_poser"
-title="Poser"
-width="678">
+title="Poser - Live Editing Mode"
+width="654">
 	<!-- Expanded Size 745 -->
 	<!-- Expanded Size 906 -->
 	<panel
 	height="1"
 	visible="false"
 	name="max_panel"
-	width="906"/>
+	width="886"/>
 	<!-- Collapsed Size 517 -->
 	<!-- Collapsed Size 678 -->
 	<panel
 	height="1"
 	visible="false"
 	name="min_panel"
-	width="678"/>
+	width="654"/>
 	<string
 	name="linear_time" translate="false">
 	0.000+
@@ -29,6 +29,14 @@ width="678">
 	0.001 to 1.0
 	</string>
 	<string
+	name="live_mode_str" translate="false">
+	Poser - Live Editing Mode
+	</string>
+	<string
+	name="creation_mode_str" translate="false">
+	Poser - Create A Pose Mode
+	</string>
+	<string
 	name="icon_category" translate="false">
 	Inv_Shape
 	</string>
@@ -89,14 +97,14 @@ width="678">
 	name="poser_stack"
 	orientation="horizontal"
 	top="0"
-	width="666">
+	width="642">
 		<layout_panel
 		follows="top|left|right"
 		layout="topleft"
 		height="420"
 		auto_resize="false"
 		visible="true"
-		name="poser_layout"
+		name="poser_list_layout"
 		width="162">
 			<panel
 			follows="left|top|bottom"
@@ -108,47 +116,59 @@ width="678">
 			name="title"
 			top="0"
 			width="163">
-				<scroll_list
-				column_padding="0"
-				draw_heading="true"
-				height="279"
-				can_sort="false"
-				follows="left|top|right"
-				layout="topleft"
-				left="1"
-				tool_tip="Select the targeted avatar or animesh."
-				width="154"
-				multi_select="false"
-				name="avatar_scroll"
-				top="20">
-				<scroll_list.columns
-				label=""
-				name="icon"
-				relative_width="0.14"  />
-				<scroll_list.columns
-				label="Name"
-				name="name"
-				relative_width="0.86"  />
-				<scroll_list.columns
-				label="UUID"
-				name="uuid"
-				relative_width="0.0"  />
-				</scroll_list>
-				<button
-				height="21"
-				follows="top|left"
+				<panel
+				follows="left|top|bottom"
+				height="319"
+				background_visible="false"
 				layout="topleft"
-				label="Refresh Avatars"
-				image_hover_unselected="Toolbar_Middle_Over"
-				image_selected="Toolbar_Middle_Selected"
-				image_unselected="Toolbar_Middle_Off"
-				name="refresh"
-				width="151"
-				top_pad="-1"
-				left="2">
-				<button.commit_callback
-				function="Poser.RefreshAvatars"/>
-				</button>
+				visible="true"
+				mouse_opaque="false"
+				left="0"
+				name="live_panel"
+				top="0"
+				width="153">
+					<scroll_list
+					column_padding="0"
+					draw_heading="true"
+					height="279"
+					can_sort="false"
+					follows="left|top|right"
+					layout="topleft"
+					left="1"
+					tool_tip="Select the targeted avatar or animesh."
+					width="153"
+					multi_select="false"
+					name="avatar_scroll"
+					top="20">
+					<scroll_list.columns
+					label=""
+					name="icon"
+					relative_width="0.14"  />
+					<scroll_list.columns
+					label="Name"
+					name="name"
+					relative_width="0.86"  />
+					<scroll_list.columns
+					label="UUID"
+					name="uuid"
+					relative_width="0.0"  />
+					</scroll_list>
+					<button
+					height="21"
+					follows="top|left"
+					layout="topleft"
+					label="Refresh Avatars"
+					image_hover_unselected="Toolbar_Middle_Over"
+					image_selected="Toolbar_Middle_Selected"
+					image_unselected="Toolbar_Middle_Off"
+					name="refresh"
+					width="151"
+					top_pad="-1"
+					left="2">
+					<button.commit_callback
+					function="Poser.RefreshAvatars"/>
+					</button>
+				</panel>
 				<check_box
 				height="16"
 				initial_value="false"
@@ -185,12 +205,29 @@ width="678">
 				image_selected="Toolbar_Middle_Selected"
 				image_unselected="Toolbar_Middle_Off"
 				name="flip_pose"
-				width="151"
+				width="75"
 				top_pad="40"
 				left="2">
 				<button.commit_callback
 				function="Joint.FlipPose"/>
 				</button>
+				<button
+				height="21"
+				follows="top|left"
+				layout="topleft"
+				enabled="true"
+				label="Recapture"
+				tool_tip="Recapture all disabled joints, this is useful when disabling bones to edit them via outside means such as HUDs and then recapture these changes into the Poser to make fine adjustments."
+				image_hover_unselected="Toolbar_Middle_Over"
+				image_selected="Toolbar_Middle_Selected"
+				image_unselected="Toolbar_Middle_Off"
+				name="recapture_bones"
+				width="75"
+				top_delta="0"
+				left_pad="1">
+				<button.commit_callback
+				function="Joint.Recapture"/>
+				</button>
 			</panel>
 		</layout_panel>
 		<layout_panel
@@ -211,340 +248,369 @@ width="678">
 			name="title"
 			top="0"
 			width="485">
-				<tab_container
-				follows="all"
-				halign="center"
-				height="299"
+				<layout_stack
+				follows="top|left|right"
+				height="319"
 				layout="topleft"
 				left="0"
-				enabled="false"
-				name="joints_tabs"
-				tab_height="20"
-				tab_group="1"
-				tab_position="top"
+				border_size="-4"
+				close_time_constant="0.0"
+				open_time_constant="0.0"
+				mouse_opaque="false"
+				name="poser_layout_stack"
+				orientation="vertical"
 				top="0"
-				width="483">
-					<panel
-					follows="all"
-					background_visible="false"
-					height="299"
-					layout="topleft"
-					left="0"
-					title="Skeleton"
-					name="joints_panel"
-					top="0"
-					width="481">
-						<scroll_list
-						column_padding="2"
-						draw_heading="true"
-						height="279"
-						follows="left|top|right"
-						can_sort="false"
-						layout="topleft"
-						left="2"
-						width="479"
-						multi_select="true"
-						name="joints_scroll"
-						top="0">
-						<scroll_list.columns
-						label=""
-						name="icon"
-						relative_width="0.04"  />
-						<scroll_list.columns
-						label="Bone Name"
-						name="joint"
-						relative_width="0.33"  />
-						<scroll_list.columns
-						label="Rot X"
-						name="x"
-						relative_width="0.10"  />
-						<scroll_list.columns
-						label="Rot Y"
-						name="y"
-						relative_width="0.10"  />
-						<scroll_list.columns
-						label="Rot Z"
-						name="z"
-						relative_width="0.10"  />
-						<scroll_list.columns
-						label="Pos X"
-						name="pos_x"
-						relative_width="0.10"  />
-						<scroll_list.columns
-						label="Pos Y"
-						name="pos_y"
-						relative_width="0.10"  />
-						<scroll_list.columns
-						label="Pos Z"
-						name="pos_z"
-						relative_width="0.10"  />
-						<scroll_list.columns
-						label="Size X"
-						name="scale_x"
-						relative_width="0.0"  />
-						<scroll_list.columns
-						label="Size Y"
-						name="scale_y"
-						relative_width="0.0"  />
-						<scroll_list.columns
-						label="Size Z"
-						name="scale_z"
-						relative_width="0.0"  />
-						<scroll_list.columns
-						label="#"
-						name="number"
-						relative_width="0.00"  />
-						</scroll_list>
-					</panel>
-					<panel
+				width="666">
+					<layout_panel
 					follows="all"
-					background_visible="false"
-					height="299"
 					layout="topleft"
-					left="0"
-					title="Collision Volumes"
-					name="cv_panel"
-					top="0"
+					height="302"
+					auto_resize="true"
+					name="poser_layout"
 					width="481">
-						<scroll_list
-						column_padding="2"
-						draw_heading="true"
-						height="279"
-						follows="left|top|right"
+						<panel
+						follows="left|top|bottom"
+						height="302"
+						background_visible="false"
 						layout="topleft"
-						can_sort="false"
-						left="2"
-						width="478"
-						multi_select="true"
-						name="cv_scroll"
-						top="0">
-						<scroll_list.columns
-						label=""
-						name="icon"
-						relative_width="0.04"  />
-						<scroll_list.columns
-						label="Collision Volume Name"
-						name="joint"
-						relative_width="0.33"  />
-						<scroll_list.columns
-						label="Rot X"
-						name="x"
-						relative_width="0.0"  />
-						<scroll_list.columns
-						label="Rot Y"
-						name="y"
-						relative_width="0.0"  />
-						<scroll_list.columns
-						label="Rot Z"
-						name="z"
-						relative_width="0.0"  />
-						<scroll_list.columns
-						label="Pos X"
-						name="pos_x"
-						relative_width="0.10"  />
-						<scroll_list.columns
-						label="Pos Y"
-						name="pos_y"
-						relative_width="0.10"  />
-						<scroll_list.columns
-						label="Pos Z"
-						name="pos_z"
-						relative_width="0.10"  />
-						<scroll_list.columns
-						label="Size X"
-						name="scale_x"
-						relative_width="0.10"  />
-						<scroll_list.columns
-						label="Size Y"
-						name="scale_y"
-						relative_width="0.10"  />
-						<scroll_list.columns
-						label="Size Z"
-						name="scale_z"
-						relative_width="0.10"  />
-						<scroll_list.columns
-						label="#"
-						name="number"
-						relative_width="0.00"  />
-						</scroll_list>
-					</panel>
-					<panel
-					follows="all"
-					background_visible="false"
-					height="299"
+						mouse_opaque="false"
+						left="0"
+						name="title"
+						top="0"
+						width="481">
+							<tab_container
+							follows="all"
+							halign="center"
+							height="299"
+							layout="topleft"
+							left="0"
+							enabled="false"
+							name="joints_tabs"
+							tab_height="20"
+							tab_group="1"
+							tab_position="top"
+							top="0"
+							width="483">
+								<panel
+								follows="all"
+								background_visible="false"
+								height="299"
+								layout="topleft"
+								left="0"
+								title="Skeleton"
+								name="joints_panel"
+								top="0"
+								width="481">
+									<panel
+									follows="left|top|bottom"
+									height="302"
+									background_visible="false"
+									layout="topleft"
+									mouse_opaque="false"
+									left="0"
+									name="title"
+									top="0"
+									width="481">
+										<scroll_list
+										column_padding="2"
+										draw_heading="true"
+										height="300"
+										follows="all"
+										can_sort="false"
+										layout="topleft"
+										left="2"
+										width="479"
+										multi_select="true"
+										name="joints_scroll"
+										top="0">
+										<scroll_list.columns
+										label=""
+										name="icon"
+										relative_width="0.04"  />
+										<scroll_list.columns
+										label="Bone Name"
+										name="joint"
+										relative_width="0.33"  />
+										<scroll_list.columns
+										label="Rot X"
+										name="x"
+										relative_width="0.10"  />
+										<scroll_list.columns
+										label="Rot Y"
+										name="y"
+										relative_width="0.10"  />
+										<scroll_list.columns
+										label="Rot Z"
+										name="z"
+										relative_width="0.10"  />
+										<scroll_list.columns
+										label="Pos X"
+										name="pos_x"
+										relative_width="0.10"  />
+										<scroll_list.columns
+										label="Pos Y"
+										name="pos_y"
+										relative_width="0.10"  />
+										<scroll_list.columns
+										label="Pos Z"
+										name="pos_z"
+										relative_width="0.10"  />
+										<scroll_list.columns
+										label="Size X"
+										name="scale_x"
+										relative_width="0.0"  />
+										<scroll_list.columns
+										label="Size Y"
+										name="scale_y"
+										relative_width="0.0"  />
+										<scroll_list.columns
+										label="Size Z"
+										name="scale_z"
+										relative_width="0.0"  />
+										</scroll_list>
+									</panel>
+								</panel>
+								<panel
+								follows="all"
+								background_visible="false"
+								height="299"
+								layout="topleft"
+								left="0"
+								title="Collision Volumes"
+								name="cv_panel"
+								top="0"
+								width="481">
+									<scroll_list
+									column_padding="2"
+									draw_heading="true"
+									height="300"
+									follows="left|top|right"
+									layout="topleft"
+									can_sort="false"
+									left="2"
+									width="479"
+									multi_select="true"
+									name="cv_scroll"
+									top="0">
+									<scroll_list.columns
+									label=""
+									name="icon"
+									relative_width="0.04"  />
+									<scroll_list.columns
+									label="Collision Volume Name"
+									name="joint"
+									relative_width="0.33"  />
+									<scroll_list.columns
+									label="Rot X"
+									name="x"
+									relative_width="0.0"  />
+									<scroll_list.columns
+									label="Rot Y"
+									name="y"
+									relative_width="0.0"  />
+									<scroll_list.columns
+									label="Rot Z"
+									name="z"
+									relative_width="0.0"  />
+									<scroll_list.columns
+									label="Pos X"
+									name="pos_x"
+									relative_width="0.10"  />
+									<scroll_list.columns
+									label="Pos Y"
+									name="pos_y"
+									relative_width="0.10"  />
+									<scroll_list.columns
+									label="Pos Z"
+									name="pos_z"
+									relative_width="0.10"  />
+									<scroll_list.columns
+									label="Size X"
+									name="scale_x"
+									relative_width="0.10"  />
+									<scroll_list.columns
+									label="Size Y"
+									name="scale_y"
+									relative_width="0.10"  />
+									<scroll_list.columns
+									label="Size Z"
+									name="scale_z"
+									relative_width="0.10"  />
+									<scroll_list.columns
+									label="#"
+									name="number"
+									relative_width="0.00"  />
+									</scroll_list>
+								</panel>
+								<panel
+								follows="all"
+								background_visible="false"
+								height="299"
+								layout="topleft"
+								left="0"
+								title="Attachment Bones"
+								name="attach_panel"
+								top="0"
+								width="481">
+									<scroll_list
+									column_padding="2"
+									draw_heading="true"
+									height="300"
+									follows="left|top|right"
+									layout="topleft"
+									can_sort="false"
+									left="2"
+									width="479"
+									multi_select="true"
+									name="attach_scroll"
+									top="0">
+									<scroll_list.columns
+									label=""
+									name="icon"
+									relative_width="0.04"  />
+									<scroll_list.columns
+									label="Attachment Bone Name"
+									name="joint"
+									relative_width="0.33"  />
+									<scroll_list.columns
+									label="Rot X"
+									name="x"
+									relative_width="0.0"  />
+									<scroll_list.columns
+									label="Rot Y"
+									name="y"
+									relative_width="0.0"  />
+									<scroll_list.columns
+									label="Rot Z"
+									name="z"
+									relative_width="0.0"  />
+									<scroll_list.columns
+									label="Pos X"
+									name="pos_x"
+									relative_width="0.10"  />
+									<scroll_list.columns
+									label="Pos Y"
+									name="pos_y"
+									relative_width="0.10"  />
+									<scroll_list.columns
+									label="Pos Z"
+									name="pos_z"
+									relative_width="0.10"  />
+									<scroll_list.columns
+									label="Size X"
+									name="scale_x"
+									relative_width="0.10"  />
+									<scroll_list.columns
+									label="Size Y"
+									name="scale_y"
+									relative_width="0.10"  />
+									<scroll_list.columns
+									label="Size Z"
+									name="scale_z"
+									relative_width="0.10"  />
+									<scroll_list.columns
+									label="#"
+									name="number"
+									relative_width="0.00"  />
+									</scroll_list>
+								</panel>
+							</tab_container>
+						</panel>
+					</layout_panel>
+					<layout_panel
+					follows="top|left|right"
 					layout="topleft"
-					left="0"
-					title="Attachment Bones"
-					name="attach_panel"
-					top="0"
+					height="21"
+					auto_resize="true"
+					visible="true"
+					name="poser_options_layout"
 					width="481">
-						<scroll_list
-						column_padding="2"
-						draw_heading="true"
-						height="279"
-						follows="left|top|right"
+						<panel
+						follows="left|top"
+						height="21"
+						background_visible="false"
 						layout="topleft"
-						can_sort="false"
-						left="2"
-						width="478"
-						multi_select="true"
-						name="attach_scroll"
-						top="0">
-						<scroll_list.columns
-						label=""
-						name="icon"
-						relative_width="0.04"  />
-						<scroll_list.columns
-						label="Attachment Bone Name"
-						name="joint"
-						relative_width="0.33"  />
-						<scroll_list.columns
-						label="Rot X"
-						name="x"
-						relative_width="0.0"  />
-						<scroll_list.columns
-						label="Rot Y"
-						name="y"
-						relative_width="0.0"  />
-						<scroll_list.columns
-						label="Rot Z"
-						name="z"
-						relative_width="0.0"  />
-						<scroll_list.columns
-						label="Pos X"
-						name="pos_x"
-						relative_width="0.10"  />
-						<scroll_list.columns
-						label="Pos Y"
-						name="pos_y"
-						relative_width="0.10"  />
-						<scroll_list.columns
-						label="Pos Z"
-						name="pos_z"
-						relative_width="0.10"  />
-						<scroll_list.columns
-						label="Size X"
-						name="scale_x"
-						relative_width="0.10"  />
-						<scroll_list.columns
-						label="Size Y"
-						name="scale_y"
-						relative_width="0.10"  />
-						<scroll_list.columns
-						label="Size Z"
-						name="scale_z"
-						relative_width="0.10"  />
-						<scroll_list.columns
-						label="#"
-						name="number"
-						relative_width="0.00"  />
-						</scroll_list>
-					</panel>
-				</tab_container>
-				<button
-				height="21"
-				follows="top|left"
-				is_toggle="true"
-				layout="topleft"
-				label="Start Posing"
-				label_selected="Stop Posing"
-				image_hover_unselected="Toolbar_Middle_Over"
-				image_selected="Toolbar_Middle_Selected"
-				image_unselected="Toolbar_Middle_Off"
-				button_flash_enable="true"
-				flash_color="0.7 0.7 1 1"
-				button_flash_count="64"
-				button_flash_rate="0.5"
-				name="activate"
-				width="100"
-				top_pad="-1"
-				left="4">
-				<button.commit_callback
-				function="Pose.Start"/>
-				</button>
-				<line_editor
-				commit_on_focus_lost="false"
-				follows="left|top"
-				height="20"
-				label="Enter Pose Name"
-				left_pad="1"
-				enabled="false"
-				top_delta="0"
-				name="pose_name"
-				width="157"/>
-				<line_editor
-				commit_on_focus_lost="false"
-				follows="left|top"
-				height="20"
-				enabled="false"
-				label="0.001 - 1.0"
-				left_pad="1"
-				top_delta="0"
-				name="interpolation_time"
-				width="78"/>
-				<combo_box
-				follows="left|top"
-				height="21"
-				enabled="false"
-				layout="topleft"
-				left_pad="1"
-				top_delta="0"
-				max_chars="135"
-				name="interpolation_type"
-				width="86">
-				<combo_box.item
-				enabled="true"
-				label="None"
-				name="0"
-				value="0" />
-				<combo_box.item
-				enabled="true"
-				label="Linear"
-				name="1"
-				value="1" />
-				<combo_box.item
-				enabled="true"
-				label="Spherical Linear"
-				name="2"
-				value="2" />
-				<combo_box.commit_callback
-				function="Pose.Interpolation"/>
-				</combo_box>
-				<button
-				height="21"
-				follows="top|left"
-				layout="topleft"
-				label=""
-				enabled="false"
-				image_overlay="Icon_Dock_Foreground"
-				image_overlay_alignment="right"
-				image_hover_unselected="Toolbar_Middle_Over"
-				image_selected="Toolbar_Middle_Selected"
-				image_unselected="Toolbar_Middle_Off"
-				tool_tip="Save the current pose."
-				name="save_poses"
-				width="25"
-				top_delta="0"
-				left_pad="1">
-				<button.commit_callback
-				function="Pose.Save"/>
-				</button>
-				<button
-				follows="left|top"
-				height="21"
-				is_toggle="true"
-				layout="topleft"
-				image_overlay="Hierarchy_View_On"
-				image_hover_unselected="Toolbar_Middle_Over"
-				image_selected="Toolbar_Middle_Selected"
-				image_unselected="Toolbar_Middle_Off"
-				name="extend"
-				left_pad="1"
-				top_delta="0"
-				width="25" >
-				<button.commit_callback
-				function="Pose.Layout"/>
-				</button>
+						mouse_opaque="false"
+						left="0"
+						name="title"
+						top="0"
+						width="481">
+							<button
+							height="21"
+							follows="top|left"
+							is_toggle="true"
+							layout="topleft"
+							label="Start Posing"
+							label_selected="Stop Posing"
+							image_hover_unselected="Toolbar_Middle_Over"
+							image_selected="Toolbar_Middle_Selected"
+							image_unselected="Toolbar_Middle_Off"
+							button_flash_enable="true"
+							flash_color="0.7 0.7 1 1"
+							button_flash_count="64"
+							button_flash_rate="0.5"
+							name="activate"
+							width="106"
+							top_pad="0"
+							left="4">
+							<button.commit_callback
+							function="Pose.Start"/>
+							</button>
+							<line_editor
+							commit_on_focus_lost="false"
+							follows="left|top"
+							height="20"
+							label="Enter Pose Name"
+							left_pad="1"
+							enabled="false"
+							top_delta="0"
+							name="pose_name"
+							width="157"/>
+							<button
+							height="21"
+							follows="top|left"
+							layout="topleft"
+							label="Save Pose"
+							enabled="false"
+							image_overlay="Icon_Dock_Foreground"
+							image_overlay_alignment="left"
+							image_hover_unselected="Toolbar_Middle_Over"
+							image_selected="Toolbar_Middle_Selected"
+							image_unselected="Toolbar_Middle_Off"
+							tool_tip="Save the current pose."
+							name="save_poses"
+							width="105"
+							top_delta="0"
+							left_pad="1">
+								<button.commit_callback
+								function="Pose.Save"/>
+							</button>
+							<button
+							follows="left|top"
+							height="21"
+							is_toggle="true"
+							layout="topleft"
+							label="Pose Library"
+							image_overlay="Hierarchy_View_On"
+							image_overlay_alignment="left"
+							image_bottom_pad="1"
+							image_hover_unselected="Toolbar_Middle_Over"
+							image_selected="Toolbar_Middle_Selected"
+							image_unselected="Toolbar_Middle_Off"
+							button_flash_enable="true"
+							flash_color="0.7 0.7 1 1"
+							button_flash_count="64"
+							button_flash_rate="0.5"
+							name="extend"
+							left_pad="1"
+							top_delta="0"
+							width="105" >
+							<button.commit_callback
+							function="Pose.Layout"/>
+							</button>
+						</panel>
+					</layout_panel>
+				</layout_stack>
 				<tab_container
 				follows="all"
 				halign="center"
@@ -650,12 +716,28 @@ width="678">
 							image_selected="Toolbar_Middle_Selected"
 							image_unselected="Toolbar_Middle_Off"
 							name="reset_bone_rot"
-							width="238"
+							width="159"
 							top="0"
 							left_delta="2">
 							<button.commit_callback
 							function="Joint.ResetJointRotation"/>
 							</button>
+							<button
+							height="21"
+							follows="top|left"
+							layout="topleft"
+							enabled="true"
+							label="Revert Selected Rotation(s)"
+							image_hover_unselected="Toolbar_Middle_Over"
+							image_selected="Toolbar_Middle_Selected"
+							image_unselected="Toolbar_Middle_Off"
+							name="revert_pose"
+							width="158"
+							top_delta="0"
+							left_pad="1">
+							<button.commit_callback
+							function="Joint.RevertJointRotation"/>
+							</button>
 						</panel>
 					</panel>
 					<panel
@@ -749,7 +831,7 @@ width="678">
 							image_selected="Toolbar_Middle_Selected"
 							image_unselected="Toolbar_Middle_Off"
 							name="reset_bone_pos"
-							width="238"
+							width="318"
 							top="0"
 							left_delta="2">
 							<button.commit_callback
@@ -848,7 +930,7 @@ width="678">
 							image_selected="Toolbar_Middle_Selected"
 							image_unselected="Toolbar_Middle_Off"
 							name="reset_bone_scale"
-							width="238"
+							width="318"
 							top="0"
 							left_delta="2">
 							<button.commit_callback
@@ -869,9 +951,9 @@ width="678">
 				image_selected="Toolbar_Middle_Selected"
 				image_unselected="Toolbar_Middle_Off"
 				name="toggle_bone"
-				width="237"
+				width="157"
 				top_pad="-19"
-				left_delta="242">
+				left_delta="322">
 				<button.commit_callback
 				function="Joint.ChangeState"/>
 				</button>
@@ -909,101 +991,40 @@ width="678">
 				top="0">
 				<scroll_list.columns
 				label="Pose Name"
-				name="name"
-				relative_width="0.66"/>
-				<scroll_list.columns
-				label="Time"
-				name="time"
-				relative_width="0.17"  />
-				<scroll_list.columns
-				label="Type"
-				name="type"
-				relative_width="0.15"  />
+				name="name"/>
 				</scroll_list>
 				<menu_button
 				height="21"
 				follows="top|left"
 				layout="topleft"
-				label=""
+				label="Load Pose"
 				enabled="false"
 				tool_tip="Load the currently selected pose and apply all bone rotations saved in it."
 				image_overlay="Icon_Undock_Foreground"
+				image_overlay_alignment="left"
 				image_hover_unselected="Toolbar_Middle_Over"
 				image_selected="Toolbar_Middle_Selected"
 				image_unselected="Toolbar_Middle_Off"
-				menu_filename="menu_poser_poses.xml"
+				menu_filename="menu_poser_poses_btn.xml"
                 menu_position="topright"
 				name="load_poses"
-				width="25"
+				width="110"
 				top_pad="-1"
 				left_delta="2"/>
-				<line_editor
-				commit_on_focus_lost="true"
-				follows="left|top"
-				height="20"
-				enabled="false"
-				label="0.001 - 1.0"
-				tool_tip="Change the interpolation time of the currently selected pose."
-				left_pad="1"
-				top_delta="0"
-				name="interp_time"
-				width="79">
-				<line_editor.commit_callback
-				function="Pose.Set"
-				parameter="time"/>
-				</line_editor>
-				<combo_box
-				follows="left|top"
-				height="21"
-				enabled="false"
-				layout="topleft"
-				left_pad="1"
-				top_delta="0"
-				max_chars="135"
-				name="interp_type"
-				width="87">
-				<combo_box.combo_button
-				image_unselected="ToolbarDropDown_Off"
-				image_selected="ToolbarDropDown_Press"
-				image_disabled="ToolbarDropDown_Off" />
-				<combo_box.drop_down_button
-				image_unselected="ToolbarDropDown_Off"
-				image_selected="ToolbarDropDown_Press"
-				image_pressed="ToolbarDropDown_Press" 
-				image_pressed_selected="ToolbarDropDown_Press"
-				image_disabled="ToolbarDropDown_Off" />
-				<combo_box.item
-				enabled="true"
-				label="None"
-				name="0"
-				value="0" />
-				<combo_box.item
-				enabled="true"
-				label="Linear"
-				name="1"
-				value="1" />
-				<combo_box.item
-				enabled="true"
-				label="Spherical Linear"
-				name="2"
-				value="2" />
-				<combo_box.commit_callback
-				function="Pose.Set"
-				parameter="type"/>
-				</combo_box>
 				<button
 				height="21"
 				follows="top|left"
 				layout="topleft"
-				label=""
+				label="Delete Pose"
 				enabled="false"
 				tool_tip="Delete the currently selected pose."
 				image_hover_unselected="Toolbar_Middle_Over"
 				image_selected="Toolbar_Middle_Selected"
 				image_unselected="Toolbar_Middle_Off"
 				image_overlay="TrashItem_Off"
+				image_overlay_alignment="left"
 				name="delete_poses"
-				width="27"
+				width="110"
 				top_delta="0"
 				left_pad="1">
 				<button.commit_callback
@@ -1011,7 +1032,7 @@ width="678">
 				</button>
 			</panel>
 		</layout_panel>
-		<layout_panel
+		<!--<layout_panel
 		follows="top|left|right"
 		layout="topleft"
 		height="420"
@@ -1140,6 +1161,18 @@ width="678">
 				function="Anim.Move"
 				parameter="Down"/>
 				</button>
+				<icon
+				follows="top|left"
+				height="423"
+				color="1 1 1 1"
+				image_name="Panel_Background"
+				layout="topleft"
+				name="vicon"
+				mouse_opaque="false"
+				visible="true"
+				width="223"
+				top="0"
+				left_pad="3"/>
 				<panel
 				follows="left|top|right"
 				height="20"
@@ -1147,9 +1180,9 @@ width="678">
 				bg_alpha_color="0.10 0.43 0.77 0.28"
 				layout="topleft"
 				mouse_opaque="false"
-				left_pad="3"
+				left_delta="2"
 				name="title"
-				top="0"
+				top="2"
 				width="219"/>
 				<text
 				name="first_person_label"
@@ -1243,45 +1276,7 @@ width="678">
 				function="Anim.Set"/>
 				</line_editor>
 			</panel>
-		</layout_panel>
-		<layout_panel
-		follows="top|left|right"
-		layout="topleft"
-		height="420"
-		auto_resize="false"
-		visible="true"
-		name="animator_btn_layout"
-		width="30">
-			<panel
-			follows="left|top|bottom"
-			height="420"
-			background_visible="false"
-			layout="topleft"
-			mouse_opaque="false"
-			left="1"
-			name="title"
-			top="0"
-			width="20">
-				<button
-				follows="left|top"
-				height="423"
-				is_toggle="true"
-				layout="topleft"
-				image_overlay="Command_Poser_Icon"
-				image_overlay_color="1 1 1 0.6"
-				image_bottom_pad="1"
-				image_hover_unselected="Toolbar_Middle_Over"
-				image_selected="Toolbar_Middle_Selected"
-				image_unselected="Toolbar_Middle_Off"
-				name="animator"
-				left="1"
-				top="0"
-				width="18" >
-				<button.commit_callback
-				function="Pose.Layout"/>
-				</button>
-			</panel>
-		</layout_panel>
+		</layout_panel>-->
 	</layout_stack>
 	
 	<!--<multi_slider
diff --git a/indra/newview/skins/default/xui/en/floater_poser_creator.xml b/indra/newview/skins/default/xui/en/floater_poser_creator.xml
new file mode 100644
index 0000000000000000000000000000000000000000..fc8d78b0117a458175d9f1a919621607d918fd86
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/floater_poser_creator.xml
@@ -0,0 +1,1412 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<floater
+positioning="centered"
+height="431"
+layout="topleft"
+name="floater_poser"
+title="Pose Creator"
+width="654">
+	<string
+	name="icon_category" translate="false">
+	Inv_Shape
+	</string>
+	<string
+	name="icon_bone" translate="false">
+	Transparent
+	</string>
+	<string
+	name="icon_object" translate="false">
+	Inv_Object
+	</string>
+	<string
+	name="title_mPelvis" translate="false">
+	Main Body
+	</string>
+	<string
+	name="title_mHead" translate="false">
+	Head
+	</string>
+	<string
+	name="title_mCollarLeft" translate="false">
+	Left Arm
+	</string>
+	<string
+	name="title_mCollarRight" translate="false">
+	Right Arm
+	</string>
+	<string
+	name="title_mWingsRoot" translate="false">
+	Back
+	</string>
+	<string
+	name="title_mHipLeft" translate="false">
+	Left Leg
+	</string>
+	<string
+	name="title_mHipRight" translate="false">
+	Right Leg
+	</string>
+	<string
+	name="title_mTail1" translate="false">
+	Tail
+	</string>
+	<string
+	name="title_mGroin" translate="false">
+	Misc
+	</string>
+	
+	<tab_container
+	follows="all"
+	halign="center"
+	height="426"
+	layout="topleft"
+	left="5"
+	name="animation_tabs"
+	tab_height="20"
+	tab_group="1"
+	tab_position="top"
+	top="0"
+	width="643">
+	<panel
+		follows="all"
+		background_visible="false"
+		height="426"
+		layout="topleft"
+		left="0"
+		title="General"
+		name="general_panel"
+		top="0"
+		width="644">
+				<icon
+				follows="all"
+				height="426"
+				color="1 1 1 1"
+				image_name="Panel_Background"
+				layout="topleft"
+				name="vicon"
+				mouse_opaque="false"
+				visible="true"
+				width="644"
+				top_pad="-2"
+				left="0"/>
+				
+				<text
+				follows="top|left|right"
+				height="25"
+				layout="topleft"
+				left="5"
+				name="linden_intro"
+				top="1"
+				width="435"
+				wrap="true">
+				Pose Filename
+- This is the name the animation will have on upload.
+				</text>
+				<line_editor
+				commit_on_focus_lost="false"
+				follows="left|top"
+				height="20"
+				label=""
+				left="2"
+				top_pad="7"
+				name="export_name"
+				width="151"/>
+				<text
+				follows="top|left|right"
+				height="25"
+				layout="topleft"
+				left="5"
+				name="linden_intro"
+				top_pad="5"
+				width="635"
+				wrap="true">
+				Ease In / Ease Out Time
+- This is the time in seconds it takes for the animation to "fade" in and out when started or stopped.
+				</text>
+				<line_editor
+				commit_on_focus_lost="true"
+				follows="left|top"
+				height="20"
+				label=""
+				value="1.0"
+				left="2"
+				top_pad="7"
+				name="ease_in"
+				width="75">
+				<line_editor.commit_callback
+				function="Pose.EditInfo"
+				parameter="ease_in"/>
+				</line_editor>
+				<line_editor
+				commit_on_focus_lost="true"
+				follows="left|top"
+				height="20"
+				label=""
+				value="1.0"
+				left_pad="1"
+				top_delta="0"
+				name="ease_out"
+				width="75">
+				<line_editor.commit_callback
+				function="Pose.EditInfo"
+				parameter="ease_out"/>
+				</line_editor>
+				<text
+				follows="top|left|right"
+				height="25"
+				layout="topleft"
+				left="5"
+				name="linden_intro"
+				top_pad="5"
+				width="635"
+				wrap="true">
+				Animation Duration
+- How long in seconds the animation should last. It is recommended to keep "Automatic Duration" enabled.
+				</text>
+				<check_box
+				height="16"
+				initial_value="true"
+				label="Automatic Duration"
+				tool_tip="Automatically calculate the duration of the animation according to last keyframe."
+				layout="topleft"
+				left="5"
+				name="AutoDuration"
+				top_pad="6"
+				width="115">
+				<check_box.commit_callback
+				function="Pose.EditInfo"
+				parameter="duration_auto"/>
+				</check_box>
+				<slider
+				decimal_digits="1"
+				can_edit_text="true"
+				follows="left|top"
+				height="14"
+				increment="0.0166666666666667"
+				initial_value="1.0"
+				label=""
+				layout="topleft"
+				left_pad="50"
+				max_val="60.0"
+				min_val="0.0166666666666667"
+				name="keyframe_duration"
+				top_delta="3"
+				width="150" >
+				<slider.commit_callback
+				function="Pose.EditInfo"
+				parameter="duration"/>
+				</slider>
+				<text
+				follows="top|left|right"
+				height="25"
+				layout="topleft"
+				left="5"
+				name="linden_intro"
+				top_pad="5"
+				width="635"
+				wrap="true">
+				Loop Animation
+- Toggle whether the animation should be looped at the defined start and end time in seconds.
+				</text>
+				<check_box
+				height="16"
+				initial_value="true"
+				label="Loop animation between"
+				tool_tip="Toggle whether the animation should be looped at set timeframes in the animation."
+				layout="topleft"
+				left="5"
+				name="LoopAnimation"
+				top_pad="6"
+				width="115">
+				<check_box.commit_callback
+				function="Pose.EditInfo"
+				parameter="loop"/>
+				</check_box>
+				<line_editor
+				commit_on_focus_lost="true"
+				follows="left|top"
+				height="20"
+				label=""
+				value="0.0"
+				increment="0.0166666666666667"
+				left_pad="55"
+				top_delta="0"
+				name="loop_in"
+				width="50">
+				<line_editor.commit_callback
+				function="Pose.EditInfo"
+				parameter="loop_in"/>
+				</line_editor>
+				<text
+				follows="top|left|right"
+				height="25"
+				layout="topleft"
+				left_pad="10"
+				name="linden_intro"
+				top_delta="5"
+				width="30"
+				wrap="true">
+				and
+				</text>
+				<line_editor
+				commit_on_focus_lost="true"
+				follows="left|top"
+				height="20"
+				label=""
+				value="1.0"
+				increment="0.0166666666666667"
+				left_pad="1"
+				top_delta="-5"
+				name="loop_out"
+				width="50">
+				<line_editor.commit_callback
+				function="Pose.EditInfo"
+				parameter="loop_out"/>
+				</line_editor>
+				<text
+				follows="top|left|right"
+				height="50"
+				layout="topleft"
+				left="5"
+				name="linden_intro"
+				top_pad="5"
+				width="435"
+				wrap="true">
+				Base Priority
+- Set the minimum priority of this animation.
+- This affects whether this animation overrides others or is overridden by them.
+- Individual bones can potentially increase this.
+				</text>
+				<combo_box
+				follows="left|top"
+				height="21"
+				layout="topleft"
+				left="2"
+				top_pad="7"
+				max_chars="135"
+				name="base_priority"
+				width="151">
+				<combo_box.combo_button
+				image_unselected="ToolbarDropDown_Off"
+				image_selected="ToolbarDropDown_Press"
+				image_disabled="ToolbarDropDown_Off" />
+				<combo_box.drop_down_button
+				image_unselected="ToolbarDropDown_Off"
+				image_selected="ToolbarDropDown_Press"
+				image_pressed="ToolbarDropDown_Press" 
+				image_pressed_selected="ToolbarDropDown_Press"
+				image_disabled="ToolbarDropDown_Off" />
+				<combo_box.item
+				label="Low Priority (0)"
+				name="0"
+				value="0" />
+				<combo_box.item
+				label="Medium Priority (1)"
+				name="1"
+				value="1" />
+				<combo_box.item
+				label="High Priority (2)"
+				name="2"
+				value="2" />
+				<combo_box.item
+				label="Higher Priority (3)"
+				name="3"
+				value="3" />
+				<combo_box.item
+				label="Highest Priority (4)"
+				name="4"
+				value="4" />
+				<combo_box.item
+				label="Additive Priority (7)"
+				name="7"
+				value="7" />
+				<combo_box.commit_callback
+				function="Pose.EditInfo"
+				parameter="priority"/>
+				</combo_box>
+				<text
+				follows="top|left|right"
+				height="25"
+				layout="topleft"
+				left="5"
+				name="linden_intro"
+				top_pad="5"
+				width="435"
+				wrap="true">
+				Hand Pose
+- (OPTIONAL) Sets the default hand pose when using the default Linden body.
+				</text>
+				<combo_box
+				follows="left|top"
+				height="21"
+				layout="topleft"
+				left="2"
+				top_pad="7"
+				max_chars="135"
+				name="hand_poses"
+				width="151">
+				<combo_box.combo_button
+				image_unselected="ToolbarDropDown_Off"
+				image_selected="ToolbarDropDown_Press"
+				image_disabled="ToolbarDropDown_Off" />
+				<combo_box.drop_down_button
+				image_unselected="ToolbarDropDown_Off"
+				image_selected="ToolbarDropDown_Press"
+				image_pressed="ToolbarDropDown_Press" 
+				image_pressed_selected="ToolbarDropDown_Press"
+				image_disabled="ToolbarDropDown_Off" />
+				<combo_box.item
+				label="Spread"
+				name="0"
+				value="0" />
+				<combo_box.item
+				label="Relaxed"
+				name="1"
+				value="1" />
+				<combo_box.item
+				label="Point"
+				name="2"
+				value="2" />
+				<combo_box.item
+				label="Fist"
+				name="3"
+				value="3" />
+				<combo_box.commit_callback
+				function="Pose.EditInfo"
+				parameter="hand"/>
+				</combo_box>
+				
+				<button
+				height="21"
+				follows="bottom|left"
+				layout="topleft"
+				label="Import"
+				image_overlay="Icon_Undock_Foreground"
+				image_overlay_alignment="right"
+				image_hover_unselected="Toolbar_Middle_Over"
+				image_selected="Toolbar_Middle_Selected"
+				image_unselected="Toolbar_Middle_Off"
+				tool_tip="Import pose from disk."
+				name="import_poses"
+				width="75"
+				top="-24"
+				left="3">
+					<button.commit_callback
+					function="Pose.Import"/>
+				</button>
+				<button
+				height="21"
+				follows="bottom|left"
+				layout="topleft"
+				label="Export"
+				image_overlay="Icon_Dock_Foreground"
+				image_overlay_alignment="right"
+				image_hover_unselected="Toolbar_Middle_Over"
+				image_selected="Toolbar_Middle_Selected"
+				image_unselected="Toolbar_Middle_Off"
+				tool_tip="Export the current pose."
+				name="export_poses"
+				width="75"
+				top_delta="0"
+				left_pad="1">
+					<button.commit_callback
+					function="Pose.Save"
+					parameter="anim"/>
+				</button>
+		</panel>
+		<panel
+		follows="all"
+		background_visible="false"
+		height="299"
+		layout="topleft"
+		left="0"
+		title="Skeleton"
+		name="joints_panel"
+		top="0"
+		width="481">
+				
+				
+				<!--<icon
+				follows="top|left"
+				height="320"
+				color="1 1 1 1"
+				image_name="Panel_Background"
+				layout="topleft"
+				name="vicon"
+				mouse_opaque="false"
+				visible="true"
+				width="480"
+				top="0"
+				left="160"/>-->
+				
+				<icon
+				follows="top|left"
+				height="304"
+				color="1 1 1 1"
+				image_name="Panel_Background"
+				layout="topleft"
+				name="vicon"
+				mouse_opaque="false"
+				visible="true"
+				width="220"
+				top_pad="-2"
+				left="0"/>
+				
+				<icon
+				follows="top|left"
+				height="304"
+				color="1 1 1 1"
+				image_name="Panel_Background"
+				layout="topleft"
+				name="vicon"
+				mouse_opaque="false"
+				visible="true"
+				width="420"
+				top_delta="0"
+				left_pad="2"/>
+				
+							<tab_container
+							follows="top|left"
+							halign="center"
+							height="279"
+							layout="topleft"
+							left="0"
+							enabled="false"
+							name="joints_tabs"
+							tab_height="20"
+							tab_group="1"
+							tab_position="top"
+							top="0"
+							width="220">
+								<panel
+								follows="all"
+								background_visible="false"
+								height="299"
+								layout="topleft"
+								left="0"
+								title="Bones"
+								name="joints_panel"
+								top="0"
+								width="481">
+									<scroll_list
+									column_padding="2"
+									draw_heading="true"
+									height="302"
+									follows="top|left|bottom"
+									can_sort="false"
+									layout="topleft"
+									left="2"
+									width="169"
+									multi_select="true"
+									name="joints_scroll"
+									top="0">
+									<scroll_list.columns
+									label=""
+									name="icon"
+									width="18"  />
+									<scroll_list.columns
+									label="Bone Name"
+									name="joint"
+									width="108"  />
+									<scroll_list.columns
+									label="Rot X"
+									name="x"
+									width="0"  />
+									<scroll_list.columns
+									label="Rot Y"
+									name="y"
+									width="0"  />
+									<scroll_list.columns
+									label="Rot Z"
+									name="z"
+									width="0"  />
+									<scroll_list.columns
+									label="Pos X"
+									name="pos_x"
+									width="0"  />
+									<scroll_list.columns
+									label="Pos Y"
+									name="pos_y"
+									width="0"  />
+									<scroll_list.columns
+									label="Pos Z"
+									name="pos_z"
+									width="0"  />
+									<scroll_list.columns
+									label="Size X"
+									name="scale_x"
+									width="0"  />
+									<scroll_list.columns
+									label="Size Y"
+									name="scale_y"
+									width="0"  />
+									<scroll_list.columns
+									label="Size Z"
+									name="scale_z"
+									width="0"  />
+									</scroll_list>
+									<scroll_list
+									column_padding="2"
+									draw_heading="true"
+									height="302"
+									follows="top|left|bottom"
+									can_sort="false"
+									layout="topleft"
+									left_pad="1"
+									width="45"
+									multi_select="true"
+									name="keyframe_scroll"
+									top_delta="0">
+									<scroll_list.columns
+									label="Time"
+									name="time"
+									width="38"  />
+									<scroll_list.columns
+									label="Value"
+									name="value"
+									width="20"  />
+									<scroll_list.columns
+									label="X"
+									name="x"
+									width="48"  />
+									<scroll_list.columns
+									label="Y"
+									name="y"
+									width="48"  />
+									<scroll_list.columns
+									label="Z"
+									name="z"
+									width="48"  />
+									</scroll_list>
+								</panel>
+								<panel
+								follows="all"
+								background_visible="false"
+								height="299"
+								layout="topleft"
+								left="0"
+								title="Collision"
+								name="cv_panel"
+								top="0"
+								width="481">
+									<scroll_list
+									column_padding="2"
+									draw_heading="true"
+									height="300"
+									follows="left|top|right"
+									layout="topleft"
+									can_sort="false"
+									left="2"
+									width="479"
+									multi_select="true"
+									name="cv_scroll"
+									top="0">
+									<scroll_list.columns
+									label=""
+									name="icon"
+									relative_width="0.04"  />
+									<scroll_list.columns
+									label="Collision Volume Name"
+									name="joint"
+									relative_width="0.33"  />
+									<scroll_list.columns
+									label="Rot X"
+									name="x"
+									relative_width="0.0"  />
+									<scroll_list.columns
+									label="Rot Y"
+									name="y"
+									relative_width="0.0"  />
+									<scroll_list.columns
+									label="Rot Z"
+									name="z"
+									relative_width="0.0"  />
+									<scroll_list.columns
+									label="Pos X"
+									name="pos_x"
+									relative_width="0.10"  />
+									<scroll_list.columns
+									label="Pos Y"
+									name="pos_y"
+									relative_width="0.10"  />
+									<scroll_list.columns
+									label="Pos Z"
+									name="pos_z"
+									relative_width="0.10"  />
+									<scroll_list.columns
+									label="Size X"
+									name="scale_x"
+									relative_width="0.10"  />
+									<scroll_list.columns
+									label="Size Y"
+									name="scale_y"
+									relative_width="0.10"  />
+									<scroll_list.columns
+									label="Size Z"
+									name="scale_z"
+									relative_width="0.10"  />
+									<scroll_list.columns
+									label="#"
+									name="number"
+									relative_width="0.00"  />
+									</scroll_list>
+								</panel>
+								<panel
+								follows="all"
+								background_visible="false"
+								height="299"
+								layout="topleft"
+								left="0"
+								title="Attachment"
+								name="attach_panel"
+								top="0"
+								width="481">
+									<scroll_list
+									column_padding="2"
+									draw_heading="true"
+									height="300"
+									follows="left|top|right"
+									layout="topleft"
+									can_sort="false"
+									left="2"
+									width="479"
+									multi_select="true"
+									name="attach_scroll"
+									top="0">
+									<scroll_list.columns
+									label=""
+									name="icon"
+									relative_width="0.04"  />
+									<scroll_list.columns
+									label="Attachment Bone Name"
+									name="joint"
+									relative_width="0.33"  />
+									<scroll_list.columns
+									label="Rot X"
+									name="x"
+									relative_width="0.0"  />
+									<scroll_list.columns
+									label="Rot Y"
+									name="y"
+									relative_width="0.0"  />
+									<scroll_list.columns
+									label="Rot Z"
+									name="z"
+									relative_width="0.0"  />
+									<scroll_list.columns
+									label="Pos X"
+									name="pos_x"
+									relative_width="0.10"  />
+									<scroll_list.columns
+									label="Pos Y"
+									name="pos_y"
+									relative_width="0.10"  />
+									<scroll_list.columns
+									label="Pos Z"
+									name="pos_z"
+									relative_width="0.10"  />
+									<scroll_list.columns
+									label="Size X"
+									name="scale_x"
+									relative_width="0.10"  />
+									<scroll_list.columns
+									label="Size Y"
+									name="scale_y"
+									relative_width="0.10"  />
+									<scroll_list.columns
+									label="Size Z"
+									name="scale_z"
+									relative_width="0.10"  />
+									<scroll_list.columns
+									label="#"
+									name="number"
+									relative_width="0.00"  />
+									</scroll_list>
+								</panel>
+							</tab_container>
+						
+									<scroll_list
+									column_padding="2"
+									draw_heading="true"
+									height="280"
+									follows="top|left"
+									can_sort="false"
+									layout="topleft"
+									left_pad="3"
+									width="417"
+									multi_select="true"
+									name="timeframe_scroll"
+									top_delta="-1">
+									<scroll_list.columns
+									label="Bone Name"
+									name="joint"
+									width="108" />
+									<scroll_list.columns
+									label="Timeline"
+									name="multislider"
+									width="200" >
+										<column
+										name="multislider"
+										type="multislider"
+										min_value="0"
+										max_value="60"
+										increment="1"
+										max_sliders="600"/>
+									</scroll_list.columns>
+									</scroll_list>
+						
+						<panel
+						follows="left|top"
+						height="21"
+						background_visible="false"
+						layout="topleft"
+						mouse_opaque="false"
+						left="0"
+						name="title"
+						top_pad="1"
+						width="681">
+							<button
+							height="21"
+							follows="top|left"
+							is_toggle="true"
+							layout="topleft"
+							label="Start Posing"
+							label_selected="Stop Posing"
+							image_hover_unselected="Toolbar_Middle_Over"
+							image_selected="Toolbar_Middle_Selected"
+							image_unselected="Toolbar_Middle_Off"
+							button_flash_enable="true"
+							flash_color="0.7 0.7 1 1"
+							button_flash_count="64"
+							button_flash_rate="0.5"
+							name="activate"
+							width="114"
+							top_pad="0"
+							left="3">
+							<button.commit_callback
+							function="Pose.Start"/>
+							</button>
+							<button
+							height="21"
+							follows="top|left"
+							is_toggle="true"
+							layout="topleft"
+							label="Preview"
+							label_selected="Stop"
+							image_hover_unselected="Toolbar_Middle_Over"
+							image_selected="Toolbar_Middle_Selected"
+							image_unselected="Toolbar_Middle_Off"
+							button_flash_enable="true"
+							flash_color="0.7 0.7 1 1"
+							button_flash_count="64"
+							button_flash_rate="0.5"
+							name="preview"
+							width="100"
+							top_delta="0"
+							left_pad="1">
+							<button.commit_callback
+							function="Pose.StartStop"/>
+							</button>
+							<combo_box
+							follows="left|top"
+							height="21"
+							layout="topleft"
+							left_pad="6"
+							top_delta="0"
+							max_chars="135"
+							name="interpolation"
+							width="139">
+							<combo_box.combo_button
+							image_unselected="ToolbarDropDown_Off"
+							image_selected="ToolbarDropDown_Press"
+							image_disabled="ToolbarDropDown_Off" />
+							<combo_box.drop_down_button
+							image_unselected="ToolbarDropDown_Off"
+							image_selected="ToolbarDropDown_Press"
+							image_pressed="ToolbarDropDown_Press" 
+							image_pressed_selected="ToolbarDropDown_Press"
+							image_disabled="ToolbarDropDown_Off" />
+							<combo_box.item
+							label="Step"
+							name="0"
+							value="0" />
+							<combo_box.item
+							label="Linear"
+							name="1"
+							value="1" />
+							<combo_box.item
+							label="Spline"
+							name="2"
+							value="2" />
+							<combo_box.commit_callback
+							function="Pose.Interpolation"/>
+							</combo_box>
+							<line_editor
+							commit_on_focus_lost="false"
+							follows="left|top"
+							height="20"
+							label=""
+							left_pad="1"
+							top_delta="0"
+							name="key_time"
+							width="43">
+							<line_editor.commit_callback
+							function="Keyframe.Time"/>
+							</line_editor>
+							<button
+							height="21"
+							follows="top|left"
+							layout="topleft"
+							label="Add Key"
+							image_overlay="AddItem_Off"
+							image_overlay_alignment="right"
+							image_hover_unselected="Toolbar_Middle_Over"
+							image_selected="Toolbar_Middle_Selected"
+							image_unselected="Toolbar_Middle_Off"
+							tool_tip="Add a keyframe after the currently selected keyframe. Hold SHIFT to add it at the end. Hold CTRL to add it to the beginning."
+							name="add_keyframe"
+							width="105"
+							top_delta="0"
+							left_pad="1">
+								<button.commit_callback
+								function="Keyframe.Add"/>
+							</button>
+							<button
+							height="21"
+							follows="top|left"
+							layout="topleft"
+							label="Remove Key"
+							image_overlay="TrashItem_Off"
+							image_overlay_alignment="right"
+							image_hover_unselected="Toolbar_Middle_Over"
+							image_selected="Toolbar_Middle_Selected"
+							image_unselected="Toolbar_Middle_Off"
+							tool_tip="Remove the currently selected keyframe."
+							name="remove_keyframe"
+							width="125"
+							top_delta="0"
+							left_pad="1">
+								<button.commit_callback
+								function="Keyframe.Remove"/>
+							</button>
+						</panel>
+				
+				<icon
+				follows="top|left"
+				height="100"
+				color="1 1 1 1"
+				image_name="Panel_Background"
+				layout="topleft"
+				name="vicon"
+				mouse_opaque="false"
+				visible="true"
+				width="156"
+				top_pad="4"
+				left="0"/>
+				<check_box
+				height="16"
+				initial_value="false"
+				label="Mirror Changes"
+				tool_tip="This option toggles whether all changes to rotations should be mirrored to the opposide side's bone as well. Left Arm will mirror to Right Arm and so on."
+				layout="topleft"
+				left="5"
+				name="Mirror"
+				top_delta="5"
+				width="115">
+				<check_box.commit_callback
+                    function="Joint.ToggleMirror"/>
+				</check_box>
+				<check_box
+				height="16"
+				initial_value="true"
+				label="Easy Rotations"
+				tool_tip="This option toggles whether the poser should display and use simplified rotation axis. This essentially means that when moving any axis, all other axis stay untouched, this can bring the rotations out of place so that 0 0 0 no longer is the default resting position depending on in which order bones were moved. Disabling this option will allow the other axis to be dynamically changed to represent the actual rotations which means sliders you are not touching will change with the slider you are currently moving. This is disabled by default as it may look confusing to the user. It is highly recommended that if you use this you start zeroing out Z then Y and lastly X in this order, otherwise you will not be able to zero out all axis unless you reset the bone rotation."
+				layout="topleft"
+				left="5"
+				name="EasyRotations"
+				top_pad="1"
+				width="115">
+				<check_box.commit_callback
+                    function="Joint.EasyRotations"/>
+				</check_box>
+				<button
+				height="21"
+				follows="top|left"
+				layout="topleft"
+				enabled="true"
+				label="Mirror Pose"
+				image_hover_unselected="Toolbar_Middle_Over"
+				image_selected="Toolbar_Middle_Selected"
+				image_unselected="Toolbar_Middle_Off"
+				name="flip_pose"
+				width="151"
+				top_pad="40"
+				left="3">
+				<button.commit_callback
+				function="Joint.FlipPose"/>
+				</button>
+				
+				<icon
+				follows="top|left"
+				height="100"
+				color="1 1 1 1"
+				image_name="Panel_Background"
+				layout="topleft"
+				name="vicon"
+				mouse_opaque="false"
+				visible="true"
+				width="480"
+				top_delta="-78"
+				left="161"/>
+				<tab_container
+				follows="top|left|bottom"
+				halign="center"
+				height="95"
+				layout="topleft"
+				left_delta="-1"
+				name="modifier_tabs"
+				enabled="false"
+				tab_height="20"
+				tab_group="1"
+				tab_position="top"
+				top_delta="2"
+				width="481">
+					<panel
+					follows="all"
+					background_visible="false"
+					height="95"
+					layout="topleft"
+					left="0"
+					title="Rotation"
+					name="rotation_panel"
+					top="0"
+					width="622">
+						<slider
+						decimal_digits="3"
+						can_edit_text="true"
+						follows="left|top"
+						height="14"
+						increment="0.001"
+						initial_value="0"
+						label="Rotation X:"
+						label_width="70"
+						layout="topleft"
+						left="10"
+						max_val="3.142"
+						min_val="-3.142"
+						name="Rotation_X"
+						top_pad="6"
+						width="459" >
+						<slider.commit_callback
+						function="Joint.Set"
+						parameter="0"/>
+						</slider>
+						<slider
+						decimal_digits="3"
+						can_edit_text="true"
+						follows="left|top"
+						height="14"
+						increment="0.001"
+						initial_value="0"
+						label="Rotation Y:"
+						label_width="70"
+						layout="topleft"
+						left="10"
+						max_val="3.142"
+						min_val="-3.142"
+						name="Rotation_Y"
+						top_pad="1"
+						width="459" >
+						<slider.commit_callback
+						function="Joint.Set"
+						parameter="1"/>
+						</slider>
+						<slider
+						decimal_digits="3"
+						visible="true"
+						can_edit_text="true"
+						follows="left|top"
+						height="14"
+						increment="0.001"
+						initial_value="0"
+						label="Rotation Z:"
+						label_width="70"
+						layout="topleft"
+						left="10"
+						max_val="3.142"
+						min_val="-3.142"
+						name="Rotation_Z"
+						top_pad="1"
+						width="459" >
+						<slider.commit_callback
+						function="Joint.Set"
+						parameter="2"/>
+						</slider>
+						<panel
+						follows="top|left|right"
+						height="21"
+						background_visible="false"
+						layout="topleft"
+						mouse_opaque="false"
+						left="0"
+						name="title"
+						top_pad="6"
+						width="480">
+							<button
+							height="21"
+							follows="top|left"
+							layout="topleft"
+							enabled="true"
+							label="Reset Selected Rotation(s)"
+							label_selected="Disable Selected Bone(s)"
+							image_hover_unselected="Toolbar_Middle_Over"
+							image_selected="Toolbar_Middle_Selected"
+							image_unselected="Toolbar_Middle_Off"
+							name="reset_bone_rot"
+							width="159"
+							top="0"
+							left_delta="2">
+							<button.commit_callback
+							function="Joint.ResetJointRotation"/>
+							</button>
+							<button
+							height="21"
+							follows="top|left"
+							layout="topleft"
+							enabled="true"
+							label="Revert Selected Rotation(s)"
+							image_hover_unselected="Toolbar_Middle_Over"
+							image_selected="Toolbar_Middle_Selected"
+							image_unselected="Toolbar_Middle_Off"
+							name="revert_pose"
+							width="158"
+							top_delta="0"
+							left_pad="1">
+							<button.commit_callback
+							function="Joint.RevertJointRotation"/>
+							</button>
+						</panel>
+					</panel>
+					<panel
+					follows="all"
+					background_visible="false"
+					height="95"
+					layout="topleft"
+					left="0"
+					title="Position"
+					name="position_panel"
+					top="0"
+					width="622">
+						<slider
+						decimal_digits="3"
+						can_edit_text="true"
+						follows="left|top"
+						height="14"
+						increment="0.001"
+						initial_value="0"
+						label="Position X:"
+						label_width="70"
+						layout="topleft"
+						left="10"
+						max_val="0.5"
+						min_val="-0.5"
+						name="Position_X"
+						top_pad="6"
+						width="459" >
+						<slider.commit_callback
+						function="Joint.PosSet"
+						parameter="0"/>
+						</slider>
+						<slider
+						decimal_digits="3"
+						can_edit_text="true"
+						follows="left|top"
+						height="14"
+						increment="0.001"
+						initial_value="0"
+						label="Position Y:"
+						label_width="70"
+						layout="topleft"
+						left_delta="0"
+						max_val="0.5"
+						min_val="-0.5"
+						name="Position_Y"
+						top_pad="1"
+						width="459" >
+						<slider.commit_callback
+						function="Joint.PosSet"
+						parameter="1"/>
+						</slider>
+						<slider
+						decimal_digits="3"
+						can_edit_text="true"
+						follows="left|top"
+						height="14"
+						increment="0.001"
+						initial_value="0"
+						label="Position Z:"
+						label_width="70"
+						layout="topleft"
+						left_delta="0"
+						max_val="0.5"
+						min_val="-0.5"
+						name="Position_Z"
+						top_pad="1"
+						width="459" >
+						<slider.commit_callback
+						function="Joint.PosSet"
+						parameter="2"/>
+						</slider>
+						<panel
+						follows="top|left|right"
+						height="21"
+						background_visible="false"
+						layout="topleft"
+						mouse_opaque="false"
+						left="0"
+						name="title"
+						top_pad="6"
+						width="480">
+							<button
+							height="21"
+							follows="top|left"
+							layout="topleft"
+							enabled="true"
+							label="Reset Selected Position(s)"
+							label_selected="Disable Selected Bone(s)"
+							image_hover_unselected="Toolbar_Middle_Over"
+							image_selected="Toolbar_Middle_Selected"
+							image_unselected="Toolbar_Middle_Off"
+							name="reset_bone_pos"
+							width="318"
+							top="0"
+							left_delta="2">
+							<button.commit_callback
+							function="Joint.ResetJointPosition"/>
+							</button>
+						</panel>
+					</panel>
+					<panel
+					follows="all"
+					background_visible="false"
+					height="95"
+					layout="topleft"
+					left="0"
+					title="Scale"
+					name="scale_panel"
+					top="0"
+					width="622">
+						<slider
+						decimal_digits="3"
+						can_edit_text="true"
+						follows="left|top"
+						height="14"
+						increment="0.001"
+						initial_value="0"
+						label="Scale X:"
+						label_width="70"
+						layout="topleft"
+						left="10"
+						max_val="2"
+						min_val="0"
+						name="Scale_X"
+						top_pad="6"
+						width="459" >
+						<slider.commit_callback
+						function="Joint.SetScale"
+						parameter="0"/>
+						</slider>
+						<slider
+						decimal_digits="3"
+						can_edit_text="true"
+						follows="left|top"
+						height="14"
+						increment="0.001"
+						initial_value="0"
+						label="Scale Y:"
+						label_width="70"
+						layout="topleft"
+						left_delta="0"
+						max_val="2"
+						min_val="0"
+						name="Scale_Y"
+						top_pad="1"
+						width="459" >
+						<slider.commit_callback
+						function="Joint.SetScale"
+						parameter="1"/>
+						</slider>
+						<slider
+						decimal_digits="3"
+						can_edit_text="true"
+						follows="left|top"
+						height="14"
+						increment="0.001"
+						initial_value="0"
+						label="Scale Z:"
+						label_width="70"
+						layout="topleft"
+						left_delta="0"
+						max_val="2"
+						min_val="0"
+						name="Scale_Z"
+						top_pad="1"
+						width="459" >
+						<slider.commit_callback
+						function="Joint.SetScale"
+						parameter="2"/>
+						</slider>
+						<panel
+						follows="top|left|right"
+						height="21"
+						background_visible="false"
+						layout="topleft"
+						mouse_opaque="false"
+						left="0"
+						name="title"
+						top_pad="6"
+						width="480">
+							<button
+							height="21"
+							follows="top|left"
+							layout="topleft"
+							enabled="true"
+							label="Reset Selected Scale(s)"
+							label_selected="Disable Selected Bone(s)"
+							image_hover_unselected="Toolbar_Middle_Over"
+							image_selected="Toolbar_Middle_Selected"
+							image_unselected="Toolbar_Middle_Off"
+							name="reset_bone_scale"
+							width="318"
+							top="0"
+							left_delta="2">
+							<button.commit_callback
+							function="Joint.ResetJointScale"/>
+							</button>
+						</panel>
+					</panel>
+				</tab_container>
+				<button
+				height="21"
+				follows="top|left"
+				layout="topleft"
+				is_toggle="true"
+				enabled="false"
+				label="Activate Selected Bone(s)"
+				label_selected="Disable Selected Bone(s)"
+				image_hover_unselected="Toolbar_Middle_Over"
+				image_selected="Toolbar_Middle_Selected"
+				image_unselected="Toolbar_Middle_Off"
+				name="toggle_bone"
+				width="157"
+				top_pad="-19"
+				left_delta="322">
+				<button.commit_callback
+				function="Joint.ChangeState"/>
+				</button>
+			</panel>
+	</tab_container>
+	<!--<multi_slider
+	can_edit_text="false"
+	decimal_digits="0"
+	follows="bottom"
+	height="10"
+	increment="0.1"
+	initial_value="0"
+	layout="topleft"
+	left_delta="0"
+	max_sliders="120"
+	max_val="30"
+	min_val="25"
+	name="key_slider"
+	show_text="false"
+	top_pad="3"
+	width="475" />
+	<line_editor
+	commit_on_focus_lost="true"
+	follows="left|top"
+	height="20"
+	label=""
+	enabled="true"
+	left_pad="1"
+	top_delta="0"
+	name="time"
+	width="62">
+	<line_editor.commit_callback
+	function="Anim.Edit"/>
+	</line_editor>-->
+	
+	
+	<!--<multi_slider
+	can_edit_text="true"
+	decimal_digits="0"
+	draw_track="false"
+	follows="bottom"
+	height="10"
+	increment="0.1"
+	initial_value="0"
+	layout="topleft"
+	left="10"
+	max_sliders="60"
+	max_val="60"
+	name="time_slider"
+	show_text="false"
+	top_pad="5"
+	use_triangle="true"
+	width="475" >
+	<multi_slider.commit_callback
+	function="Anim.SetValue"/>
+	</multi_slider>
+	<multi_slider
+	can_edit_text="true"
+	decimal_digits="0"
+	follows="bottom"
+	height="10"
+	increment="0.1"
+	initial_value="0"
+	layout="topleft"
+	left_delta="0"
+	max_sliders="60"
+	max_val="60"
+	name="key_slider"
+	show_text="true"
+	top_pad="0"
+	width="475" />
+	<button
+	height="23"
+	label=" + "
+	label_selected="Add Key"
+	image_hover_unselected="Toolbar_Middle_Over"
+	image_selected="Toolbar_Middle_Selected"
+	image_unselected="Toolbar_Middle_Off"
+	layout="topleft"
+	left_pad="0"
+	name="add_key"
+	top_delta="-20"
+	width="26" />
+	<button
+	height="23"
+	label=" - "
+	label_selected="Delete Key"
+	image_hover_unselected="Toolbar_Middle_Over"
+	image_selected="Toolbar_Middle_Selected"
+	image_unselected="Toolbar_Middle_Off"
+	layout="topleft"
+	name="delete_key"
+	top_pad="0"
+	width="26" />
+	<line_editor
+	commit_on_focus_lost="true"
+	follows="left|top"
+	height="20"
+	label="Wait time"
+	tool_tip="Change the time for the wait event."
+	enabled="true"
+	left_pad="1"
+	top_delta="0"
+	name="time"
+	width="62">
+	<line_editor.commit_callback
+	function="Anim.SetValue"/>
+	</line_editor>-->
+</floater>
diff --git a/indra/newview/skins/default/xui/en/menu_poser_joints.xml b/indra/newview/skins/default/xui/en/menu_poser_joints.xml
new file mode 100644
index 0000000000000000000000000000000000000000..59d4fe2710d3b372dfac08a4cd49999ad8233f50
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/menu_poser_joints.xml
@@ -0,0 +1,137 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<context_menu
+ layout="topleft"
+ name="Actions">
+    <menu_item_call
+     label="Mirror Rotation"
+     layout="topleft"
+     name="rotations">
+        <on_click function="Joints.Menu" parameter="mirror"/>
+    </menu_item_call>
+    <menu_item_call
+     label="Symmetrize Rotation from Mirror Bone"
+     layout="topleft"
+     name="rotations">
+        <on_click function="Joints.Menu" parameter="symmetrize"/>
+    </menu_item_call>
+    <menu_item_call
+     label="Recapture Bone"
+     layout="topleft"
+     name="rotations">
+        <on_click function="Joints.Menu" parameter="recapture"/>
+    </menu_item_call>
+    <menu_item_check
+     label="Enable This Bone"
+     layout="topleft"
+     name="rotations">
+        <on_click function="Joints.Menu" parameter="enable_bone"/>
+        <on_check function="Joints.OnEnable" parameter="enable_bone"/>
+    </menu_item_check>
+    <menu_item_check
+     label="Enable Override"
+     layout="topleft"
+     name="rotations">
+        <on_click function="Joints.Menu" parameter="enable_override"/>
+        <on_enable function="Joints.OnEnable" parameter="enable_bone"/>
+        <on_check function="Joints.OnEnable" parameter="enable_override"/>
+    </menu_item_check>
+    <menu_item_check
+     label="Enable Offset"
+     layout="topleft"
+     name="rotations">
+        <on_click function="Joints.Menu" parameter="enable_offset"/>
+        <on_enable function="Joints.OnEnable" parameter="enable_bone"/>
+        <on_check function="Joints.OnEnable" parameter="enable_offset"/>
+    </menu_item_check>
+    <menu_item_separator
+     layout="topleft" />
+    <menu_item_call
+     label="Copy Transforms"
+     layout="topleft"
+     name="copy_all">
+        <on_click function="Joints.Menu" parameter="copy_transforms"/>
+    </menu_item_call>
+    <menu_item_separator
+     layout="topleft" />
+    <menu_item_call
+     label="Paste Rotation"
+     layout="topleft"
+     name="positions">
+        <on_click function="Joints.Menu" parameter="paste_rot"/>
+        <on_enable function="Joints.OnEnable" parameter="clipboard"/>
+    </menu_item_call>
+    <menu_item_call
+     label="Paste Position"
+     layout="topleft"
+     name="positions">
+        <on_click function="Joints.Menu" parameter="paste_pos"/>
+        <on_enable function="Joints.OnEnable" parameter="clipboard"/>
+    </menu_item_call>
+    <menu_item_call
+     label="Paste Scale"
+     layout="topleft"
+     name="scale">
+        <on_click function="Joints.Menu" parameter="paste_scale"/>
+        <on_enable function="Joints.OnEnable" parameter="clipboard"/>
+    </menu_item_call>
+    <menu_item_separator
+     layout="topleft" />
+    <menu_item_call
+     label="Paste Rotations &amp; Positions"
+     layout="topleft"
+     name="rot_pos">
+        <on_click function="Joints.Menu" parameter="paste_rot_pos"/>
+        <on_enable function="Joints.OnEnable" parameter="clipboard"/>
+    </menu_item_call>
+    <menu_item_call
+     label="Paste Rotations &amp; Scales"
+     layout="topleft"
+     name="rot_scale">
+        <on_click function="Joints.Menu" parameter="paste_rot_scale"/>
+        <on_enable function="Joints.OnEnable" parameter="clipboard"/>
+    </menu_item_call>
+    <menu_item_call
+     label="Paste Position &amp; Scales"
+     layout="topleft"
+     name="pos_scale">
+        <on_click function="Joints.Menu" parameter="paste_pos_scale"/>
+        <on_enable function="Joints.OnEnable" parameter="clipboard"/>
+    </menu_item_call>
+    <menu_item_separator
+     layout="topleft" />
+    <menu_item_call
+     label="Paste All Transforms"
+     layout="topleft"
+     name="all_transforms">
+        <on_click function="Joints.Menu" parameter="paste_all"/>
+        <on_enable function="Joints.OnEnable" parameter="clipboard"/>
+    </menu_item_call>
+    <menu_item_separator
+     layout="topleft" />
+    <menu_item_call
+     label="Reset Rotation"
+     layout="topleft"
+     name="reset_rot">
+        <on_click function="Joints.Menu" parameter="reset_rot"/>
+    </menu_item_call>
+    <menu_item_call
+     label="Reset Position"
+     layout="topleft"
+     name="reset_pos">
+        <on_click function="Joints.Menu" parameter="reset_pos"/>
+    </menu_item_call>
+    <menu_item_call
+     label="Reset Scale"
+     layout="topleft"
+     name="reset_scale">
+        <on_click function="Joints.Menu" parameter="reset_scale"/>
+    </menu_item_call>
+    <menu_item_separator
+     layout="topleft" />
+    <menu_item_call
+     label="Reset All Transforms"
+     layout="topleft"
+     name="reset_all">
+        <on_click function="Joints.Menu" parameter="reset_all"/>
+    </menu_item_call>
+</context_menu>
diff --git a/indra/newview/skins/default/xui/en/menu_poser_poses.xml b/indra/newview/skins/default/xui/en/menu_poser_poses.xml
index 17373f0c6991fe863d471c396f1759f2fc99c4be..701f0766f513d9d64cdd7d7462df4f2fca1de134 100644
--- a/indra/newview/skins/default/xui/en/menu_poser_poses.xml
+++ b/indra/newview/skins/default/xui/en/menu_poser_poses.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8" standalone="yes" ?>
-<toggleable_menu
+<context_menu
  layout="topleft"
- name="Settings">
+ name="Poser Settings">
     <menu_item_call
      label="Load Rotations only"
      layout="topleft"
@@ -48,4 +48,4 @@
      name="rotations">
         <on_click function="Pose.Menu" parameter="all"/>
     </menu_item_call>
-</toggleable_menu>
+</context_menu>
diff --git a/indra/newview/skins/default/xui/en/menu_poser_poses_btn.xml b/indra/newview/skins/default/xui/en/menu_poser_poses_btn.xml
new file mode 100644
index 0000000000000000000000000000000000000000..86906f1f22a75f2b301d5eee7161127ac7c04063
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/menu_poser_poses_btn.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<toggleable_menu
+ layout="topleft"
+ name="Settings">
+	<menu_item_call
+     label="Load Rotations only"
+     layout="topleft"
+     name="rotations">
+		<on_click function="Pose.Menu" parameter="rotation"/>
+	</menu_item_call>
+	<menu_item_call
+     label="Load Positions only"
+     layout="topleft"
+     name="positions">
+		<on_click function="Pose.Menu" parameter="position"/>
+	</menu_item_call>
+	<menu_item_call
+     label="Load Scales only"
+     layout="topleft"
+     name="scale">
+		<on_click function="Pose.Menu" parameter="scale"/>
+	</menu_item_call>
+	<menu_item_separator
+     layout="topleft" />
+	<menu_item_call
+     label="Load Rotations &amp; Positions"
+     layout="topleft"
+     name="rotations">
+		<on_click function="Pose.Menu" parameter="rot_pos"/>
+	</menu_item_call>
+	<menu_item_call
+     label="Load Rotations &amp; Scales"
+     layout="topleft"
+     name="rotations">
+		<on_click function="Pose.Menu" parameter="rot_scale"/>
+	</menu_item_call>
+	<menu_item_call
+     label="Load Position &amp; Scales"
+     layout="topleft"
+     name="rotations">
+		<on_click function="Pose.Menu" parameter="pos_scale"/>
+	</menu_item_call>
+	<menu_item_separator
+     layout="topleft" />
+	<menu_item_call
+     label="Load all"
+     layout="topleft"
+     name="rotations">
+		<on_click function="Pose.Menu" parameter="all"/>
+	</menu_item_call>
+</toggleable_menu>
diff --git a/indra/newview/skins/default/xui/en/menu_viewer.xml b/indra/newview/skins/default/xui/en/menu_viewer.xml
index 716869bccfda0066776d8756d168e51b9a36f67d..6d56d7215e997edc214b1a489cc900d4007d324f 100644
--- a/indra/newview/skins/default/xui/en/menu_viewer.xml
+++ b/indra/newview/skins/default/xui/en/menu_viewer.xml
@@ -704,6 +704,16 @@
              function="Floater.Toggle"
              parameter="poser" />
 		</menu_item_check>
+		<menu_item_check
+			 label="Pose Creator"
+			 name="Pose Creator">
+			<menu_item_check.on_check
+             function="Floater.Visible"
+             parameter="poser_creator" />
+			<menu_item_check.on_click
+             function="Floater.Toggle"
+             parameter="poser_creator" />
+		</menu_item_check>
         <menu_item_call
          label="Resync Animations"
          name="Resync Animations"
diff --git a/indra/newview/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml
index b8f8263da6086f7c2e2d311690d94185f06b68c0..9b554134d1bb7484acb6dd29c8e1ef0a60477ee2 100644
--- a/indra/newview/skins/default/xui/en/notifications.xml
+++ b/indra/newview/skins/default/xui/en/notifications.xml
@@ -13099,4 +13099,26 @@ Please download the latest version from https://www.alchemyviewer.org/downloads
      yestext="OK"/>
   </notification>
 	
+<notification
+   icon="notify.tga"
+   name="PoserExportXMLSuccess"
+   type="notify">
+The pose has been exported successfully as XML file.
+
+The pose can be loaded from the expanded pose list.
+
+The file can be found in:
+&lt;b&gt;C:\Users\USERNAME\AppData\Roaming\AlchemyNext\user_settings\poses&lt;/b&gt;
+  </notification>
+
+<notification
+   icon="notify.tga"
+   name="PoserExportANIMSuccess"
+   type="notify">
+The pose has been exported successfully as ANIM file.
+
+The file can be found in:
+&lt;b&gt;C:\Users\USERNAME\AppData\Roaming\AlchemyNext\user_settings\animations&lt;/b&gt;
+  </notification>
+	
 </notifications>