From b8c5ae8d8fcf9219112c4ab48233615732737e44 Mon Sep 17 00:00:00 2001
From: andreykproductengine <andreykproductengine@lindenlab.com>
Date: Thu, 3 Oct 2019 19:46:12 +0300
Subject: SL-6109 Better menu accelerator support and slight reorganization

---
 indra/llui/llmenugl.cpp                  |  21 +++
 indra/llui/llmenugl.h                    |   2 +
 indra/newview/CMakeLists.txt             |   2 +
 indra/newview/llfloaterpreference.cpp    | 306 +------------------------------
 indra/newview/llfloaterpreference.h      |   5 +-
 indra/newview/llkeyconflict.cpp          |  21 +++
 indra/newview/llkeyconflict.h            |   2 +
 indra/newview/llpanelpresetspulldown.cpp |   1 -
 indra/newview/llsetkeybinddialog.cpp     | 298 ++++++++++++++++++++++++++++++
 indra/newview/llsetkeybinddialog.h       |  88 +++++++++
 indra/newview/llstartup.cpp              |   1 -
 11 files changed, 440 insertions(+), 307 deletions(-)
 create mode 100644 indra/newview/llsetkeybinddialog.cpp
 create mode 100644 indra/newview/llsetkeybinddialog.h

diff --git a/indra/llui/llmenugl.cpp b/indra/llui/llmenugl.cpp
index e6a3de281e..986f3362b7 100644
--- a/indra/llui/llmenugl.cpp
+++ b/indra/llui/llmenugl.cpp
@@ -3514,6 +3514,27 @@ S32 LLMenuBarGL::getRightmostMenuEdge()
 	return (*item_iter)->getRect().mRight;
 }
 
