From c21812bf095de2defa4e61978b2659764c838abf Mon Sep 17 00:00:00 2001
From: andreykproductengine <andreykproductengine@lindenlab.com>
Date: Tue, 17 Sep 2019 21:36:59 +0300
Subject: [PATCH] SL-6109 Keyaboard support ready

---
 indra/llcommon/llkeybind.cpp                  | 180 +++-
 indra/llcommon/llkeybind.h                    |  23 +-
 indra/llui/llscrolllistctrl.cpp               |   4 +
 indra/llui/llscrolllistctrl.h                 |   4 +-
 indra/newview/CMakeLists.txt                  |   2 +
 indra/newview/llfloaterpreference.cpp         | 680 ++++++++-------
 indra/newview/llfloaterpreference.h           |  31 +-
 indra/newview/llkeyconflict.cpp               | 789 ++++++++++++++++++
 indra/newview/llkeyconflict.h                 | 214 +++++
 indra/newview/llviewerkeyboard.cpp            |  56 +-
 indra/newview/llviewerkeyboard.h              |   6 +-
 indra/newview/llvoiceclient.cpp               |  18 +-
 .../default/xui/en/floater_select_key.xml     |  40 +-
 .../xui/en/panel_preferences_controls.xml     | 161 +++-
 14 files changed, 1798 insertions(+), 410 deletions(-)
 create mode 100644 indra/newview/llkeyconflict.cpp
 create mode 100644 indra/newview/llkeyconflict.h

diff --git a/indra/llcommon/llkeybind.cpp b/indra/llcommon/llkeybind.cpp
index f227c0a1a50..765084bbf65 100644
--- a/indra/llcommon/llkeybind.cpp
+++ b/indra/llcommon/llkeybind.cpp
@@ -36,6 +36,11 @@ LLKeyData::LLKeyData()
 {
 }
 
+LLKeyData::LLKeyData(EMouseClickType mouse, KEY key, MASK mask)
+: mMouse(mouse), mKey(key), mMask(mask)
+{
+}
+
 LLKeyData::LLKeyData(const LLSD &key_data)
 {
     mMouse = (EMouseClickType)key_data["mouse"].asInteger();
@@ -72,65 +77,100 @@ LLKeyData& LLKeyData::operator=(const LLKeyData& rhs)
     return *this;
 }
 
+bool LLKeyData::operator==(const LLKeyData& rhs)
+{
+    if (mMouse != rhs.mMouse) return false;
+    if (mKey != rhs.mKey) return false;
+    if (mMask != rhs.mMask) return false;
+    return true;
+}
+
+bool LLKeyData::operator!=(const LLKeyData& rhs)
+{
+    if (mMouse != rhs.mMouse) return true;
+    if (mKey != rhs.mKey) return true;
+    if (mMask != rhs.mMask) return true;
+    return false;
+}
+
 // LLKeyBind
 
 LLKeyBind::LLKeyBind(const LLSD &key_bind)
 {
-    if (key_bind.has("DataPrimary"))
-    {
-        mDataPrimary = LLKeyData(key_bind["DataPrimary"]);
-    }
-    if (key_bind.has("DataSecondary"))
+    if (key_bind.isArray())
     {
-        mDataSecondary = LLKeyData(key_bind["DataSecondary"]);
+        for (LLSD::array_const_iterator data = key_bind.beginArray(), endLists = key_bind.endArray();
+            data != endLists;
+            data++
+            )
+        {
+            mData.push_back(LLKeyData(*data));
+        }
     }
 }
 
 bool LLKeyBind::operator==(const LLKeyBind& rhs)
 {
-    if (mDataPrimary.mMouse != rhs.mDataPrimary.mMouse) return false;
-    if (mDataPrimary.mKey != rhs.mDataPrimary.mKey) return false;
-    if (mDataPrimary.mMask != rhs.mDataPrimary.mMask) return false;
-    if (mDataSecondary.mMouse != rhs.mDataSecondary.mMouse) return false;
-    if (mDataSecondary.mKey != rhs.mDataSecondary.mKey) return false;
-    if (mDataSecondary.mMask != rhs.mDataSecondary.mMask) return false;
+    U32 size = mData.size();
+    if (size != rhs.mData.size()) return false;
+
+    for (U32 i = 0; i < size; i++)
+    {
+        if (mData[i] != rhs.mData[i]) return false;
+    }
+
     return true;
 }
 
-bool LLKeyBind::empty()
+bool LLKeyBind::operator!=(const LLKeyBind& rhs)
 {
-    if (mDataPrimary.mMouse != CLICK_NONE) return false;
-    if (mDataPrimary.mKey != KEY_NONE) return false;
-    if (mDataPrimary.mMask != MASK_NONE) return false;
-    if (mDataSecondary.mMouse != CLICK_NONE) return false;
-    if (mDataSecondary.mKey != KEY_NONE) return false;
-    if (mDataSecondary.mMask != MASK_NONE) return false;
+    U32 size = mData.size();
+    if (size != rhs.mData.size()) return true;
+
+    for (U32 i = 0; i < size; i++)
+    {
+        if (mData[i] != rhs.mData[i]) return true;
+    }
+
     return false;
 }
 
-LLSD LLKeyBind::asLLSD() const
+bool LLKeyBind::isEmpty() const
 {
-    LLSD data;
-    if (!mDataPrimary.isEmpty())
+    for (data_vector_t::const_iterator iter = mData.begin(); iter != mData.end(); iter++)
     {
-        data["DataPrimary"] = mDataPrimary.asLLSD();
+        if (!iter->isEmpty()) return false;
     }
-    if (!mDataSecondary.isEmpty())
+    return true;
+}
+
+LLSD LLKeyBind::asLLSD() const
+{
+    LLSD data;
+    for (data_vector_t::const_iterator iter = mData.begin(); iter != mData.end(); iter++)
     {
-        data["DataSecondary"] = mDataSecondary.asLLSD();
+        if (!iter->isEmpty())
+        {
+            data.append(iter->asLLSD());
+        }
     }
     return data;
 }
 
 bool LLKeyBind::canHandle(EMouseClickType mouse, KEY key, MASK mask) const
 {
-    if (mDataPrimary.mKey == key && mDataPrimary.mMask == mask && mDataPrimary.mMouse == mouse)
+    if (mouse == CLICK_NONE && key == KEY_NONE)
     {
-        return true;
+        // assume placeholder
+        return false;
     }
-    if (mDataSecondary.mKey == key && mDataSecondary.mMask == mask && mDataSecondary.mMouse == mouse)
+
+    for (data_vector_t::const_iterator iter = mData.begin(); iter != mData.end(); iter++)
     {
-        return true;
+        if (iter->mKey == key && iter->mMask == mask && iter->mMouse == mouse)
+        {
+            return true;
+        }
     }
     return false;
 }
@@ -145,3 +185,85 @@ bool LLKeyBind::canHandleMouse(EMouseClickType mouse, MASK mask) const
     return canHandle(mouse, KEY_NONE, mask);
 }
 
+bool LLKeyBind::addKeyData(EMouseClickType mouse, KEY key, MASK mask)
+{
+    if (!canHandle(mouse, key, mask))
+    {
+        mData.push_back(LLKeyData(mouse, key, mask));
+        return true;
+    }
+    return false;
+}
+
+bool LLKeyBind::addKeyData(const LLKeyData& data)
+{
+    if (!canHandle(data.mMouse, data.mKey, data.mMask))
+    {
+        mData.push_back(data);
+        return true;
+    }
+    return false;
+}
+
+void LLKeyBind::replaceKeyData(EMouseClickType mouse, KEY key, MASK mask, U32 index)
+{
+    if (mouse != CLICK_NONE && key != KEY_NONE && mask != MASK_NONE)
+    {
+        for (data_vector_t::const_iterator iter = mData.begin(); iter != mData.end(); iter++)
+        {
+            if (iter->mKey == key && iter->mMask == mask && iter->mMouse == mouse)
+            {
+                mData.erase(iter);
+                break;
+            }
+        }
+    }
+    if (mData.size() > index)
+    {
+        mData[index] = LLKeyData(mouse, key, mask);
+    }
+    else
+    {
+        mData.push_back(LLKeyData(mouse, key, mask));
+    }
+}
+
+void LLKeyBind::replaceKeyData(const LLKeyData& data, U32 index)
+{
+    for (data_vector_t::const_iterator iter = mData.begin(); iter != mData.end(); iter++)
+    {
+        if (iter->mKey == data.mKey && iter->mMask == data.mMask && iter->mMouse == data.mMouse)
+        {
+            mData.erase(iter);
+            break;
+        }
+    }
+    if (mData.size() > index)
+    {
+        mData[index] = data;
+    }
+    else
+    {
+        mData.push_back(data);
+    }
+}
+
+bool LLKeyBind::hasKeyData(U32 index) const
+{
+    return mData.size() > index;
+}
+
+LLKeyData LLKeyBind::getKeyData(U32 index) const
+{
+    if (mData.size() > index)
+    {
+        return mData[index];
+    }
+    return LLKeyData();
+}
+
+U32 LLKeyBind::getDataCount()
+{
+    return mData.size();
+}
+
diff --git a/indra/llcommon/llkeybind.h b/indra/llcommon/llkeybind.h
index 4fe622fb795..481949f275b 100644
--- a/indra/llcommon/llkeybind.h
+++ b/indra/llcommon/llkeybind.h
@@ -34,12 +34,16 @@ class LL_COMMON_API LLKeyData
 {
 public:
     LLKeyData();
+    LLKeyData(EMouseClickType mouse, KEY key, MASK mask);
     LLKeyData(const LLSD &key_data);
 
     LLSD asLLSD() const;
     bool isEmpty() const;
+    bool empty() const { return isEmpty(); };
     void reset();
     LLKeyData& operator=(const LLKeyData& rhs);
+    bool operator==(const LLKeyData& rhs);
+    bool operator!=(const LLKeyData& rhs);
 
     EMouseClickType mMouse;
     KEY mKey;
@@ -54,7 +58,9 @@ class LLKeyBind
     LLKeyBind(const LLSD &key_bind);
 
     bool operator==(const LLKeyBind& rhs);
-    bool empty();
+    bool operator!=(const LLKeyBind& rhs);
+    bool isEmpty() const;
+    bool empty() const { return isEmpty(); };
 
     LLSD asLLSD() const;
 
@@ -62,8 +68,19 @@ class LLKeyBind
     bool canHandleKey(KEY key, MASK mask) const;
     bool canHandleMouse(EMouseClickType mouse, MASK mask) const;
 
-    LLKeyData mDataPrimary;
-    LLKeyData mDataSecondary;
+    // these methods enshure there will be no repeats
+    bool addKeyData(EMouseClickType mouse, KEY key, MASK mask);
+    bool addKeyData(const LLKeyData& data);
+    void replaceKeyData(EMouseClickType mouse, KEY key, MASK mask, U32 index);
+    void replaceKeyData(const LLKeyData& data, U32 index);
+    bool hasKeyData(U32 index) const;
+    void clear() { mData.clear(); };
+    LLKeyData getKeyData(U32 index) const;
+    U32 getDataCount();
+
+private:
+    typedef std::vector<LLKeyData> data_vector_t;
+    data_vector_t mData;
 };
 
 
diff --git a/indra/llui/llscrolllistctrl.cpp b/indra/llui/llscrolllistctrl.cpp
index 6c8fde580f6..7e17dd7c8d1 100644
--- a/indra/llui/llscrolllistctrl.cpp
+++ b/indra/llui/llscrolllistctrl.cpp
@@ -130,6 +130,7 @@ LLScrollListCtrl::Params::Params()
 	search_column("search_column", 0),
 	sort_column("sort_column", -1),
 	sort_ascending("sort_ascending", true),
+	can_sort("can_sort", true),
 	mouse_wheel_opaque("mouse_wheel_opaque", false),
 	commit_on_keyboard_movement("commit_on_keyboard_movement", true),
 	heading_height("heading_height"),
@@ -165,6 +166,7 @@ LLScrollListCtrl::LLScrollListCtrl(const LLScrollListCtrl::Params& p)
 	mSelectionChanged(false),
 	mNeedsScroll(false),
 	mCanSelect(true),
+	mCanSort(p.can_sort),
 	mColumnsDirty(false),
 	mMaxItemCount(INT_MAX), 
 	mBorderThickness( 2 ),
@@ -2801,6 +2803,8 @@ void LLScrollListCtrl::onClickColumn(void *userdata)
 	LLScrollListCtrl *parent = info->mParentCtrl;
 	if (!parent) return;
 
+	if (!parent->mCanSort) return;
+
 	S32 column_index = info->mIndex;
 
 	LLScrollListColumn* column = parent->mColumnsIndexed[info->mIndex];
diff --git a/indra/llui/llscrolllistctrl.h b/indra/llui/llscrolllistctrl.h
index d7572d9fcf6..249d9a6f15e 100644
--- a/indra/llui/llscrolllistctrl.h
+++ b/indra/llui/llscrolllistctrl.h
@@ -114,7 +114,8 @@ class LLScrollListCtrl : public LLUICtrl, public LLEditMenuHandler,
 		// sort and search behavior
 		Optional<S32>	search_column,
 						sort_column;
