summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--indra/llcommon/llkeybind.cpp180
-rw-r--r--indra/llcommon/llkeybind.h23
-rw-r--r--indra/llui/llscrolllistctrl.cpp4
-rw-r--r--indra/llui/llscrolllistctrl.h4
-rw-r--r--indra/newview/CMakeLists.txt2
-rw-r--r--indra/newview/llfloaterpreference.cpp680
-rw-r--r--indra/newview/llfloaterpreference.h31
-rw-r--r--indra/newview/llkeyconflict.cpp789
-rw-r--r--indra/newview/llkeyconflict.h214
-rw-r--r--indra/newview/llviewerkeyboard.cpp56
-rw-r--r--indra/newview/llviewerkeyboard.h6
-rw-r--r--indra/newview/llvoiceclient.cpp18
-rw-r--r--indra/newview/skins/default/xui/en/floater_select_key.xml40
-rw-r--r--indra/newview/skins/default/xui/en/panel_preferences_controls.xml161
14 files changed, 1798 insertions, 410 deletions
diff --git a/indra/llcommon/llkeybind.cpp b/indra/llcommon/llkeybind.cpp
index f227c0a1a5..765084bbf6 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 4fe622fb79..481949f275 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 @@ public:
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 @@ public:
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 763c3aeb81..6c30bdde17 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"),
@@ -166,6 +167,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 43e1c0d707..edfbaa6548 100644
--- a/indra/llui/llscrolllistctrl.h
+++ b/indra/llui/llscrolllistctrl.h
@@ -115,7 +115,8 @@ public:
// 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,
@@ -460,6 +461,7 @@ private:
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 33fa186a2e..340af8ece0 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
lllegacyatmospherics.cpp
@@ -1007,6 +1008,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 38ec8805a1..34cfe6ebb4 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;
}
@@ -1711,53 +1741,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()
@@ -1782,18 +1765,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");
@@ -2794,7 +2765,7 @@ void LLPanelPreferenceGraphics::setPresetText()
}
}
- if (hasDirtyChilds() && !preset_graphic_active.empty())
+ if (hasDirtyChilds() && !preset_graphic_active.empty())
{
gSavedSettings.setString("PresetGraphicActive", "");
preset_graphic_active.clear();
@@ -2914,98 +2885,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();
@@ -3013,81 +3010,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);
@@ -3095,23 +3082,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()
@@ -3124,61 +3152,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 bfccd64624..c36dee114c 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 @@ public:
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();
@@ -298,21 +298,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 0000000000..0f0129bf68
--- /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 0000000000..79bd9b8438
--- /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 6914e0fc2b..1c93a2e954 100644
--- a/indra/newview/llviewerkeyboard.cpp
+++ b/indra/newview/llviewerkeyboard.cpp
@@ -605,6 +605,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);
@@ -646,6 +652,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()
@@ -807,7 +814,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;
}
@@ -818,21 +825,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;
@@ -840,16 +851,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(),
@@ -857,12 +868,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 110dc89d28..2bfe285be4 100644
--- a/indra/newview/llviewerkeyboard.h
+++ b/indra/newview/llviewerkeyboard.h
@@ -71,8 +71,8 @@ public:
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 @@ public:
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 8951fcf368..f8588156c1 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"
@@ -692,33 +691,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 4e89df5a73..c00b805954 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 a1a2fd0598..1bfee20c81 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,10 +123,86 @@
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
</panel.string>
@@ -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>