diff options
author | andreykproductengine <andreykproductengine@lindenlab.com> | 2019-09-17 21:36:59 +0300 |
---|---|---|
committer | Andrey Kleshchev <andreykproductengine@lindenlab.com> | 2020-06-23 14:48:02 +0300 |
commit | 4df05c5a8995158922c7b7aacfef442ac8ae6fdd (patch) | |
tree | 8fa91984693507aad5722b5ac2cd7f5b87b03d68 | |
parent | 3633ccf1a1077d3e53e7f125d1dd5ed2aaa98906 (diff) |
SL-6109 Keyaboard support ready
-rw-r--r-- | indra/llcommon/llkeybind.cpp | 180 | ||||
-rw-r--r-- | indra/llcommon/llkeybind.h | 23 | ||||
-rw-r--r-- | indra/llui/llscrolllistctrl.cpp | 4 | ||||
-rw-r--r-- | indra/llui/llscrolllistctrl.h | 4 | ||||
-rw-r--r-- | indra/newview/CMakeLists.txt | 2 | ||||
-rw-r--r-- | indra/newview/llfloaterpreference.cpp | 680 | ||||
-rw-r--r-- | indra/newview/llfloaterpreference.h | 31 | ||||
-rw-r--r-- | indra/newview/llkeyconflict.cpp | 789 | ||||
-rw-r--r-- | indra/newview/llkeyconflict.h | 214 | ||||
-rw-r--r-- | indra/newview/llviewerkeyboard.cpp | 56 | ||||
-rw-r--r-- | indra/newview/llviewerkeyboard.h | 6 | ||||
-rw-r--r-- | indra/newview/llvoiceclient.cpp | 18 | ||||
-rw-r--r-- | indra/newview/skins/default/xui/en/floater_select_key.xml | 40 | ||||
-rw-r--r-- | indra/newview/skins/default/xui/en/panel_preferences_controls.xml | 161 |
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> |