-		Optional<bool>	sort_ascending;
+		Optional<bool>	sort_ascending,
+						can_sort; // whether user is allowed to sort
 
 		// colors
 		Optional<LLUIColor>	fg_unselected_color,
@@ -457,6 +458,7 @@ class LLScrollListCtrl : public LLUICtrl, public LLEditMenuHandler,
 	bool			mNeedsScroll;
 	bool			mMouseWheelOpaque;
 	bool			mCanSelect;
+    bool			mCanSort;		// Whether user is allowed to sort
 	bool			mDisplayColumnHeaders;
 	bool			mColumnsDirty;
 	bool			mColumnWidthsDirty;
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index 60891621907..bfbe1a0cfd8 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -376,6 +376,7 @@ set(viewer_SOURCE_FILES
     llinventoryobserver.cpp
     llinventorypanel.cpp
     lljoystickbutton.cpp
+    llkeyconflict.cpp
     lllandmarkactions.cpp
     lllandmarklist.cpp
     lllistbrowser.cpp
@@ -1004,6 +1005,7 @@ set(viewer_HEADER_FILES
     llinventoryobserver.h
     llinventorypanel.h
     lljoystickbutton.h
+    llkeyconflict.h
     lllandmarkactions.h
     lllandmarklist.h
     lllightconstants.h
diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp
index 182341685be..4a4f66db145 100644
--- a/indra/newview/llfloaterpreference.cpp
+++ b/indra/newview/llfloaterpreference.cpp
@@ -52,7 +52,6 @@
 #include "llfavoritesbar.h"
 #include "llfloatersidepanelcontainer.h"
 #include "llfloaterimsession.h"
-#include "llkeybindings.h"
 #include "llkeyboard.h"
 #include "llmodaldialog.h"
 #include "llnavigationbar.h"
@@ -72,8 +71,9 @@
 #include "lltrans.h"
 #include "llviewercontrol.h"
 #include "llviewercamera.h"
-#include "llviewerwindow.h"
+#include "llviewereventrecorder.h"
 #include "llviewermessage.h"
+#include "llviewerwindow.h"
 #include "llviewershadermgr.h"
 #include "llviewerthrottle.h"
 #include "llvoavatarself.h"
@@ -160,36 +160,49 @@ struct LabelTable : public LLInitParam::Block<LabelTable>
     {}
 };
 
-class LLVoiceSetKeyDialog : public LLModalDialog
+// Filters for LLSetKeyBindDialog
+static const U32 ALLOW_MOUSE = 1;
+static const U32 ALLOW_MASK_MOUSE = 2;
+static const U32 ALLOW_KEYS = 4; //keyboard
+static const U32 ALLOW_MASK_KEYS = 8;
+static const U32 ALLOW_MASKS = 16;
+static const U32 IGNORE_MASKS = 32; // For example W (aka Forward) should work regardless of SHIFT being pressed
+static const U32 DEFAULT_KEY_FILTER = ALLOW_MOUSE | ALLOW_MASK_MOUSE | ALLOW_KEYS | ALLOW_MASK_KEYS;
+
+class LLSetKeyBindDialog : public LLModalDialog
 {
 public:
-	LLVoiceSetKeyDialog(const LLSD& key);
-	~LLVoiceSetKeyDialog();
-	
+	LLSetKeyBindDialog(const LLSD& key);
+	~LLSetKeyBindDialog();
+
 	/*virtual*/ BOOL postBuild();
-	
-	void setParent(LLFloaterPreference* parent) { mParent = parent; }
-	void setTmpParent(LLPanelPreferenceControls* parent) { mTmpParent = parent; } // todo: voice key will be removed, class renamved, so it will have only one parent
-	
+
+	void setParent(LLPanelPreferenceControls* parent, U32 key_mask = DEFAULT_KEY_FILTER);
+
 	BOOL handleKeyHere(KEY key, MASK mask);
 	BOOL handleAnyMouseClick(S32 x, S32 y, MASK mask, EMouseClickType clicktype, BOOL down);
 	static void onCancel(void* user_data);
-		
+	static void onBlank(void* user_data);
+	static void onDefault(void* user_data);
+
 private:
-	LLFloaterPreference* mParent;
-	LLPanelPreferenceControls* mTmpParent;// todo: voice key will be removed, class renamved, so it will have only one parent
+	LLPanelPreferenceControls* mParent;
+
+	U32 mKeyMask;
 };
 
-LLVoiceSetKeyDialog::LLVoiceSetKeyDialog(const LLSD& key)
+LLSetKeyBindDialog::LLSetKeyBindDialog(const LLSD& key)
   : LLModalDialog(key),
 	mParent(NULL),
-	mTmpParent(NULL)
+	mKeyMask(DEFAULT_KEY_FILTER)
 {
 }
 
 //virtual
-BOOL LLVoiceSetKeyDialog::postBuild()
+BOOL LLSetKeyBindDialog::postBuild()
 {
+	childSetAction("SetEmpty", onBlank, this);
+	childSetAction("Default", onDefault, this);
 	childSetAction("Cancel", onCancel, this);
 	getChild<LLUICtrl>("Cancel")->setFocus(TRUE);
 	
@@ -198,47 +211,85 @@ BOOL LLVoiceSetKeyDialog::postBuild()
 	return TRUE;
 }
 
-LLVoiceSetKeyDialog::~LLVoiceSetKeyDialog()
+void LLSetKeyBindDialog::setParent(LLPanelPreferenceControls* parent, U32 key_mask)
+{
+    mParent = parent;
+    mKeyMask = key_mask;
+
+    LLTextBase *text_ctrl = getChild<LLTextBase>("descritption");
+
+    std::string input;
+    if ((key_mask & ALLOW_MOUSE) != 0)
+    {
+        input = getString("mouse");
+    }
+    if ((key_mask & ALLOW_KEYS) != 0)
+    {
+        if (!input.empty())
+        {
+            input += ", ";
+        }
+        input += getString("keyboard");
+    }
+    text_ctrl->setTextArg("[INPUT]", input);
+}
+
+LLSetKeyBindDialog::~LLSetKeyBindDialog()
 {
 }
 
-BOOL LLVoiceSetKeyDialog::handleKeyHere(KEY key, MASK mask)
+BOOL LLSetKeyBindDialog::handleKeyHere(KEY key, MASK mask)
 {
 	BOOL result = TRUE;
 
-    if (key == KEY_CONTROL || key == KEY_SHIFT || key == KEY_ALT || key == KEY_NONE)
+    if ((key == 'Q' && mask == MASK_CONTROL)
+        || key == KEY_ESCAPE)
+    {
+        closeFloater();
+        return true;
+    }
+
+    // forbidden keys
+    if (key == KEY_NONE
+        || key == KEY_RETURN
+        || key == KEY_DELETE
+        || key == KEY_BACKSPACE)
     {
-        // temp
         return false;
     }
-	
-    // todo, same for escape
-	if (key == 'Q' && mask == MASK_CONTROL)
-	{
-		result = FALSE;
-        if (mTmpParent)
-        {
-            mTmpParent->onSetKey(KEY_NONE, MASK_NONE);
-        }
+
+    if ((mKeyMask & ALLOW_MASKS) == 0
+        && (key == KEY_CONTROL || key == KEY_SHIFT || key == KEY_ALT))
+    {
+        // mask by themself are not allowed
+        return false;
+    }
+    else if ((mKeyMask & ALLOW_KEYS) == 0)
+    {
+        // basic keys not allowed
+        return false;
+    }
+    else if ((mKeyMask & ALLOW_MASK_KEYS) == 0 && mask != 0)
+    {
+        // masked keys not allowed
+        return false;
     }
+
 	else if (mParent)
 	{
-		mParent->setKey(key);
-	}
-	else if (mTmpParent)
-	{
-		mTmpParent->onSetKey(key, mask);
+		mParent->onSetKeyBind(CLICK_NONE, key, mask);
 	}
 	closeFloater();
 	return result;
 }
 
-BOOL LLVoiceSetKeyDialog::handleAnyMouseClick(S32 x, S32 y, MASK mask, EMouseClickType clicktype, BOOL down)
+BOOL LLSetKeyBindDialog::handleAnyMouseClick(S32 x, S32 y, MASK mask, EMouseClickType clicktype, BOOL down)
 {
     BOOL result = FALSE;
 
     if (clicktype == CLICK_LEFT)
     {
+        // try handling buttons first
         if (down)
         {
             result = LLView::handleMouseDown(x, y, mask);
@@ -248,30 +299,21 @@ BOOL LLVoiceSetKeyDialog::handleAnyMouseClick(S32 x, S32 y, MASK mask, EMouseCli
             result = LLView::handleMouseUp(x, y, mask);
         }
     }
-    if (clicktype == LLMouseHandler::CLICK_LEFT)
-    {
-        result = LLView::handleDoubleClick(x, y, mask);
-    }
-    if (result)
-    {
-        return TRUE;
-    }
-    if (down && clicktype != LLMouseHandler::CLICK_RIGHT) //tmp
-        //&& (clicktype == LLMouseHandler::CLICK_MIDDLE || clicktype == LLMouseHandler::CLICK_BUTTON4 || clicktype == LLMouseHandler::CLICK_BUTTON5)
-        //&& mask == 0)
+
+    if (!result
+        && ((mKeyMask & ALLOW_MOUSE) != 0)
+        && (clicktype != CLICK_RIGHT || mask != 0) // reassigning menu button is not supported
+        && ((mKeyMask & ALLOW_MASK_MOUSE) != 0 || mask == 0))
     {
         if (mParent)
         {
-            mParent->setMouse(clicktype);
-        }
-        else if (mTmpParent)
-        {
-            mTmpParent->onSetMouse(clicktype, mask);
+            mParent->onSetKeyBind(clicktype, KEY_NONE, mask);
         }
         result = TRUE;
         closeFloater();
     }
-    else
+    
+    if (!result)
     {
         result = LLMouseHandler::handleAnyMouseClick(x, y, mask, clicktype, down);
     }
@@ -280,17 +322,36 @@ BOOL LLVoiceSetKeyDialog::handleAnyMouseClick(S32 x, S32 y, MASK mask, EMouseCli
 }
 
 //static
-void LLVoiceSetKeyDialog::onCancel(void* user_data)
+void LLSetKeyBindDialog::onCancel(void* user_data)
 {
-	LLVoiceSetKeyDialog* self = (LLVoiceSetKeyDialog*)user_data;
-    // tmp needs 'no key' button
-    if (self->mTmpParent)
-        {
-            self->mTmpParent->onSetKey(KEY_NONE, MASK_NONE);
-        }
+    LLSetKeyBindDialog* self = (LLSetKeyBindDialog*)user_data;
 	self->closeFloater();
 }
 
+//static
+void LLSetKeyBindDialog::onBlank(void* user_data)
+{
+    LLSetKeyBindDialog* self = (LLSetKeyBindDialog*)user_data;
+    // tmp needs 'no key' button
+    if (self->mParent)
+    {
+        self->mParent->onSetKeyBind(CLICK_NONE, KEY_NONE, MASK_NONE);
+    }
+    self->closeFloater();
+}
+
+//static
+void LLSetKeyBindDialog::onDefault(void* user_data)
+{
+    LLSetKeyBindDialog* self = (LLSetKeyBindDialog*)user_data;
+    // tmp needs 'no key' button
+    if (self->mParent)
+    {
+        self->mParent->onDefaultKeyBind();
+    }
+    self->closeFloater();
+}
+
 
 // global functions 
 
@@ -370,37 +431,6 @@ void handleAppearanceCameraMovementChanged(const LLSD& newvalue)
 	}
 }
 
-/*bool callback_skip_dialogs(const LLSD& notification, const LLSD& response, LLFloaterPreference* floater)
-{
-	S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
-	if (0 == option && floater )
-	{
-		if ( floater )
-		{
-			floater->setAllIgnored();
-		//	LLFirstUse::disableFirstUse();
-			floater->buildPopupLists();
-		}
-	}
-	return false;
-}
-
-bool callback_reset_dialogs(const LLSD& notification, const LLSD& response, LLFloaterPreference* floater)
-{
-	S32 option = LLNotificationsUtil::getSelectedOption(notification, response);
-	if ( 0 == option && floater )
-	{
-		if ( floater )
-		{
-			floater->resetAllIgnored();
-			//LLFirstUse::resetFirstUse();
-			floater->buildPopupLists();
-		}
-	}
-	return false;
-}
-*/
-
 void fractionFromDecimal(F32 decimal_val, S32& numerator, S32& denominator)
 {
 	numerator = 0;
@@ -435,7 +465,7 @@ LLFloaterPreference::LLFloaterPreference(const LLSD& key)
 	static bool registered_dialog = false;
 	if (!registered_dialog)
 	{
-		LLFloaterReg::add("voice_set_key", "floater_select_key.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLVoiceSetKeyDialog>);
+		LLFloaterReg::add("keybind_dialog", "floater_select_key.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLSetKeyBindDialog>);
 		registered_dialog = true;
 	}
 	
@@ -1754,53 +1784,6 @@ void LLFloaterPreference::onChangeQuality(const LLSD& data)
 
 void LLFloaterPreference::onClickSetKey()
 {
-	LLVoiceSetKeyDialog* dialog = LLFloaterReg::showTypedInstance<LLVoiceSetKeyDialog>("voice_set_key", LLSD(), TRUE);
-	if (dialog)
-	{
-		dialog->setParent(this);
-	}
-}
-
-void LLFloaterPreference::setKey(KEY key)
-{
-	getChild<LLUICtrl>("modifier_combo")->setValue(LLKeyboard::stringFromKey(key));
-	// update the control right away since we no longer wait for apply
-	getChild<LLUICtrl>("modifier_combo")->onCommit();
-}
-
-void LLFloaterPreference::setMouse(EMouseClickType click)
-{
-    std::string bt_name;
-    std::string ctrl_value;
-    switch (click)
-    {
-        case CLICK_MIDDLE:
-            bt_name = "middle_mouse";
-            ctrl_value = MIDDLE_MOUSE_CV;
-            break;
-        case CLICK_BUTTON4:
-            bt_name = "button4_mouse";
-            ctrl_value = MOUSE_BUTTON_4_CV;
-            break;
-        case CLICK_BUTTON5:
-            bt_name = "button5_mouse";
-            ctrl_value = MOUSE_BUTTON_5_CV;
-            break;
-        default:
-            break;
-    }
-
-    if (!ctrl_value.empty())
-    {
-        LLUICtrl* p2t_line_editor = getChild<LLUICtrl>("modifier_combo");
-        // We are using text control names for readability and compatibility with voice
-        p2t_line_editor->setControlValue(ctrl_value);
-        LLPanel* advanced_preferences = dynamic_cast<LLPanel*>(p2t_line_editor->getParent());
-        if (advanced_preferences)
-        {
-            p2t_line_editor->setValue(advanced_preferences->getString(bt_name));
-        }
-    }
 }
 
 void LLFloaterPreference::onClickSetMiddleMouse()
@@ -1825,18 +1808,6 @@ void LLFloaterPreference::onClickSetSounds()
 	getChild<LLCheckBoxCtrl>("gesture_audio_play_btn")->setEnabled(!gSavedSettings.getBOOL("MuteSounds"));
 }
 
-/*
-void LLFloaterPreference::onClickSkipDialogs()
-{
-	LLNotificationsUtil::add("SkipShowNextTimeDialogs", LLSD(), LLSD(), boost::bind(&callback_skip_dialogs, _1, _2, this));
-}
-
-void LLFloaterPreference::onClickResetDialogs()
-{
-	LLNotificationsUtil::add("ResetShowNextTimeDialogs", LLSD(), LLSD(), boost::bind(&callback_reset_dialogs, _1, _2, this));
-}
- */
-
 void LLFloaterPreference::onClickEnablePopup()
 {	
 	LLScrollListCtrl& disabled_popups = getChildRef<LLScrollListCtrl>("disabled_popups");
@@ -2845,7 +2816,7 @@ void LLPanelPreferenceGraphics::setPresetText()
 		}
 	}
 
-	if (hasDirtyChilds() && !preset_graphic_active.empty())
+    if (hasDirtyChilds() && !preset_graphic_active.empty())
 	{
 		gSavedSettings.setString("PresetGraphicActive", "");
 		preset_graphic_active.clear();
@@ -2965,98 +2936,124 @@ void LLPanelPreferenceGraphics::setHardwareDefaults()
 	resetDirtyChilds();
 }
 
+//------------------------LLPanelPreferenceControls--------------------------------
+static LLPanelInjector<LLPanelPreferenceControls> t_pref_contrls("panel_preference_controls");
 
-//-------------------For LLPanelPreferenceControls' list---------------------------
-class LLGroupControlsListItem : public LLScrollListItem, public LLHandleProvider<LLGroupControlsListItem>
+LLPanelPreferenceControls::LLPanelPreferenceControls()
+    :LLPanelPreference(),
+    mEditingIndex(-1),
+    mEditingColumn(-1),
+    mEditingMode(0),
+    mShowKeyDialog(false)
 {
-public:
-    LLGroupControlsListItem(const LLScrollListItem::Params& p)
-        : LLScrollListItem(p)
-    {
-    }
-
-    LLGroupControlsListItem(const LLScrollListItem::Params& p, const LLUUID& icon_id)
-        : LLScrollListItem(p), mIconId(icon_id)
-    {
-    }
-
-    void draw(const LLRect& rect, const LLColor4& fg_color, const LLColor4& bg_color, const LLColor4& highlight_color, S32 column_padding)
-    {
-        // todo: insert image and adjust rect
-        LLScrollListItem::draw(rect, fg_color, bg_color, highlight_color, column_padding);
-    }
-private:
-    LLUUID mIconId;
-};
-
-static const std::string tmp_typetostring[LLControlBindings::CONTROL_NUM_INDICES] = {
-    "control_view_actions",
-    "control_about",
-    "control_orbit",
-    "control_pan",
-    "control_world_map",
-    "control_zoom",
-    "control_interactions",
-    "control_build",
-    "control_drag",
-    "control_edit",
-    "control_menu",
-    "control_open",
-    "control_touch",
-    "control_wear",
-    "control_movements",
-    "control_moveto",
-    "control_sit",
-    "control_teleportto",
-    "control_forward",
-    "control_backward",
-    "control_left",
-    "control_right",
-    "control_lstrafe",
-    "control_rstrafe",
-    "control_jump",
-    "control_down",
-    "control_run",
-    "control_toggle_run",
-    "control_fly",
-    "control_mediacontent",
-    "control_parcel",
-    "control_media",
-    "control_voice",
-    "control_toggle_voice",
-    "control_reserved",
-    "control_menu",
-    "control_reserved_select",
-    "control_shift_select",
-    "control_cntrl_select"
-};
+}
 
-//------------------------LLPanelPreferenceControls--------------------------------
-static LLPanelInjector<LLPanelPreferenceControls> t_pref_contrls("panel_preference_controls");
+LLPanelPreferenceControls::~LLPanelPreferenceControls()
+{
+}
 
 BOOL LLPanelPreferenceControls::postBuild()
 {
-    //todo: on open instead of on the go
     //todo: add pitch/yaw?
-    //todo: Scroll?
     //todo: should be auto-expandable with menu items and should pull names from menu when possible
 
 
     // populate list of controls
     pControlsTable = getChild<LLScrollListCtrl>("controls_list");
-    populateControlTable();
+    pKeyModeBox = getChild<LLComboBox>("key_mode");
 
     pControlsTable->setCommitCallback(boost::bind(&LLPanelPreferenceControls::onListCommit, this));
+    pKeyModeBox->setCommitCallback(boost::bind(&LLPanelPreferenceControls::onModeCommit, this));
+    getChild<LLButton>("restore_defaults")->setCommitCallback(boost::bind(&LLPanelPreferenceControls::onRestoreDefaults, this));
 
     return TRUE;
 }
 
+// Something of a workaround: cells don't handle clicks, so we catch a click, then process it on hover.
+BOOL LLPanelPreferenceControls::handleHover(S32 x, S32 y, MASK mask)
+{
+    if (mShowKeyDialog)
+    {
+        if (mEditingIndex > 0 && mConflictHandler[mEditingMode].canAssignControl((LLKeyConflictHandler::EControlTypes)mEditingIndex))
+        {
+            mEditingColumn = pControlsTable->getColumnIndexFromOffset(x);
+
+            if (mEditingColumn >0)
+            {
+                LLSetKeyBindDialog* dialog = LLFloaterReg::showTypedInstance<LLSetKeyBindDialog>("keybind_dialog", LLSD(), TRUE);
+                if (dialog)
+                {
+                    if (mConflictHandler[mEditingMode].getLoadedMode() == LLKeyConflictHandler::MODE_GENERAL)
+                    {
+                        dialog->setParent(this, DEFAULT_KEY_FILTER);
+                    }
+                    else
+                    {
+                        dialog->setParent(this, ALLOW_KEYS | ALLOW_MASK_KEYS);
+                    }
+                }
+            }
+        }
+        mShowKeyDialog = false;
+    }
+    return LLPanelPreference::handleHover(x, y, mask);
+}
+
+void LLPanelPreferenceControls::addGroupRow(const std::string &icon, S32 index)
+{
+    LLScrollListItem::Params item_params;
+    item_params.value = LLSD::Integer(-1);
+
+    LLScrollListCell::Params icon_cell_params;
+    icon_cell_params.font = LLFontGL::getFontSansSerif();
+    icon_cell_params.font_halign = LLFontGL::LEFT;
+    icon_cell_params.type = "icontext";
+
+    LLScrollListCell::Params cell_params;
+    // init basic cell params
+    cell_params.font = LLFontGL::getFontSansSerif();
+    cell_params.font_halign = LLFontGL::LEFT;
+
+    std::string control_name = LLKeyConflictHandler::getControlName((LLKeyConflictHandler::EControlTypes)index);
+    std::string label;
+    if (hasString(control_name))
+    {
+        label = getString(control_name);
+    }
+    else
+    {
+        label = control_name;
+    }
+    icon_cell_params.column = "lst_action";
+    icon_cell_params.text = label;
+    icon_cell_params.value = icon;
+    item_params.columns.add(icon_cell_params);
+    //dummy cells
+    cell_params.column = "lst_ctrl1";
+    cell_params.value = "";
+    item_params.columns.add(cell_params);
+    cell_params.column = "lst_ctrl2";
+    cell_params.value = "";
+    item_params.columns.add(cell_params);
+    cell_params.column = "lst_ctrl3";
+    cell_params.value = "";
+    item_params.columns.add(cell_params);
+    pControlsTable->addRow(item_params, EAddPosition::ADD_BOTTOM);
+}
+
+void LLPanelPreferenceControls::regenerateControls()
+{
+    mEditingMode = pKeyModeBox->getValue().asInteger();
+    mConflictHandler[mEditingMode].loadFromSettings((LLKeyConflictHandler::EModes)mEditingMode);
+    populateControlTable();
+}
+
 void LLPanelPreferenceControls::populateControlTable()
 {
     pControlsTable->clearRows();
 
     // todo: subsections need sorting?
-    std::string label;
+    std::string label, control_name;
     LLScrollListCell::Params cell_params;
     // init basic cell params
     cell_params.font = LLFontGL::getFontSansSerif();
@@ -3064,81 +3061,71 @@ void LLPanelPreferenceControls::populateControlTable()
     cell_params.column = "";
     cell_params.value = label;
 
-    LLScrollListItem::Params item_params_blank;
-    cell_params.enabled = false;
-    item_params_blank.value = LLSD::Integer(-1);
-    item_params_blank.columns.add(cell_params);
-    cell_params.enabled = true;
-
-    for (U32 i = LLControlBindings::CONTROL_VIEW_ACTIONS; i < LLControlBindings::CONTROL_NUM_INDICES; i++)
+    S32 start = mEditingMode == LLKeyConflictHandler::MODE_GENERAL ? LLKeyConflictHandler::CONTROL_VIEW_ACTIONS : LLKeyConflictHandler::CONTROL_MOVEMENTS;
+    S32 end = mEditingMode == LLKeyConflictHandler::MODE_GENERAL ? LLKeyConflictHandler::CONTROL_NUM_INDICES : LLKeyConflictHandler::CONTROL_RESERVED;
+    for (S32 i = start; i < end; i++)
     {
-        switch (i)
+        LLKeyConflictHandler::EControlTypes type = (LLKeyConflictHandler::EControlTypes)i;
+        switch (type)
         {
-            case LLControlBindings::CONTROL_VIEW_ACTIONS:
-            {
-                // same as below, but without separator
-                LLScrollListItem::Params item_params;
-                item_params.value = LLSD::Integer(i);
-
-                label = getString(tmp_typetostring[i]);
-                cell_params.column = "lst_action";
-                cell_params.value = label;
-                //dummy cells
-                item_params.columns.add(cell_params);
-                cell_params.column = "lst_ctrl1";
-                cell_params.value = "";
-                item_params.columns.add(cell_params);
-                cell_params.column = "lst_ctrl2";
-                cell_params.value = "";
-                item_params.columns.add(cell_params);
-                LLUUID id;
-                LLGroupControlsListItem* item = new LLGroupControlsListItem(item_params, id);
-                pControlsTable->addRow(item, item_params, EAddPosition::ADD_BOTTOM);
-                break;
-            }
-            case LLControlBindings::CONTROL_INTERACTIONS:
-            case LLControlBindings::CONTROL_MOVEMENTS:
-            case LLControlBindings::CONTROL_MEDIACONTENT:
-            case LLControlBindings::CONTROL_RESERVED:
-            {
-                // insert blank
-                pControlsTable->addRow(item_params_blank, EAddPosition::ADD_BOTTOM);
-                // inster with icon
-                LLScrollListItem::Params item_params;
-                item_params.value = LLSD::Integer(i);
-
-                label = getString(tmp_typetostring[i]);
-                cell_params.column = "lst_action";
-                cell_params.value = label;
-                //dummy cells
-                item_params.columns.add(cell_params);
-                cell_params.column = "lst_ctrl1";
-                cell_params.value = "";
-                item_params.columns.add(cell_params);
-                cell_params.column = "lst_ctrl2";
-                cell_params.value = "";
-                item_params.columns.add(cell_params);
-                LLUUID id;
-                LLGroupControlsListItem* item = new LLGroupControlsListItem(item_params, id);
-                pControlsTable->addRow(item, item_params, EAddPosition::ADD_BOTTOM);
-                break;
-            }
-            default:
+        case LLKeyConflictHandler::CONTROL_VIEW_ACTIONS:
+            addSeparator();
+            addGroupRow("Search_Icon", i);
+            break;
+        case LLKeyConflictHandler::CONTROL_INTERACTIONS:
+            addSeparator();
+            addGroupRow("Command_Gestures_Icon", i);
+            break;
+        case LLKeyConflictHandler::CONTROL_MOVEMENTS:
+            addSeparator();
+            addGroupRow("Move_Walk_Off", i);
+            break;
+        case LLKeyConflictHandler::CONTROL_MEDIACONTENT:
+            addSeparator();
+            addGroupRow("Audio_Press", i);
+            break;
+        case LLKeyConflictHandler::CONTROL_CAMERA:
+            addSeparator();
+            addGroupRow("Cam_FreeCam_Off", i);
+            break;
+        case LLKeyConflictHandler::CONTROL_EDIT_TITLE:
+            addSeparator();
+            addGroupRow("Tool_Dozer", i);
+            break;
+        case LLKeyConflictHandler::CONTROL_RESERVED:
+            addSeparator();
+            addGroupRow("Info_Small", i);
+            break;
+        default:
             {
                 //default insert
                 LLScrollListItem::Params item_params;
                 item_params.value = LLSD::Integer(i);
 
-                // todo oddset
                 cell_params.column = "lst_action";
-                label = getString(tmp_typetostring[i]);
+                bool enabled = mConflictHandler[mEditingMode].canAssignControl(type);
+                control_name = LLKeyConflictHandler::getControlName(type);
+                if (hasString(control_name))
+                {
+                    label = getString(control_name);
+                }
+                else
+                {
+                    label = control_name;
+                }
                 cell_params.value = label;
                 item_params.columns.add(cell_params);
                 cell_params.column = "lst_ctrl1";
-                cell_params.value = gControlBindings.getPrimaryControl((LLControlBindings::EControlTypes)i).asString();
+                cell_params.value = mConflictHandler[mEditingMode].getControlString(type, 0);
+                cell_params.enabled = enabled;
                 item_params.columns.add(cell_params);
                 cell_params.column = "lst_ctrl2";
-                cell_params.value = gControlBindings.getSecondaryControl((LLControlBindings::EControlTypes)i).asString();
+                cell_params.value = mConflictHandler[mEditingMode].getControlString(type, 1);
+                cell_params.enabled = enabled;
+                item_params.columns.add(cell_params);
+                cell_params.column = "lst_ctrl3";
+                cell_params.value = mConflictHandler[mEditingMode].getControlString(type, 2);
+                cell_params.enabled = enabled;
                 item_params.columns.add(cell_params);
 
                 pControlsTable->addRow(item_params, EAddPosition::ADD_BOTTOM);
@@ -3146,23 +3133,64 @@ void LLPanelPreferenceControls::populateControlTable()
             }
         }
     }
+
+    //temp
+    if (mEditingMode == LLKeyConflictHandler::MODE_GENERAL)
+        pControlsTable->setEnabled(false);
 }
 
-void LLPanelPreferenceControls::cancel()
+// Just a workaround to not care about first separator before headers (we can start from random header)
+void LLPanelPreferenceControls::addSeparator()
 {
+    if (pControlsTable->getItemCount() > 0)
+    {
+        pControlsTable->addSeparator(EAddPosition::ADD_BOTTOM);
+    }
 }
 
-void LLPanelPreferenceControls::saveSettings()
+void LLPanelPreferenceControls::apply()
 {
+    for (U32 i = 0; i < LLKeyConflictHandler::MODE_COUNT; ++i)
+    {
+        if (mConflictHandler[i].hasUnsavedChanges())
+        {
+            mConflictHandler[i].saveToSettings();
+        }
+    }
 }
 
-void LLPanelPreferenceControls::resetDirtyChilds()
+void LLPanelPreferenceControls::cancel()
 {
+    for (U32 i = 0; i < LLKeyConflictHandler::MODE_COUNT; ++i)
+    {
+        if (mConflictHandler[i].hasUnsavedChanges())
+        {
+            mConflictHandler[i].clear();
+        }
+    }
+    pControlsTable->clear();
 }
 
-bool LLPanelPreferenceControls::hasDirtyChilds()
+void LLPanelPreferenceControls::saveSettings()
 {
-    return false;
+    for (U32 i = 0; i < LLKeyConflictHandler::MODE_COUNT; ++i)
+    {
+        if (mConflictHandler[i].hasUnsavedChanges())
+        {
+            mConflictHandler[i].saveToSettings();
+        }
+    }
+
+    S32 mode = pKeyModeBox->getValue().asInteger();
+    if (mConflictHandler[mode].empty())
+    {
+        regenerateControls();
+    }
+}
+
+void LLPanelPreferenceControls::resetDirtyChilds()
+{
+    regenerateControls();
 }
 
 void LLPanelPreferenceControls::onListCommit()
@@ -3175,61 +3203,85 @@ void LLPanelPreferenceControls::onListCommit()
 
     S32 control = item->getValue().asInteger();
 
-    if (!gControlBindings.canAssignControl((LLControlBindings::EControlTypes)control))
+    if (control <= 0)
     {
         return;
     }
-    
-    // todo: add code to determine what cell was clicked, probably cells themself should be clickable
 
-    LLVoiceSetKeyDialog* dialog = LLFloaterReg::showTypedInstance<LLVoiceSetKeyDialog>("voice_set_key", LLSD(), TRUE);
-    if (dialog)
+    if (!mConflictHandler[mEditingMode].canAssignControl((LLKeyConflictHandler::EControlTypes)control))
     {
-        dialog->setTmpParent(this); // will be remade from being voice later
+        return;
     }
+
+    // List does not tell us what cell was clicked, so we have to figure it out manually, but
+    // fresh mouse coordinates are not yet accessible during onCommit() and there are other issues,
+    // so we cheat: remember item user clicked at, trigger 'key dialog' on hover that comes next,
+    // use coordinates from hover to calculate cell
+    mEditingIndex = control;
+    mShowKeyDialog = true;
 }
 
-void LLPanelPreferenceControls::onSetKey(KEY key, MASK mask)
+void LLPanelPreferenceControls::onModeCommit()
 {
-    LLScrollListItem* item = pControlsTable->getFirstSelected();
-    if (item == NULL)
+    regenerateControls();
+}
+
+void LLPanelPreferenceControls::onSetKeyBind(EMouseClickType click, KEY key, MASK mask)
+{
+    LLKeyConflictHandler::EControlTypes control = (LLKeyConflictHandler::EControlTypes)mEditingIndex;
+
+    if (!mConflictHandler[mEditingMode].canAssignControl(control))
     {
         return;
     }
 
-    LLControlBindings::EControlTypes control = (LLControlBindings::EControlTypes)item->getValue().asInteger();
-
-    if (!gControlBindings.canAssignControl(control))
+    pControlsTable->deselectAllItems();
+    pControlsTable->selectByValue(mEditingIndex);
+    LLScrollListItem *item = pControlsTable->getFirstSelected();
+    if (item && mEditingColumn > 0)
     {
-        return;
+
+        mConflictHandler[mEditingMode].registerControl(control, mEditingColumn - 1, click, key, mask);
+
+        LLScrollListCell *cell = item->getColumn(1);
+        cell->setValue(mConflictHandler[mEditingMode].getControlString(control, 0));
+        cell = item->getColumn(2);
+        cell->setValue(mConflictHandler[mEditingMode].getControlString(control, 1));
+        cell = item->getColumn(3);
+        cell->setValue(mConflictHandler[mEditingMode].getControlString(control, 2));
     }
 
-    gControlBindings.registerPrimaryControl(control, LLMouseHandler::CLICK_NONE, key, mask);
+    populateControlTable();
+}
 
-    // instead of populating, update single element
+void LLPanelPreferenceControls::onRestoreDefaults()
+{
+    mConflictHandler[mEditingMode].resetToDefaults();
     populateControlTable();
 }
 
-void LLPanelPreferenceControls::onSetMouse(LLMouseHandler::EClickType click, MASK mask)
+void LLPanelPreferenceControls::onDefaultKeyBind()
 {
+    LLKeyConflictHandler::EControlTypes control = (LLKeyConflictHandler::EControlTypes)mEditingIndex;
 
-    LLScrollListItem* item = pControlsTable->getFirstSelected();
-    if (item == NULL)
+    if (!mConflictHandler[mEditingMode].canAssignControl(control))
     {
         return;
     }
 
-    LLControlBindings::EControlTypes control = (LLControlBindings::EControlTypes)item->getValue().asInteger();
-
-    if (!gControlBindings.canAssignControl(control))
+    pControlsTable->deselectAllItems();
+    pControlsTable->selectByValue(mEditingIndex);
+    LLScrollListItem *item = pControlsTable->getFirstSelected();
+    if (item)
     {
-        return;
-    }
+        LLScrollListCell *cell = item->getColumn(mEditingColumn);
 
-    gControlBindings.registerPrimaryControl(control, click, KEY_NONE, mask);
-
-    // instead of populating, update single element
-    populateControlTable();
+        if (mEditingColumn > 0)
+        {
+            mConflictHandler[mEditingMode].resetToDefault(control, mEditingColumn - 1);
+            cell->setValue(mConflictHandler[mEditingMode].getControlString(control, mEditingColumn - 1));
+        }
+    }
 }
 
 LLFloaterPreferenceGraphicsAdvanced::LLFloaterPreferenceGraphicsAdvanced(const LLSD& key)