+bool LLMenuBarGL::hasAccelerator(const KEY &key, const MASK &mask) const
+{
+    if (key == KEY_NONE)
+    {
+        return false;
+    }
+
+    LLMenuKeyboardBinding *accelerator = NULL;
+    std::list<LLMenuKeyboardBinding*>::const_iterator list_it;
+    for (list_it = mAccelerators.begin(); list_it != mAccelerators.end(); ++list_it)
+    {
+        accelerator = *list_it;
+        if ((accelerator->mKey == key) && (accelerator->mMask == (mask & MASK_NORMALKEYS)))
+        {
+            return true;
+        }
+    }
+
+    return false;
+}
+
 // add a vertical separator to this menu
 BOOL LLMenuBarGL::addSeparator()
 {
diff --git a/indra/llui/llmenugl.h b/indra/llui/llmenugl.h
index 3a6b849e73..0653864892 100644
--- a/indra/llui/llmenugl.h
+++ b/indra/llui/llmenugl.h
@@ -792,6 +792,8 @@ public:
 
 	void resetMenuTrigger() { mAltKeyTrigger = FALSE; }
 
+	bool hasAccelerator(const KEY &key, const MASK &mask) const;
+
 private:
 	// add a menu - this will create a drop down menu.
 	virtual BOOL appendMenu( LLMenuGL* menu );
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index a35d0cba50..8345d5c445 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -551,6 +551,7 @@ set(viewer_SOURCE_FILES
     llsecapi.cpp
     llsechandler_basic.cpp
     llselectmgr.cpp
+    llsetkeybinddialog.cpp
     llshareavatarhandler.cpp
     llsidepanelappearance.cpp
     llsidepanelinventory.cpp
@@ -1172,6 +1173,7 @@ set(viewer_HEADER_FILES
     llsecapi.h
     llsechandler_basic.h
     llselectmgr.h
+    llsetkeybinddialog.h
     llsidepanelappearance.h
     llsidepanelinventory.h
     llsidepanelinventorysubpanel.h
diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp
index 2354be25e9..6ee1bbee1a 100644
--- a/indra/newview/llfloaterpreference.cpp
+++ b/indra/newview/llfloaterpreference.cpp
@@ -43,7 +43,6 @@
 #include "llcombobox.h"
 #include "llcommandhandler.h"
 #include "lldirpicker.h"
-#include "lldrawfrustum.h"
 #include "lleventtimer.h"
 #include "llfeaturemanager.h"
 #include "llfocusmgr.h"
@@ -161,306 +160,6 @@ struct LabelTable : public LLInitParam::Block<LabelTable>
     {}
 };
 
-// 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 CAN_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 | CAN_IGNORE_MASKS;
-
-class LLSetKeyBindDialog : public LLModalDialog, public LLDrawFrustum
-{
-public:
-	LLSetKeyBindDialog(const LLSD& key);
-	~LLSetKeyBindDialog();
-
-	/*virtual*/ BOOL postBuild();
-    /*virtual*/ void onClose(bool app_quiting);
-    /*virtual*/ void draw();
-
-	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);
-	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 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),
-	mKeyFilterMask(DEFAULT_KEY_FILTER),
-	pUpdater(NULL)
-{
-}
-
-LLSetKeyBindDialog::~LLSetKeyBindDialog()
-{
-}
-
-//virtual
-BOOL LLSetKeyBindDialog::postBuild()
-{
-	childSetAction("SetEmpty", onBlank, this);
-	childSetAction("Default", onDefault, this);
-	childSetAction("Cancel", onCancel, this);
-	getChild<LLUICtrl>("Cancel")->setFocus(TRUE);
-
-	pCheckBox = getChild<LLCheckBoxCtrl>("ignore_masks");
-
-	gFocusMgr.setKeystrokesOnly(TRUE);
-	
-	return TRUE;
-}
-
-//virtual
-void LLSetKeyBindDialog::onClose(bool app_quiting)
-{
-    if (pParent)
-    {
-        pParent->onCancelKeyBind();
-        pParent = NULL;
-    }
-    if (pUpdater)
-    {
-        // Doubleclick timer has't fired, delete it
-        delete pUpdater;
-        pUpdater = NULL;
-    }
-    LLModalDialog::onClose(app_quiting);
-}
-
-//virtual
-void LLSetKeyBindDialog::draw()
-{
-    LLRect local_rect;
-    drawFrustum(local_rect, this, (LLView*)getDragHandle(), hasFocus());
-    LLModalDialog::draw();
-}
-
-void LLSetKeyBindDialog::setParent(LLPanelPreferenceControls* parent, U32 key_mask)
-{
-    pParent = parent;
-    setFrustumOrigin(parent);
-    mKeyFilterMask = 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);
-
-    bool can_ignore_masks = (key_mask & CAN_IGNORE_MASKS) != 0;
-    pCheckBox->setVisible(can_ignore_masks);
-    pCheckBox->setValue(false);
-}
-
-BOOL LLSetKeyBindDialog::handleKeyHere(KEY key, MASK mask)
-{
-    if ((key == 'Q' && mask == MASK_CONTROL)
-        || key == KEY_ESCAPE)
-    {
-        closeFloater();
-        return TRUE;
-    }
-
-    if (key == KEY_DELETE)
-    {
-        setKeyBind(CLICK_NONE, KEY_NONE, MASK_NONE, false);
-        closeFloater();
-        return FALSE;
-    }
-
-    // forbidden keys
-    if (key == KEY_NONE
-        || key == KEY_RETURN
-        || key == KEY_BACKSPACE)
-    {
-        return FALSE;
-    }
-
-    if ((mKeyFilterMask & ALLOW_MASKS) == 0
-        && (key == KEY_CONTROL || key == KEY_SHIFT || key == KEY_ALT))
-    {
-        // mask by themself are not allowed
-        return FALSE;
-    }
-    else if ((mKeyFilterMask & ALLOW_KEYS) == 0)
-    {
-        // basic keys not allowed
-        return FALSE;
-    }
-    else if ((mKeyFilterMask & ALLOW_MASK_KEYS) == 0 && mask != 0)
-    {
-        // masked keys not allowed
-        return FALSE;
-    }
-
-    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 (!pParent)
-    {
-        // we already processed 'down' event, this is 'up', consume
-        closeFloater();
-        result = TRUE;
-    }
-    if (!result && clicktype == CLICK_LEFT)
-    {
-        // try handling buttons first
-        if (down)
-        {
-            result = LLView::handleMouseDown(x, y, mask);
-        }
-        else
-        {
-            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
-        && (clicktype != CLICK_LEFT) // subcases were handled above
-        && ((mKeyFilterMask & ALLOW_MOUSE) != 0)
-        && (clicktype != CLICK_RIGHT || mask != 0) // reassigning menu button is not supported
-        && ((mKeyFilterMask & ALLOW_MASK_MOUSE) != 0 || mask == 0)) // reserved for selection
-    {
-        setKeyBind(clicktype, KEY_NONE, mask, pCheckBox->getValue().asBoolean());
-        result = TRUE;
-        if (!down)
-        {
-            // wait for 'up' event before closing
-            // alternative: set pUpdater
-            closeFloater();
-        }
-    }
-
-    return result;
-}
-
-//static
-void LLSetKeyBindDialog::onCancel(void* user_data)
-{
-    LLSetKeyBindDialog* self = (LLSetKeyBindDialog*)user_data;
-    self->closeFloater();
-}
-
-//static
-void LLSetKeyBindDialog::onBlank(void* user_data)
-{
-    LLSetKeyBindDialog* self = (LLSetKeyBindDialog*)user_data;
-    // tmp needs 'no key' button
-    self->setKeyBind(CLICK_NONE, KEY_NONE, MASK_NONE, false);
-    self->closeFloater();
-}
-
-//static
-void LLSetKeyBindDialog::onDefault(void* user_data)
-{
-    LLSetKeyBindDialog* self = (LLSetKeyBindDialog*)user_data;
-    if (self->pParent)
-    {
-        self->pParent->onDefaultKeyBind();
-        self->pParent = NULL;
-    }
-    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 
 
@@ -3248,11 +2947,11 @@ void LLPanelPreferenceControls::onModeCommit()
 }
 
 // todo: copy onSetKeyBind to interface and inherit from interface
-void LLPanelPreferenceControls::onSetKeyBind(EMouseClickType click, KEY key, MASK mask, bool ignore_mask)
+bool LLPanelPreferenceControls::onSetKeyBind(EMouseClickType click, KEY key, MASK mask, bool ignore_mask)
 {
     if (!mConflictHandler[mEditingMode].canAssignControl(mEditingControl))
     {
-        return;
+        return true;
     }
 
     if ( mEditingColumn > 0)
@@ -3261,6 +2960,7 @@ void LLPanelPreferenceControls::onSetKeyBind(EMouseClickType click, KEY key, MAS
     }
 
     updateTable();
+    return true;
 }
 
 void LLPanelPreferenceControls::onRestoreDefaults()
diff --git a/indra/newview/llfloaterpreference.h b/indra/newview/llfloaterpreference.h
index c1f58d290b..9734ea30a2 100644
--- a/indra/newview/llfloaterpreference.h
+++ b/indra/newview/llfloaterpreference.h
@@ -37,6 +37,7 @@
 #include "llavatarpropertiesprocessor.h"
 #include "llconversationlog.h"
 #include "llsearcheditor.h"
+#include "llsetkeybinddialog.h"
 #include "llkeyconflict.h"
 
 class LLConversationLogObserver;
@@ -285,7 +286,7 @@ private:
 	LOG_CLASS(LLPanelPreferenceGraphics);
 };
 
