From a8e56e67c3545cf857f80e81b3e98a278224f421 Mon Sep 17 00:00:00 2001
From: andreykproductengine <andreykproductengine@lindenlab.com>
Date: Fri, 27 Sep 2019 19:53:33 +0300
Subject: [PATCH] SL-6109 Cleanup, run commands, and preparation for tooltip
 support

---
 indra/newview/llfloaterpreference.cpp         | 369 +++++++++------
 indra/newview/llfloaterpreference.h           |   4 +-
 indra/newview/llkeyconflict.cpp               | 426 ++++++------------
 indra/newview/llkeyconflict.h                 | 117 +----
 indra/newview/llviewerinput.cpp               |  76 ++++
 indra/newview/llviewerwindow.cpp              |   2 +
 .../xui/en/panel_preferences_controls.xml     |  55 +--
 7 files changed, 510 insertions(+), 539 deletions(-)

diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp
index c0e334795ab..088a0820aa8 100644
--- a/indra/newview/llfloaterpreference.cpp
+++ b/indra/newview/llfloaterpreference.cpp
@@ -185,18 +185,53 @@ class LLSetKeyBindDialog : public LLModalDialog
 	static void onCancel(void* user_data);
 	static void onBlank(void* user_data);
 	static void onDefault(void* user_data);
+	static void onClickTimeout(void* user_data, MASK mask);
+
+	class Updater;
 
 private:
+	void setKeyBind(EMouseClickType click, KEY key, MASK mask, bool ignore);
 	LLPanelPreferenceControls* pParent;
 	LLCheckBoxCtrl* pCheckBox;
 
-	U32 mKeyMask;
+	U32 mKeyFilterMask;
+	Updater *pUpdater;
+};
+
+class LLSetKeyBindDialog::Updater : public LLEventTimer
+{
+public:
+
+    typedef boost::function<void(MASK)> callback_t;
+
+    Updater(callback_t cb, F32 period, MASK mask)
+        :LLEventTimer(period),
+        mMask(mask),
+        mCallback(cb)
+    {
+        mEventTimer.start();
+    }
+
+    virtual ~Updater(){}
+
+protected:
+    BOOL tick()
+    {
+        mCallback(mMask);
+        // Deletes itseft after execution
+        return TRUE;
+    }
+
+private:
+    MASK mMask;
+    callback_t mCallback;
 };
 
 LLSetKeyBindDialog::LLSetKeyBindDialog(const LLSD& key)
   : LLModalDialog(key),
 	pParent(NULL),
-	mKeyMask(DEFAULT_KEY_FILTER)
+	mKeyFilterMask(DEFAULT_KEY_FILTER),
+	pUpdater(NULL)
 {
 }
 
@@ -227,13 +262,19 @@ void LLSetKeyBindDialog::onClose(bool app_quiting)
         pParent->onCancelKeyBind();
         pParent = NULL;
     }