diff --git a/indra/newview/llfloaterpreference.h b/indra/newview/llfloaterpreference.h
index 74f55a7f91d..bce84387ab9 100644
--- a/indra/newview/llfloaterpreference.h
+++ b/indra/newview/llfloaterpreference.h
@@ -37,12 +37,14 @@
 #include "llavatarpropertiesprocessor.h"
 #include "llconversationlog.h"
 #include "llsearcheditor.h"
+#include "llkeyconflict.h"
 
 class LLConversationLogObserver;
 class LLPanelPreference;
 class LLPanelLCD;
 class LLPanelDebug;
 class LLMessageSystem;
+class LLComboBox;
 class LLScrollListCtrl;
 class LLSliderCtrl;
 class LLSD;
@@ -147,8 +149,6 @@ class LLFloaterPreference : public LLFloater, public LLAvatarPropertiesObserver,
 	void onClickSkin(LLUICtrl* ctrl,const LLSD& userdata);
 	void onSelectSkin();
 	void onClickSetKey();
-	void setKey(KEY key);
-	void setMouse(EMouseClickType click);
 	void onClickSetMiddleMouse();
 	void onClickSetSounds();
 	void onClickEnablePopup();
@@ -297,21 +297,36 @@ class LLPanelPreferenceControls : public LLPanelPreference
 {
 	LOG_CLASS(LLPanelPreferenceControls);
 public:
+	LLPanelPreferenceControls();
+	~LLPanelPreferenceControls();
+
 	BOOL postBuild();
-	void populateControlTable();
+	BOOL handleHover(S32 x, S32 y, MASK mask);
+
+	void apply();
 	void cancel();
 	void saveSettings();
 	void resetDirtyChilds();
 
 	void onListCommit();
-	void onSetKey(KEY key, MASK mask);
-	void onSetMouse(LLMouseHandler::EClickType click, MASK mask);
-
-protected:
-	bool hasDirtyChilds();
+	void onModeCommit();
+	void onSetKeyBind(EMouseClickType click, KEY key, MASK mask);
+	void onRestoreDefaults();
+	void onDefaultKeyBind();
 
 private:
+	void addGroupRow(const std::string &icon, S32 index);
+	void regenerateControls();
+	void populateControlTable();
+	void addSeparator();
+
 	LLScrollListCtrl* pControlsTable;
+	LLComboBox *pKeyModeBox;
+	LLKeyConflictHandler mConflictHandler[LLKeyConflictHandler::MODE_COUNT];
+	S32 mEditingIndex;
+	S32 mEditingColumn;
+	S32 mEditingMode;
+	bool mShowKeyDialog;
 };
 
 class LLFloaterPreferenceGraphicsAdvanced : public LLFloater