-class LLPanelPreferenceControls : public LLPanelPreference
+class LLPanelPreferenceControls : public LLPanelPreference, public LLKeyBindResponderInterface
 {
 	LOG_CLASS(LLPanelPreferenceControls);
 public:
@@ -301,7 +302,7 @@ public:
 
 	void onListCommit();
 	void onModeCommit();
-	void onSetKeyBind(EMouseClickType click, KEY key, MASK mask, bool ignore_mask);
+	bool onSetKeyBind(EMouseClickType click, KEY key, MASK mask, bool ignore_mask);
 	void onRestoreDefaults();
 	void onDefaultKeyBind();
 	void onCancelKeyBind();
diff --git a/indra/newview/llkeyconflict.cpp b/indra/newview/llkeyconflict.cpp
index 01230ea26a..bad4e4a2d8 100644
--- a/indra/newview/llkeyconflict.cpp
+++ b/indra/newview/llkeyconflict.cpp
@@ -38,6 +38,7 @@
 #include "llkeyboard.h"
 #include "llviewercontrol.h"
 #include "llviewerinput.h"
+#include "llviewermenu.h"
 #include "llxuiparser.h"
 //#include "llstring.h"
 
@@ -170,6 +171,22 @@ bool LLKeyConflictHandler::canAssignControl(const std::string &control_name)
     return true;
 }
 