+    if (pUpdater)
+    {
+        // Doubleclick timer has't fired, delete it
+        delete pUpdater;
+        pUpdater = NULL;
+    }
     LLModalDialog::onClose(app_quiting);
 }
 
 void LLSetKeyBindDialog::setParent(LLPanelPreferenceControls* parent, U32 key_mask)
 {
     pParent = parent;
-    mKeyMask = key_mask;
+    mKeyFilterMask = key_mask;
 
     LLTextBase *text_ctrl = getChild<LLTextBase>("descritption");
 
@@ -259,24 +300,18 @@ void LLSetKeyBindDialog::setParent(LLPanelPreferenceControls* parent, U32 key_ma
 
 BOOL LLSetKeyBindDialog::handleKeyHere(KEY key, MASK mask)
 {
-	BOOL result = TRUE;
-
     if ((key == 'Q' && mask == MASK_CONTROL)
         || key == KEY_ESCAPE)
     {
         closeFloater();
-        return true;
+        return TRUE;
     }
 
     if (key == KEY_DELETE)
     {
-        if (pParent)
-        {
-            pParent->onSetKeyBind(CLICK_NONE, KEY_NONE, MASK_NONE, false);
-            pParent = NULL;
-        }
+        setKeyBind(CLICK_NONE, KEY_NONE, MASK_NONE, false);
         closeFloater();
-        return true;
+        return FALSE;
     }
 
     // forbidden keys
@@ -284,40 +319,41 @@ BOOL LLSetKeyBindDialog::handleKeyHere(KEY key, MASK mask)
         || key == KEY_RETURN
         || key == KEY_BACKSPACE)
     {
-        return false;
+        return FALSE;
     }
 
-    if ((mKeyMask & ALLOW_MASKS) == 0
+    if ((mKeyFilterMask & ALLOW_MASKS) == 0
         && (key == KEY_CONTROL || key == KEY_SHIFT || key == KEY_ALT))
     {
         // mask by themself are not allowed
-        return false;
+        return FALSE;
     }
-    else if ((mKeyMask & ALLOW_KEYS) == 0)
+    else if ((mKeyFilterMask & ALLOW_KEYS) == 0)
     {
         // basic keys not allowed
-        return false;
+        return FALSE;
     }
-    else if ((mKeyMask & ALLOW_MASK_KEYS) == 0 && mask != 0)
+    else if ((mKeyFilterMask & ALLOW_MASK_KEYS) == 0 && mask != 0)
     {
         // masked keys not allowed
-        return false;
+        return FALSE;
     }
 
-	if (pParent)
-	{
-        pParent->onSetKeyBind(CLICK_NONE, key, mask, pCheckBox->getValue().asBoolean());
-        pParent = NULL;
-	}
-	closeFloater();
-	return result;
+    setKeyBind(CLICK_NONE, key, mask, pCheckBox->getValue().asBoolean());
+    closeFloater();
+    return TRUE;
 }
 
 BOOL LLSetKeyBindDialog::handleAnyMouseClick(S32 x, S32 y, MASK mask, EMouseClickType clicktype, BOOL down)
 {
     BOOL result = FALSE;
-
-    if (clicktype == CLICK_LEFT)
+    if (!pParent)
+    {
+        // we already processed 'down' event, this is 'up', consume
+        closeFloater();
+        result = TRUE;
+    }
+    if (!result && clicktype == CLICK_LEFT)
     {
         // try handling buttons first
         if (down)
@@ -328,25 +364,38 @@ BOOL LLSetKeyBindDialog::handleAnyMouseClick(S32 x, S32 y, MASK mask, EMouseClic
         {
             result = LLView::handleMouseUp(x, y, mask);
         }
+        if (result)
+        {
+            setFocus(TRUE);
+            gFocusMgr.setKeystrokesOnly(TRUE);
+        }
+        // ignore selection related combinations
+        else if (down && (mask & (MASK_SHIFT | MASK_CONTROL)) == 0)
+        {
+            // this can be a double click, wait a bit;
+            if (!pUpdater)
+            {
+                // Note: default doubleclick time is 500ms, but can stretch up to 5s
+                pUpdater = new Updater(boost::bind(&onClickTimeout, this, _1), 0.7f, mask);
+                result = TRUE;
+            }
+        }
     }
 
     if (!result
-        && ((mKeyMask & ALLOW_MOUSE) != 0)
+        && (clicktype != CLICK_LEFT) // subcases were handled above
+        && ((mKeyFilterMask & ALLOW_MOUSE) != 0)
         && (clicktype != CLICK_RIGHT || mask != 0) // reassigning menu button is not supported
-        && ((mKeyMask & ALLOW_MASK_MOUSE) != 0 || mask == 0))
+        && ((mKeyFilterMask & ALLOW_MASK_MOUSE) != 0 || mask == 0)) // reserved for selection
     {
-        if (pParent)
+        setKeyBind(clicktype, KEY_NONE, mask, pCheckBox->getValue().asBoolean());
+        result = TRUE;
+        if (!down)
         {
-            pParent->onSetKeyBind(clicktype, KEY_NONE, mask, pCheckBox->getValue().asBoolean());
-            pParent = NULL;
+            // wait for 'up' event before closing
+            // alternative: set pUpdater
+            closeFloater();
         }
-        result = TRUE;
-        closeFloater();
-    }
-    
-    if (!result)
-    {
-        result = LLMouseHandler::handleAnyMouseClick(x, y, mask, clicktype, down);
     }
 
     return result;
@@ -356,7 +405,7 @@ BOOL LLSetKeyBindDialog::handleAnyMouseClick(S32 x, S32 y, MASK mask, EMouseClic
 void LLSetKeyBindDialog::onCancel(void* user_data)
 {
     LLSetKeyBindDialog* self = (LLSetKeyBindDialog*)user_data;
-	self->closeFloater();
+    self->closeFloater();
 }
 
 //static
@@ -364,11 +413,7 @@ void LLSetKeyBindDialog::onBlank(void* user_data)
 {
     LLSetKeyBindDialog* self = (LLSetKeyBindDialog*)user_data;
     // tmp needs 'no key' button
-    if (self->pParent)
-    {
-        self->pParent->onSetKeyBind(CLICK_NONE, KEY_NONE, MASK_NONE, false);
-        self->pParent = NULL;
-    }
+    self->setKeyBind(CLICK_NONE, KEY_NONE, MASK_NONE, false);
     self->closeFloater();
 }
 
@@ -376,7 +421,6 @@ void LLSetKeyBindDialog::onBlank(void* user_data)
 void LLSetKeyBindDialog::onDefault(void* user_data)
 {
     LLSetKeyBindDialog* self = (LLSetKeyBindDialog*)user_data;
-    // tmp needs 'no key' button
     if (self->pParent)
     {
         self->pParent->onDefaultKeyBind();
@@ -385,6 +429,27 @@ void LLSetKeyBindDialog::onDefault(void* user_data)
     self->closeFloater();
 }
 
+//static
+void LLSetKeyBindDialog::onClickTimeout(void* user_data, MASK mask)
+{
+    LLSetKeyBindDialog* self = (LLSetKeyBindDialog*)user_data;
+
+    // timer will delete itself after timeout
+    self->pUpdater = NULL;
+
+    self->setKeyBind(CLICK_LEFT, KEY_NONE, mask, self->pCheckBox->getValue().asBoolean());
+    self->closeFloater();
+}
+
+void LLSetKeyBindDialog::setKeyBind(EMouseClickType click, KEY key, MASK mask, bool ignore)
+{
+    if (pParent)
+    {
+        pParent->onSetKeyBind(click, key, mask, ignore);
+        pParent = NULL;
+    }
+}
+
 
 // global functions 
 
@@ -2972,9 +3037,72 @@ void LLPanelPreferenceGraphics::setHardwareDefaults()
 //------------------------LLPanelPreferenceControls--------------------------------
 static LLPanelInjector<LLPanelPreferenceControls> t_pref_contrls("panel_preference_controls");
 
+//name of control and name of icon if it is a group, likely 'TEMP' until xml gets properly populated
+typedef std::vector<std::pair<std::string, std::string> > controls_to_icon_t;
+static const controls_to_icon_t commands_and_headers =
+{
+    //{ "control_view_actions", "Search_Icon" },
+    //{ "control_interactions", "Command_Gestures_Icon" },
+    { "control_movements", "Move_Walk_Off" },
+    { "walk_to", "" },
+    { "teleport_to", "" },
+    { "push_forward", "" },
+    { "push_backward", "" },
+    { "turn_left", "" },
+    { "turn_right", "" },
+    { "slide_left", "" },
+    { "slide_right", "" },
+    { "jump", "" },
+    { "push_down", "" },
+    { "run_forward", "" },
+    { "run_backward", "" },
+    { "run_left", "" },
+    { "run_right", "" },
+    { "toggle_run", "" },
+    { "toggle_fly", "" },
+    { "toggle_sit", "" },
+    { "stop_moving", "" },
+    { "control_camera", "Cam_FreeCam_Off" },
+    { "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", "Tool_Dozer" },
+    { "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", "Audio_Press" },
+    { "toggle_pause_media", "" },
+    { "toggle_enable_media", "" },
+    { "voice_follow_key", "" },
+    { "toggle_voice", "" },
+    { "start_chat", "" },
+    { "start_gesture", "" },
+};
+
 LLPanelPreferenceControls::LLPanelPreferenceControls()
     :LLPanelPreference(),
-    mEditingIndex(-1),
     mEditingColumn(-1),
     mEditingMode(0),
     mShowKeyDialog(false),
@@ -3013,8 +3141,8 @@ BOOL LLPanelPreferenceControls::handleHover(S32 x, S32 y, MASK mask)
     if (mShowKeyDialog)
     {
         mShowKeyDialog = false;
-        if (mEditingIndex > 0
-            && mConflictHandler[mEditingMode].canAssignControl((LLKeyConflictHandler::EControlTypes)mEditingIndex))
+        if (!mEditingControl.empty()
+            && mConflictHandler[mEditingMode].canAssignControl(mEditingControl))
         {
             LLScrollListItem* item = pControlsTable->getFirstSelected(); // don't use pControlsTable->hitItem(x, y) dur to drift;
             if (item)
@@ -3042,10 +3170,10 @@ BOOL LLPanelPreferenceControls::handleHover(S32 x, S32 y, MASK mask)
     return LLPanelPreference::handleHover(x, y, mask);
 }
 
-void LLPanelPreferenceControls::addGroupRow(const std::string &icon, S32 index)
+void LLPanelPreferenceControls::addGroupRow(const std::string &control_name, const std::string &icon)
 {
     LLScrollListItem::Params item_params;
-    item_params.value = LLSD::Integer(-1);
+    item_params.value = "";
 
     LLScrollListCell::Params icon_cell_params;
     icon_cell_params.font = LLFontGL::getFontSansSerif();
@@ -3057,7 +3185,6 @@ void LLPanelPreferenceControls::addGroupRow(const std::string &icon, S32 index)
     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))
     {
@@ -3110,76 +3237,48 @@ void LLPanelPreferenceControls::populateControlTable()
     cell_params.column = "";
     cell_params.value = label;
 
-    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++)
+    controls_to_icon_t::const_iterator iter = commands_and_headers.begin();
+    controls_to_icon_t::const_iterator end = commands_and_headers.end();
+    for (; iter != end; ++iter)
     {
-        LLKeyConflictHandler::EControlTypes type = (LLKeyConflictHandler::EControlTypes)i;
-        switch (type)
+        if (iter->second.empty())
         {
-        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:
+            // general control
+            LLScrollListItem::Params item_params;
+            item_params.value = LLSD(iter->first);
+
+            cell_params.column = "lst_action";
+            bool enabled = mConflictHandler[mEditingMode].canAssignControl(iter->first);
+            if (hasString(iter->first))
             {
-                //default insert
-                LLScrollListItem::Params item_params;
-                item_params.value = LLSD::Integer(i);
-
-                cell_params.column = "lst_action";
-                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 = mConflictHandler[mEditingMode].getControlString(type, 0);
-                cell_params.enabled = enabled;
-                item_params.columns.add(cell_params);
-                cell_params.column = "lst_ctrl2";
-                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);
-                break;
+                label = getString(iter->first);
             }
+            else
+            {
+                label = iter->first;
+            }
+            cell_params.value = label;
+            item_params.columns.add(cell_params);
+            cell_params.column = "lst_ctrl1";
+            cell_params.value = mConflictHandler[mEditingMode].getControlString(iter->first, 0);
+            cell_params.enabled = enabled;
+            item_params.columns.add(cell_params);
+            cell_params.column = "lst_ctrl2";
+            cell_params.value = mConflictHandler[mEditingMode].getControlString(iter->first, 1);
+            cell_params.enabled = enabled;
+            item_params.columns.add(cell_params);
+            cell_params.column = "lst_ctrl3";
+            cell_params.value = mConflictHandler[mEditingMode].getControlString(iter->first, 2);
+            cell_params.enabled = enabled;
+            item_params.columns.add(cell_params);
+
+            pControlsTable->addRow(item_params, EAddPosition::ADD_BOTTOM);
+        }
+        else
+        {
+            // header
+            addSeparator();
+            addGroupRow(iter->first, iter->second);
         }
     }
 }
@@ -3195,13 +3294,13 @@ void LLPanelPreferenceControls::addSeparator()
 
 void LLPanelPreferenceControls::updateTable()
 {
+    mEditingControl.clear();
     std::vector<LLScrollListItem*> list = pControlsTable->getAllData();
     for (S32 i = 0; i < list.size(); ++i)
     {
-        S32 value = list[i]->getValue().asInteger();
-        if (value > 0)
+        std::string control = list[i]->getValue();
+        if (!control.empty())
         {
-            LLKeyConflictHandler::EControlTypes control = (LLKeyConflictHandler::EControlTypes)value;
             LLScrollListCell* cell = list[i]->getColumn(1);
             cell->setValue(mConflictHandler[mEditingMode].getControlString(control, 0));
             cell = list[i]->getColumn(2);
@@ -3258,6 +3357,7 @@ void LLPanelPreferenceControls::saveSettings()
         if (mConflictHandler[i].hasUnsavedChanges())
         {
             mConflictHandler[i].saveToSettings();
+            mConflictHandler[i].clear();
         }
     }
 
@@ -3280,20 +3380,21 @@ void LLPanelPreferenceControls::resetDirtyChilds()
 
 void LLPanelPreferenceControls::onListCommit()
 {
+    mShowKeyDialog = false;
     LLScrollListItem* item = pControlsTable->getFirstSelected();
     if (item == NULL)
     {
         return;
     }
 
-    S32 control = item->getValue().asInteger();
+    std::string control = item->getValue();
 
-    if (control <= 0)
+    if (control.empty())
     {
         return;
     }
 
-    if (!mConflictHandler[mEditingMode].canAssignControl((LLKeyConflictHandler::EControlTypes)control))
+    if (!mConflictHandler[mEditingMode].canAssignControl(control))
     {
         return;
     }
@@ -3302,7 +3403,7 @@ void LLPanelPreferenceControls::onListCommit()
     // 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;
+    mEditingControl = control;
     mShowKeyDialog = true;
 
     if (mHighlightedCell)
@@ -3320,18 +3421,16 @@ void LLPanelPreferenceControls::onModeCommit()
 // todo: copy onSetKeyBind to interface and inherit from interface
 void LLPanelPreferenceControls::onSetKeyBind(EMouseClickType click, KEY key, MASK mask, bool ignore_mask)
 {
-    LLKeyConflictHandler::EControlTypes control = (LLKeyConflictHandler::EControlTypes)mEditingIndex;
-
-    if (!mConflictHandler[mEditingMode].canAssignControl(control))
+    if (!mConflictHandler[mEditingMode].canAssignControl(mEditingControl))
     {
         return;
     }
 
     pControlsTable->deselectAllItems();
-    pControlsTable->selectByValue(mEditingIndex);
+    pControlsTable->selectByValue(mEditingControl);
     if ( mEditingColumn > 0)
     {
-        mConflictHandler[mEditingMode].registerControl(control, mEditingColumn - 1, click, key, mask, ignore_mask);
+        mConflictHandler[mEditingMode].registerControl(mEditingControl, mEditingColumn - 1, click, key, mask, ignore_mask);
     }
 
     updateTable();
@@ -3347,19 +3446,17 @@ void LLPanelPreferenceControls::onRestoreDefaults()
 
 void LLPanelPreferenceControls::onDefaultKeyBind()
 {
-    LLKeyConflictHandler::EControlTypes control = (LLKeyConflictHandler::EControlTypes)mEditingIndex;
-
-    if (!mConflictHandler[mEditingMode].canAssignControl(control))
+    if (!mConflictHandler[mEditingMode].canAssignControl(mEditingControl))
     {
         return;
     }
 
     pControlsTable->deselectAllItems();
-    pControlsTable->selectByValue(mEditingIndex);
+    pControlsTable->selectByValue(mEditingControl);
 
     if (mEditingColumn > 0)
     {
-        mConflictHandler[mEditingMode].resetToDefault(control, mEditingColumn - 1);
+        mConflictHandler[mEditingMode].resetToDefault(mEditingControl, mEditingColumn - 1);
     }
     updateTable();
 }
diff --git a/indra/newview/llfloaterpreference.h b/indra/newview/llfloaterpreference.h
index 513fda96dfa..2fe3b98abd5 100644
--- a/indra/newview/llfloaterpreference.h
+++ b/indra/newview/llfloaterpreference.h
@@ -317,7 +317,7 @@ class LLPanelPreferenceControls : public LLPanelPreference
 	void onCancelKeyBind();
 
 private:
-	void addGroupRow(const std::string &icon, S32 index);
+	void addGroupRow(const std::string &control_name, const std::string &icon);
 	void regenerateControls();
 	void populateControlTable();
 	void addSeparator();
@@ -327,7 +327,7 @@ class LLPanelPreferenceControls : public LLPanelPreference
 	LLComboBox *pKeyModeBox;
 	LLScrollListCell *mHighlightedCell;
 	LLKeyConflictHandler mConflictHandler[LLKeyConflictHandler::MODE_COUNT];
-	S32 mEditingIndex;
+	std::string mEditingControl;
 	S32 mEditingColumn;
 	S32 mEditingMode;
 	bool mShowKeyDialog;
diff --git a/indra/newview/llkeyconflict.cpp b/indra/newview/llkeyconflict.cpp
index e81d121e3bb..c74eea6e478 100644
--- a/indra/newview/llkeyconflict.cpp
+++ b/indra/newview/llkeyconflict.cpp
@@ -41,135 +41,7 @@
 #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",
-    "walk_to",
-    "teleport_to",
-    "push_forward",
-    "push_backward",
-    "turn_left",
-    "turn_right",
-    "slide_left",
-    "slide_right",
-    "jump",
-    "push_down",
-    //"control_run",
-    "toggle_run",
-    "toggle_fly",
-    "toggle_sit",
-    "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",
-    "toggle_pause_media",
-    "toggle_enable_media",
-    "voice_follow_key",
-    "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 llviewerinput
-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 },
-    { "toggle_run", LLKeyConflictHandler::CONTROL_TOGGLE_RUN },
-    { "toggle_sit", LLKeyConflictHandler::CONTROL_SIT },
-    { "toggle_parcel_media", LLKeyConflictHandler::CONTROL_PAUSE_MEDIA },
-    { "toggle_enable_media", LLKeyConflictHandler::CONTROL_ENABLE_MEDIA },
-    { "walk_to", LLKeyConflictHandler::CONTROL_MOVETO },
-    { "teleport_to", LLKeyConflictHandler::CONTROL_TELEPORTTO },
-    { "toggle_voice", LLKeyConflictHandler::CONTROL_TOGGLE_VOICE },
-    { "voice_follow_key", LLKeyConflictHandler::CONTROL_VOICE },
-};
+static const std::string saved_settings_key_controls[] = { "placeholder" };
 
 
 // LLKeyboard::stringFromMask is meant for UI and is OS dependent,
@@ -267,29 +139,29 @@ LLKeyConflictHandler::LLKeyConflictHandler(ESourceMode mode)
     loadFromSettings(mode);
 }
 
-bool LLKeyConflictHandler::canHandleControl(LLKeyConflictHandler::EControlTypes control_type, EMouseClickType mouse_ind, KEY key, MASK mask)
+bool LLKeyConflictHandler::canHandleControl(const std::string &control_name, EMouseClickType mouse_ind, KEY key, MASK mask)
 {
-    return mControlsMap[control_type].canHandle(mouse_ind, key, mask);
+    return mControlsMap[control_name].canHandle(mouse_ind, key, mask);
 }
 
-bool LLKeyConflictHandler::canHandleKey(EControlTypes control_type, KEY key, MASK mask)
+bool LLKeyConflictHandler::canHandleKey(const std::string &control_name, KEY key, MASK mask)
 {
-    return canHandleControl(control_type, CLICK_NONE, key, mask);
+    return canHandleControl(control_name, CLICK_NONE, key, mask);
 }
 
-bool LLKeyConflictHandler::canHandleMouse(LLKeyConflictHandler::EControlTypes control_type, EMouseClickType mouse_ind, MASK mask)
+bool LLKeyConflictHandler::canHandleMouse(const std::string &control_name, EMouseClickType mouse_ind, MASK mask)
 {
-    return canHandleControl(control_type, mouse_ind, KEY_NONE, mask);
+    return canHandleControl(control_name, mouse_ind, KEY_NONE, mask);
 }
 
-bool LLKeyConflictHandler::canHandleMouse(EControlTypes control_type, S32 mouse_ind, MASK mask)
+bool LLKeyConflictHandler::canHandleMouse(const std::string &control_name, S32 mouse_ind, MASK mask)
 {
-    return canHandleControl(control_type, (EMouseClickType)mouse_ind, KEY_NONE, mask);
+    return canHandleControl(control_name, (EMouseClickType)mouse_ind, KEY_NONE, mask);
 }
 
-bool LLKeyConflictHandler::canAssignControl(EControlTypes control_type)
+bool LLKeyConflictHandler::canAssignControl(const std::string &control_name)
 {
-    std::map<EControlTypes, LLKeyConflict>::iterator iter = mControlsMap.find(control_type);
+    control_map_t::iterator iter = mControlsMap.find(control_name);
     if (iter != mControlsMap.end())
     {
         return iter->second.mAssignable;
@@ -297,9 +169,13 @@ bool LLKeyConflictHandler::canAssignControl(EControlTypes control_type)
     return false;
 }
 
-bool LLKeyConflictHandler::registerControl(EControlTypes control_type, U32 index, EMouseClickType mouse, KEY key, MASK mask, bool ignore_mask)
+bool LLKeyConflictHandler::registerControl(const std::string &control_name, U32 index, EMouseClickType mouse, KEY key, MASK mask, bool ignore_mask)
 {
-    LLKeyConflict &type_data = mControlsMap[control_type];
+    if (control_name.empty())
+    {
+        return false;
+    }
+    LLKeyConflict &type_data = mControlsMap[control_name];
     if (!type_data.mAssignable)
     {
         LL_ERRS() << "Error in code, user or system should not be able to change certain controls" << LL_ENDL;
@@ -319,9 +195,13 @@ bool LLKeyConflictHandler::registerControl(EControlTypes control_type, U32 index
     return false;
 }
 
-LLKeyData LLKeyConflictHandler::getControl(EControlTypes control_type, U32 index)
+LLKeyData LLKeyConflictHandler::getControl(const std::string &control_name, U32 index)
 {
-    return mControlsMap[control_type].getKeyData(index);
+    if (control_name.empty())
+    {
+        return LLKeyData();
+    }
+    return mControlsMap[control_name].getKeyData(index);
 }
 
 // static
@@ -341,24 +221,37 @@ std::string LLKeyConflictHandler::getStringFromKeyData(const LLKeyData& keydata)
     {
         result = LLKeyboard::stringFromAccelerator(keydata.mMask);
     }
+    else if (keydata.mIgnoreMasks)
+    {
+        result = "acc+";
+    }
 
     result += string_from_mouse(keydata.mMouse);
 
     return result;
 }
 
-// static
-std::string LLKeyConflictHandler::getControlName(EControlTypes control_type)
+std::string LLKeyConflictHandler::getControlString(const std::string &control_name, U32 index)
 {
-    return typetostring[control_type];
+    if (control_name.empty())
+    {
+        return "";
+    }
+    return getStringFromKeyData(mControlsMap[control_name].getKeyData(index));
 }
 
-std::string LLKeyConflictHandler::getControlString(EControlTypes control_type, U32 index)
+void LLKeyConflictHandler::loadFromControlSettings(const std::string &name)
 {
-    return getStringFromKeyData(mControlsMap[control_type].getKeyData(index));
+    LLControlVariablePtr var = gSavedSettings.getControl(name);
+    if (var)
+    {
+        LLKeyBind bind(var->getValue());
+        LLKeyConflict key(bind, true, 0);
+        mControlsMap[name] = key;
+    }
 }
 
-void  LLKeyConflictHandler::loadFromSettings(const LLViewerInput::KeyMode& keymode, control_map_t *destination)
+void LLKeyConflictHandler::loadFromSettings(const LLViewerInput::KeyMode& keymode, control_map_t *destination)
 {
     for (LLInitParam::ParamIterator<LLViewerInput::KeyBinding>::const_iterator it = keymode.bindings.begin(),
         end_it = keymode.bindings.end();
@@ -378,28 +271,23 @@ void  LLKeyConflictHandler::loadFromSettings(const LLViewerInput::KeyMode& keymo
             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(mouse, key, mask, ignore);
-        }
+        // Note: it->command is also the name of UI element, howhever xml we are loading from
+        // might not know all the commands, so UI will have to know what to fill by its own
+        LLKeyConflict &type_data = (*destination)[it->command];
+        type_data.mAssignable = true;
+        type_data.mKeyBind.addKeyData(mouse, key, mask, ignore);
     }
 }
 
-void LLKeyConflictHandler::loadFromSettings(const ESourceMode &load_mode, const std::string &filename, control_map_t *destination)
+bool LLKeyConflictHandler::loadFromSettings(const ESourceMode &load_mode, const std::string &filename, control_map_t *destination)
 {
     if (filename.empty())
     {
-        return;
+        return false;
     }
 
+    bool res = false;
+
     LLViewerInput::Keys keys;
     LLSimpleXUIParser parser;
 
@@ -412,39 +300,45 @@ void LLKeyConflictHandler::loadFromSettings(const ESourceMode &load_mode, const
             if (keys.first_person.isProvided())
             {
                 loadFromSettings(keys.first_person, destination);
+                res = true;
             }
             break;
         case MODE_THIRD_PERSON:
             if (keys.third_person.isProvided())
             {
                 loadFromSettings(keys.third_person, destination);
+                res = true;
             }
             break;
         case MODE_EDIT:
             if (keys.edit.isProvided())
             {
                 loadFromSettings(keys.edit, destination);
+                res = true;
             }
             break;
         case MODE_EDIT_AVATAR:
             if (keys.edit_avatar.isProvided())
             {
                 loadFromSettings(keys.edit_avatar, destination);
+                res = true;
             }
             break;
         case MODE_SITTING:
             if (keys.sitting.isProvided())
             {
                 loadFromSettings(keys.sitting, destination);
+                res = true;
             }
             break;
         default:
             break;
         }
     }
+    return res;
 }
 
-void  LLKeyConflictHandler::loadFromSettings(ESourceMode load_mode)
+void LLKeyConflictHandler::loadFromSettings(ESourceMode load_mode)
 {
     mControlsMap.clear();
     mDefaultsMap.clear();
@@ -454,48 +348,26 @@ void  LLKeyConflictHandler::loadFromSettings(ESourceMode load_mode)
 
     if (load_mode == MODE_GENERAL)
     {
-        for (U32 i = 0; i < CONTROL_NUM_INDICES; i++)
+        // load settings clss knows about, but it also possible to load settings by name separately
+        const S32 size = std::extent<decltype(saved_settings_key_controls)>::value;
+        for (U32 i = 0; i < size; 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;
-                }
-            }
+            loadFromControlSettings(saved_settings_key_controls[i]);
         }
     }
     else
     {
         // load defaults
         std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "keys.xml");
-        loadFromSettings(load_mode, filename, &mDefaultsMap);
+        if (!loadFromSettings(load_mode, filename, &mDefaultsMap))
+        {
+            LL_WARNS() << "Failed to load default settings, aborting" << LL_ENDL;
+            return;
+        }
 
         // load user's
         filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "keys.xml");
-        if (gDirUtilp->fileExists(filename))
-        {
-            loadFromSettings(load_mode, filename, &mControlsMap);
-        }
-        else
+        if (!gDirUtilp->fileExists(filename) || loadFromSettings(load_mode, filename, &mControlsMap))
         {
             // mind placeholders
             mControlsMap.insert(mDefaultsMap.begin(), mDefaultsMap.end());
@@ -504,7 +376,7 @@ void  LLKeyConflictHandler::loadFromSettings(ESourceMode load_mode)
     mLoadMode = load_mode;
 }
 
-void  LLKeyConflictHandler::saveToSettings()
+void LLKeyConflictHandler::saveToSettings()
 {
     if (mControlsMap.empty())
     {
@@ -513,39 +385,33 @@ void  LLKeyConflictHandler::saveToSettings()
 
     if (mLoadMode == MODE_GENERAL)
     {
-        for (U32 i = 0; i < CONTROL_NUM_INDICES; i++)
+        control_map_t::iterator iter = mControlsMap.begin();
+        control_map_t::iterator end = mControlsMap.end();
+
+        for (; iter != end; ++iter)
         {
-            EControlTypes type = (EControlTypes)i;
-            switch (type)
+            if (iter->first.empty())
             {
-            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:
+                continue;
+            }
+
+            LLKeyConflict &key = iter->second;
+            key.mKeyBind.trimEmpty();
+            if (!key.mAssignable)
             {
-                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;
+                continue;
+            }
+
+            if (gSavedSettings.controlExists(iter->first))
+            {
+                gSavedSettings.setLLSD(iter->first, key.mKeyBind.asLLSD());
             }
+            else if (!key.mKeyBind.empty())
+            {
+                // Note: this is currently not in use, might be better for load mechanics to ask for and retain control group
+                // otherwise settings loaded from other control groups will end in this one
+                LL_INFOS() << "Creating new keybinding " << iter->first << LL_ENDL;
+                gSavedSettings.declareLLSD(iter->first, key.mKeyBind.asLLSD(), "comment", LLControlVariable::PERSIST_ALWAYS);
             }
         }
     }
@@ -564,8 +430,8 @@ void  LLKeyConflictHandler::saveToSettings()
         {
             // replace category we edited
 
-            // todo: fix this
-            // workaround to avoid doing own param container 
+            // mode is a HACK to correctly reset bindings without reparsing whole file and avoid doing
+            // own param container (which will face issues with inasseesible members of LLInitParam)
             LLViewerInput::KeyMode mode;
             LLViewerInput::KeyBinding binding;
 
@@ -581,8 +447,20 @@ void  LLKeyConflictHandler::saveToSettings()
                 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 (iter->first.empty())
+                    {
+                        continue;
+                    }
+
+                    LLKeyConflict &key = iter->second;
+                    key.mKeyBind.trimEmpty();
+                    if (key.mKeyBind.empty() || !key.mAssignable)
+                    {
+                        continue;
+                    }
+
+                    LLKeyData data = key.mKeyBind.getKeyData(i);
+                    // Still write empty LLKeyData to make sure we will maintain UI position
                     if (data.mKey == KEY_NONE)
                     {
                         binding.key = "";
@@ -595,7 +473,7 @@ void  LLKeyConflictHandler::saveToSettings()
                     binding.mask = string_from_mask(data.mMask);
                     binding.mouse.set(string_from_mouse(data.mMouse), true); //set() because 'optional', for compatibility purposes
                     binding.ignore.set(data.mIgnoreMasks, true);
-                    binding.command = getControlName(iter->first);
+                    binding.command = iter->first;
                     mode.bindings.add(binding);
                 }
             }
@@ -664,12 +542,15 @@ void  LLKeyConflictHandler::saveToSettings()
     mHasUnsavedChanges = false;
 }
 
-LLKeyData LLKeyConflictHandler::getDefaultControl(EControlTypes control_type, U32 index)
+LLKeyData LLKeyConflictHandler::getDefaultControl(const std::string &control_name, U32 index)
 {
+    if (control_name.empty())
+    {
+        return LLKeyData();
+    }
     if (mLoadMode == MODE_GENERAL)
     {
-        std::string name = getControlName(control_type);
-        LLControlVariablePtr var = gSavedSettings.getControl(name);
+        LLControlVariablePtr var = gSavedSettings.getControl(control_name);
         if (var)
         {
             return LLKeyBind(var->getDefault()).getKeyData(index);
@@ -678,7 +559,7 @@ LLKeyData LLKeyConflictHandler::getDefaultControl(EControlTypes control_type, U3
     }
     else
     {
-        control_map_t::iterator iter = mDefaultsMap.find(control_type);
+        control_map_t::iterator iter = mDefaultsMap.find(control_name);
         if (iter != mDefaultsMap.end())
         {
             return iter->second.mKeyBind.getKeyData(index);
@@ -687,24 +568,31 @@ LLKeyData LLKeyConflictHandler::getDefaultControl(EControlTypes control_type, U3
     }
 }
 
-void LLKeyConflictHandler::resetToDefault(EControlTypes control_type, U32 index)
+void LLKeyConflictHandler::resetToDefault(const std::string &control_name, U32 index)
 {
-    LLKeyData data = getDefaultControl(control_type, index);
+    if (control_name.empty())
+    {
+        return;
+    }
+    LLKeyData data = getDefaultControl(control_name, index);
 
-    if (data != mControlsMap[control_type].getKeyData(index))
+    if (data != mControlsMap[control_name].getKeyData(index))
     {
         // reset controls that might have been switched to our current control
-        removeConflicts(data, mControlsMap[control_type].mConflictMask);
-        mControlsMap[control_type].setKeyData(data, index);
+        removeConflicts(data, mControlsMap[control_name].mConflictMask);
+        mControlsMap[control_name].setKeyData(data, index);
     }
 }
 
-void LLKeyConflictHandler::resetToDefaultAndResolve(EControlTypes control_type, bool ignore_conflicts)
+void LLKeyConflictHandler::resetToDefaultAndResolve(const std::string &control_name, bool ignore_conflicts)
 {
+    if (control_name.empty())
+    {
+        return;
+    }
     if (mLoadMode == MODE_GENERAL)
     {
-        std::string name = getControlName(control_type);
-        LLControlVariablePtr var = gSavedSettings.getControl(name);
+        LLControlVariablePtr var = gSavedSettings.getControl(control_name);
         if (var)
         {
             LLKeyBind bind(var->getDefault());
@@ -712,67 +600,53 @@ void LLKeyConflictHandler::resetToDefaultAndResolve(EControlTypes control_type,
             {
                 for (S32 i = 0; i < bind.getDataCount(); ++i)
                 {
-                    removeConflicts(bind.getKeyData(i), mControlsMap[control_type].mConflictMask);
+                    removeConflicts(bind.getKeyData(i), mControlsMap[control_name].mConflictMask);
                 }
             }
-            mControlsMap[control_type].mKeyBind = bind;
+            mControlsMap[control_name].mKeyBind = bind;
         }
         else
         {
-            mControlsMap[control_type].mKeyBind.clear();
+            mControlsMap[control_name].mKeyBind.clear();
         }
     }
     else
     {
-        control_map_t::iterator iter = mDefaultsMap.find(control_type);
+        control_map_t::iterator iter = mDefaultsMap.find(control_name);
         if (iter != mDefaultsMap.end())
         {
             if (!ignore_conflicts)
             {
                 for (S32 i = 0; i < iter->second.mKeyBind.getDataCount(); ++i)
                 {
-                    removeConflicts(iter->second.mKeyBind.getKeyData(i), mControlsMap[control_type].mConflictMask);
+                    removeConflicts(iter->second.mKeyBind.getKeyData(i), mControlsMap[control_name].mConflictMask);
                 }
             }
-            mControlsMap[control_type].mKeyBind = iter->second.mKeyBind;
+            mControlsMap[control_name].mKeyBind = iter->second.mKeyBind;
         }
         else
         {
-            mControlsMap[control_type].mKeyBind.clear();
+            mControlsMap[control_name].mKeyBind.clear();
         }
     }
 }
 
-void LLKeyConflictHandler::resetToDefault(EControlTypes control_type)
+void LLKeyConflictHandler::resetToDefault(const std::string &control_name)
 {
     // reset specific binding without ignoring conflicts
-    resetToDefaultAndResolve(control_type, false);
+    resetToDefaultAndResolve(control_name, false);
 }
 
 void LLKeyConflictHandler::resetToDefaults(ESourceMode mode)
 {
     if (mode == MODE_GENERAL)
     {
-        for (U32 i = 0; i < CONTROL_NUM_INDICES; i++)
+        control_map_t::iterator iter = mControlsMap.begin();
+        control_map_t::iterator end = mControlsMap.end();
+
+        for (; iter != end; ++iter)
         {
-            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:
-            {
-                resetToDefaultAndResolve(type, true);
-                break;
-            }
-            }
+            resetToDefaultAndResolve(iter->first, true);
         }
     }
     else
@@ -834,7 +708,7 @@ bool LLKeyConflictHandler::removeConflicts(const LLKeyData &data, const U32 &con
         // Can't conflict
         return true;
     }
-    std::map<EControlTypes, S32> conflict_list;
+    std::map<std::string, S32> conflict_list;
     control_map_t::iterator cntrl_iter = mControlsMap.begin();
     control_map_t::iterator cntrl_end = mControlsMap.end();
     for (; cntrl_iter != cntrl_end; ++cntrl_iter)
@@ -859,8 +733,8 @@ bool LLKeyConflictHandler::removeConflicts(const LLKeyData &data, const U32 &con
         }
     }
 
-    std::map<EControlTypes, S32>::iterator cnflct_iter = conflict_list.begin();
-    std::map<EControlTypes, S32>::iterator cnflct_end = conflict_list.end();
+    std::map<std::string, S32>::iterator cnflct_iter = conflict_list.begin();
+    std::map<std::string, S32>::iterator cnflct_end = conflict_list.end();
     for (; cnflct_iter != cnflct_end; ++cnflct_iter)
     {
         mControlsMap[cnflct_iter->first].mKeyBind.resetKeyData(cnflct_iter->second);
@@ -868,9 +742,9 @@ bool LLKeyConflictHandler::removeConflicts(const LLKeyData &data, const U32 &con
     return true;
 }
 
-void LLKeyConflictHandler::registerTemporaryControl(EControlTypes control_type, EMouseClickType mouse, KEY key, MASK mask, U32 conflict_mask)
+void LLKeyConflictHandler::registerTemporaryControl(const std::string &control_name, EMouseClickType mouse, KEY key, MASK mask, U32 conflict_mask)
 {
-    LLKeyConflict *type_data = &mControlsMap[control_type];
+    LLKeyConflict *type_data = &mControlsMap[control_name];
     type_data->mAssignable = false;
     type_data->mConflictMask = conflict_mask;
     type_data->mKeyBind.addKeyData(mouse, key, mask, false);
diff --git a/indra/newview/llkeyconflict.h b/indra/newview/llkeyconflict.h
index e4a6da30d0e..a0886bedce2 100644
--- a/indra/newview/llkeyconflict.h
+++ b/indra/newview/llkeyconflict.h
@@ -70,114 +70,35 @@ class LLKeyConflictHandler
     // at the moment this just means that key will conflict with everything that is identical
     const U32 CONFLICT_ANY = U32_MAX;
 
-    // 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_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_SIT,
-        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_PAUSE_MEDIA, // Play pause
-        CONTROL_ENABLE_MEDIA, // Play stop
-        CONTROL_VOICE, // Keep pressing for voice 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(ESourceMode 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);
-    bool registerControl(EControlTypes control_type, U32 data_index, EMouseClickType mouse_ind, KEY key, MASK mask, bool ignore_mask); //todo: return conflicts?
-
-    LLKeyData getControl(EControlTypes control_type, U32 data_index);
+    bool canHandleControl(const std::string &control_name, EMouseClickType mouse_ind, KEY key, MASK mask);
+    bool canHandleKey(const std::string &control_name, KEY key, MASK mask);
+    bool canHandleMouse(const std::string &control_name, EMouseClickType mouse_ind, MASK mask);
+    bool canHandleMouse(const std::string &control_name, S32 mouse_ind, MASK mask); //Just for convinience
+    bool canAssignControl(const std::string &control_name);
+    bool registerControl(const std::string &control_name, U32 data_index, EMouseClickType mouse_ind, KEY key, MASK mask, bool ignore_mask); //todo: return conflicts?
 
-    static std::string LLKeyConflictHandler::getStringFromKeyData(const LLKeyData& keydata);
-    static std::string getControlName(EControlTypes control_type);
-    std::string getControlString(EControlTypes control_type, U32 data_index);
+    LLKeyData getControl(const std::string &control_name, U32 data_index);
 
+    static std::string getStringFromKeyData(const LLKeyData& keydata);
+    std::string getControlString(const std::string &control_name, U32 data_index);
 
+    // Load single control, overrides existing one if names match
+    void loadFromControlSettings(const std::string &name);
     // Drops any changes loads controls with ones from 'saved settings' or from xml
     void loadFromSettings(ESourceMode load_mode);
     // Saves settings to 'saved settings' or to xml
     void saveToSettings();
 
-    LLKeyData getDefaultControl(EControlTypes control_type, U32 data_index);
+    LLKeyData getDefaultControl(const std::string &control_name, 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);
-    // resets current mode to defaults, 
+    void resetToDefault(const std::string &control_name, U32 index);
+    void resetToDefault(const std::string &control_name);
+    // resets current mode to defaults
     void resetToDefaults();
 
     bool empty() { return mControlsMap.empty(); }
@@ -188,15 +109,15 @@ class LLKeyConflictHandler
     ESourceMode getLoadMode() { return mLoadMode; }
 
 private:
-    void resetToDefaultAndResolve(EControlTypes control_type, bool ignore_conflicts);
+    void resetToDefaultAndResolve(const std::string &control_name, bool ignore_conflicts);
     void resetToDefaults(ESourceMode mode);
 
     // 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);
+    void registerTemporaryControl(const std::string &control_name, EMouseClickType mouse_ind, KEY key, MASK mask, U32 conflict_mask);
 
-    typedef std::map<EControlTypes, LLKeyConflict> control_map_t;
+    typedef std::map<std::string, LLKeyConflict> control_map_t;
     void loadFromSettings(const LLViewerInput::KeyMode& keymode, control_map_t *destination);
-    void loadFromSettings(const ESourceMode &load_mode, const std::string &filename, control_map_t *destination);
+    bool loadFromSettings(const ESourceMode &load_mode, const std::string &filename, control_map_t *destination);
     void resetKeyboardBindings();
     void generatePlaceholders(ESourceMode load_mode); //E.x. non-assignable values
     // returns false in case user is trying to reuse control that can't be reassigned
diff --git a/indra/newview/llviewerinput.cpp b/indra/newview/llviewerinput.cpp
index 11f430cda3c..eff578cf26e 100644
--- a/indra/newview/llviewerinput.cpp
+++ b/indra/newview/llviewerinput.cpp
@@ -648,6 +648,78 @@ bool start_gesture( EKeystate s )
 	return true;
 }
 
+bool run_forward(EKeystate s)
+{
+    if (KEYSTATE_DOWN == s)
+    {
+        gAgent.setAlwaysRun();
+        gAgent.setRunning();
+        gAgent.sendWalkRun(true);
+    }
+    else if(KEYSTATE_UP == s)
+    {
+        gAgent.clearAlwaysRun();
+        gAgent.clearRunning();
+        gAgent.sendWalkRun(false);
+    }
+    agent_push_forward(s);
+    return true;
+}
+
+bool run_backward(EKeystate s)
+{
+    if (KEYSTATE_DOWN == s)
+    {
+        gAgent.setAlwaysRun();
+        gAgent.setRunning();
+        gAgent.sendWalkRun(true);
+    }
+    else if (KEYSTATE_UP == s)
+    {
+        gAgent.clearAlwaysRun();
+        gAgent.clearRunning();
+        gAgent.sendWalkRun(false);
+    }
+    agent_push_backward(s);
+    return true;
+}
+
+bool run_left(EKeystate s)
+{
+    if (KEYSTATE_DOWN == s)
+    {
+        gAgent.setAlwaysRun();
+        gAgent.setRunning();
+        gAgent.sendWalkRun(true);
+    }
+    else if (KEYSTATE_UP == s)
+    {
+        gAgent.clearAlwaysRun();
+        gAgent.clearRunning();
+        gAgent.sendWalkRun(false);
+    }
+    agent_slide_left(s);
+    return true;
+}
+
+bool run_right(EKeystate s)
+{
+    if (KEYSTATE_DOWN == s)
+    {
+        gAgent.setAlwaysRun();
+        gAgent.setRunning();
+        gAgent.sendWalkRun(true);
+    }
+    else if (KEYSTATE_UP == s)
+    {
+        gAgent.clearAlwaysRun();
+        gAgent.clearRunning();
+        gAgent.sendWalkRun(false);
+    }
+    agent_slide_right(s);
+    return true;
+}
+
 bool toggle_run(EKeystate s)
 {
     if (KEYSTATE_DOWN != s) return true;
@@ -789,6 +861,10 @@ 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("run_forward", run_forward);
+REGISTER_KEYBOARD_ACTION("run_backward", run_backward);
+REGISTER_KEYBOARD_ACTION("run_left", run_left);
+REGISTER_KEYBOARD_ACTION("run_right", run_right);
 REGISTER_KEYBOARD_ACTION("toggle_run", toggle_run);
 REGISTER_KEYBOARD_ACTION("toggle_sit", toggle_sit);
 REGISTER_KEYBOARD_ACTION("toggle_pause_media", toggle_pause_media);
diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp
index 5388d13a041..3c2ec369ecd 100644
--- a/indra/newview/llviewerwindow.cpp
+++ b/indra/newview/llviewerwindow.cpp
@@ -955,6 +955,8 @@ BOOL LLViewerWindow::handleAnyMouseClick(LLWindow *window, LLCoordGL pos, MASK m
 		case CLICK_BUTTON5:
 			buttonname = "Button 5";
 			break;
+		default:
+			break; // COUNT and NONE
 		}
 		
 		LLView::sMouseHandlerMessage.clear();
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 62267dce8af..385d4796a09 100644
--- a/indra/newview/skins/default/xui/en/panel_preferences_controls.xml
+++ b/indra/newview/skins/default/xui/en/panel_preferences_controls.xml
@@ -9,6 +9,10 @@
  name="controls"
  top="1"
  width="517">
+  <panel.string
+   name="walk_to">
+    Walk to
+  </panel.string>
   <panel.string
    name="control_view_actions">
     View Actions
@@ -70,8 +74,8 @@
     Move Actions
   </panel.string>
   <panel.string
-   name="control_moveto">
-    Move To
+   name="teleport_to">
+    Teleport to
   </panel.string>
   <panel.string
    name="toggle_sit">
@@ -118,6 +122,22 @@
    name="control_run">
     Run
   </panel.string>
+  <panel.string
+   name="run_forward">
+    Run Forward
+  </panel.string>
+  <panel.string
+   name="run_backward">
+    Run Backward
+  </panel.string>
+  <panel.string
+   name="run_left">
+    Run Left
+  </panel.string>
+  <panel.string
+   name="run_right">
+    Run Right
+  </panel.string>
   <panel.string
    name="toggle_run">
     Toggle Run
@@ -126,6 +146,10 @@
    name="toggle_fly">
     Fly/Stop flying
   </panel.string>
+  <panel.string
+   name="stop_moving">
+    Stop Moving
+  </panel.string>
   <panel.string
    name="control_camera">
     Camera
@@ -215,36 +239,13 @@
     Play/Stop All Media
   </panel.string>
   <panel.string
-   name="control_voice">
+   name="voice_follow_key">
     Voice
   </panel.string>
   <panel.string
-   name="control_toggle_voice">
+   name="toggle_voice">
     Toggle Voice
   </panel.string>
-  <panel.string
-   name="control_reserved">
-    Reserved Controls
-  </panel.string>
-  <panel.string
-   name="control_delete">
-    Delete
-  </panel.string>
-  <!--
-   name="control_menu" not needed
-   -->
-  <panel.string
-   name="control_reserved_select">
-    Select
-  </panel.string>
-  <panel.string
-   name="control_shift_select">
-    Multi-Select
-  </panel.string>
-  <panel.string
-   name="control_cntrl_select">
-    Add to Selection
-  </panel.string>
 
   <combo_box
    follows="top|left"
-- 
GitLab