diff --git a/indra/newview/llkeyconflict.cpp b/indra/newview/llkeyconflict.cpp
new file mode 100644
index 00000000000..0f0129bf686
--- /dev/null
+++ b/indra/newview/llkeyconflict.cpp
@@ -0,0 +1,789 @@
+/** 
+ * @file llkeyconflict.cpp
+ * @brief 
+ *
+ * $LicenseInfo:firstyear=2019&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2019, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+/*
+ * App-wide preferences.  Note that these are not per-user,
+ * because we need to load many preferences before we have
+ * a login name.
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llkeyconflict.h"
+
+#include "llinitparam.h"
+#include "llkeyboard.h"
+#include "llviewercontrol.h"
+#include "llviewerkeyboard.h"
+#include "llxuiparser.h"
+//#include "llstring.h"
+
+static const std::string typetostring[LLKeyConflictHandler::CONTROL_NUM_INDICES] = {
+    "control_view_actions",
+    "control_about",
+    "control_orbit",
+    "control_pan",
+    "control_world_map",
+    "control_zoom",
+    "control_interactions",
+    "control_build",
+    //"control_drag",
+    "control_edit",
+    //"control_menu",
+    "control_open",
+    "control_touch",
+    "control_wear",
+    "control_movements",
+    "control_moveto",
+    "control_sit",
+    "control_teleportto",
+    "push_forward",
+    "push_backward",
+    "turn_left",
+    "turn_right",
+    "slide_left",
+    "slide_right",
+    "jump",
+    "push_down",
+    //"control_run",
+    "control_toggle_run",
+    "toggle_fly",
+    "stop_moving",
+    "control_camera",
+    "look_up",
+    "look_down",
+    "move_forward",
+    "move_backward",
+    "move_forward_fast",
+    "move_backward_fast",
+    "move_forward_sitting",
+    "move_backward_sitting",
+    "spin_over",
+    "spin_under",
+    "spin_over_sitting",
+    "spin_under_sitting",
+    "pan_up",
+    "pan_down",
+    "pan_left",
+    "pan_right",
+    "pan_in",
+    "pan_out",
+    "spin_around_ccw",
+    "spin_around_cw",
+    "spin_around_ccw_sitting",
+    "spin_around_cw_sitting",
+    "control_edit_title",
+    "edit_avatar_spin_ccw",
+    "edit_avatar_spin_cw",
+    "edit_avatar_spin_over",
+    "edit_avatar_spin_under",
+    "edit_avatar_move_forward",
+    "edit_avatar_move_backward",
+    "control_mediacontent",
+    "control_parcel",
+    "control_media",
+    "control_voice",
+    "control_toggle_voice",
+    "start_chat",
+    "start_gesture",
+    "control_reserved",
+    "control_delete",
+    "control_menu",
+    "control_reserved_select",
+    "control_shift_select",
+    "control_cntrl_select"
+};
+
+// note, a solution is needed that will keep this up to date with llviewerkeyboard
+typedef std::map<std::string, LLKeyConflictHandler::EControlTypes> control_enum_t;
+static const control_enum_t command_to_key =
+{
+    { "jump", LLKeyConflictHandler::CONTROL_JUMP },
+    { "push_down", LLKeyConflictHandler::CONTROL_DOWN },
+    { "push_forward", LLKeyConflictHandler::CONTROL_FORWARD },
+    { "push_backward", LLKeyConflictHandler::CONTROL_BACKWARD },
+    { "look_up", LLKeyConflictHandler::CONTROL_LOOK_UP },
+    { "look_down", LLKeyConflictHandler::CONTROL_LOOK_DOWN },
+    { "toggle_fly", LLKeyConflictHandler::CONTROL_TOGGLE_FLY },
+    { "turn_left", LLKeyConflictHandler::CONTROL_LEFT },
+    { "turn_right", LLKeyConflictHandler::CONTROL_RIGHT },
+    { "slide_left", LLKeyConflictHandler::CONTROL_LSTRAFE },
+    { "slide_right", LLKeyConflictHandler::CONTROL_RSTRAFE },
+    { "spin_around_ccw", LLKeyConflictHandler::CONTROL_CAMERA_SPIN_CCW }, // todo, no idea what these spins are
+    { "spin_around_cw", LLKeyConflictHandler::CONTROL_CAMERA_SPIN_CW },
+    { "spin_around_ccw_sitting", LLKeyConflictHandler::CONTROL_CAMERA_SPIN_CCW_SITTING },
+    { "spin_around_cw_sitting", LLKeyConflictHandler::CONTROL_CAMERA_SPIN_CCW_SITTING },
+    { "spin_over", LLKeyConflictHandler::CONTROL_CAMERA_SOVER },
+    { "spin_under", LLKeyConflictHandler::CONTROL_CAMERA_SUNDER },
+    { "spin_over_sitting", LLKeyConflictHandler::CONTROL_CAMERA_SOVER_SITTING },
+    { "spin_under_sitting", LLKeyConflictHandler::CONTROL_CAMERA_SUNDER_SITTING },
+    { "move_forward", LLKeyConflictHandler::CONTROL_CAMERA_FORWARD },
+    { "move_backward", LLKeyConflictHandler::CONTROL_CAMERA_BACKWARD },
+    { "move_forward_sitting", LLKeyConflictHandler::CONTROL_CAMERA_FSITTING },
+    { "move_backward_sitting", LLKeyConflictHandler::CONTROL_CAMERA_BSITTING },
+    { "pan_up", LLKeyConflictHandler::CONTROL_CAMERA_PANUP },
+    { "pan_down", LLKeyConflictHandler::CONTROL_CAMERA_PANDOWN },
+    { "pan_left", LLKeyConflictHandler::CONTROL_CAMERA_PANLEFT },
+    { "pan_right", LLKeyConflictHandler::CONTROL_CAMERA_PANRIGHT },
+    { "pan_in", LLKeyConflictHandler::CONTROL_CAMERA_PANIN },
+    { "pan_out", LLKeyConflictHandler::CONTROL_CAMERA_PANOUT },
+    { "move_forward_fast", LLKeyConflictHandler::CONTROL_CAMERA_FFORWARD },
+    { "move_backward_fast", LLKeyConflictHandler::CONTROL_CAMERA_FBACKWARD },
+    { "edit_avatar_spin_ccw", LLKeyConflictHandler::CONTROL_EDIT_AV_SPIN_CCW },
+    { "edit_avatar_spin_cw", LLKeyConflictHandler::CONTROL_EDIT_AV_SPIN_CW },
+    { "edit_avatar_spin_over", LLKeyConflictHandler::CONTROL_EDIT_AV_SPIN_OVER },
+    { "edit_avatar_spin_under", LLKeyConflictHandler::CONTROL_EDIT_AV_SPIN_UNDER },
+    { "edit_avatar_move_forward", LLKeyConflictHandler::CONTROL_EDIT_AV_MV_FORWARD },
+    { "edit_avatar_move_backward", LLKeyConflictHandler::CONTROL_EDIT_AV_MV_BACKWARD },
+    { "stop_moving", LLKeyConflictHandler::CONTROL_STOP },
+    { "start_chat", LLKeyConflictHandler::CONTROL_START_CHAT },
+    { "start_gesture", LLKeyConflictHandler::CONTROL_START_GESTURE },
+};
+
+
+// LLKeyboard::stringFromMask is meant for UI and is OS dependent,
+// so this class uses it's own version
+std::string string_from_mask(MASK mask)
+{
+    std::string res;
+    if ((mask & MASK_CONTROL) != 0)
+    {
+        res = "CTL";
+    }
+    if ((mask & MASK_ALT) != 0)
+    {
+        if (!res.empty()) res += "_";
+        res += "ALT";
+    }
+    if ((mask & MASK_SHIFT) != 0)
+    {
+        if (!res.empty()) res += "_";
+        res += "SHIFT";
+    }
+
+    if (mask == MASK_NONE)
+    {
+        res = "NONE";
+    }
+    return res;
+}
+
+// LLKeyConflictHandler
+
+LLKeyConflictHandler::LLKeyConflictHandler()
+    : mHasUnsavedChanges(false)
+{
+    // todo: assign conflic priorities
+    // todo: load from keys.xml?
+
+    // Thise controls are meant to cause conflicts when user tries to assign same control somewhere else
+    /*registerTemporaryControl(CONTROL_RESERVED_MENU, CLICK_RIGHT, KEY_NONE, MASK_NONE, 0);
+    registerTemporaryControl(CONTROL_SHIFT_SELECT, CLICK_LEFT, KEY_NONE, MASK_SHIFT, 0);
+    registerTemporaryControl(CONTROL_CNTRL_SELECT, CLICK_LEFT, KEY_NONE, MASK_CONTROL, 0);
+    registerTemporaryControl(CONTROL_DELETE, CLICK_NONE, KEY_DELETE, MASK_NONE, 0);
+
+    loadFromSettings();*/
+}
+
+LLKeyConflictHandler::LLKeyConflictHandler(EModes mode)
+    : mHasUnsavedChanges(false),
+    mLoadedMode(mode)
+{
+    loadFromSettings(mode);
+}
+
+bool LLKeyConflictHandler::canHandleControl(LLKeyConflictHandler::EControlTypes control_type, EMouseClickType mouse_ind, KEY key, MASK mask)
+{
+    return mControlsMap[control_type].canHandle(mouse_ind, key, mask);
+}
+
+bool LLKeyConflictHandler::canHandleKey(EControlTypes control_type, KEY key, MASK mask)
+{
+    return canHandleControl(control_type, CLICK_NONE, key, mask);
+}
+
+bool LLKeyConflictHandler::canHandleMouse(LLKeyConflictHandler::EControlTypes control_type, EMouseClickType mouse_ind, MASK mask)
+{
+    return canHandleControl(control_type, mouse_ind, KEY_NONE, mask);
+}
+
+bool LLKeyConflictHandler::canHandleMouse(EControlTypes control_type, S32 mouse_ind, MASK mask)
+{
+    return canHandleControl(control_type, (EMouseClickType)mouse_ind, KEY_NONE, mask);
+}
+
+bool LLKeyConflictHandler::canAssignControl(EControlTypes control_type)
+{
+    std::map<EControlTypes, LLKeyConflict>::iterator iter = mControlsMap.find(control_type);
+    if (iter != mControlsMap.end())
+    {
+        return iter->second.mAssignable;
+    }
+    return false;
+}
+
+void LLKeyConflictHandler::registerControl(EControlTypes control_type, U32 index, EMouseClickType mouse, KEY key, MASK mask)
+{
+    LLKeyConflict &type_data = mControlsMap[control_type];
+    if (!type_data.mAssignable)
+    {
+        LL_ERRS() << "Error in code, user or system should not be able to change certain controls" << LL_ENDL;
+    }
+    type_data.mKeyBind.replaceKeyData(mouse, key, mask, index);
+
+    mHasUnsavedChanges = true;
+}
+
+LLKeyData LLKeyConflictHandler::getControl(EControlTypes control_type, U32 index)
+{
+    return mControlsMap[control_type].getKeyData(index);
+}
+
+// static
+std::string LLKeyConflictHandler::getStringFromKeyData(const LLKeyData& keydata)
+{
+    std::string result;
+
+    if (keydata.mMask != MASK_NONE && keydata.mKey != KEY_NONE)
+    {
+        result = LLKeyboard::stringFromAccelerator(keydata.mMask, keydata.mKey);
+    }
+    else if (keydata.mKey != KEY_NONE)
+    {
+        result = LLKeyboard::stringFromKey(keydata.mKey);
+    }
+    else if (keydata.mMask != MASK_NONE)
+    {
+        LL_ERRS() << "Masks binding without keys is not supported yet" << LL_ENDL;
+    }
+
+    #ifdef LL_DARWIN
+    // darwin uses special symbols and doesn't need '+' for masks
+    if (mMouse != CLICK_NONE && mKey != KEY_NONE)
+    {
+    result += " + ";
+    }
+    #else
+    if (keydata.mMouse != CLICK_NONE && !result.empty())
+    {
+    result += " + ";
+    }
+    #endif
+
+    switch (keydata.mMouse)
+    {
+    case CLICK_LEFT:
+    result += "LMB";
+    break;
+    case CLICK_MIDDLE:
+    result += "MMB";
+    break;
+    case CLICK_RIGHT:
+    result += "RMB";
+    break;
+    case CLICK_BUTTON4:
+    result += "MB4";
+    break;
+    case CLICK_BUTTON5:
+    result += "MB5";
+    break;
+    case CLICK_DOUBLELEFT:
+    result += "Double LMB";
+    break;
+    default:
+    break;
+    }
+    return result;
+}
+
+// static
+std::string LLKeyConflictHandler::getControlName(EControlTypes control_type)
+{
+    return typetostring[control_type];
+}
+
+std::string LLKeyConflictHandler::getControlString(EControlTypes control_type, U32 index)
+{
+    return getStringFromKeyData(mControlsMap[control_type].getKeyData(index));
+}
+
+void  LLKeyConflictHandler::loadFromSettings(const LLViewerKeyboard::KeyMode& keymode, control_map_t *destination)
+{
+    for (LLInitParam::ParamIterator<LLViewerKeyboard::KeyBinding>::const_iterator it = keymode.bindings.begin(),
+        end_it = keymode.bindings.end();
+        it != end_it;
+    ++it)
+    {
+        KEY key;
+        MASK mask;
+        if (it->key.getValue().empty())
+        {
+            key = KEY_NONE;
+        }
+        else
+        {
+            LLKeyboard::keyFromString(it->key, &key);
+        }
+        LLKeyboard::maskFromString(it->mask, &mask);
+        std::string command_name = it->command;
+        // it->command
+        // It might be better to have <string,bind> map, but at the moment enum is easier to iterate through.
+        // Besides keys.xml might not contain all commands
+        control_enum_t::const_iterator iter = command_to_key.find(command_name);
+        if (iter != command_to_key.end())
+        {
+            LLKeyConflict &type_data = (*destination)[iter->second];
+            type_data.mAssignable = true;
+            // Don't care about conflict level, all movement and view commands already account for it
+            type_data.mKeyBind.addKeyData(CLICK_NONE, key, mask);
+        }
+    }
+}
+
+void LLKeyConflictHandler::loadFromSettings(const EModes &load_mode, const std::string &filename, control_map_t *destination)
+{
+    if (filename.empty())
+    {
+        return;
+    }
+
+    LLViewerKeyboard::Keys keys;
+    LLSimpleXUIParser parser;
+
+    if (parser.readXUI(filename, keys)
+        && keys.validateBlock())
+    {
+        switch (load_mode)
+        {
+        case MODE_FIRST_PERSON:
+            if (keys.first_person.isProvided())
+            {
+                loadFromSettings(keys.first_person, destination);
+            }
+            break;
+        case MODE_THIRD_PERSON:
+            if (keys.third_person.isProvided())
+            {
+                loadFromSettings(keys.third_person, destination);
+            }
+            break;
+        case MODE_EDIT:
+            if (keys.edit.isProvided())
+            {
+                loadFromSettings(keys.edit, destination);
+            }
+            break;
+        case MODE_EDIT_AVATAR:
+            if (keys.edit_avatar.isProvided())
+            {
+                loadFromSettings(keys.edit_avatar, destination);
+            }
+            break;
+        case MODE_SITTING:
+            if (keys.sitting.isProvided())
+            {
+                loadFromSettings(keys.sitting, destination);
+            }
+            break;
+        default:
+            break;
+        }
+    }
+}
+
+void  LLKeyConflictHandler::loadFromSettings(EModes load_mode)
+{
+    mControlsMap.clear();
+    mDefaultsMap.clear();
+    if (load_mode == MODE_GENERAL)
+    {
+        for (U32 i = 0; i < CONTROL_NUM_INDICES; i++)
+        {
+            EControlTypes type = (EControlTypes)i;
+            switch (type)
+            {
+            case LLKeyConflictHandler::CONTROL_VIEW_ACTIONS:
+            case LLKeyConflictHandler::CONTROL_INTERACTIONS:
+            case LLKeyConflictHandler::CONTROL_MOVEMENTS:
+            case LLKeyConflictHandler::CONTROL_MEDIACONTENT:
+            case LLKeyConflictHandler::CONTROL_CAMERA:
+            case LLKeyConflictHandler::CONTROL_EDIT_TITLE:
+            case LLKeyConflictHandler::CONTROL_RESERVED:
+                // ignore 'headers', they are for representation and organization purposes
+                break;
+            default:
+                {
+                    std::string name = getControlName(type);
+                    LLControlVariablePtr var = gSavedSettings.getControl(name);
+                    if (var)
+                    {
+                        LLKeyBind bind(var->getValue());
+                        LLKeyConflict key(bind, true, 0);
+                        mControlsMap[type] = key;
+                    }
+                    break;
+                }
+            }
+        }
+    }
+    else
+    {
+        // load defaults
+        std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "keys.xml");
+        loadFromSettings(load_mode, filename, &mDefaultsMap);
+
+        // load user's
+        filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "keys.xml");
+        if (gDirUtilp->fileExists(filename))
+        {
+            loadFromSettings(load_mode, filename, &mControlsMap);
+        }
+        else
+        {
+            mControlsMap = mDefaultsMap;
+        }
+    }
+    mLoadedMode = load_mode;
+
+    generatePlaceholders();
+}
+
+void  LLKeyConflictHandler::saveToSettings()
+{
+    if (mControlsMap.empty())
+    {
+        return;
+    }
+
+    if (mLoadedMode == MODE_GENERAL)
+    {
+        for (U32 i = 0; i < CONTROL_NUM_INDICES; i++)
+        {
+            EControlTypes type = (EControlTypes)i;
+            switch (type)
+            {
+            case LLKeyConflictHandler::CONTROL_VIEW_ACTIONS:
+            case LLKeyConflictHandler::CONTROL_INTERACTIONS:
+            case LLKeyConflictHandler::CONTROL_MOVEMENTS:
+            case LLKeyConflictHandler::CONTROL_MEDIACONTENT:
+            case LLKeyConflictHandler::CONTROL_CAMERA:
+            case LLKeyConflictHandler::CONTROL_EDIT_TITLE:
+            case LLKeyConflictHandler::CONTROL_RESERVED:
+                // ignore 'headers', they are for representation and organization purposes
+                break;
+            default:
+            {
+                if (mControlsMap[type].mAssignable)
+                {
+                    std::string name = getControlName(type);
+                    if (gSavedSettings.controlExists(name))
+                    {
+                        gSavedSettings.setLLSD(name, mControlsMap[type].mKeyBind.asLLSD());
+                    }
+                    else if (!mControlsMap[type].mKeyBind.empty())
+                    {
+                        // shouldn't happen user side since all settings are supposed to be declared already, but handy when creating new ones
+                        // (just don't forget to change comment and to copy them from user's folder)
+                        LL_INFOS() << "Creating new keybinding " << name << LL_ENDL;
+                        gSavedSettings.declareLLSD(name, mControlsMap[type].mKeyBind.asLLSD(), "comment", LLControlVariable::PERSIST_ALWAYS);
+                    }
+                }
+                break;
+            }
+            }
+        }
+    }
+    else
+    {
+        // loaded full copy of original file
+        std::string filename = gDirUtilp->findFile("keys.xml",
+            gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, ""),
+            gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, ""));
+
+        LLViewerKeyboard::Keys keys;
+        LLSimpleXUIParser parser;
+
+        if (parser.readXUI(filename, keys)
+            && keys.validateBlock())
+        {
+            // replace category we edited
+
+            // todo: fix this
+            // workaround to avoid doing own param container 
+            LLViewerKeyboard::KeyMode mode;
+            LLViewerKeyboard::KeyBinding binding;
+
+            control_map_t::iterator iter = mControlsMap.begin();
+            control_map_t::iterator end = mControlsMap.end();
+            for (; iter != end; ++iter)
+            {
+                U32 size = iter->second.mKeyBind.getDataCount();
+                for (U32 i = 0; i < size; ++i)
+                {
+                    // Still write empty keys to make sure we will maintain UI position
+                    LLKeyData data = iter->second.mKeyBind.getKeyData(i);
+                    if (data.mKey == KEY_NONE)
+                    {
+                        binding.key = "";
+                    }
+                    else
+                    {
+                        // Note: this is UI string, we might want to hardcode our own for 'fixed' use in keys.xml
+                        binding.key = LLKeyboard::stringFromKey(data.mKey);
+                    }
+                    binding.mask = string_from_mask(data.mMask);
+                    binding.command = getControlName(iter->first);
+                    mode.bindings.add(binding);
+                }
+            }
+
+            switch (mLoadedMode)
+            {
+            case MODE_FIRST_PERSON:
+                if (keys.first_person.isProvided())
+                {
+                    keys.first_person.bindings.set(mode.bindings, true);
+                }
+                break;
+            case MODE_THIRD_PERSON:
+                if (keys.third_person.isProvided())
+                {
+                    keys.third_person.bindings.set(mode.bindings, true);
+                }
+                break;
+            case MODE_EDIT:
+                if (keys.edit.isProvided())
+                {
+                    keys.edit.bindings.set(mode.bindings, true);
+                }
+                break;
+            case MODE_EDIT_AVATAR:
+                if (keys.edit_avatar.isProvided())
+                {
+                    keys.edit_avatar.bindings.set(mode.bindings, true);
+                }
+                break;
+            case MODE_SITTING:
+                if (keys.sitting.isProvided())
+                {
+                    keys.sitting.bindings.set(mode.bindings, true);
+                }
+                break;
+            default:
+                break;
+            }
+
+            // write back to user's xml;
+            std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "keys.xml");
+
+            LLXMLNodePtr output_node = new LLXMLNode("keys", false);
+            LLXUIParser parser;
+            parser.writeXUI(output_node, keys);
+
+            // Write the resulting XML to file
+            if (!output_node->isNull())
+            {
+                LLFILE *fp = LLFile::fopen(filename, "w");
+                if (fp != NULL)
+                {
+                    LLXMLNode::writeHeaderToFile(fp);
+                    output_node->writeToFile(fp);
+                    fclose(fp);
+                }
+            }
+            // Now force a rebind for keyboard
+            if (gDirUtilp->fileExists(filename))
+            {
+                gViewerKeyboard.loadBindingsXML(filename);
+            }
+        }
+    }
+    mHasUnsavedChanges = false;
+}
+
+LLKeyData LLKeyConflictHandler::getDefaultControl(EControlTypes control_type, U32 index)
+{
+    if (mLoadedMode == MODE_GENERAL)
+    {
+        std::string name = getControlName(control_type);
+        LLControlVariablePtr var = gSavedSettings.getControl(name);
+        if (var)
+        {
+            return LLKeyBind(var->getDefault()).getKeyData(index);
+        }
+        return LLKeyData();
+    }
+    else
+    {
+        control_map_t::iterator iter = mDefaultsMap.find(control_type);
+        if (iter != mDefaultsMap.end())
+        {
+            return iter->second.mKeyBind.getKeyData(index);
+        }
+        return LLKeyData();
+    }
+}
+
+void LLKeyConflictHandler::resetToDefault(EControlTypes control_type, U32 index)
+{
+    LLKeyData data = getDefaultControl(control_type, index);
+    mControlsMap[control_type].setKeyData(data, index);
+}
+
+void LLKeyConflictHandler::resetToDefault(EControlTypes control_type)
+{
+    if (mLoadedMode == MODE_GENERAL)
+    {
+        std::string name = getControlName(control_type);
+        LLControlVariablePtr var = gSavedSettings.getControl(name);
+        if (var)
+        {
+            mControlsMap[control_type].mKeyBind = LLKeyBind(var->getDefault());
+        }
+        else
+        {
+            mControlsMap[control_type].mKeyBind.clear();
+        }
+    }
+    else
+    {
+        control_map_t::iterator iter = mDefaultsMap.find(control_type);
+        if (iter != mDefaultsMap.end())
+        {
+            mControlsMap[control_type].mKeyBind = iter->second.mKeyBind;
+        }
+        else
+        {
+            mControlsMap[control_type].mKeyBind.clear();
+        }
+    }
+}
+
+void LLKeyConflictHandler::resetToDefaults(EModes mode)
+{
+    if (mode == MODE_GENERAL)
+    {
+        for (U32 i = 0; i < CONTROL_NUM_INDICES; i++)
+        {
+            EControlTypes type = (EControlTypes)i;
+            switch (type)
+            {
+            case LLKeyConflictHandler::CONTROL_VIEW_ACTIONS:
+            case LLKeyConflictHandler::CONTROL_INTERACTIONS:
+            case LLKeyConflictHandler::CONTROL_MOVEMENTS:
+            case LLKeyConflictHandler::CONTROL_MEDIACONTENT:
+            case LLKeyConflictHandler::CONTROL_CAMERA:
+            case LLKeyConflictHandler::CONTROL_EDIT_TITLE:
+            case LLKeyConflictHandler::CONTROL_RESERVED:
+                // ignore 'headers', they are for representation and organization purposes
+                break;
+            default:
+            {
+                resetToDefault(type);
+                break;
+            }
+            }
+        }
+    }
+    else
+    {
+        mControlsMap.clear();
+        mControlsMap = mDefaultsMap;
+        generatePlaceholders();
+    }
+
+    mHasUnsavedChanges = true;
+}
+
+void LLKeyConflictHandler::resetToDefaults()
+{
+    if (!empty())
+    {
+        resetToDefaults(mLoadedMode);
+    }
+}
+
+void LLKeyConflictHandler::resetAllToDefaults()
+{
+    std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "keys.xml");
+    if (gDirUtilp->fileExists(filename))
+    {
+        LLFile::remove(filename);
+        std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "keys.xml");
+        gViewerKeyboard.loadBindingsXML(filename);
+    }
+
+    for (U32 i = 0; i < CONTROL_NUM_INDICES; i++)
+    {
+        EControlTypes type = (EControlTypes)i;
+        switch (type)
+        {
+        case LLKeyConflictHandler::CONTROL_VIEW_ACTIONS:
+        case LLKeyConflictHandler::CONTROL_INTERACTIONS:
+        case LLKeyConflictHandler::CONTROL_MOVEMENTS:
+        case LLKeyConflictHandler::CONTROL_MEDIACONTENT:
+        case LLKeyConflictHandler::CONTROL_RESERVED:
+            // ignore 'headers', they are for representation and organization purposes
+            break;
+        default:
+            {
+                resetToDefault(type);
+                break;
+            }
+        }
+    }
+    mHasUnsavedChanges = false;
+}
+
+void LLKeyConflictHandler::clear()
+{
+    mHasUnsavedChanges = false;
+    mControlsMap.clear();
+    mDefaultsMap.clear();
+}
+
+void LLKeyConflictHandler::resetKeyboardBindings()
+{
+    std::string filename = gDirUtilp->findFile("keys.xml",
+        gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, ""),
+        gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, ""));
+    
+    gViewerKeyboard.loadBindingsXML(filename);
+}
+
+void LLKeyConflictHandler::generatePlaceholders()
+{
+
+}
+
+void LLKeyConflictHandler::registerTemporaryControl(EControlTypes control_type, EMouseClickType mouse, KEY key, MASK mask, U32 conflict_mask)
+{
+    LLKeyConflict *type_data = &mControlsMap[control_type];
+    type_data->mAssignable = false;
+    type_data->mConflictMask = conflict_mask;
+    type_data->mKeyBind.addKeyData(mouse, key, mask);
+}
+
diff --git a/indra/newview/llkeyconflict.h b/indra/newview/llkeyconflict.h
new file mode 100644
index 00000000000..79bd9b84381
--- /dev/null
+++ b/indra/newview/llkeyconflict.h
@@ -0,0 +1,214 @@
+/** 
+ * @file llkeyconflict.h
+ * @brief 
+ *
+ * $LicenseInfo:firstyear=2019&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2019, Linden Research, Inc.
+ * 
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ * 
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLKEYCONFLICT_H
+#define LL_LLKEYCONFLICT_H
+
+#include "llkeybind.h"
+#include "llviewerkeyboard.h"
+
+
+class LLKeyConflict
+{
+public:
+    LLKeyConflict() : mAssignable(true), mConflictMask(0) {} //temporary assignable, don't forget to change once all keys are recorded
+    LLKeyConflict(bool assignable, U32 conflict_mask)
+        : mAssignable(assignable), mConflictMask(conflict_mask) {}
+    LLKeyConflict(const LLKeyBind &bind, bool assignable, U32 conflict_mask)
+        : mAssignable(assignable), mConflictMask(conflict_mask), mKeyBind(bind) {}
+
+    LLKeyData getPrimaryKeyData() { return mKeyBind.getKeyData(0); }
+    LLKeyData getKeyData(U32 index) { return mKeyBind.getKeyData(index); }
+    void setPrimaryKeyData(const LLKeyData& data) { mKeyBind.replaceKeyData(data, 0); }
+    void setKeyData(const LLKeyData& data, U32 index) { mKeyBind.replaceKeyData(data, index); }
+    bool canHandle(EMouseClickType mouse, KEY key, MASK mask) { return mKeyBind.canHandle(mouse, key, mask); }
+
+    LLKeyBind mKeyBind;
+    bool mAssignable; // whether user can change key or key simply acts as placeholder
+    U32 mConflictMask;
+};
+
+class LLKeyConflictHandler
+{
+public:
+
+    enum EModes // partially repeats e_keyboard_mode
+    {
+        MODE_FIRST_PERSON,
+        MODE_THIRD_PERSON,
+        MODE_EDIT,
+        MODE_EDIT_AVATAR,
+        MODE_SITTING,
+        MODE_GENERAL,
+        MODE_COUNT
+    };
+
+    enum EConflictTypes // priority higherst to lowest
+    {
+        CONFLICT_LAND = 1,
+        CONFLICT_OBJECT = 2,
+        CONFLICT_TOUCH = 4,
+        CONFLICT_INTERACTIBLE = 8,
+        CONFLICT_AVATAR = 16,
+        CONFLICT_ANY = 511
+    };
+
+    // todo, unfortunately will have to remove this and use map/array of strings
+    enum EControlTypes
+    {
+        CONTROL_VIEW_ACTIONS = 0, // Group control, for visual representation in view, not for use
+        CONTROL_ABOUT,
+        CONTROL_ORBIT,
+        CONTROL_PAN,
+        CONTROL_WORLD_MAP,
+        CONTROL_ZOOM,
+        CONTROL_INTERACTIONS, // Group control, for visual representation
+        CONTROL_BUILD,
+        //CONTROL_DRAG,
+        CONTROL_EDIT,
+        //CONTROL_MENU,
+        CONTROL_OPEN,
+        CONTROL_TOUCH,
+        CONTROL_WEAR,
+        CONTROL_MOVEMENTS, // Group control, for visual representation
+        CONTROL_MOVETO,
+        CONTROL_SIT,
+        CONTROL_TELEPORTTO,
+        CONTROL_FORWARD,
+        CONTROL_BACKWARD,
+        CONTROL_LEFT, // Check and sinc name with real movement names
+        CONTROL_RIGHT,
+        CONTROL_LSTRAFE,
+        CONTROL_RSTRAFE,
+        CONTROL_JUMP,
+        CONTROL_DOWN,
+        //CONTROL_RUN,
+        CONTROL_TOGGLE_RUN,
+        CONTROL_TOGGLE_FLY,
+        CONTROL_STOP,
+        CONTROL_CAMERA, // Group control, for visual representation
+        CONTROL_LOOK_UP,
+        CONTROL_LOOK_DOWN,
+        CONTROL_CAMERA_FORWARD,
+        CONTROL_CAMERA_BACKWARD,
+        CONTROL_CAMERA_FFORWARD,
+        CONTROL_CAMERA_FBACKWARD,
+        CONTROL_CAMERA_FSITTING,
+        CONTROL_CAMERA_BSITTING,
+        CONTROL_CAMERA_SOVER,
+        CONTROL_CAMERA_SUNDER,
+        CONTROL_CAMERA_SOVER_SITTING,
+        CONTROL_CAMERA_SUNDER_SITTING,
+        CONTROL_CAMERA_PANUP,
+        CONTROL_CAMERA_PANDOWN,
+        CONTROL_CAMERA_PANLEFT,
+        CONTROL_CAMERA_PANRIGHT,
+        CONTROL_CAMERA_PANIN,
+        CONTROL_CAMERA_PANOUT,
+        CONTROL_CAMERA_SPIN_CCW,
+        CONTROL_CAMERA_SPIN_CW,
+        CONTROL_CAMERA_SPIN_CCW_SITTING,
+        CONTROL_CAMERA_SPIN_CW_SITTING,
+        CONTROL_EDIT_TITLE, // Group control, for visual representation
+        CONTROL_EDIT_AV_SPIN_CCW,
+        CONTROL_EDIT_AV_SPIN_CW,
+        CONTROL_EDIT_AV_SPIN_OVER,
+        CONTROL_EDIT_AV_SPIN_UNDER,
+        CONTROL_EDIT_AV_MV_FORWARD,
+        CONTROL_EDIT_AV_MV_BACKWARD,
+        CONTROL_MEDIACONTENT, // Group control, for visual representation
+        CONTROL_PARCEL, // Play pause
+        CONTROL_MEDIA, // Play stop
+        CONTROL_VOICE, // Keep pressing for it to be ON
+        CONTROL_TOGGLE_VOICE, // Press once to ON/OFF
+        CONTROL_START_CHAT, // Press once to ON/OFF
+        CONTROL_START_GESTURE, // Press once to ON/OFF
+        CONTROL_RESERVED, // Special group control, controls that are disabled by default and not meant to be changed
+        CONTROL_DELETE,
+        CONTROL_RESERVED_MENU,
+        CONTROL_RESERVED_SELECT,
+        CONTROL_SHIFT_SELECT,
+        CONTROL_CNTRL_SELECT,
+        CONTROL_NUM_INDICES // Size, always should be last
+    };
+
+    // Note: missed selection and edition commands (would be really nice to go through selection via MB4/5 or wheel)
+
+    LLKeyConflictHandler();
+    LLKeyConflictHandler(EModes mode);
+
+    bool canHandleControl(EControlTypes control_type, EMouseClickType mouse_ind, KEY key, MASK mask);
+    bool canHandleKey(EControlTypes control_type, KEY key, MASK mask);
+    bool canHandleMouse(EControlTypes control_type, EMouseClickType mouse_ind, MASK mask);
+    bool canHandleMouse(EControlTypes control_type, S32 mouse_ind, MASK mask); //Just for convinience
+    bool canAssignControl(EControlTypes control_type);
+    void registerControl(EControlTypes control_type, U32 data_index, EMouseClickType mouse_ind, KEY key, MASK mask); //todo: return conflicts?
+
+    LLKeyData getControl(EControlTypes control_type, U32 data_index);
+
+    static std::string LLKeyConflictHandler::getStringFromKeyData(const LLKeyData& keydata);
+    static std::string getControlName(EControlTypes control_type);
+    std::string getControlString(EControlTypes control_type, U32 data_index);
+
+
+    // Drops any changes loads controls with ones from 'saved settings' or from xml
+    void loadFromSettings(EModes load_mode);
+    // Saves settings to 'saved settings' or to xml
+    void saveToSettings();
+
+    LLKeyData getDefaultControl(EControlTypes control_type, U32 data_index);
+    // Resets keybinding to default variant from 'saved settings' or xml
+    void resetToDefault(EControlTypes control_type, U32 index);
+    void resetToDefault(EControlTypes control_type);
+    void resetToDefaults(EModes mode);
+    void resetToDefaults();
+    void resetAllToDefaults();
+
+    bool empty() { return mControlsMap.empty(); }
+    void clear();
+
+    bool hasUnsavedChanges() { return mHasUnsavedChanges; }
+    EModes getLoadedMode() { return mLoadedMode; }
+    // todo: conflict search
+
+private:
+    // at the moment these kind of control is not savable, but takes part will take part in conflict resolution
+    void registerTemporaryControl(EControlTypes control_type, EMouseClickType mouse_ind, KEY key, MASK mask, U32 conflict_mask);
+
+    typedef std::map<EControlTypes, LLKeyConflict> control_map_t;
+    void loadFromSettings(const LLViewerKeyboard::KeyMode& keymode, control_map_t *destination);
+    void loadFromSettings(const EModes &load_mode, const std::string &filename, control_map_t *destination);
+    void resetKeyboardBindings();
+    void generatePlaceholders(); //'headers' for ui and non-assignable values
+
+    control_map_t mControlsMap;
+    control_map_t mDefaultsMap;
+    bool mHasUnsavedChanges;
+    EModes mLoadedMode;
+};
+
+
+#endif  // LL_LLKEYCONFLICT_H
diff --git a/indra/newview/llviewerkeyboard.cpp b/indra/newview/llviewerkeyboard.cpp
index a14041717f2..878810e0dd9 100644
--- a/indra/newview/llviewerkeyboard.cpp
+++ b/indra/newview/llviewerkeyboard.cpp
@@ -603,6 +603,12 @@ void start_gesture( EKeystate s )
 	}
 }
 
+void toggle_parcel_media(EKeystate s)
+{
+    bool pause = LLViewerMedia::isAnyMediaPlaying();
+    LLViewerMedia::setAllMediaPaused(pause);
+}
+
 #define REGISTER_KEYBOARD_ACTION(KEY, ACTION) LLREGISTER_STATIC(LLKeyboardActionRegistry, KEY, ACTION);
 REGISTER_KEYBOARD_ACTION("jump", agent_jump);
 REGISTER_KEYBOARD_ACTION("push_down", agent_push_down);
@@ -644,6 +650,7 @@ REGISTER_KEYBOARD_ACTION("edit_avatar_move_backward", edit_avatar_move_backward)
 REGISTER_KEYBOARD_ACTION("stop_moving", stop_moving);
 REGISTER_KEYBOARD_ACTION("start_chat", start_chat);
 REGISTER_KEYBOARD_ACTION("start_gesture", start_gesture);
+REGISTER_KEYBOARD_ACTION("toggle_parcel_media", toggle_parcel_media);
 #undef REGISTER_KEYBOARD_ACTION
 
 LLViewerKeyboard::LLViewerKeyboard()
@@ -805,7 +812,7 @@ BOOL LLViewerKeyboard::bindKey(const S32 mode, const KEY key, const MASK mask, c
 	mBindings[mode][index].mFunction = function;
 
 	if (index == mBindingCount[mode])
-		mBindingCount[mode]++;
+        mBindingCount[mode]++;
 
 	return TRUE;
 }
@@ -816,21 +823,25 @@ LLViewerKeyboard::KeyBinding::KeyBinding()
 	command("command")
 {}
 
-LLViewerKeyboard::KeyMode::KeyMode(EKeyboardMode _mode)
-:	bindings("binding"),
-	mode(_mode)
+LLViewerKeyboard::KeyMode::KeyMode()
+:	bindings("binding")
 {}
 
 LLViewerKeyboard::Keys::Keys()
-:	first_person("first_person", KeyMode(MODE_FIRST_PERSON)),
-	third_person("third_person", KeyMode(MODE_THIRD_PERSON)),
-	edit("edit", KeyMode(MODE_EDIT)),
-	sitting("sitting", KeyMode(MODE_SITTING)),
-	edit_avatar("edit_avatar", KeyMode(MODE_EDIT_AVATAR))
+:	first_person("first_person"),
+	third_person("third_person"),
+	edit("edit"),
+	sitting("sitting"),
+	edit_avatar("edit_avatar")
 {}
 
 S32 LLViewerKeyboard::loadBindingsXML(const std::string& filename)
 {
+	for (S32 i = 0; i < MODE_COUNT; i++)
+	{
+		mBindingCount[i] = 0;
+	}
+
 	S32 binding_count = 0;
 	Keys keys;
 	LLSimpleXUIParser parser;
@@ -838,16 +849,16 @@ S32 LLViewerKeyboard::loadBindingsXML(const std::string& filename)
 	if (parser.readXUI(filename, keys) 
 		&& keys.validateBlock())
 	{
-		binding_count += loadBindingMode(keys.first_person);
-		binding_count += loadBindingMode(keys.third_person);
-		binding_count += loadBindingMode(keys.edit);
-		binding_count += loadBindingMode(keys.sitting);
-		binding_count += loadBindingMode(keys.edit_avatar);
+		binding_count += loadBindingMode(keys.first_person, MODE_FIRST_PERSON);
+		binding_count += loadBindingMode(keys.third_person, MODE_THIRD_PERSON);
+		binding_count += loadBindingMode(keys.edit, MODE_EDIT);
+		binding_count += loadBindingMode(keys.sitting, MODE_SITTING);
+		binding_count += loadBindingMode(keys.edit_avatar, MODE_EDIT_AVATAR);
 	}
 	return binding_count;
 }
 
-S32 LLViewerKeyboard::loadBindingMode(const LLViewerKeyboard::KeyMode& keymode)
+S32 LLViewerKeyboard::loadBindingMode(const LLViewerKeyboard::KeyMode& keymode, S32 mode)
 {
 	S32 binding_count = 0;
 	for (LLInitParam::ParamIterator<KeyBinding>::const_iterator it = keymode.bindings.begin(), 
@@ -855,12 +866,15 @@ S32 LLViewerKeyboard::loadBindingMode(const LLViewerKeyboard::KeyMode& keymode)
 		it != end_it;
 		++it)
 	{
-		KEY key;
-		MASK mask;
-		LLKeyboard::keyFromString(it->key, &key);
-		LLKeyboard::maskFromString(it->mask, &mask);
-		bindKey(keymode.mode, key, mask, it->command);
-		binding_count++;
+        if (!it->key.getValue().empty())
+        {
+            KEY key;
+            MASK mask;
+            LLKeyboard::keyFromString(it->key, &key);
+            LLKeyboard::maskFromString(it->mask, &mask);
+            bindKey(mode, key, mask, it->command);
+            binding_count++;
+        }
 	}
 
 	return binding_count;
diff --git a/indra/newview/llviewerkeyboard.h b/indra/newview/llviewerkeyboard.h
index 110dc89d289..2bfe285be4c 100644
--- a/indra/newview/llviewerkeyboard.h
+++ b/indra/newview/llviewerkeyboard.h
@@ -71,8 +71,8 @@ class LLViewerKeyboard
 	struct KeyMode : public LLInitParam::Block<KeyMode>
 	{
 		Multiple<KeyBinding>		bindings;
-		EKeyboardMode				mode;
-		KeyMode(EKeyboardMode mode);
+
+		KeyMode();
 	};
 
 	struct Keys : public LLInitParam::Block<Keys>
@@ -100,7 +100,7 @@ class LLViewerKeyboard
 	void			scanKey(KEY key, BOOL key_down, BOOL key_up, BOOL key_level);
 
 private:
-	S32				loadBindingMode(const LLViewerKeyboard::KeyMode& keymode);
+	S32				loadBindingMode(const LLViewerKeyboard::KeyMode& keymode, S32 mode);
 	BOOL			bindKey(const S32 mode, const KEY key, const MASK mask, const std::string& function_name);
 
 	// Hold all the ugly stuff torn out to make LLKeyboard non-viewer-specific here
diff --git a/indra/newview/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp
index 676f06bcb95..fcca081647e 100644
--- a/indra/newview/llvoiceclient.cpp
+++ b/indra/newview/llvoiceclient.cpp
@@ -35,7 +35,6 @@
 #include "llnotificationsutil.h"
 #include "llsdserialize.h"
 #include "llui.h"
-#include "llkeybindings.h"
 #include "llkeyboard.h"
 #include "llagent.h"
 
@@ -691,33 +690,38 @@ void LLVoiceClient::keyDown(KEY key, MASK mask)
 		return;
 	}
 	
-	if (LLAgent::isActionAllowed("speak") && gControlBindings.canHandleKey(LLControlBindings::CONTROL_VOICE, key, mask))
+    //
+	/*static LLCachedControl<LLSD> key_bind(gSavedSettings, "control_toggle_voice");
+    LLKeyBind bind(key_bind);
+	if (LLAgent::isActionAllowed("speak") && bind().canHandleKey(key, mask))
 	{
 		bool down = gKeyboard->getKeyDown(mPTTKey);
 		if (down)
 		{
 			inputUserControlState(down);
 		}
-	}
+	}*/
 	
 }
 void LLVoiceClient::keyUp(KEY key, MASK mask)
 {
-	if (gControlBindings.canHandleKey(LLControlBindings::CONTROL_VOICE, key, mask))
+	/*static LLCachedControl<LLKeyBind> key_bind(gSavedSettings, "control_toggle_voice");
+	if (key_bind().canHandleKey(key, mask))
 	{
 		bool down = gKeyboard->getKeyDown(mPTTKey);
 		if (!down)
 		{
 			inputUserControlState(down);
 		}
-	}
+	}*/
 }
 void LLVoiceClient::updateMouseState(S32 click, MASK mask, bool down)
 {
-	if(LLAgent::isActionAllowed("speak") && gControlBindings.canHandleMouse(LLControlBindings::CONTROL_VOICE, click, mask))
+	/*static LLCachedControl<LLKeyBind> mouse_bind(gSavedSettings, "control_toggle_voice");
+	if (mouse_bind().canHandleMouse((EMouseClickType)click, mask))
 	{
 		inputUserControlState(down);
-	}
+	}*/
 }
 
 