+// static
+bool LLKeyConflictHandler::isReservedByMenu(const KEY &key, const MASK &mask)
+{
+    return gMenuBarView->hasAccelerator(key, mask) || gLoginMenuBarView->hasAccelerator(key, mask);
+}
+
+// static
+bool LLKeyConflictHandler::isReservedByMenu(const LLKeyData &data)
+{
+    if (data.mMouse != CLICK_NONE)
+    {
+        return false;
+    }
+    return gMenuBarView->hasAccelerator(data.mKey, data.mMask) || gLoginMenuBarView->hasAccelerator(data.mKey, data.mMask);
+}
+
 bool LLKeyConflictHandler::registerControl(const std::string &control_name, U32 index, EMouseClickType mouse, KEY key, MASK mask, bool ignore_mask)
 {
     if (control_name.empty())
@@ -186,6 +203,10 @@ bool LLKeyConflictHandler::registerControl(const std::string &control_name, U32
     {
         return true;
     }
+    if (isReservedByMenu(data))
+    {
+        return false;
+    }
     if (removeConflicts(data, type_data.mConflictMask))
     {
         type_data.mKeyBind.replaceKeyData(data, index);
diff --git a/indra/newview/llkeyconflict.h b/indra/newview/llkeyconflict.h
index e339264aaa..5c3b860ec6 100644
--- a/indra/newview/llkeyconflict.h
+++ b/indra/newview/llkeyconflict.h
@@ -80,6 +80,8 @@ public:
     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);
+    static bool isReservedByMenu(const KEY &key, const MASK &mask);
+    static bool isReservedByMenu(const LLKeyData &data);
     bool registerControl(const std::string &control_name, U32 data_index, EMouseClickType mouse_ind, KEY key, MASK mask, bool ignore_mask); //todo: return conflicts?
 
     LLKeyData getControl(const std::string &control_name, U32 data_index);
diff --git a/indra/newview/llpanelpresetspulldown.cpp b/indra/newview/llpanelpresetspulldown.cpp
index 9b4dc5474a..1a49cbc250 100644
--- a/indra/newview/llpanelpresetspulldown.cpp
+++ b/indra/newview/llpanelpresetspulldown.cpp
@@ -34,7 +34,6 @@
 #include "llbutton.h"
 #include "lltabcontainer.h"
 #include "llfloaterreg.h"
-#include "llfloaterpreference.h"
 #include "llpresetsmanager.h"
 #include "llsliderctrl.h"
 #include "llscrolllistctrl.h"
diff --git a/indra/newview/llsetkeybinddialog.cpp b/indra/newview/llsetkeybinddialog.cpp
new file mode 100644
index 0000000000..0ad71cb372
--- /dev/null
+++ b/indra/newview/llsetkeybinddialog.cpp
@@ -0,0 +1,298 @@
+/** 
+ * @file llsetkeybinddialog.cpp
+ * @brief LLSetKeyBindDialog class implementation.
+ *
+ * $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$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llsetkeybinddialog.h"
+
+//#include "llkeyboard.h"
+
+#include "llbutton.h"
+#include "llcheckboxctrl.h"
+#include "lleventtimer.h"
+#include "llfocusmgr.h"
+
+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),
+    mKeyFilterMask(DEFAULT_KEY_FILTER),
+    pUpdater(NULL)
+{
+}
+
+LLSetKeyBindDialog::~LLSetKeyBindDialog()
+{
+}
+
+//virtual
+BOOL LLSetKeyBindDialog::postBuild()
+{
+    childSetAction("SetEmpty", onBlank, this);
+    childSetAction("Default", onDefault, this);
+    childSetAction("Cancel", onCancel, this);
+    getChild<LLUICtrl>("Cancel")->setFocus(TRUE);
+
+    pCheckBox = getChild<LLCheckBoxCtrl>("ignore_masks");
+
+    gFocusMgr.setKeystrokesOnly(TRUE);
+
+    return TRUE;
+}
+
+//virtual
+void LLSetKeyBindDialog::onClose(bool app_quiting)
+{
+    if (pParent)
+    {
+        pParent->onCancelKeyBind();
+        pParent = NULL;
+    }
+    if (pUpdater)
+    {
+        // Doubleclick timer has't fired, delete it
+        delete pUpdater;
+        pUpdater = NULL;
+    }
+    LLModalDialog::onClose(app_quiting);
+}
+
+//virtual
+void LLSetKeyBindDialog::draw()
+{
+    LLRect local_rect;
+    drawFrustum(local_rect, this, (LLView*)getDragHandle(), hasFocus());
+    LLModalDialog::draw();
+}
+
+void LLSetKeyBindDialog::setParent(LLPanelPreferenceControls* parent, U32 key_mask)
+{
+    pParent = parent;
+    setFrustumOrigin(parent);
+    mKeyFilterMask = 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);
+
+    bool can_ignore_masks = (key_mask & CAN_IGNORE_MASKS) != 0;
+    pCheckBox->setVisible(can_ignore_masks);
+    pCheckBox->setValue(false);
+}
+
+BOOL LLSetKeyBindDialog::handleKeyHere(KEY key, MASK mask)
+{
+    if ((key == 'Q' && mask == MASK_CONTROL)
+        || key == KEY_ESCAPE)
+    {
+        closeFloater();
+        return TRUE;
+    }
+
+    if (key == KEY_DELETE)
+    {
+        setKeyBind(CLICK_NONE, KEY_NONE, MASK_NONE, false);
+        closeFloater();
+        return FALSE;
+    }
+
+    // forbidden keys
+    if (key == KEY_NONE
+        || key == KEY_RETURN
+        || key == KEY_BACKSPACE)
+    {
+        return FALSE;
+    }
+
+    if ((mKeyFilterMask & ALLOW_MASKS) == 0
+        && (key == KEY_CONTROL || key == KEY_SHIFT || key == KEY_ALT))
+    {
+        // mask by themself are not allowed
+        return FALSE;
+    }
+    else if ((mKeyFilterMask & ALLOW_KEYS) == 0)
+    {
+        // basic keys not allowed
+        return FALSE;
+    }
+    else if ((mKeyFilterMask & ALLOW_MASK_KEYS) == 0 && mask != 0)
+    {
+        // masked keys not allowed
+        return FALSE;
+    }
+
+    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 (!pParent)
+    {
+        // we already processed 'down' event, this is 'up', consume
+        closeFloater();
+        result = TRUE;
+    }
+    if (!result && clicktype == CLICK_LEFT)
+    {
+        // try handling buttons first
+        if (down)
+        {
+            result = LLView::handleMouseDown(x, y, mask);
+        }
+        else
+        {
+            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
+        && (clicktype != CLICK_LEFT) // subcases were handled above
+        && ((mKeyFilterMask & ALLOW_MOUSE) != 0)
+        && (clicktype != CLICK_RIGHT || mask != 0) // reassigning menu button is not supported
+        && ((mKeyFilterMask & ALLOW_MASK_MOUSE) != 0 || mask == 0)) // reserved for selection
+    {
+        setKeyBind(clicktype, KEY_NONE, mask, pCheckBox->getValue().asBoolean());
+        result = TRUE;
+        if (!down)
+        {
+            // wait for 'up' event before closing
+            // alternative: set pUpdater
+            closeFloater();
+        }
+    }
+
+    return result;
+}
+
+//static
+void LLSetKeyBindDialog::onCancel(void* user_data)
+{
+    LLSetKeyBindDialog* self = (LLSetKeyBindDialog*)user_data;
+    self->closeFloater();
+}
+
+//static
+void LLSetKeyBindDialog::onBlank(void* user_data)
+{
+    LLSetKeyBindDialog* self = (LLSetKeyBindDialog*)user_data;
+    // tmp needs 'no key' button
+    self->setKeyBind(CLICK_NONE, KEY_NONE, MASK_NONE, false);
+    self->closeFloater();
+}
+
+//static
+void LLSetKeyBindDialog::onDefault(void* user_data)
+{
+    LLSetKeyBindDialog* self = (LLSetKeyBindDialog*)user_data;
+    if (self->pParent)
+    {
+        self->pParent->onDefaultKeyBind();
+        self->pParent = NULL;
+    }
+    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;
+    }
+}
+
diff --git a/indra/newview/llsetkeybinddialog.h b/indra/newview/llsetkeybinddialog.h
new file mode 100644
index 0000000000..fb3b2a2269
--- /dev/null
+++ b/indra/newview/llsetkeybinddialog.h
@@ -0,0 +1,88 @@
+/** 
+ * @file llsetkeybinddialog.h
+ * @brief LLSetKeyBindDialog class definition
+ *
+ * $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_LLSETKEYBINDDIALOG_H
+#define LL_LLSETKEYBINDDIALOG_H
+
+#include "llmodaldialog.h"
+#include "lldrawfrustum.h"
+
+class LLCheckBoxCtrl;
+
+// 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 CAN_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 | CAN_IGNORE_MASKS;
+
+
+class LLKeyBindResponderInterface
+{
+public:
+    virtual ~LLKeyBindResponderInterface();
+
+    virtual void onCancelKeyBind();
+    virtual void onDefaultKeyBind();
+    // returns true if parent failed to set key due to key being in use
+    virtual bool onSetKeyBind(EMouseClickType click, KEY key, MASK mask, bool ignore);
+};
+
+class LLSetKeyBindDialog : public LLModalDialog, public LLDrawFrustum
+{
+public:
+    LLSetKeyBindDialog(const LLSD& key);
+    ~LLSetKeyBindDialog();
+
+    /*virtual*/ BOOL postBuild();
+    /*virtual*/ void onClose(bool app_quiting);
+    /*virtual*/ void draw();
+
+    void setParent(LLKeyBindResponderInterface* 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);
+    static void onClickTimeout(void* user_data, MASK mask);
+
+    class Updater;
+
+private:
+    void setKeyBind(EMouseClickType click, KEY key, MASK mask, bool ignore);
+    LLKeyBindResponderInterface* pParent;
+    LLCheckBoxCtrl* pCheckBox;
+
+    U32 mKeyFilterMask;
+    Updater *pUpdater;
+};
+
+
+#endif  // LL_LLSETKEYBINDDIALOG_H
diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp
index 3771797929..b1bdaa1c1a 100644
--- a/indra/newview/llstartup.cpp
+++ b/indra/newview/llstartup.cpp
@@ -106,7 +106,6 @@
 //#include "llfirstuse.h"
 #include "llfloaterhud.h"
 #include "llfloaterland.h"
-#include "llfloaterpreference.h"
 #include "llfloatertopobjects.h"
 #include "llfloaterworldmap.h"
 #include "llgesturemgr.h"
-- 
cgit v1.2.3