diff --git a/indra/newview/skins/default/xui/en/floater_select_key.xml b/indra/newview/skins/default/xui/en/floater_select_key.xml
index 4e89df5a735..c00b8059542 100644
--- a/indra/newview/skins/default/xui/en/floater_select_key.xml
+++ b/indra/newview/skins/default/xui/en/floater_select_key.xml
@@ -7,7 +7,15 @@
  height="90"
  layout="topleft"
  name="modal container"
- width="240">
+ width="272">
+  <floater.string
+   name="keyboard">
+    Keyboard
+  </floater.string>
+  <floater.string
+   name="mouse">
+    Mouse Buttons
+  </floater.string>
     <text
      type="string"
      halign="center"
@@ -16,19 +24,35 @@
      height="30"
      layout="topleft"
      left="30"
-     name="Save item as:"
+     name="descritption"
      top="25"
      word_wrap="true"
-     width="180">
-        Press a key to set your Speak button trigger.
+     width="212">
+Press a key to set your trigger.
+Allowed input: [INPUT].
     </text>
+    <button
+     height="23"
+     label="Set Empty"
+     layout="topleft"
+     left="8"
+     name="SetEmpty"
+     top_pad="8"
+     width="80" />
+    <button
+     height="23"
+     label="Default"
+     layout="topleft"
+     left_pad="8"
+     name="Default"
+     top_delta="0"
+     width="80" />
     <button
      height="23"
      label="Cancel"
-     label_selected="Cancel"
      layout="topleft"
-     right="-10"
+     left_pad="8"
      name="Cancel"
-     top_pad="8"
-     width="100" />
+     top_delta="0"
+     width="80" />
 </floater>
diff --git a/indra/newview/skins/default/xui/en/panel_preferences_controls.xml b/indra/newview/skins/default/xui/en/panel_preferences_controls.xml
index a1a2fd0598d..1bfee20c81e 100644
--- a/indra/newview/skins/default/xui/en/panel_preferences_controls.xml
+++ b/indra/newview/skins/default/xui/en/panel_preferences_controls.xml
@@ -82,37 +82,37 @@
     Teleport To
   </panel.string>
   <panel.string
-   name="control_forward">
+   name="push_forward">
     Move Forward
   </panel.string>
   <panel.string
-   name="control_backward">
+   name="push_backward">
     Move Backward
   </panel.string>
   <panel.string
-   name="control_left">
+   name="turn_left">
     Left
   </panel.string>
   <panel.string
-   name="control_right">
+   name="turn_right">
     Right
   </panel.string>
   <!--(check with move floater)-->
   <panel.string
-   name="control_lstrafe">
+   name="slide_left">
     Strafe left
   </panel.string>
   <panel.string
-   name="control_rstrafe">
+   name="slide_right">
     Strafe right
   </panel.string>
   <panel.string
-   name="control_jump">
-    Strafe right
+   name="jump">
+    Jump/Up
   </panel.string>
   <panel.string
-   name="control_down">
-    Strafe right
+   name="push_down">
+    Down
   </panel.string>
   <panel.string
    name="control_run">
@@ -123,9 +123,85 @@
     Toggle Run
   </panel.string>
   <panel.string
-   name="control_fly">
+   name="toggle_fly">
     Fly/Stop flying
   </panel.string>
+  <panel.string
+   name="control_camera">
+    Camera
+  </panel.string>
+  <panel.string
+   name="look_up">
+    Look Up
+  </panel.string>
+  <panel.string
+   name="look_down">
+    Look Down
+  </panel.string>
+  <panel.string
+   name="move_forward">
+    Camera Forward
+  </panel.string>
+  <panel.string
+   name="move_backward">
+    Camera Backward
+  </panel.string>
+  <panel.string
+   name="move_forward_fast">
+    Camera Forward Fast
+  </panel.string>
+  <panel.string
+   name="move_backward_fast">
+    Camera Backward Fast
+  </panel.string>
+  <panel.string
+   name="move_forward_sitting">
+    Camera Forward Sitting
+  </panel.string>
+  <panel.string
+   name="move_backward_sitting">
+    Camera Backward Sitting
+  </panel.string>
+  <panel.string
+   name="spin_over">
+    Camera Spin Over
+  </panel.string>
+  <panel.string
+   name="spin_under">
+    Camera Spin Under
+  </panel.string>
+  <panel.string
+   name="spin_over_sitting">
+    Camera Spin Over
+  </panel.string>
+  <panel.string
+   name="spin_under_sitting">
+    Camera Spin Under
+  </panel.string>
+  <panel.string
+   name="pan_up">
+    Camera Pan Up
+  </panel.string>
+  <panel.string
+   name="pan_down">
+    Camera Pan Down
+  </panel.string>
+  <panel.string
+   name="pan_left">
+    Camera Pan Left
+  </panel.string>
+  <panel.string
+   name="pan_right">
+    Camera Pan Right
+  </panel.string>
+  <panel.string
+   name="pan_left">
+    Camera Pan In
+  </panel.string>
+  <panel.string
+   name="pan_right">
+    Camera Pan Out
+  </panel.string>
   <panel.string
    name="control_mediacontent">
     Sound and Media
@@ -150,6 +226,10 @@
    name="control_reserved">
     Reserved Controls
   </panel.string>
+  <panel.string
+   name="control_delete">
+    Delete
+  </panel.string>
   <!--
    name="control_menu" not needed
    -->
@@ -166,15 +246,60 @@
     Add to Selection
   </panel.string>
 
+  <combo_box
+   follows="top|left"
+   layout="topleft"
+   top="6"
+   left="10"
+   height="23"
+   width="110"
+   name="key_mode">
+    <combo_box.item
+     label="First Person "
+     name="first_person"
+     value="0"/>
+    <combo_box.item
+     label="Third Person "
+     name="third_person"
+     value="1"/>
+    <combo_box.item
+     label="Edit"
+     name="edit"
+     value="2"/>
+    <combo_box.item
+     label="Edit Avatar"
+     name="edit_avatar"
+     value="3"/>
+    <combo_box.item
+     label="Sitting"
+     name="sitting"
+     value="4"/>
+    <combo_box.item
+     label="General"
+     name="general"
+     value="5"/>
+  </combo_box>
+
+  <button
+   follows="top|left"
+   layout="topleft"
+   top="6"
+   right="-10"
+   height="23"
+   width="110"
+   label="Restore Default"
+   name="restore_defaults"/>
+  
   <scroll_list
    draw_heading="true"
    follows="all"
    layout="topleft"
    column_padding="0"
-   top="3"
+   top="31"
    left="3"
    bottom="-3"
    right="-3"
+   can_sort="false"
    multi_select="false"
    name="controls_list">
     <scroll_list.columns
@@ -182,13 +307,17 @@
      label="Action"
      name="lst_action" />
     <scroll_list.columns
-     relative_width="0.33"
-     label="Primary Control Method"
+     relative_width="0.22"
+     label="Control Method 1"
      name="lst_ctrl1" />
     <scroll_list.columns
-     relative_width="0.33"
-     label="Secondary Control Method"
+     relative_width="0.22"
+     label="Control Method 2"
      name="lst_ctrl2" />
+    <scroll_list.columns
+     relative_width="0.22"
+     label="Control Method 3"
+     name="lst_ctrl3" />
     <scroll_list.commit_callback
       function="Pref.CommitControl" />
   </scroll_list>
-- 
GitLab