From f8137f68a0f157c7dc7766a695a62d59b4198291 Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Tue, 23 Jun 2020 16:16:11 +0300 Subject: Reverted SL-6109 keybinding changes Changes were moved to DRTVWR-514 --- indra/newview/CMakeLists.txt | 10 +- indra/newview/app_settings/key_bindings.xml | 255 ---- indra/newview/app_settings/keys.xml | 363 +++++ indra/newview/app_settings/settings.xml | 10 +- indra/newview/llappviewer.cpp | 117 +- indra/newview/llfloaterpreference.cpp | 874 +++-------- indra/newview/llfloaterpreference.h | 69 +- indra/newview/llkeyconflict.cpp | 989 ------------- indra/newview/llkeyconflict.h | 174 --- indra/newview/llsetkeybinddialog.cpp | 346 ----- indra/newview/llsetkeybinddialog.h | 105 -- indra/newview/llspatialpartition.cpp | 2 +- indra/newview/lltool.cpp | 8 +- indra/newview/lltool.h | 2 +- indra/newview/lltoolpie.cpp | 258 ++-- indra/newview/lltoolpie.h | 7 +- indra/newview/llviewerinput.cpp | 1528 -------------------- indra/newview/llviewerinput.h | 177 --- indra/newview/llviewerkeyboard.cpp | 1039 +++++++++++++ indra/newview/llviewerkeyboard.h | 118 ++ indra/newview/llviewermenu.cpp | 1 + indra/newview/llviewerwindow.cpp | 114 +- indra/newview/llviewerwindow.h | 3 +- indra/newview/llvoiceclient.cpp | 65 + indra/newview/llvoiceclient.h | 10 +- indra/newview/llwindowlistener.cpp | 6 +- .../xui/en/control_table_contents_camera.xml | 178 --- .../en/control_table_contents_camera_sitting.xml | 65 - .../en/control_table_contents_columns_basic.xml | 19 - .../xui/en/control_table_contents_editing.xml | 80 - .../xui/en/control_table_contents_media.xml | 76 - .../xui/en/control_table_contents_movement.xml | 198 --- .../skins/default/xui/en/floater_preferences.xml | 7 - .../skins/default/xui/en/floater_select_key.xml | 63 +- .../newview/skins/default/xui/en/notifications.xml | 13 - .../default/xui/en/panel_preferences_controls.xml | 63 - .../default/xui/en/panel_preferences_sound.xml | 41 +- indra/newview/skins/default/xui/en/strings.xml | 9 - 38 files changed, 2119 insertions(+), 5343 deletions(-) delete mode 100644 indra/newview/app_settings/key_bindings.xml create mode 100644 indra/newview/app_settings/keys.xml delete mode 100644 indra/newview/llkeyconflict.cpp delete mode 100644 indra/newview/llkeyconflict.h delete mode 100644 indra/newview/llsetkeybinddialog.cpp delete mode 100644 indra/newview/llsetkeybinddialog.h delete mode 100644 indra/newview/llviewerinput.cpp delete mode 100644 indra/newview/llviewerinput.h create mode 100644 indra/newview/llviewerkeyboard.cpp create mode 100644 indra/newview/llviewerkeyboard.h delete mode 100644 indra/newview/skins/default/xui/en/control_table_contents_camera.xml delete mode 100644 indra/newview/skins/default/xui/en/control_table_contents_camera_sitting.xml delete mode 100644 indra/newview/skins/default/xui/en/control_table_contents_columns_basic.xml delete mode 100644 indra/newview/skins/default/xui/en/control_table_contents_editing.xml delete mode 100644 indra/newview/skins/default/xui/en/control_table_contents_media.xml delete mode 100644 indra/newview/skins/default/xui/en/control_table_contents_movement.xml delete mode 100644 indra/newview/skins/default/xui/en/panel_preferences_controls.xml (limited to 'indra/newview') diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt index 00d19e3ba4..33fa186a2e 100644 --- a/indra/newview/CMakeLists.txt +++ b/indra/newview/CMakeLists.txt @@ -376,7 +376,6 @@ set(viewer_SOURCE_FILES llinventoryobserver.cpp llinventorypanel.cpp lljoystickbutton.cpp - llkeyconflict.cpp lllandmarkactions.cpp lllandmarklist.cpp lllegacyatmospherics.cpp @@ -557,7 +556,6 @@ set(viewer_SOURCE_FILES llsecapi.cpp llsechandler_basic.cpp llselectmgr.cpp - llsetkeybinddialog.cpp llsettingspicker.cpp llsettingsvo.cpp llshareavatarhandler.cpp @@ -663,7 +661,7 @@ set(viewer_SOURCE_FILES llviewerjointattachment.cpp llviewerjointmesh.cpp llviewerjoystick.cpp - llviewerinput.cpp + llviewerkeyboard.cpp llviewerlayer.cpp llviewermedia.cpp llviewermedia_streamingaudio.cpp @@ -1009,7 +1007,6 @@ set(viewer_HEADER_FILES llinventoryobserver.h llinventorypanel.h lljoystickbutton.h - llkeyconflict.h lllandmarkactions.h lllandmarklist.h lllightconstants.h @@ -1180,7 +1177,6 @@ set(viewer_HEADER_FILES llsecapi.h llsechandler_basic.h llselectmgr.h - llsetkeybinddialog.h llsettingspicker.h llsettingsvo.h llsidepanelappearance.h @@ -1288,7 +1284,7 @@ set(viewer_HEADER_FILES llviewerjointattachment.h llviewerjointmesh.h llviewerjoystick.h - llviewerinput.h + llviewerkeyboard.h llviewerlayer.h llviewermedia.h llviewermediafocus.h @@ -1669,7 +1665,7 @@ set(viewer_APPSETTINGS_FILES app_settings/grass.xml app_settings/high_graphics.xml app_settings/ignorable_dialogs.xml - app_settings/key_bindings.xml + app_settings/keys.xml app_settings/keywords_lsl_default.xml app_settings/logcontrol.xml app_settings/low_graphics.xml diff --git a/indra/newview/app_settings/key_bindings.xml b/indra/newview/app_settings/key_bindings.xml deleted file mode 100644 index 4f6deb1f98..0000000000 --- a/indra/newview/app_settings/key_bindings.xml +++ /dev/null @@ -1,255 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/indra/newview/app_settings/keys.xml b/indra/newview/app_settings/keys.xml new file mode 100644 index 0000000000..a8037fec05 --- /dev/null +++ b/indra/newview/app_settings/keys.xml @@ -0,0 +1,363 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index b1606a0f3c..e8f01f7a60 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -3542,7 +3542,7 @@ DoubleClickAutoPilot Comment - (Obsolete)Enable double-click auto pilot + Enable double-click auto pilot Persist 1 Type @@ -3550,10 +3550,10 @@ Value 0 - DoubleClickTeleport + DoubleClickTeleport Comment - Enable double-click to teleport where allowed (afects minimap and people panel) + Enable double-click to teleport where allowed Persist 1 Type @@ -8274,7 +8274,7 @@ PushToTalkButton Comment - (Obsolete)Which button or keyboard key is used for push-to-talk + Which button or keyboard key is used for push-to-talk Persist 1 Type @@ -15559,7 +15559,7 @@ ClickToWalk Comment - (obsolete)Click in world to walk to location + Click in world to walk to location Persist 1 Type diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index f81f9557ff..59fc3500d9 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -49,7 +49,6 @@ #include "llwindow.h" #include "llviewerstats.h" #include "llviewerstatsrecorder.h" -#include "llkeyconflict.h" // for legacy keybinding support, remove later #include "llmarketplacefunctions.h" #include "llmarketplacenotifications.h" #include "llmd5.h" @@ -150,7 +149,7 @@ #include "llapr.h" #include -#include "llviewerinput.h" +#include "llviewerkeyboard.h" #include "lllfsthread.h" #include "llworkerthread.h" #include "lltexturecache.h" @@ -1003,113 +1002,20 @@ bool LLAppViewer::init() gGLManager.getGLInfo(gDebugInfo); gGLManager.printGLInfoString(); - // Load User's bindings - std::string key_bindings_file = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, "key_bindings.xml"); -#if 1 - // Legacy support - // Remove #if-#endif section half a year after DRTVWR-501 releases. - // Mouse actions are part of keybinding file since DRTVWR-501 instead of being stored in - // settings.xml. To support legacy viewers that were storing in settings.xml we need to - // transfer old variables to new format. - // Also part of backward compatibility is present in LLKeyConflictHandler to modify - // legacy variables on changes in new system (to make sure we won't enforce - // legacy values again if user dropped to defaults in new system) - if (LLVersionInfo::getChannelAndVersion() != gLastRunVersion - || !gDirUtilp->fileExists(key_bindings_file)) // if file is missing, assume that there were no changes by user yet - { - // copy mouse actions and voice key changes to new file - LL_INFOS("InitInfo") << "Converting legacy mouse bindings to new format" << LL_ENDL; - // Load settings from file - LLKeyConflictHandler third_person_view(LLKeyConflictHandler::MODE_THIRD_PERSON); - - // Since we are only modifying keybindings if personal file doesn't exist yet, - // it should be safe to just overwrite the value - // If key is already in use somewhere by default, LLKeyConflictHandler should resolve it. - BOOL value = gSavedSettings.getBOOL("DoubleClickAutoPilot"); - third_person_view.registerControl("walk_to", - 0, - value ? EMouseClickType::CLICK_DOUBLELEFT : EMouseClickType::CLICK_NONE, - KEY_NONE, - MASK_NONE, - value); - - U32 index = value ? 1 : 0; // we can store multiple combinations per action, so if first is in use by doubleclick, go to second - value = gSavedSettings.getBOOL("ClickToWalk"); - third_person_view.registerControl("walk_to", - index, - value ? EMouseClickType::CLICK_LEFT : EMouseClickType::CLICK_NONE, - KEY_NONE, - MASK_NONE, - value); - - value = gSavedSettings.getBOOL("DoubleClickTeleport"); - third_person_view.registerControl("teleport_to", - 0, - value ? EMouseClickType::CLICK_DOUBLELEFT : EMouseClickType::CLICK_NONE, - KEY_NONE, - MASK_NONE, - value); - - std::string key_string = gSavedSettings.getString("PushToTalkButton"); - EMouseClickType mouse = EMouseClickType::CLICK_NONE; - KEY key = KEY_NONE; - if (key_string == "MiddleMouse") - { - mouse = EMouseClickType::CLICK_MIDDLE; - } - else if (key_string == "MouseButton4") - { - mouse = EMouseClickType::CLICK_BUTTON4; - } - else if (key_string == "MouseButton5") - { - mouse = EMouseClickType::CLICK_BUTTON5; - } - else - { - LLKeyboard::keyFromString(key_string, &key); - } - - value = gSavedSettings.getBOOL("PushToTalkToggle"); - std::string control_name = value ? "toggle_voice" : "voice_follow_key"; - third_person_view.registerControl(control_name, 0, mouse, key, MASK_NONE, true); - - if (third_person_view.hasUnsavedChanges()) - { - // calls loadBindingsXML() - third_person_view.saveToSettings(); - } - - // in case of voice we need to repeat this in other modes (teleports and - // autopilot are not entirely practical when sitting or editing) - - for (U32 i = 0; i < LLKeyConflictHandler::MODE_COUNT - 1; ++i) - { - if (i != LLKeyConflictHandler::MODE_THIRD_PERSON) - { - LLKeyConflictHandler handler((LLKeyConflictHandler::ESourceMode)i); + // Load Default bindings + std::string key_bindings_file = gDirUtilp->findFile("keys.xml", + gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, ""), + gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "")); - handler.registerControl(control_name, 0, mouse, key, MASK_NONE, true); - if (handler.hasUnsavedChanges()) - { - // calls loadBindingsXML() - handler.saveToSettings(); - } - } - } - } - // since something might have gone wrong or there might have been nothing to save - // (and because otherwise following code will have to be encased in else{}), - // load everything one last time -#endif - if (!gDirUtilp->fileExists(key_bindings_file) || !gViewerInput.loadBindingsXML(key_bindings_file)) + if (!gViewerKeyboard.loadBindingsXML(key_bindings_file)) { - // Failed to load custom bindings, try default ones - key_bindings_file = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "key_bindings.xml"); - if (!gViewerInput.loadBindingsXML(key_bindings_file)) + std::string key_bindings_file = gDirUtilp->findFile("keys.ini", + gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, ""), + gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "")); + if (!gViewerKeyboard.loadBindings(key_bindings_file)) { - LL_ERRS("InitInfo") << "Unable to open default key bindings from " << key_bindings_file << LL_ENDL; + LL_ERRS("InitInfo") << "Unable to open keys.ini" << LL_ENDL; } } @@ -1544,7 +1450,6 @@ bool LLAppViewer::doFrame() { joystick->scanJoystick(); gKeyboard->scanKeyboard(); - gViewerInput.scanMouse(); } // Update state based on messages, user input, object idle. diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp index 91a3ae384d..81f4b2234c 100644 --- a/indra/newview/llfloaterpreference.cpp +++ b/indra/newview/llfloaterpreference.cpp @@ -71,9 +71,8 @@ #include "lltrans.h" #include "llviewercontrol.h" #include "llviewercamera.h" -#include "llviewereventrecorder.h" -#include "llviewermessage.h" #include "llviewerwindow.h" +#include "llviewermessage.h" #include "llviewershadermgr.h" #include "llviewerthrottle.h" #include "llvoavatarself.h" @@ -160,6 +159,87 @@ struct LabelTable : public LLInitParam::Block {} }; +class LLVoiceSetKeyDialog : public LLModalDialog +{ +public: + LLVoiceSetKeyDialog(const LLSD& key); + ~LLVoiceSetKeyDialog(); + + /*virtual*/ BOOL postBuild(); + + void setParent(LLFloaterPreference* parent) { mParent = parent; } + + BOOL handleKeyHere(KEY key, MASK mask); + BOOL handleAnyMouseClick(S32 x, S32 y, MASK mask, LLMouseHandler::EClickType clicktype, BOOL down); + static void onCancel(void* user_data); + +private: + LLFloaterPreference* mParent; +}; + +LLVoiceSetKeyDialog::LLVoiceSetKeyDialog(const LLSD& key) + : LLModalDialog(key), + mParent(NULL) +{ +} + +//virtual +BOOL LLVoiceSetKeyDialog::postBuild() +{ + childSetAction("Cancel", onCancel, this); + getChild("Cancel")->setFocus(TRUE); + + gFocusMgr.setKeystrokesOnly(TRUE); + + return TRUE; +} + +LLVoiceSetKeyDialog::~LLVoiceSetKeyDialog() +{ +} + +BOOL LLVoiceSetKeyDialog::handleKeyHere(KEY key, MASK mask) +{ + BOOL result = TRUE; + + if (key == 'Q' && mask == MASK_CONTROL) + { + result = FALSE; + } + else if (mParent) + { + mParent->setKey(key); + } + closeFloater(); + return result; +} + +BOOL LLVoiceSetKeyDialog::handleAnyMouseClick(S32 x, S32 y, MASK mask, LLMouseHandler::EClickType clicktype, BOOL down) +{ + BOOL result = FALSE; + if (down + && (clicktype == LLMouseHandler::CLICK_MIDDLE || clicktype == LLMouseHandler::CLICK_BUTTON4 || clicktype == LLMouseHandler::CLICK_BUTTON5) + && mask == 0) + { + mParent->setMouse(clicktype); + result = TRUE; + closeFloater(); + } + else + { + result = LLMouseHandler::handleAnyMouseClick(x, y, mask, clicktype, down); + } + + return result; +} + +//static +void LLVoiceSetKeyDialog::onCancel(void* user_data) +{ + LLVoiceSetKeyDialog* self = (LLVoiceSetKeyDialog*)user_data; + self->closeFloater(); +} + // global functions @@ -239,6 +319,37 @@ 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; @@ -263,7 +374,8 @@ LLFloaterPreference::LLFloaterPreference(const LLSD& key) mGotPersonalInfo(false), mOriginalIMViaEmail(false), mLanguageChanged(false), - mAvatarDataInitialized(false) + mAvatarDataInitialized(false), + mClickActionDirty(false) { LLConversationLog::instance().addObserver(this); @@ -272,7 +384,7 @@ LLFloaterPreference::LLFloaterPreference(const LLSD& key) static bool registered_dialog = false; if (!registered_dialog) { - LLFloaterReg::add("keybind_dialog", "floater_select_key.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); + LLFloaterReg::add("voice_set_key", "floater_select_key.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); registered_dialog = true; } @@ -285,6 +397,8 @@ LLFloaterPreference::LLFloaterPreference(const LLSD& key) mCommitCallbackRegistrar.add("Pref.ResetCache", boost::bind(&LLFloaterPreference::onClickResetCache, this)); mCommitCallbackRegistrar.add("Pref.ClickSkin", boost::bind(&LLFloaterPreference::onClickSkin, this,_1, _2)); mCommitCallbackRegistrar.add("Pref.SelectSkin", boost::bind(&LLFloaterPreference::onSelectSkin, this)); + mCommitCallbackRegistrar.add("Pref.VoiceSetKey", boost::bind(&LLFloaterPreference::onClickSetKey, this)); + mCommitCallbackRegistrar.add("Pref.VoiceSetMiddleMouse", boost::bind(&LLFloaterPreference::onClickSetMiddleMouse, this)); mCommitCallbackRegistrar.add("Pref.SetSounds", boost::bind(&LLFloaterPreference::onClickSetSounds, this)); mCommitCallbackRegistrar.add("Pref.ClickEnablePopup", boost::bind(&LLFloaterPreference::onClickEnablePopup, this)); mCommitCallbackRegistrar.add("Pref.ClickDisablePopup", boost::bind(&LLFloaterPreference::onClickDisablePopup, this)); @@ -579,6 +693,12 @@ void LLFloaterPreference::apply() } saveAvatarProperties(); + + if (mClickActionDirty) + { + updateClickActionSettings(); + mClickActionDirty = false; + } } void LLFloaterPreference::cancel() @@ -611,7 +731,11 @@ void LLFloaterPreference::cancel() // reverts any changes to current skin gSavedSettings.setString("SkinCurrent", sSkin); - updateClickActionViews(); + if (mClickActionDirty) + { + updateClickActionControls(); + mClickActionDirty = false; + } LLFloaterPreferenceProxy * advanced_proxy_settings = LLFloaterReg::findTypedInstance("prefs_proxy"); if (advanced_proxy_settings) @@ -696,7 +820,7 @@ void LLFloaterPreference::onOpen(const LLSD& key) onChangeAnimationFolder(); // Load (double-)click to walk/teleport settings. - updateClickActionViews(); + updateClickActionControls(); // Enabled/disabled popups, might have been changed by user actions // while preferences floater was closed. @@ -1493,7 +1617,6 @@ void LLFloaterPreference::refresh() { advanced->refresh(); } - updateClickActionViews(); } void LLFloaterPreferenceGraphicsAdvanced::refresh() @@ -1535,6 +1658,72 @@ void LLFloaterPreference::onChangeQuality(const LLSD& data) refresh(); } +void LLFloaterPreference::onClickSetKey() +{ + LLVoiceSetKeyDialog* dialog = LLFloaterReg::showTypedInstance("voice_set_key", LLSD(), TRUE); + if (dialog) + { + dialog->setParent(this); + } +} + +void LLFloaterPreference::setKey(KEY key) +{ + getChild("modifier_combo")->setValue(LLKeyboard::stringFromKey(key)); + // update the control right away since we no longer wait for apply + getChild("modifier_combo")->onCommit(); +} + +void LLFloaterPreference::setMouse(LLMouseHandler::EClickType click) +{ + std::string bt_name; + std::string ctrl_value; + switch (click) + { + case LLMouseHandler::CLICK_MIDDLE: + bt_name = "middle_mouse"; + ctrl_value = MIDDLE_MOUSE_CV; + break; + case LLMouseHandler::CLICK_BUTTON4: + bt_name = "button4_mouse"; + ctrl_value = MOUSE_BUTTON_4_CV; + break; + case LLMouseHandler::CLICK_BUTTON5: + bt_name = "button5_mouse"; + ctrl_value = MOUSE_BUTTON_5_CV; + break; + default: + break; + } + + if (!ctrl_value.empty()) + { + LLUICtrl* p2t_line_editor = getChild("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(p2t_line_editor->getParent()); + if (advanced_preferences) + { + p2t_line_editor->setValue(advanced_preferences->getString(bt_name)); + } + } +} + +void LLFloaterPreference::onClickSetMiddleMouse() +{ + LLUICtrl* p2t_line_editor = getChild("modifier_combo"); + + // update the control right away since we no longer wait for apply + p2t_line_editor->setControlValue(MIDDLE_MOUSE_CV); + + //push2talk button "middle mouse" control value is in English, need to localize it for presentation + LLPanel* advanced_preferences = dynamic_cast(p2t_line_editor->getParent()); + if (advanced_preferences) + { + p2t_line_editor->setValue(advanced_preferences->getString("middle_mouse")); + } +} + void LLFloaterPreference::onClickSetSounds() { // Disable Enable gesture sounds checkbox if the master sound is disabled @@ -1542,6 +1731,18 @@ void LLFloaterPreference::onClickSetSounds() getChild("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("disabled_popups"); @@ -2005,7 +2206,7 @@ void LLFloaterPreference::onClickAdvanced() void LLFloaterPreference::onClickActionChange() { - updateClickActionControls(); + mClickActionDirty = true; } void LLFloaterPreference::onClickPermsDefault() @@ -2045,81 +2246,21 @@ void LLFloaterPreference::onLogChatHistorySaved() } } -void LLFloaterPreference::updateClickActionControls() +void LLFloaterPreference::updateClickActionSettings() { - const int single_clk_action = getChild("single_click_action_combo")->getValue().asInteger(); - const int double_clk_action = getChild("double_click_action_combo")->getValue().asInteger(); - - // Todo: This is a very ugly way to get access to keybindings. - // Reconsider possible options. - // Potential option: make constructor of LLKeyConflictHandler private - // but add a getter that will return shared pointer for specific - // mode, pointer should only exist so long as there are external users. - // In such case we won't need to do this 'dynamic_cast' nightmare. - // updateTable() can also be avoided - LLTabContainer* tabcontainer = getChild("pref core"); - for (child_list_t::const_iterator iter = tabcontainer->getChildList()->begin(); - iter != tabcontainer->getChildList()->end(); ++iter) - { - LLView* view = *iter; - LLPanelPreferenceControls* panel = dynamic_cast(view); - if (panel) - { - panel->setKeyBind("walk_to", - EMouseClickType::CLICK_LEFT, - KEY_NONE, - MASK_NONE, - single_clk_action == 1); - - panel->setKeyBind("walk_to", - EMouseClickType::CLICK_DOUBLELEFT, - KEY_NONE, - MASK_NONE, - double_clk_action == 1); - - panel->setKeyBind("teleport_to", - EMouseClickType::CLICK_DOUBLELEFT, - KEY_NONE, - MASK_NONE, - double_clk_action == 2); - - panel->updateAndApply(); - } - } + const int single_clk_action = getChild("single_click_action_combo")->getValue().asInteger(); + const int double_clk_action = getChild("double_click_action_combo")->getValue().asInteger(); + + gSavedSettings.setBOOL("ClickToWalk", single_clk_action == 1); + gSavedSettings.setBOOL("DoubleClickAutoPilot", double_clk_action == 1); + gSavedSettings.setBOOL("DoubleClickTeleport", double_clk_action == 2); } -void LLFloaterPreference::updateClickActionViews() +void LLFloaterPreference::updateClickActionControls() { - bool click_to_walk = false; - bool dbl_click_to_walk = false; - bool dbl_click_to_teleport = false; - - // Todo: This is a very ugly way to get access to keybindings. - // Reconsider possible options. - LLTabContainer* tabcontainer = getChild("pref core"); - for (child_list_t::const_iterator iter = tabcontainer->getChildList()->begin(); - iter != tabcontainer->getChildList()->end(); ++iter) - { - LLView* view = *iter; - LLPanelPreferenceControls* panel = dynamic_cast(view); - if (panel) - { - click_to_walk = panel->canKeyBindHandle("walk_to", - EMouseClickType::CLICK_LEFT, - KEY_NONE, - MASK_NONE); - - dbl_click_to_walk = panel->canKeyBindHandle("walk_to", - EMouseClickType::CLICK_DOUBLELEFT, - KEY_NONE, - MASK_NONE); - - dbl_click_to_teleport = panel->canKeyBindHandle("teleport_to", - EMouseClickType::CLICK_DOUBLELEFT, - KEY_NONE, - MASK_NONE); - } - } + const bool click_to_walk = gSavedSettings.getBOOL("ClickToWalk"); + const bool dbl_click_to_walk = gSavedSettings.getBOOL("DoubleClickAutoPilot"); + const bool dbl_click_to_teleport = gSavedSettings.getBOOL("DoubleClickTeleport"); getChild("single_click_action_combo")->setValue((int)click_to_walk); getChild("double_click_action_combo")->setValue(dbl_click_to_teleport ? 2 : (int)dbl_click_to_walk); @@ -2306,6 +2447,25 @@ BOOL LLPanelPreference::postBuild() getChild("mute_chb_label")->setClickedCallback(boost::bind(&toggleMuteWhenMinimized)); } + //////////////////////PanelAdvanced /////////////////// + if (hasChild("modifier_combo", TRUE)) + { + //localizing if push2talk button is set to middle mouse + std::string modifier_value = getChild("modifier_combo")->getValue().asString(); + if (MIDDLE_MOUSE_CV == modifier_value) + { + getChild("modifier_combo")->setValue(getString("middle_mouse")); + } + else if (MOUSE_BUTTON_4_CV == modifier_value) + { + getChild("modifier_combo")->setValue(getString("button4_mouse")); + } + else if (MOUSE_BUTTON_5_CV == modifier_value) + { + getChild("modifier_combo")->setValue(getString("button5_mouse")); + } + } + //////////////////////PanelSetup /////////////////// if (hasChild("max_bandwidth"), TRUE) { @@ -2583,7 +2743,7 @@ void LLPanelPreferenceGraphics::setPresetText() } } - if (hasDirtyChilds() && !preset_graphic_active.empty()) + if (hasDirtyChilds() && !preset_graphic_active.empty()) { gSavedSettings.setString("PresetGraphicActive", ""); preset_graphic_active.clear(); @@ -2703,560 +2863,6 @@ void LLPanelPreferenceGraphics::setHardwareDefaults() resetDirtyChilds(); } -//------------------------LLPanelPreferenceControls-------------------------------- -static LLPanelInjector t_pref_contrls("panel_preference_controls"); - -LLPanelPreferenceControls::LLPanelPreferenceControls() - :LLPanelPreference(), - mEditingColumn(-1), - mEditingMode(0) -{ - // MODE_COUNT - 1 because there are currently no settings assigned to 'saved settings'. - for (U32 i = 0; i < LLKeyConflictHandler::MODE_COUNT - 1; ++i) - { - mConflictHandler[i].setLoadMode((LLKeyConflictHandler::ESourceMode)i); - } -} - -LLPanelPreferenceControls::~LLPanelPreferenceControls() -{ -} - -BOOL LLPanelPreferenceControls::postBuild() -{ - // populate list of controls - pControlsTable = getChild("controls_list"); - pKeyModeBox = getChild("key_mode"); - - pControlsTable->setCommitCallback(boost::bind(&LLPanelPreferenceControls::onListCommit, this)); - pKeyModeBox->setCommitCallback(boost::bind(&LLPanelPreferenceControls::onModeCommit, this)); - getChild("restore_defaults")->setCommitCallback(boost::bind(&LLPanelPreferenceControls::onRestoreDefaultsBtn, this)); - - return TRUE; -} - -void LLPanelPreferenceControls::regenerateControls() -{ - mEditingMode = pKeyModeBox->getValue().asInteger(); - mConflictHandler[mEditingMode].loadFromSettings((LLKeyConflictHandler::ESourceMode)mEditingMode); - populateControlTable(); -} - -bool LLPanelPreferenceControls::addControlTableColumns(const std::string &filename) -{ - LLXMLNodePtr xmlNode; - LLScrollListCtrl::Contents contents; - if (!LLUICtrlFactory::getLayeredXMLNode(filename, xmlNode)) - { - LL_WARNS() << "Failed to load " << filename << LL_ENDL; - return false; - } - LLXUIParser parser; - parser.readXUI(xmlNode, contents, filename); - - if (!contents.validateBlock()) - { - return false; - } - - for (LLInitParam::ParamIterator::const_iterator col_it = contents.columns.begin(); - col_it != contents.columns.end(); - ++col_it) - { - pControlsTable->addColumn(*col_it); - } - - return true; -} - -bool LLPanelPreferenceControls::addControlTableRows(const std::string &filename) -{ - LLXMLNodePtr xmlNode; - LLScrollListCtrl::Contents contents; - if (!LLUICtrlFactory::getLayeredXMLNode(filename, xmlNode)) - { - LL_WARNS() << "Failed to load " << filename << LL_ENDL; - return false; - } - LLXUIParser parser; - parser.readXUI(xmlNode, contents, filename); - - if (!contents.validateBlock()) - { - return false; - } - - LLScrollListCell::Params cell_params; - // init basic cell params - cell_params.font = LLFontGL::getFontSansSerif(); - cell_params.font_halign = LLFontGL::LEFT; - cell_params.column = ""; - cell_params.value = ""; - - - for (LLInitParam::ParamIterator::const_iterator row_it = contents.rows.begin(); - row_it != contents.rows.end(); - ++row_it) - { - std::string control = row_it->value.getValue().asString(); - if (!control.empty() && control != "menu_separator") - { - // At the moment viewer is hardcoded to assume that there are 4 collumns - LLScrollListItem::Params item_params(*row_it); - bool enabled = mConflictHandler[mEditingMode].canAssignControl(control); - item_params.enabled.setValue(enabled); - cell_params.column = "lst_ctrl1"; - cell_params.value = mConflictHandler[mEditingMode].getControlString(control, 0); - item_params.columns.add(cell_params); - cell_params.column = "lst_ctrl2"; - cell_params.value = mConflictHandler[mEditingMode].getControlString(control, 1); - item_params.columns.add(cell_params); - cell_params.column = "lst_ctrl3"; - cell_params.value = mConflictHandler[mEditingMode].getControlString(control, 2); - item_params.columns.add(cell_params); - pControlsTable->addRow(item_params, EAddPosition::ADD_BOTTOM); - } - else - { - // Separator example: - // - // - // - pControlsTable->addRow(*row_it, EAddPosition::ADD_BOTTOM); - } - } - return true; -} - -void LLPanelPreferenceControls::addControlTableSeparator() -{ - pControlsTable->addSeparator(EAddPosition::ADD_BOTTOM); -} - -void LLPanelPreferenceControls::populateControlTable() -{ - pControlsTable->clearRows(); - pControlsTable->clearColumns(); - - // add columns - std::string filename; - switch ((LLKeyConflictHandler::ESourceMode)mEditingMode) - { - case LLKeyConflictHandler::MODE_THIRD_PERSON: - case LLKeyConflictHandler::MODE_FIRST_PERSON: - case LLKeyConflictHandler::MODE_EDIT_AVATAR: - case LLKeyConflictHandler::MODE_SITTING: - filename = "control_table_contents_columns_basic.xml"; - break; - default: - // Either unknown mode or MODE_SAVED_SETTINGS - // It doesn't have UI or actual settings yet - LL_INFOS() << "Unimplemented mode" << LL_ENDL; - return; - } - addControlTableColumns(filename); - - - if (mEditingMode == LLKeyConflictHandler::MODE_FIRST_PERSON) - { - addControlTableRows("control_table_contents_movement.xml"); - addControlTableSeparator(); - addControlTableRows("control_table_contents_media.xml"); - } - // MODE_THIRD_PERSON; MODE_EDIT_AVATAR; MODE_SITTING - else if (mEditingMode < LLKeyConflictHandler::MODE_SAVED_SETTINGS) - { - // In case of 'sitting' mode, movements still apply due to vehicles - addControlTableRows("control_table_contents_movement.xml"); - addControlTableSeparator(); - - addControlTableRows("control_table_contents_camera.xml"); - if (mEditingMode == LLKeyConflictHandler::MODE_SITTING) - { - addControlTableRows("control_table_contents_camera_sitting.xml"); - } - addControlTableSeparator(); - - addControlTableRows("control_table_contents_editing.xml"); - addControlTableSeparator(); - - addControlTableRows("control_table_contents_media.xml"); - } - else - { - LL_INFOS() << "Unimplemented mode" << LL_ENDL; - return; - } -} - -void LLPanelPreferenceControls::updateTable() -{ - mEditingControl.clear(); - std::vector list = pControlsTable->getAllData(); - for (S32 i = 0; i < list.size(); ++i) - { - std::string control = list[i]->getValue(); - if (!control.empty()) - { - LLScrollListCell* cell = list[i]->getColumn(1); - cell->setValue(mConflictHandler[mEditingMode].getControlString(control, 0)); - cell = list[i]->getColumn(2); - cell->setValue(mConflictHandler[mEditingMode].getControlString(control, 1)); - cell = list[i]->getColumn(3); - cell->setValue(mConflictHandler[mEditingMode].getControlString(control, 2)); - } - } - pControlsTable->deselectAllItems(); -} - -void LLPanelPreferenceControls::apply() -{ - for (U32 i = 0; i < LLKeyConflictHandler::MODE_COUNT - 1; ++i) - { - if (mConflictHandler[i].hasUnsavedChanges()) - { - mConflictHandler[i].saveToSettings(); - } - } -} - -void LLPanelPreferenceControls::cancel() -{ - for (U32 i = 0; i < LLKeyConflictHandler::MODE_COUNT - 1; ++i) - { - if (mConflictHandler[i].hasUnsavedChanges()) - { - mConflictHandler[i].clear(); - } - } - pControlsTable->clearRows(); - pControlsTable->clearColumns(); -} - -void LLPanelPreferenceControls::saveSettings() -{ - for (U32 i = 0; i < LLKeyConflictHandler::MODE_COUNT - 1; ++i) - { - if (mConflictHandler[i].hasUnsavedChanges()) - { - mConflictHandler[i].saveToSettings(); - mConflictHandler[i].clear(); - } - } - - S32 mode = pKeyModeBox->getValue().asInteger(); - if (mConflictHandler[mode].empty() || pControlsTable->isEmpty()) - { - regenerateControls(); - } -} - -void LLPanelPreferenceControls::resetDirtyChilds() -{ - regenerateControls(); -} - -void LLPanelPreferenceControls::onListCommit() -{ - LLScrollListItem* item = pControlsTable->getFirstSelected(); - if (item == NULL) - { - return; - } - - std::string control = item->getValue(); - - if (control.empty()) - { - pControlsTable->deselectAllItems(); - return; - } - - if (!mConflictHandler[mEditingMode].canAssignControl(control)) - { - pControlsTable->deselectAllItems(); - return; - } - - S32 cell_ind = item->getSelectedCell(); - if (cell_ind <= 0) - { - pControlsTable->deselectAllItems(); - 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 - - LLScrollListCell* cell = item->getColumn(cell_ind); - if (cell) - { - LLSetKeyBindDialog* dialog = LLFloaterReg::getTypedInstance("keybind_dialog", LLSD()); - if (dialog) - { - mEditingControl = control; - mEditingColumn = cell_ind; - dialog->setParent(this, pControlsTable, DEFAULT_KEY_FILTER); - - LLFloater* root_floater = gFloaterView->getParentFloater(this); - if (root_floater) - root_floater->addDependentFloater(dialog); - dialog->openFloater(); - dialog->setFocus(TRUE); - } - } - else - { - pControlsTable->deselectAllItems(); - } -} - -void LLPanelPreferenceControls::onModeCommit() -{ - mEditingMode = pKeyModeBox->getValue().asInteger(); - if (mConflictHandler[mEditingMode].empty()) - { - // opening for first time - mConflictHandler[mEditingMode].loadFromSettings((LLKeyConflictHandler::ESourceMode)mEditingMode); - } - populateControlTable(); -} - -void LLPanelPreferenceControls::onRestoreDefaultsBtn() -{ - LLNotificationsUtil::add("PreferenceControlsDefaults", LLSD(), LLSD(), boost::bind(&LLPanelPreferenceControls::onRestoreDefaultsResponse, this, _1, _2)); -} - -void LLPanelPreferenceControls::onRestoreDefaultsResponse(const LLSD& notification, const LLSD& response) -{ - S32 option = LLNotificationsUtil::getSelectedOption(notification, response); - switch(option) - { - case 0: // All - for (U32 i = 0; i < LLKeyConflictHandler::MODE_COUNT - 1; ++i) - { - mConflictHandler[i].resetToDefaults(); - // Apply changes to viewer as 'temporary' - mConflictHandler[i].saveToSettings(true); - - // notify comboboxes in move&view about potential change - LLFloaterPreference* instance = LLFloaterReg::findTypedInstance("preferences"); - if (instance) - { - instance->updateClickActionViews(); - } - } - - updateTable(); - break; - case 1: // Current - mConflictHandler[mEditingMode].resetToDefaults(); - // Apply changes to viewer as 'temporary' - mConflictHandler[mEditingMode].saveToSettings(true); - - if (mEditingMode == LLKeyConflictHandler::MODE_THIRD_PERSON) - { - // notify comboboxes in move&view about potential change - LLFloaterPreference* instance = LLFloaterReg::findTypedInstance("preferences"); - if (instance) - { - instance->updateClickActionViews(); - } - } - - updateTable(); - break; - case 2: // Cancel - default: - //exit; - break; - } -} - -// Bypass to let Move & view read values without need to create own key binding handler -// Assumes third person view -// Might be better idea to just move whole mConflictHandler into LLFloaterPreference -bool LLPanelPreferenceControls::canKeyBindHandle(const std::string &control, EMouseClickType click, KEY key, MASK mask) -{ - S32 mode = LLKeyConflictHandler::MODE_THIRD_PERSON; - if (mConflictHandler[mode].empty()) - { - // opening for first time - mConflictHandler[mode].loadFromSettings(LLKeyConflictHandler::MODE_THIRD_PERSON); - } - - return mConflictHandler[mode].canHandleControl(control, click, key, mask); -} - -// Bypass to let Move & view modify values without need to create own key binding handler -// Assumes third person view -// Might be better idea to just move whole mConflictHandler into LLFloaterPreference -void LLPanelPreferenceControls::setKeyBind(const std::string &control, EMouseClickType click, KEY key, MASK mask, bool set) -{ - S32 mode = LLKeyConflictHandler::MODE_THIRD_PERSON; - if (mConflictHandler[mode].empty()) - { - // opening for first time - mConflictHandler[mode].loadFromSettings(LLKeyConflictHandler::MODE_THIRD_PERSON); - } - - if (!mConflictHandler[mode].canAssignControl(mEditingControl)) - { - return; - } - - bool already_recorded = mConflictHandler[mode].canHandleControl(control, click, key, mask); - if (set) - { - if (already_recorded) - { - // nothing to do - return; - } - - // find free spot to add data, if no free spot, assign to first - S32 index = 0; - for (S32 i = 0; i < 3; i++) - { - if (mConflictHandler[mode].getControl(control, i).isEmpty()) - { - index = i; - break; - } - } - mConflictHandler[mode].registerControl(control, index, click, key, mask, true); - } - else if (!set) - { - if (!already_recorded) - { - // nothing to do - return; - } - - // find specific control and reset it - for (S32 i = 0; i < 3; i++) - { - LLKeyData data = mConflictHandler[mode].getControl(control, i); - if (data.mMouse == click && data.mKey == key && data.mMask == mask) - { - mConflictHandler[mode].clearControl(control, i); - } - } - } -} - -void LLPanelPreferenceControls::updateAndApply() -{ - S32 mode = LLKeyConflictHandler::MODE_THIRD_PERSON; - mConflictHandler[mode].saveToSettings(true); - updateTable(); -} - -// from LLSetKeybindDialog's interface -bool LLPanelPreferenceControls::onSetKeyBind(EMouseClickType click, KEY key, MASK mask, bool all_modes) -{ - if (!mConflictHandler[mEditingMode].canAssignControl(mEditingControl)) - { - return true; - } - - if ( mEditingColumn > 0) - { - if (all_modes) - { - for (U32 i = 0; i < LLKeyConflictHandler::MODE_COUNT - 1; ++i) - { - if (mConflictHandler[i].empty()) - { - mConflictHandler[i].loadFromSettings((LLKeyConflictHandler::ESourceMode)i); - } - mConflictHandler[i].registerControl(mEditingControl, mEditingColumn - 1, click, key, mask, true); - // Apply changes to viewer as 'temporary' - mConflictHandler[i].saveToSettings(true); - } - } - else - { - mConflictHandler[mEditingMode].registerControl(mEditingControl, mEditingColumn - 1, click, key, mask, true); - // Apply changes to viewer as 'temporary' - mConflictHandler[mEditingMode].saveToSettings(true); - } - } - - updateTable(); - - if ((mEditingMode == LLKeyConflictHandler::MODE_THIRD_PERSON || all_modes) - && (mEditingControl == "walk_to" - || mEditingControl == "teleport_to" - || click == CLICK_LEFT - || click == CLICK_DOUBLELEFT)) - { - // notify comboboxes in move&view about potential change - LLFloaterPreference* instance = LLFloaterReg::findTypedInstance("preferences"); - if (instance) - { - instance->updateClickActionViews(); - } - } - - return true; -} - -void LLPanelPreferenceControls::onDefaultKeyBind(bool all_modes) -{ - if (!mConflictHandler[mEditingMode].canAssignControl(mEditingControl)) - { - return; - } - - if (mEditingColumn > 0) - { - if (all_modes) - { - for (U32 i = 0; i < LLKeyConflictHandler::MODE_COUNT - 1; ++i) - { - if (mConflictHandler[i].empty()) - { - mConflictHandler[i].loadFromSettings((LLKeyConflictHandler::ESourceMode)i); - } - mConflictHandler[i].resetToDefault(mEditingControl, mEditingColumn - 1); - // Apply changes to viewer as 'temporary' - mConflictHandler[i].saveToSettings(true); - } - } - else - { - mConflictHandler[mEditingMode].resetToDefault(mEditingControl, mEditingColumn - 1); - // Apply changes to viewer as 'temporary' - mConflictHandler[mEditingMode].saveToSettings(true); - } - } - updateTable(); - - if (mEditingMode == LLKeyConflictHandler::MODE_THIRD_PERSON || all_modes) - { - // notify comboboxes in move&view about potential change - LLFloaterPreference* instance = LLFloaterReg::findTypedInstance("preferences"); - if (instance) - { - instance->updateClickActionViews(); - } - } -} - -void LLPanelPreferenceControls::onCancelKeyBind() -{ - pControlsTable->deselectAllItems(); -} - LLFloaterPreferenceGraphicsAdvanced::LLFloaterPreferenceGraphicsAdvanced(const LLSD& key) : LLFloater(key) { diff --git a/indra/newview/llfloaterpreference.h b/indra/newview/llfloaterpreference.h index 1268935712..526214a617 100644 --- a/indra/newview/llfloaterpreference.h +++ b/indra/newview/llfloaterpreference.h @@ -37,17 +37,13 @@ #include "llavatarpropertiesprocessor.h" #include "llconversationlog.h" #include "llsearcheditor.h" -#include "llsetkeybinddialog.h" -#include "llkeyconflict.h" class LLConversationLogObserver; class LLPanelPreference; class LLPanelLCD; class LLPanelDebug; class LLMessageSystem; -class LLComboBox; class LLScrollListCtrl; -class LLScrollListCell; class LLSliderCtrl; class LLSD; class LLTextBox; @@ -106,8 +102,6 @@ public: void selectPrivacyPanel(); void selectChatPanel(); void getControlNames(std::vector& names); - // updates click/double-click action controls depending on values from settings.xml - void updateClickActionViews(); protected: void onBtnOK(const LLSD& userdata); @@ -135,7 +129,9 @@ protected: // callback for commit in the "Single click on land" and "Double click on land" comboboxes. void onClickActionChange(); - // updates click/double-click action keybindngs depending on view values + // updates click/double-click action settings depending on controls values + void updateClickActionSettings(); + // updates click/double-click action controls depending on values from settings.xml void updateClickActionControls(); public: @@ -150,6 +146,10 @@ public: void onClickResetCache(); void onClickSkin(LLUICtrl* ctrl,const LLSD& userdata); void onSelectSkin(); + void onClickSetKey(); + void setKey(KEY key); + void setMouse(LLMouseHandler::EClickType click); + void onClickSetMiddleMouse(); void onClickSetSounds(); void onClickEnablePopup(); void onClickDisablePopup(); @@ -204,6 +204,7 @@ private: static std::string sSkin; notifications_map mNotificationOptions; + bool mClickActionDirty; ///< Set to true when the click/double-click options get changed by user. bool mGotPersonalInfo; bool mOriginalIMViaEmail; bool mLanguageChanged; @@ -293,60 +294,6 @@ private: LOG_CLASS(LLPanelPreferenceGraphics); }; -class LLPanelPreferenceControls : public LLPanelPreference, public LLKeyBindResponderInterface -{ - LOG_CLASS(LLPanelPreferenceControls); -public: - LLPanelPreferenceControls(); - virtual ~LLPanelPreferenceControls(); - - BOOL postBuild(); - - void apply(); - void cancel(); - void saveSettings(); - void resetDirtyChilds(); - - void onListCommit(); - void onModeCommit(); - void onRestoreDefaultsBtn(); - void onRestoreDefaultsResponse(const LLSD& notification, const LLSD& response); - - // Bypass to let Move & view read values without need to create own key binding handler - // Todo: consider a better way to share access to keybindings - bool canKeyBindHandle(const std::string &control, EMouseClickType click, KEY key, MASK mask); - // Bypasses to let Move & view modify values without need to create own key binding handler - void setKeyBind(const std::string &control, EMouseClickType click, KEY key, MASK mask, bool set /*set or reset*/ ); - void updateAndApply(); - - // from interface - /*virtual*/ bool onSetKeyBind(EMouseClickType click, KEY key, MASK mask, bool all_modes); - /*virtual*/ void onDefaultKeyBind(bool all_modes); - /*virtual*/ void onCancelKeyBind(); - -private: - // reloads settings, discards current changes, updates table - void regenerateControls(); - - // These fuctions do not clean previous content - bool addControlTableColumns(const std::string &filename); - bool addControlTableRows(const std::string &filename); - void addControlTableSeparator(); - - // Cleans content and then adds content from xml files according to current mEditingMode - void populateControlTable(); - - // Updates keybindings from storage to table - void updateTable(); - - LLScrollListCtrl* pControlsTable; - LLComboBox *pKeyModeBox; - LLKeyConflictHandler mConflictHandler[LLKeyConflictHandler::MODE_COUNT]; - std::string mEditingControl; - S32 mEditingColumn; - S32 mEditingMode; -}; - class LLFloaterPreferenceGraphicsAdvanced : public LLFloater { public: diff --git a/indra/newview/llkeyconflict.cpp b/indra/newview/llkeyconflict.cpp deleted file mode 100644 index 8ee50d5c52..0000000000 --- a/indra/newview/llkeyconflict.cpp +++ /dev/null @@ -1,989 +0,0 @@ -/** - * @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 "lltrans.h" -#include "llviewercontrol.h" -#include "llviewerinput.h" -#include "llviewermenu.h" -#include "llxuiparser.h" - -static const std::string saved_settings_key_controls[] = { "placeholder" }; // add settings from gSavedSettings here - -static const std::string filename_default = "key_bindings.xml"; -static const std::string filename_temporary = "key_bindings_tmp.xml"; // used to apply uncommited changes on the go. - -// 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; -} - -std::string string_from_mouse(EMouseClickType click, bool translate) -{ - std::string res; - switch (click) - { - case CLICK_LEFT: - res = "LMB"; - break; - case CLICK_MIDDLE: - res = "MMB"; - break; - case CLICK_RIGHT: - res = "RMB"; - break; - case CLICK_BUTTON4: - res = "MB4"; - break; - case CLICK_BUTTON5: - res = "MB5"; - break; - case CLICK_DOUBLELEFT: - res = "Double LMB"; - break; - default: - break; - } - - if (translate && !res.empty()) - { - res = LLTrans::getString(res); - } - return res; -} - -// LLKeyConflictHandler - -S32 LLKeyConflictHandler::sTemporaryFileUseCount = 0; - -LLKeyConflictHandler::LLKeyConflictHandler() -: mHasUnsavedChanges(false), - mUsesTemporaryFile(false), - mLoadMode(MODE_COUNT) -{ -} - -LLKeyConflictHandler::LLKeyConflictHandler(ESourceMode mode) -: mHasUnsavedChanges(false), - mUsesTemporaryFile(false), - mLoadMode(mode) -{ - loadFromSettings(mode); -} - -LLKeyConflictHandler::~LLKeyConflictHandler() -{ - clearUnsavedChanges(); - // Note: does not reset bindings if temporary file was used -} - -bool LLKeyConflictHandler::canHandleControl(const std::string &control_name, EMouseClickType mouse_ind, KEY key, MASK mask) -{ - return mControlsMap[control_name].canHandle(mouse_ind, key, mask); -} - -bool LLKeyConflictHandler::canHandleKey(const std::string &control_name, KEY key, MASK mask) -{ - return canHandleControl(control_name, CLICK_NONE, key, mask); -} - -bool LLKeyConflictHandler::canHandleMouse(const std::string &control_name, EMouseClickType mouse_ind, MASK mask) -{ - return canHandleControl(control_name, mouse_ind, KEY_NONE, mask); -} - -bool LLKeyConflictHandler::canHandleMouse(const std::string &control_name, S32 mouse_ind, MASK mask) -{ - return canHandleControl(control_name, (EMouseClickType)mouse_ind, KEY_NONE, mask); -} - -bool LLKeyConflictHandler::canAssignControl(const std::string &control_name) -{ - control_map_t::iterator iter = mControlsMap.find(control_name); - if (iter != mControlsMap.end()) - { - return iter->second.mAssignable; - } - // If we don't know this control, means it wasn't assigned by user yet and thus is editable - return true; -} - -// static -bool LLKeyConflictHandler::isReservedByMenu(const KEY &key, const MASK &mask) -{ - if (key == KEY_NONE) - { - return false; - } - return (gMenuBarView && gMenuBarView->hasAccelerator(key, mask)) - || (gLoginMenuBarView && gLoginMenuBarView->hasAccelerator(key, mask)); -} - -// static -bool LLKeyConflictHandler::isReservedByMenu(const LLKeyData &data) -{ - if (data.mMouse != CLICK_NONE || data.mKey == KEY_NONE) - { - return false; - } - return (gMenuBarView && gMenuBarView->hasAccelerator(data.mKey, data.mMask)) - || (gLoginMenuBarView && gLoginMenuBarView->hasAccelerator(data.mKey, data.mMask)); -} - -bool LLKeyConflictHandler::registerControl(const std::string &control_name, U32 index, EMouseClickType mouse, KEY key, MASK mask, bool ignore_mask) -{ - if (control_name.empty()) - { - return false; - } - LLKeyConflict &type_data = mControlsMap[control_name]; - if (!type_data.mAssignable) - { - // Example: user tried to assign camera spin to all modes, but first person mode doesn't support it - return false; - } - LLKeyData data(mouse, key, mask, ignore_mask); - if (type_data.mKeyBind.getKeyData(index) == data) - { - return true; - } - if (isReservedByMenu(data)) - { - return false; - } - if (removeConflicts(data, type_data.mConflictMask)) - { - type_data.mKeyBind.replaceKeyData(data, index); - mHasUnsavedChanges = true; - return true; - } - // control already in use/blocked - return false; -} - -bool LLKeyConflictHandler::clearControl(const std::string &control_name, U32 data_index) -{ - if (control_name.empty()) - { - return false; - } - LLKeyConflict &type_data = mControlsMap[control_name]; - if (!type_data.mAssignable) - { - // Example: user tried to assign camera spin to all modes, but first person mode doesn't support it - return false; - } - type_data.mKeyBind.resetKeyData(data_index); - mHasUnsavedChanges = true; - return true; -} - -LLKeyData LLKeyConflictHandler::getControl(const std::string &control_name, U32 index) -{ - if (control_name.empty()) - { - return LLKeyData(); - } - return mControlsMap[control_name].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) - { - result = LLKeyboard::stringFromAccelerator(keydata.mMask); - } - - result += string_from_mouse(keydata.mMouse, true); - - return result; -} - -std::string LLKeyConflictHandler::getControlString(const std::string &control_name, U32 index) -{ - if (control_name.empty()) - { - return ""; - } - return getStringFromKeyData(mControlsMap[control_name].getKeyData(index)); -} - -void LLKeyConflictHandler::loadFromControlSettings(const std::string &name) -{ - LLControlVariablePtr var = gSavedSettings.getControl(name); - if (var) - { - LLKeyBind bind(var->getValue()); - LLKeyConflict key(bind, true, 0); - mControlsMap[name] = key; - } -} - -void LLKeyConflictHandler::loadFromSettings(const LLViewerInput::KeyMode& keymode, control_map_t *destination) -{ - for (LLInitParam::ParamIterator::const_iterator it = keymode.bindings.begin(), - end_it = keymode.bindings.end(); - it != end_it; - ++it) - { - KEY key; - MASK mask; - EMouseClickType mouse = CLICK_NONE; - if (it->mouse.isProvided()) - { - LLViewerInput::mouseFromString(it->mouse.getValue(), &mouse); - } - if (it->key.getValue().empty()) - { - key = KEY_NONE; - } - else - { - LLKeyboard::keyFromString(it->key, &key); - } - LLKeyboard::maskFromString(it->mask, &mask); - // Note: it->command is also the name of UI element, howhever xml we are loading from - // might not know all the commands, so UI will have to know what to fill by its own - // Assumes U32_MAX conflict mask, and is assignable by default, - // but assignability might have been overriden by generatePlaceholders. - LLKeyConflict &type_data = (*destination)[it->command]; - type_data.mKeyBind.addKeyData(mouse, key, mask, true); - } -} - -bool LLKeyConflictHandler::loadFromSettings(const ESourceMode &load_mode, const std::string &filename, control_map_t *destination) -{ - if (filename.empty()) - { - return false; - } - - bool res = false; - - LLViewerInput::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); - res = true; - } - break; - case MODE_THIRD_PERSON: - if (keys.third_person.isProvided()) - { - loadFromSettings(keys.third_person, destination); - res = true; - } - break; - case MODE_EDIT_AVATAR: - if (keys.edit_avatar.isProvided()) - { - loadFromSettings(keys.edit_avatar, destination); - res = true; - } - break; - case MODE_SITTING: - if (keys.sitting.isProvided()) - { - loadFromSettings(keys.sitting, destination); - res = true; - } - break; - default: - LL_ERRS() << "Not implememted mode " << load_mode << LL_ENDL; - break; - } - } - return res; -} - -void LLKeyConflictHandler::loadFromSettings(ESourceMode load_mode) -{ - mControlsMap.clear(); - mDefaultsMap.clear(); - - // E.X. In case we need placeholder keys for conflict resolution. - generatePlaceholders(load_mode); - - if (load_mode == MODE_SAVED_SETTINGS) - { - // load settings clss knows about, but it also possible to load settings by name separately - const S32 size = std::extent::value; - for (U32 i = 0; i < size; i++) - { - loadFromControlSettings(saved_settings_key_controls[i]); - } - } - else - { - // load defaults - std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, filename_default); - if (!loadFromSettings(load_mode, filename, &mDefaultsMap)) - { - LL_WARNS() << "Failed to load default settings, aborting" << LL_ENDL; - return; - } - - // load user's - filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, filename_default); - if (!gDirUtilp->fileExists(filename) || !loadFromSettings(load_mode, filename, &mControlsMap)) - { - // mind placeholders - mControlsMap.insert(mDefaultsMap.begin(), mDefaultsMap.end()); - } - } - mLoadMode = load_mode; -} - -void LLKeyConflictHandler::saveToSettings(bool temporary) -{ - if (mControlsMap.empty()) - { - return; - } - - if (mLoadMode == MODE_SAVED_SETTINGS) - { - // Does not support 'temporary', preferences handle that themself - // so in case of saved settings we just do not clear mHasUnsavedChanges - control_map_t::iterator iter = mControlsMap.begin(); - control_map_t::iterator end = mControlsMap.end(); - - for (; iter != end; ++iter) - { - if (iter->first.empty()) - { - continue; - } - - LLKeyConflict &key = iter->second; - key.mKeyBind.trimEmpty(); - if (!key.mAssignable) - { - continue; - } - - if (gSavedSettings.controlExists(iter->first)) - { - gSavedSettings.setLLSD(iter->first, key.mKeyBind.asLLSD()); - } - else if (!key.mKeyBind.empty()) - { - // Note: this is currently not in use, might be better for load mechanics to ask for and retain control group - // otherwise settings loaded from other control groups will end in gSavedSettings - LL_INFOS() << "Creating new keybinding " << iter->first << LL_ENDL; - gSavedSettings.declareLLSD(iter->first, key.mKeyBind.asLLSD(), "comment", LLControlVariable::PERSIST_ALWAYS); - } - } - } - else - { - // Determine what file to load and load full copy of that file - std::string filename; - - if (temporary) - { - filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, filename_temporary); - if (!gDirUtilp->fileExists(filename)) - { - filename.clear(); - } - } - - if (filename.empty()) - { - filename = gDirUtilp->findFile(filename_default, - gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, ""), - gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "")); - } - - LLViewerInput::Keys keys; - LLSimpleXUIParser parser; - - if (parser.readXUI(filename, keys) - && keys.validateBlock()) - { - // replace category we edited - - // mode is a HACK to correctly reset bindings without reparsing whole file and avoid doing - // own param container (which will face issues with inasseesible members of LLInitParam) - LLViewerInput::KeyMode mode; - LLViewerInput::KeyBinding binding; - - control_map_t::iterator iter = mControlsMap.begin(); - control_map_t::iterator end = mControlsMap.end(); - for (; iter != end; ++iter) - { - // By default xml have (had) up to 6 elements per function - // eventually it will be cleaned up and UI will only shows 3 per function, - // so make sure to cleanup. - // Also this helps in keeping file small. - iter->second.mKeyBind.trimEmpty(); - U32 size = iter->second.mKeyBind.getDataCount(); - for (U32 i = 0; i < size; ++i) - { - if (iter->first.empty()) - { - continue; - } - - LLKeyConflict &key = iter->second; - key.mKeyBind.trimEmpty(); - if (key.mKeyBind.empty() || !key.mAssignable) - { - continue; - } - - LLKeyData data = key.mKeyBind.getKeyData(i); - // Still write empty LLKeyData to make sure we will maintain UI position - if (data.mKey == KEY_NONE) - { - // Might be better idea to be consistent and use NONE. LLViewerInput can work with both cases - binding.key = ""; - } - else - { - binding.key = LLKeyboard::stringFromKey(data.mKey, false /*Do not localize*/); - } - binding.mask = string_from_mask(data.mMask); - if (data.mMouse == CLICK_NONE) - { - binding.mouse.setProvided(false); - } - else - { - // set() because 'optional', for compatibility purposes - // just copy old keys.xml and rename to key_bindings.xml, it should work - binding.mouse.set(string_from_mouse(data.mMouse, false), true); - } - binding.command = iter->first; - mode.bindings.add(binding); - } - } - - switch (mLoadMode) - { - 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_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: - LL_ERRS() << "Not implememted mode " << mLoadMode << LL_ENDL; - break; - } - - if (temporary) - { - // write to temporary xml and use it for gViewerInput - filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, filename_temporary); - if (!mUsesTemporaryFile) - { - mUsesTemporaryFile = true; - sTemporaryFileUseCount++; - } - } - else - { - // write back to user's xml and use it for gViewerInput - filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, filename_default); - // Don't reset mUsesTemporaryFile, it will be reset at cleanup stage - } - - 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)) - { - // Ideally instead of rebinding immediately we should shedule - // the rebind since single file can have multiple handlers, - // one per mode, saving simultaneously. - // Or whatever uses LLKeyConflictHandler should control the process. - gViewerInput.loadBindingsXML(filename); - } - } - } - -#if 1 - // Legacy support - // Remove #if-#endif section half a year after DRTVWR-501 releases. - // Update legacy settings in settings.xml - // We only care for third person view since legacy settings can't store - // more than one mode. - // We are saving this even if we are in temporary mode - preferences - // will restore values on cancel - if (mLoadMode == MODE_THIRD_PERSON && mHasUnsavedChanges) - { - bool value = canHandleMouse("walk_to", CLICK_DOUBLELEFT, MASK_NONE); - gSavedSettings.setBOOL("DoubleClickAutoPilot", value); - - value = canHandleMouse("walk_to", CLICK_LEFT, MASK_NONE); - gSavedSettings.setBOOL("ClickToWalk", value); - - // new method can save both toggle and push-to-talk values simultaneously, - // but legacy one can save only one. It also doesn't support mask. - LLKeyData data = getControl("toggle_voice", 0); - bool can_toggle = !data.isEmpty(); - if (!can_toggle) - { - data = getControl("voice_follow_key", 0); - } - - gSavedSettings.setBOOL("PushToTalkToggle", can_toggle); - if (data.isEmpty()) - { - // legacy viewer has a bug that might crash it if NONE value is assigned. - // just reset to default - gSavedSettings.getControl("PushToTalkButton")->resetToDefault(false); - } - else - { - if (data.mKey != KEY_NONE) - { - gSavedSettings.setString("PushToTalkButton", LLKeyboard::stringFromKey(data.mKey)); - } - else - { - std::string ctrl_value; - switch (data.mMouse) - { - case CLICK_MIDDLE: - ctrl_value = "MiddleMouse"; - break; - case CLICK_BUTTON4: - ctrl_value = "MouseButton4"; - break; - case CLICK_BUTTON5: - ctrl_value = "MouseButton5"; - break; - default: - ctrl_value = "MiddleMouse"; - break; - } - gSavedSettings.setString("PushToTalkButton", ctrl_value); - } - } - } -#endif - - if (mLoadMode == MODE_THIRD_PERSON && mHasUnsavedChanges) - { - // Map floater should react to doubleclick if doubleclick for teleport is set - // Todo: Seems conterintuitive for map floater to share inworld controls - // after these changes release, discuss with UI UX engineer if this should just - // be set to 1 by default (before release this also doubles as legacy support) - bool value = canHandleMouse("teleport_to", CLICK_DOUBLELEFT, MASK_NONE); - gSavedSettings.setBOOL("DoubleClickTeleport", value); - } - - if (!temporary) - { - // will remove any temporary file if there were any - clearUnsavedChanges(); - } -} - -LLKeyData LLKeyConflictHandler::getDefaultControl(const std::string &control_name, U32 index) -{ - if (control_name.empty()) - { - return LLKeyData(); - } - if (mLoadMode == MODE_SAVED_SETTINGS) - { - LLControlVariablePtr var = gSavedSettings.getControl(control_name); - if (var) - { - return LLKeyBind(var->getDefault()).getKeyData(index); - } - return LLKeyData(); - } - else - { - control_map_t::iterator iter = mDefaultsMap.find(control_name); - if (iter != mDefaultsMap.end()) - { - return iter->second.mKeyBind.getKeyData(index); - } - return LLKeyData(); - } -} - -void LLKeyConflictHandler::resetToDefault(const std::string &control_name, U32 index) -{ - if (control_name.empty()) - { - return; - } - LLKeyData data = getDefaultControl(control_name, index); - - if (data != mControlsMap[control_name].getKeyData(index)) - { - // reset controls that might have been switched to our current control - removeConflicts(data, mControlsMap[control_name].mConflictMask); - mControlsMap[control_name].setKeyData(data, index); - } -} - -void LLKeyConflictHandler::resetToDefaultAndResolve(const std::string &control_name, bool ignore_conflicts) -{ - if (control_name.empty()) - { - return; - } - if (mLoadMode == MODE_SAVED_SETTINGS) - { - LLControlVariablePtr var = gSavedSettings.getControl(control_name); - if (var) - { - LLKeyBind bind(var->getDefault()); - if (!ignore_conflicts) - { - for (S32 i = 0; i < bind.getDataCount(); ++i) - { - removeConflicts(bind.getKeyData(i), mControlsMap[control_name].mConflictMask); - } - } - mControlsMap[control_name].mKeyBind = bind; - } - else - { - mControlsMap[control_name].mKeyBind.clear(); - } - } - else - { - control_map_t::iterator iter = mDefaultsMap.find(control_name); - if (iter != mDefaultsMap.end()) - { - if (!ignore_conflicts) - { - for (S32 i = 0; i < iter->second.mKeyBind.getDataCount(); ++i) - { - removeConflicts(iter->second.mKeyBind.getKeyData(i), mControlsMap[control_name].mConflictMask); - } - } - mControlsMap[control_name].mKeyBind = iter->second.mKeyBind; - } - else - { - mControlsMap[control_name].mKeyBind.clear(); - } - } -} - -void LLKeyConflictHandler::resetToDefault(const std::string &control_name) -{ - // reset specific binding without ignoring conflicts - resetToDefaultAndResolve(control_name, false); -} - -void LLKeyConflictHandler::resetToDefaults(ESourceMode mode) -{ - if (mode == MODE_SAVED_SETTINGS) - { - control_map_t::iterator iter = mControlsMap.begin(); - control_map_t::iterator end = mControlsMap.end(); - - for (; iter != end; ++iter) - { - resetToDefaultAndResolve(iter->first, true); - } - } - else - { - mControlsMap.clear(); - generatePlaceholders(mode); - mControlsMap.insert(mDefaultsMap.begin(), mDefaultsMap.end()); - } - - mHasUnsavedChanges = true; -} - -void LLKeyConflictHandler::resetToDefaults() -{ - if (!empty()) - { - resetToDefaults(mLoadMode); - } - else - { - // not optimal since: - // 1. We are not sure that mLoadMode was set - // 2. We are not sure if there are any changes in comparison to default - // 3. We are loading 'current' only to replace it - // but it is reliable and works Todo: consider optimizing. - loadFromSettings(mLoadMode); - resetToDefaults(mLoadMode); - } -} - -void LLKeyConflictHandler::clear() -{ - if (clearUnsavedChanges()) - { - // temporary file was removed, this means we were using it and need to reload keyboard's bindings - resetKeyboardBindings(); - } - mControlsMap.clear(); - mDefaultsMap.clear(); -} - -// static -void LLKeyConflictHandler::resetKeyboardBindings() -{ - // Try to load User's bindings first - std::string key_bindings_file = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, filename_default); - if (!gDirUtilp->fileExists(key_bindings_file) || !gViewerInput.loadBindingsXML(key_bindings_file)) - { - // Failed to load custom bindings, try default ones - key_bindings_file = gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, filename_default); - if (!gViewerInput.loadBindingsXML(key_bindings_file)) - { - LL_ERRS("InitInfo") << "Unable to open default key bindings from " << key_bindings_file << LL_ENDL; - } - } -} - -void LLKeyConflictHandler::generatePlaceholders(ESourceMode load_mode) -{ - // These controls are meant to cause conflicts when user tries to assign same control somewhere else - // also this can be used to pre-record controls that should not conflict or to assign conflict groups/masks - - if (load_mode == MODE_FIRST_PERSON) - { - // First person view doesn't support camera controls - // Note: might be better idea to just load these from control_table_contents_camera.xml - // or to pass from floaterpreferences when it loads said file - registerTemporaryControl("look_up"); - registerTemporaryControl("look_down"); - registerTemporaryControl("move_forward"); - registerTemporaryControl("move_backward"); - registerTemporaryControl("move_forward_fast"); - registerTemporaryControl("move_backward_fast"); - registerTemporaryControl("spin_over"); - registerTemporaryControl("spin_under"); - registerTemporaryControl("pan_up"); - registerTemporaryControl("pan_down"); - registerTemporaryControl("pan_left"); - registerTemporaryControl("pan_right"); - registerTemporaryControl("pan_in"); - registerTemporaryControl("pan_out"); - registerTemporaryControl("spin_around_ccw"); - registerTemporaryControl("spin_around_cw"); - - // control_table_contents_editing.xml - registerTemporaryControl("edit_avatar_spin_ccw"); - registerTemporaryControl("edit_avatar_spin_cw"); - registerTemporaryControl("edit_avatar_spin_over"); - registerTemporaryControl("edit_avatar_spin_under"); - registerTemporaryControl("edit_avatar_move_forward"); - registerTemporaryControl("edit_avatar_move_backward"); - } - - if (load_mode != MODE_SITTING) - { - registerTemporaryControl("move_forward_sitting"); - registerTemporaryControl("move_backward_sitting"); - registerTemporaryControl("spin_over_sitting"); - registerTemporaryControl("spin_under_sitting"); - registerTemporaryControl("spin_around_ccw_sitting"); - registerTemporaryControl("spin_around_cw_sitting"); - } -} - -bool LLKeyConflictHandler::removeConflicts(const LLKeyData &data, const U32 &conlict_mask) -{ - if (conlict_mask == CONFLICT_NOTHING) - { - // Can't conflict - return true; - } - std::map conflict_list; - control_map_t::iterator cntrl_iter = mControlsMap.begin(); - control_map_t::iterator cntrl_end = mControlsMap.end(); - for (; cntrl_iter != cntrl_end; ++cntrl_iter) - { - S32 index = cntrl_iter->second.mKeyBind.findKeyData(data); - if (index >= 0 - && cntrl_iter->second.mConflictMask != CONFLICT_NOTHING - && (cntrl_iter->second.mConflictMask & conlict_mask) != 0) - { - if (cntrl_iter->second.mAssignable) - { - // Potentially we can have multiple conflict flags conflicting - // including unassignable keys. - // So record the conflict and find all others before doing any changes. - // Assume that there is only one conflict per bind - conflict_list[cntrl_iter->first] = index; - } - else - { - return false; - } - } - } - - std::map::iterator cnflct_iter = conflict_list.begin(); - std::map::iterator cnflct_end = conflict_list.end(); - for (; cnflct_iter != cnflct_end; ++cnflct_iter) - { - mControlsMap[cnflct_iter->first].mKeyBind.resetKeyData(cnflct_iter->second); - } - return true; -} - -void LLKeyConflictHandler::registerTemporaryControl(const std::string &control_name, EMouseClickType mouse, KEY key, MASK mask, U32 conflict_mask) -{ - LLKeyConflict *type_data = &mControlsMap[control_name]; - type_data->mAssignable = false; - type_data->mConflictMask = conflict_mask; - type_data->mKeyBind.addKeyData(mouse, key, mask, false); -} - -void LLKeyConflictHandler::registerTemporaryControl(const std::string &control_name, U32 conflict_mask) -{ - LLKeyConflict *type_data = &mControlsMap[control_name]; - type_data->mAssignable = false; - type_data->mConflictMask = conflict_mask; -} - -bool LLKeyConflictHandler::clearUnsavedChanges() -{ - bool result = false; - mHasUnsavedChanges = false; - - if (mUsesTemporaryFile) - { - mUsesTemporaryFile = false; - sTemporaryFileUseCount--; - if (!sTemporaryFileUseCount) - { - result = clearTemporaryFile(); - } - // else: might be usefull to overwrite content of temp file with defaults - // but at the moment there is no such need - } - return result; -} - -//static -bool LLKeyConflictHandler::clearTemporaryFile() -{ - // At the moment single file needs five handlers (one per mode), so doing this - // will remove file for all hadlers - std::string filename = gDirUtilp->getExpandedFilename(LL_PATH_USER_SETTINGS, filename_temporary); - if (gDirUtilp->fileExists(filename)) - { - LLFile::remove(filename); - return true; - } - return false; -} - diff --git a/indra/newview/llkeyconflict.h b/indra/newview/llkeyconflict.h deleted file mode 100644 index 7566303cdd..0000000000 --- a/indra/newview/llkeyconflict.h +++ /dev/null @@ -1,174 +0,0 @@ -/** - * @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 "llviewerinput.h" - - -class LLKeyConflict -{ -public: - LLKeyConflict() : mAssignable(true), mConflictMask(U32_MAX) {} //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 ESourceMode // partially repeats e_keyboard_mode - { - MODE_FIRST_PERSON, - MODE_THIRD_PERSON, - MODE_EDIT_AVATAR, - MODE_SITTING, - MODE_SAVED_SETTINGS, // for settings from saved settings - MODE_COUNT - }; - - const U32 CONFLICT_NOTHING = 0; - // at the moment this just means that key will conflict with everything that is identical - const U32 CONFLICT_ANY = U32_MAX; - - // Note: missed selection and edition commands (would be really nice to go through selection via MB4/5 or wheel) - - LLKeyConflictHandler(); - LLKeyConflictHandler(ESourceMode mode); - ~LLKeyConflictHandler(); - - bool canHandleControl(const std::string &control_name, EMouseClickType mouse_ind, KEY key, MASK mask); - bool canHandleKey(const std::string &control_name, KEY key, MASK mask); - bool canHandleMouse(const std::string &control_name, EMouseClickType mouse_ind, MASK mask); - bool canHandleMouse(const std::string &control_name, S32 mouse_ind, MASK mask); //Just for convinience - bool canAssignControl(const std::string &control_name); - static bool isReservedByMenu(const KEY &key, const MASK &mask); - static bool isReservedByMenu(const LLKeyData &data); - - // @control_name - see REGISTER_KEYBOARD_ACTION in llviewerinput for avaliable options, - // usually this is just name of the function - // @data_index - single control (function) can have multiple key combinations trigering - // it, this index indicates combination function will change/add; Note that preferences - // floater can only display up to 3 options, but data_index can be bigger than that - // @mouse_ind - mouse action (middle click, MB5 etc) - // @key - keyboard key action - // @mask - shift/ctrl/alt flags - // @ignore_mask - Either to expect exact match (ctrl+K will not trigger if ctrl+shift+K - // is active) or ignore not expected masks as long as expected mask is present - // (ctrl+K will be triggered if ctrl+shift+K is active) - bool registerControl(const std::string &control_name, U32 data_index, EMouseClickType mouse_ind, KEY key, MASK mask, bool ignore_mask); //todo: return conflicts? - bool clearControl(const std::string &control_name, U32 data_index); - - LLKeyData getControl(const std::string &control_name, U32 data_index); - - // localized string - static std::string getStringFromKeyData(const LLKeyData& keydata); - std::string getControlString(const std::string &control_name, U32 data_index); - - // Load single control, overrides existing one if names match - void loadFromControlSettings(const std::string &name); - // Drops any changes loads controls with ones from 'saved settings' or from xml - void loadFromSettings(ESourceMode load_mode); - - // Saves settings to 'saved settings' or to xml - // If 'temporary' is set, function will save settings to temporary - // file and reload input bindings from temporary file. - // 'temporary' does not support gSavedSettings, those are handled - // by preferences, so 'temporary' is such case will simply not - // reset mHasUnsavedChanges - // - // 'temporary' exists to support ability of live-editing settings in - // preferences: temporary for testing changes 'live' without saving them, - // then hitting ok/cancel and save/discard values permanently. - void saveToSettings(bool apply_temporary = false); - - LLKeyData getDefaultControl(const std::string &control_name, U32 data_index); - // Resets keybinding to default variant from 'saved settings' or xml - void resetToDefault(const std::string &control_name, U32 index); - void resetToDefault(const std::string &control_name); - // resets current mode to defaults - void resetToDefaults(); - - bool empty() { return mControlsMap.empty(); } - void clear(); - - // reloads bindings from last valid user's xml or from default xml - // to keyboard's handler - static void resetKeyboardBindings(); - - bool hasUnsavedChanges() { return mHasUnsavedChanges; } - void setLoadMode(ESourceMode mode) { mLoadMode = mode; } - ESourceMode getLoadMode() { return mLoadMode; } - -private: - void resetToDefaultAndResolve(const std::string &control_name, bool ignore_conflicts); - void resetToDefaults(ESourceMode mode); - - // at the moment these kind of control is not savable, but takes part in conflict resolution - void registerTemporaryControl(const std::string &control_name, EMouseClickType mouse_ind, KEY key, MASK mask, U32 conflict_mask); - void registerTemporaryControl(const std::string &control_name, U32 conflict_mask = 0); - - typedef std::map control_map_t; - void loadFromSettings(const LLViewerInput::KeyMode& keymode, control_map_t *destination); - bool loadFromSettings(const ESourceMode &load_mode, const std::string &filename, control_map_t *destination); - void generatePlaceholders(ESourceMode load_mode); //E.x. non-assignable values - // returns false in case user is trying to reuse control that can't be reassigned - bool removeConflicts(const LLKeyData &data, const U32 &conlict_mask); - - // removes flags and removes temporary file, returns 'true' if file was removed - bool clearUnsavedChanges(); - // return true if there was a file to remove - static bool clearTemporaryFile(); - - control_map_t mControlsMap; - control_map_t mDefaultsMap; - bool mHasUnsavedChanges; - ESourceMode mLoadMode; - - // To implement 'apply immediately'+revert on cancel, class applies changes to temporary file - // but this only works for settings from keybndings files (key_bindings.xml) - // saved setting rely onto external mechanism of preferences floater - bool mUsesTemporaryFile; - static S32 sTemporaryFileUseCount; -}; - - -#endif // LL_LLKEYCONFLICT_H diff --git a/indra/newview/llsetkeybinddialog.cpp b/indra/newview/llsetkeybinddialog.cpp deleted file mode 100644 index 4eb76c9d89..0000000000 --- a/indra/newview/llsetkeybinddialog.cpp +++ /dev/null @@ -1,346 +0,0 @@ -/** - * @file llsetkeybinddialog.cpp - * @brief LLSetKeyBindDialog class implementation. - * - * $LicenseInfo:firstyear=2019&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2019, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "llviewerprecompiledheaders.h" - -#include "llsetkeybinddialog.h" - -#include "llbutton.h" -#include "llcheckboxctrl.h" -#include "lleventtimer.h" -#include "llfloaterreg.h" -#include "llfocusmgr.h" -#include "llkeyconflict.h" -#include "llviewercontrol.h" - -class LLSetKeyBindDialog::Updater : public LLEventTimer -{ -public: - - typedef boost::function callback_t; - - Updater(callback_t cb, F32 period, MASK mask) - :LLEventTimer(period), - mMask(mask), - mCallback(cb) - { - mEventTimer.start(); - } - - virtual ~Updater(){} - -protected: - BOOL tick() - { - mCallback(mMask); - // Deletes itseft after execution - return TRUE; - } - -private: - MASK mMask; - callback_t mCallback; -}; - -bool LLSetKeyBindDialog::sRecordKeys = false; - -LLSetKeyBindDialog::LLSetKeyBindDialog(const LLSD& key) - : LLModalDialog(key), - pParent(NULL), - mKeyFilterMask(DEFAULT_KEY_FILTER), - pUpdater(NULL), - mContextConeOpacity(0.f), - mContextConeInAlpha(0.f), - mContextConeOutAlpha(0.f), - mContextConeFadeTime(0.f) -{ - mContextConeInAlpha = gSavedSettings.getF32("ContextConeInAlpha"); - mContextConeOutAlpha = gSavedSettings.getF32("ContextConeOutAlpha"); - mContextConeFadeTime = gSavedSettings.getF32("ContextConeFadeTime"); -} - -LLSetKeyBindDialog::~LLSetKeyBindDialog() -{ -} - -//virtual -BOOL LLSetKeyBindDialog::postBuild() -{ - childSetAction("SetEmpty", onBlank, this); - childSetAction("Default", onDefault, this); - childSetAction("Cancel", onCancel, this); - getChild("Cancel")->setFocus(TRUE); - - pCheckBox = getChild("apply_all"); - pDesription = getChild("descritption"); - - gFocusMgr.setKeystrokesOnly(TRUE); - - return TRUE; -} - -//virtual -void LLSetKeyBindDialog::onOpen(const LLSD& data) -{ - sRecordKeys = true; - LLModalDialog::onOpen(data); -} - -//virtual -void LLSetKeyBindDialog::onClose(bool app_quiting) -{ - sRecordKeys = false; - if (pParent) - { - pParent->onCancelKeyBind(); - pParent = NULL; - } - if (pUpdater) - { - // Doubleclick timer has't fired, delete it - delete pUpdater; - pUpdater = NULL; - } - LLModalDialog::onClose(app_quiting); -} - -void LLSetKeyBindDialog::drawFrustum() -{ - static LLCachedControl max_opacity(gSavedSettings, "PickerContextOpacity", 0.4f); - drawConeToOwner(mContextConeOpacity, max_opacity, mFrustumOrigin.get(), mContextConeFadeTime, mContextConeInAlpha, mContextConeOutAlpha); -} - -//virtual -void LLSetKeyBindDialog::draw() -{ - drawFrustum(); - LLModalDialog::draw(); -} - -void LLSetKeyBindDialog::setParent(LLKeyBindResponderInterface* parent, LLView* frustum_origin, U32 key_mask) -{ - pParent = parent; - mFrustumOrigin = frustum_origin->getHandle(); - mKeyFilterMask = key_mask; - - 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"); - } - pDesription->setText(getString("basic_description")); - pDesription->setTextArg("[INPUT]", input); -} - -// static -bool LLSetKeyBindDialog::recordKey(KEY key, MASK mask) -{ - if (sRecordKeys) - { - LLSetKeyBindDialog* dialog = LLFloaterReg::getTypedInstance("keybind_dialog", LLSD()); - if (dialog && dialog->getVisible()) - { - return dialog->recordAndHandleKey(key, mask); - } - else - { - LL_WARNS() << "Key recording was set despite no open dialog" << LL_ENDL; - sRecordKeys = false; - } - } - return false; -} - -bool LLSetKeyBindDialog::recordAndHandleKey(KEY key, MASK mask) -{ - if ((key == 'Q' && mask == MASK_CONTROL) - || key == KEY_ESCAPE) - { - sRecordKeys = false; - closeFloater(); - return true; - } - - if (key == KEY_DELETE) - { - setKeyBind(CLICK_NONE, KEY_NONE, MASK_NONE, false); - sRecordKeys = false; - closeFloater(); - return false; - } - - // forbidden keys - if (key == KEY_NONE - || key == KEY_RETURN - || key == KEY_BACKSPACE) - { - return false; - } - - if ((mKeyFilterMask & ALLOW_MASKS) == 0 - && (key == KEY_CONTROL || key == KEY_SHIFT || key == KEY_ALT)) - { - // mask by themself are not allowed - return false; - } - else if ((mKeyFilterMask & ALLOW_KEYS) == 0) - { - // basic keys not allowed - return false; - } - else if ((mKeyFilterMask & ALLOW_MASK_KEYS) == 0 && mask != 0) - { - // masked keys not allowed - return false; - } - - if (LLKeyConflictHandler::isReservedByMenu(key, mask)) - { - pDesription->setText(getString("reserved_by_menu")); - pDesription->setTextArg("[KEYSTR]", LLKeyboard::stringFromAccelerator(mask,key)); - return true; - } - - setKeyBind(CLICK_NONE, key, mask, pCheckBox->getValue().asBoolean()); - sRecordKeys = false; - closeFloater(); - return true; -} - -BOOL LLSetKeyBindDialog::handleAnyMouseClick(S32 x, S32 y, MASK mask, EMouseClickType clicktype, BOOL down) -{ - BOOL result = FALSE; - if (!pParent) - { - // we already processed 'down' event, this is 'up', consume - closeFloater(); - result = TRUE; - } - if (!result && clicktype == CLICK_LEFT) - { - // try handling buttons first - if (down) - { - result = LLView::handleMouseDown(x, y, mask); - } - else - { - result = LLView::handleMouseUp(x, y, mask); - } - if (result) - { - setFocus(TRUE); - gFocusMgr.setKeystrokesOnly(TRUE); - } - // ignore selection related combinations - else if (down && (mask & (MASK_SHIFT | MASK_CONTROL)) == 0) - { - // this can be a double click, wait a bit; - if (!pUpdater) - { - // Note: default doubleclick time is 500ms, but can stretch up to 5s - pUpdater = new Updater(boost::bind(&onClickTimeout, this, _1), 0.7f, mask); - result = TRUE; - } - } - } - - if (!result - && (clicktype != CLICK_LEFT) // subcases were handled above - && ((mKeyFilterMask & ALLOW_MOUSE) != 0) - && (clicktype != CLICK_RIGHT || mask != 0) // reassigning menu button is not supported - && ((mKeyFilterMask & ALLOW_MASK_MOUSE) != 0 || mask == 0)) // reserved for selection - { - setKeyBind(clicktype, KEY_NONE, mask, pCheckBox->getValue().asBoolean()); - result = TRUE; - if (!down) - { - // wait for 'up' event before closing - // alternative: set pUpdater - closeFloater(); - } - } - - return result; -} - -//static -void LLSetKeyBindDialog::onCancel(void* user_data) -{ - LLSetKeyBindDialog* self = (LLSetKeyBindDialog*)user_data; - self->closeFloater(); -} - -//static -void LLSetKeyBindDialog::onBlank(void* user_data) -{ - LLSetKeyBindDialog* self = (LLSetKeyBindDialog*)user_data; - // tmp needs 'no key' button - self->setKeyBind(CLICK_NONE, KEY_NONE, MASK_NONE, false); - self->closeFloater(); -} - -//static -void LLSetKeyBindDialog::onDefault(void* user_data) -{ - LLSetKeyBindDialog* self = (LLSetKeyBindDialog*)user_data; - if (self->pParent) - { - self->pParent->onDefaultKeyBind(self->pCheckBox->getValue().asBoolean()); - self->pParent = NULL; - } - self->closeFloater(); -} - -//static -void LLSetKeyBindDialog::onClickTimeout(void* user_data, MASK mask) -{ - LLSetKeyBindDialog* self = (LLSetKeyBindDialog*)user_data; - - // timer will delete itself after timeout - self->pUpdater = NULL; - - self->setKeyBind(CLICK_LEFT, KEY_NONE, mask, self->pCheckBox->getValue().asBoolean()); - self->closeFloater(); -} - -void LLSetKeyBindDialog::setKeyBind(EMouseClickType click, KEY key, MASK mask, bool all_modes) -{ - if (pParent) - { - pParent->onSetKeyBind(click, key, mask, all_modes); - pParent = NULL; - } -} - diff --git a/indra/newview/llsetkeybinddialog.h b/indra/newview/llsetkeybinddialog.h deleted file mode 100644 index 70190230e4..0000000000 --- a/indra/newview/llsetkeybinddialog.h +++ /dev/null @@ -1,105 +0,0 @@ -/** - * @file llsetkeybinddialog.h - * @brief LLSetKeyBindDialog class definition - * - * $LicenseInfo:firstyear=2019&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2019, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - - -#ifndef LL_LLSETKEYBINDDIALOG_H -#define LL_LLSETKEYBINDDIALOG_H - -#include "llmodaldialog.h" - -class LLCheckBoxCtrl; -class LLTextBase; - -// 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 DEFAULT_KEY_FILTER = ALLOW_MOUSE | ALLOW_MASK_MOUSE | ALLOW_KEYS | ALLOW_MASK_KEYS; - - -class LLKeyBindResponderInterface -{ -public: - virtual ~LLKeyBindResponderInterface() {}; - - virtual void onCancelKeyBind() = 0; - virtual void onDefaultKeyBind(bool all_modes) = 0; - // returns true if parent failed to set key due to key being in use - virtual bool onSetKeyBind(EMouseClickType click, KEY key, MASK mask, bool all_modes) = 0; -}; - -class LLSetKeyBindDialog : public LLModalDialog -{ -public: - LLSetKeyBindDialog(const LLSD& key); - ~LLSetKeyBindDialog(); - - /*virtual*/ BOOL postBuild(); - /*virtual*/ void onOpen(const LLSD& data); - /*virtual*/ void onClose(bool app_quiting); - /*virtual*/ void draw(); - - void setParent(LLKeyBindResponderInterface* parent, LLView* frustum_origin, U32 key_mask = DEFAULT_KEY_FILTER); - - // Wrapper around recordAndHandleKey - // It does not record, it handles, but handleKey function is already in use - static bool recordKey(KEY key, MASK mask); - - BOOL handleAnyMouseClick(S32 x, S32 y, MASK mask, EMouseClickType clicktype, BOOL down); - static void onCancel(void* user_data); - static void onBlank(void* user_data); - static void onDefault(void* user_data); - static void onClickTimeout(void* user_data, MASK mask); - - class Updater; - -private: - bool recordAndHandleKey(KEY key, MASK mask); - void setKeyBind(EMouseClickType click, KEY key, MASK mask, bool all_modes); - LLKeyBindResponderInterface *pParent; - LLCheckBoxCtrl *pCheckBox; - LLTextBase *pDesription; - - U32 mKeyFilterMask; - Updater *pUpdater; - - static bool sRecordKeys; // for convinience and not to check instance each time - - // drawFrustum -private: - void drawFrustum(); - - LLHandle mFrustumOrigin; - F32 mContextConeOpacity; - F32 mContextConeInAlpha; - F32 mContextConeOutAlpha; - F32 mContextConeFadeTime; -}; - - -#endif // LL_LLSETKEYBINDDIALOG_H diff --git a/indra/newview/llspatialpartition.cpp b/indra/newview/llspatialpartition.cpp index be8a9a5d2d..77bbcdada6 100644 --- a/indra/newview/llspatialpartition.cpp +++ b/indra/newview/llspatialpartition.cpp @@ -994,7 +994,7 @@ LLSpatialGroup *LLSpatialPartition::put(LLDrawable *drawablep, BOOL was_visible) } LLSpatialGroup* group = drawablep->getSpatialGroup(); - //llassert(group != NULL); + llassert(group != NULL); if (group && was_visible && group->isOcclusionState(LLSpatialGroup::QUERY_PENDING)) { diff --git a/indra/newview/lltool.cpp b/indra/newview/lltool.cpp index 5235914c34..c5e31ff8e6 100644 --- a/indra/newview/lltool.cpp +++ b/indra/newview/lltool.cpp @@ -60,7 +60,7 @@ LLTool::~LLTool() } } -BOOL LLTool::handleAnyMouseClick(S32 x, S32 y, MASK mask, EMouseClickType clicktype, BOOL down) +BOOL LLTool::handleAnyMouseClick(S32 x, S32 y, MASK mask, LLMouseHandler::EClickType clicktype, BOOL down) { BOOL result = LLMouseHandler::handleAnyMouseClick(x, y, mask, clicktype, down); @@ -83,9 +83,9 @@ BOOL LLTool::handleMouseDown(S32 x, S32 y, MASK mask) LL_INFOS() << "LLTool left mouse down" << LL_ENDL; } // by default, didn't handle it - // AGENT_CONTROL_LBUTTON_DOWN is handled by scanMouse() and scanKey() // LL_INFOS() << "LLTool::handleMouseDown" << LL_ENDL; - return FALSE; + gAgent.setControlFlags(AGENT_CONTROL_LBUTTON_DOWN); + return TRUE; } BOOL LLTool::handleMouseUp(S32 x, S32 y, MASK mask) @@ -95,8 +95,8 @@ BOOL LLTool::handleMouseUp(S32 x, S32 y, MASK mask) LL_INFOS() << "LLTool left mouse up" << LL_ENDL; } // by default, didn't handle it - // AGENT_CONTROL_LBUTTON_UP is handled by scanMouse() and scanKey() // LL_INFOS() << "LLTool::handleMouseUp" << LL_ENDL; + gAgent.setControlFlags(AGENT_CONTROL_LBUTTON_UP); return TRUE; } diff --git a/indra/newview/lltool.h b/indra/newview/lltool.h index 41a38804ce..308983afda 100644 --- a/indra/newview/lltool.h +++ b/indra/newview/lltool.h @@ -49,7 +49,7 @@ public: virtual BOOL isView() const { return FALSE; } // Virtual functions inherited from LLMouseHandler - virtual BOOL handleAnyMouseClick(S32 x, S32 y, MASK mask, EMouseClickType clicktype, BOOL down); + virtual BOOL handleAnyMouseClick(S32 x, S32 y, MASK mask, LLMouseHandler::EClickType clicktype, BOOL down); virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask); virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask); virtual BOOL handleMiddleMouseDown(S32 x, S32 y, MASK mask); diff --git a/indra/newview/lltoolpie.cpp b/indra/newview/lltoolpie.cpp index 0839ea6cf5..aeb8bdc496 100644 --- a/indra/newview/lltoolpie.cpp +++ b/indra/newview/lltoolpie.cpp @@ -83,6 +83,7 @@ LLToolPie::LLToolPie() mMouseOutsideSlop( false ), mMouseSteerX(-1), mMouseSteerY(-1), + mBlockClickToWalk(false), mClickAction(0), mClickActionBuyEnabled( gSavedSettings.getBOOL("ClickActionBuyEnabled") ), mClickActionPayEnabled( gSavedSettings.getBOOL("ClickActionPayEnabled") ), @@ -90,7 +91,7 @@ LLToolPie::LLToolPie() { } -BOOL LLToolPie::handleAnyMouseClick(S32 x, S32 y, MASK mask, EMouseClickType clicktype, BOOL down) +BOOL LLToolPie::handleAnyMouseClick(S32 x, S32 y, MASK mask, EClickType clicktype, BOOL down) { BOOL result = LLMouseHandler::handleAnyMouseClick(x, y, mask, clicktype, down); @@ -171,8 +172,10 @@ BOOL LLToolPie::handleMouseDown(S32 x, S32 y, MASK mask) mPick.mKeyMask = mask; mMouseButtonDown = true; + + handleLeftClickPick(); - return handleLeftClickPick(); + return TRUE; } // Spawn context menus on right mouse down so you can drag over and select @@ -371,6 +374,8 @@ BOOL LLToolPie::handleLeftClickPick() // put focus back "in world" if (gFocusMgr.getKeyboardFocus()) { + // don't click to walk on attempt to give focus to world + mBlockClickToWalk = true; gFocusMgr.setKeyboardFocus(NULL); } @@ -414,7 +419,7 @@ BOOL LLToolPie::handleLeftClickPick() } object = (LLViewerObject*)object->getParent(); } - if (object && object == gAgentAvatarp) + if (object && object == gAgentAvatarp && !gSavedSettings.getBOOL("ClickToWalk")) { // we left clicked on avatar, switch to focus mode mMouseButtonDown = false; @@ -431,6 +436,7 @@ BOOL LLToolPie::handleLeftClickPick() // LLFirstUse::useLeftClickNoHit(); ///////// + // Eat the event return LLTool::handleMouseDown(x, y, mask); } @@ -537,120 +543,17 @@ void LLToolPie::resetSelection() mClickAction = 0; } -bool LLToolPie::walkToClickedLocation() +void LLToolPie::walkToClickedLocation() { - if (gAgent.getFlying() // don't auto-navigate while flying until that works - || !gAgentAvatarp - || gAgentAvatarp->isSitting()) - { - return false; - } - - LLPickInfo saved_pick = mPick; - if (gAgentCamera.getCameraMode() != CAMERA_MODE_MOUSELOOK) - { - mPick = gViewerWindow->pickImmediate(mHoverPick.mMousePt.mX, mHoverPick.mMousePt.mY, - FALSE /* ignore transparent */, - FALSE /* ignore rigged */, - FALSE /* ignore particles */); - } - else - { - // We do not handle hover in mouselook as we do in other modes, so - // use croshair's position to do a pick - mPick = gViewerWindow->pickImmediate(gViewerWindow->getWorldViewRectScaled().getWidth() / 2, - gViewerWindow->getWorldViewRectScaled().getHeight() / 2, - FALSE /* ignore transparent */, - FALSE /* ignore rigged */, - FALSE /* ignore particles */); - } - - if (mPick.mPickType == LLPickInfo::PICK_OBJECT) - { - if (mPick.getObject() && mPick.getObject()->isHUDAttachment()) - { - mPick = saved_pick; - return false; - } - } - - LLViewerObject* avatar_object = mPick.getObject(); - - // get pointer to avatar - while (avatar_object && !avatar_object->isAvatar()) - { - avatar_object = (LLViewerObject*)avatar_object->getParent(); - } - - if (avatar_object && ((LLVOAvatar*)avatar_object)->isSelf()) - { - const F64 SELF_CLICK_WALK_DISTANCE = 3.0; - // pretend we picked some point a bit in front of avatar - mPick.mPosGlobal = gAgent.getPositionGlobal() + LLVector3d(LLViewerCamera::instance().getAtAxis()) * SELF_CLICK_WALK_DISTANCE; - } - - if ((mPick.mPickType == LLPickInfo::PICK_LAND && !mPick.mPosGlobal.isExactlyZero()) || - (mPick.mObjectID.notNull() && !mPick.mPosGlobal.isExactlyZero())) - { - gAgentCamera.setFocusOnAvatar(TRUE, TRUE); - - if (mAutoPilotDestination) { mAutoPilotDestination->markDead(); } - mAutoPilotDestination = (LLHUDEffectBlob *)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_BLOB, FALSE); - mAutoPilotDestination->setPositionGlobal(mPick.mPosGlobal); - mAutoPilotDestination->setPixelSize(5); - mAutoPilotDestination->setColor(LLColor4U(170, 210, 190)); - mAutoPilotDestination->setDuration(3.f); - - LLVector3d pos = LLToolPie::getInstance()->getPick().mPosGlobal; - gAgent.startAutoPilotGlobal(pos, std::string(), NULL, NULL, NULL, 0.f, 0.03f, FALSE); - LLFirstUse::notMoving(false); - showVisualContextMenuEffect(); - return true; - } - else - { - LL_DEBUGS() << "walk target was " - << (mPick.mPosGlobal.isExactlyZero() ? "zero" : "not zero") - << ", pick type was " << (mPick.mPickType == LLPickInfo::PICK_LAND ? "land" : "not land") - << ", pick object was " << mPick.mObjectID - << LL_ENDL; - mPick = saved_pick; - return false; - } - return true; -} - -bool LLToolPie::teleportToClickedLocation() -{ - if (gAgentCamera.getCameraMode() == CAMERA_MODE_MOUSELOOK) - { - // We do not handle hover in mouselook as we do in other modes, so - // use croshair's position to do a pick - BOOL pick_rigged = false; - mHoverPick = gViewerWindow->pickImmediate(gViewerWindow->getWorldViewRectScaled().getWidth() / 2, - gViewerWindow->getWorldViewRectScaled().getHeight() / 2, - FALSE, - pick_rigged); - } - LLViewerObject* objp = mHoverPick.getObject(); - LLViewerObject* parentp = objp ? objp->getRootEdit() : NULL; - - bool is_in_world = mHoverPick.mObjectID.notNull() && objp && !objp->isHUDAttachment(); - bool is_land = mHoverPick.mPickType == LLPickInfo::PICK_LAND; - bool pos_non_zero = !mHoverPick.mPosGlobal.isExactlyZero(); - bool has_touch_handler = (objp && objp->flagHandleTouch()) || (parentp && parentp->flagHandleTouch()); - bool has_click_action = final_click_action(objp); - - if (pos_non_zero && (is_land || (is_in_world && !has_touch_handler && !has_click_action))) - { - LLVector3d pos = mHoverPick.mPosGlobal; - pos.mdV[VZ] += gAgentAvatarp->getPelvisToFoot(); - gAgent.teleportViaLocationLookAt(pos); - mPick = mHoverPick; - showVisualContextMenuEffect(); - return true; - } - return false; + if(mAutoPilotDestination) { mAutoPilotDestination->markDead(); } + mAutoPilotDestination = (LLHUDEffectBlob *)LLHUDManager::getInstance()->createViewerEffect(LLHUDObject::LL_HUD_EFFECT_BLOB, FALSE); + mAutoPilotDestination->setPositionGlobal(mPick.mPosGlobal); + mAutoPilotDestination->setPixelSize(5); + mAutoPilotDestination->setColor(LLColor4U(170, 210, 190)); + mAutoPilotDestination->setDuration(3.f); + + LLVector3d pos = LLToolPie::getInstance()->getPick().mPosGlobal; + gAgent.startAutoPilotGlobal(pos, std::string(), NULL, NULL, NULL, 0.f, 0.03f, FALSE); } // When we get object properties after left-clicking on an object @@ -726,7 +629,8 @@ BOOL LLToolPie::handleHover(S32 x, S32 y, MASK mask) LL_DEBUGS("UserInput") << "hover handled by LLToolPie (inactive)" << LL_ENDL; } else if (!mMouseOutsideSlop - && mMouseButtonDown) + && mMouseButtonDown + && gSavedSettings.getBOOL("ClickToWalk")) { S32 delta_x = x - mMouseDownX; S32 delta_y = y - mMouseDownY; @@ -811,10 +715,70 @@ BOOL LLToolPie::handleMouseUp(S32 x, S32 y, MASK mask) mDoubleClickTimer.reset(); } LLViewerObject* obj = mPick.getObject(); + U8 click_action = final_click_action(obj); + // let media have first pass at click + if (handleMediaMouseUp() || LLViewerMediaFocus::getInstance()->getFocus()) + { + mBlockClickToWalk = true; + } stopCameraSteering(); mMouseButtonDown = false; + if (click_action == CLICK_ACTION_NONE // not doing 1-click action + && gSavedSettings.getBOOL("ClickToWalk") // click to walk enabled + && !gAgent.getFlying() // don't auto-navigate while flying until that works + && gAgentAvatarp + && !gAgentAvatarp->isSitting() + && !mBlockClickToWalk // another behavior hasn't cancelled click to walk + ) + { + // We may be doing click to walk, but we don't want to use a target on + // a transparent object because the user thought they were clicking on + // whatever they were seeing through it, so recompute what was clicked on + // ignoring transparent objects + LLPickInfo savedPick = mPick; + mPick = gViewerWindow->pickImmediate(savedPick.mMousePt.mX, savedPick.mMousePt.mY, + FALSE /* ignore transparent */, + FALSE /* ignore rigged */, + FALSE /* ignore particles */); + + if (!mPick.mPosGlobal.isExactlyZero() // valid coordinates for pick + && (mPick.mPickType == LLPickInfo::PICK_LAND // we clicked on land + || mPick.mObjectID.notNull())) // or on an object + { + // handle special cases of steering picks + LLViewerObject* avatar_object = mPick.getObject(); + + // get pointer to avatar + while (avatar_object && !avatar_object->isAvatar()) + { + avatar_object = (LLViewerObject*)avatar_object->getParent(); + } + + if (avatar_object && ((LLVOAvatar*)avatar_object)->isSelf()) + { + const F64 SELF_CLICK_WALK_DISTANCE = 3.0; + // pretend we picked some point a bit in front of avatar + mPick.mPosGlobal = gAgent.getPositionGlobal() + LLVector3d(LLViewerCamera::instance().getAtAxis()) * SELF_CLICK_WALK_DISTANCE; + } + gAgentCamera.setFocusOnAvatar(TRUE, TRUE); + walkToClickedLocation(); + LLFirstUse::notMoving(false); + + return TRUE; + } + else + { + LL_DEBUGS("maint5901") << "walk target was " + << (mPick.mPosGlobal.isExactlyZero() ? "zero" : "not zero") + << ", pick type was " << (mPick.mPickType == LLPickInfo::PICK_LAND ? "land" : "not land") + << ", pick object was " << mPick.mObjectID + << LL_ENDL; + // we didn't click to walk, so restore the original target + mPick = savedPick; + } + } gViewerWindow->setCursor(UI_CURSOR_ARROW); if (hasMouseCapture()) { @@ -824,6 +788,7 @@ BOOL LLToolPie::handleMouseUp(S32 x, S32 y, MASK mask) LLToolMgr::getInstance()->clearTransientTool(); gAgentCamera.setLookAt(LOOKAT_TARGET_CONVERSATION, obj); // maybe look at object/person clicked on + mBlockClickToWalk = false; return LLTool::handleMouseUp(x, y, mask); } @@ -849,13 +814,66 @@ BOOL LLToolPie::handleDoubleClick(S32 x, S32 y, MASK mask) return TRUE; } - if (!mDoubleClickTimer.getStarted() || (mDoubleClickTimer.getElapsedTimeF32() > 0.3f)) + if (!mDoubleClickTimer.getStarted() || (mDoubleClickTimer.getElapsedTimeF32() > 0.3f)) { mDoubleClickTimer.stop(); return FALSE; } mDoubleClickTimer.stop(); + if (gSavedSettings.getBOOL("DoubleClickAutoPilot")) + { + // We may be doing double click to walk, but we don't want to use a target on + // a transparent object because the user thought they were clicking on + // whatever they were seeing through it, so recompute what was clicked on + // ignoring transparent objects + LLPickInfo savedPick = mPick; + mPick = gViewerWindow->pickImmediate(savedPick.mMousePt.mX, savedPick.mMousePt.mY, + FALSE /* ignore transparent */, + FALSE /* ignore rigged */, + FALSE /* ignore particles */); + + if(mPick.mPickType == LLPickInfo::PICK_OBJECT) + { + if (mPick.getObject() && mPick.getObject()->isHUDAttachment()) + { + mPick = savedPick; + return FALSE; + } + } + + if ((mPick.mPickType == LLPickInfo::PICK_LAND && !mPick.mPosGlobal.isExactlyZero()) || + (mPick.mObjectID.notNull() && !mPick.mPosGlobal.isExactlyZero())) + { + walkToClickedLocation(); + return TRUE; + } + else + { + // restore the original pick for any other purpose + mPick = savedPick; + } + } + else if (gSavedSettings.getBOOL("DoubleClickTeleport")) + { + LLViewerObject* objp = mPick.getObject(); + LLViewerObject* parentp = objp ? objp->getRootEdit() : NULL; + + bool is_in_world = mPick.mObjectID.notNull() && objp && !objp->isHUDAttachment(); + bool is_land = mPick.mPickType == LLPickInfo::PICK_LAND; + bool pos_non_zero = !mPick.mPosGlobal.isExactlyZero(); + bool has_touch_handler = (objp && objp->flagHandleTouch()) || (parentp && parentp->flagHandleTouch()); + bool has_click_action = final_click_action(objp); + + if (pos_non_zero && (is_land || (is_in_world && !has_touch_handler && !has_click_action))) + { + LLVector3d pos = mPick.mPosGlobal; + pos.mdV[VZ] += gAgentAvatarp->getPelvisToFoot(); + gAgent.teleportViaLocationLookAt(pos); + return TRUE; + } + } + return FALSE; } @@ -1387,6 +1405,7 @@ void LLToolPie::VisitHomePage(const LLPickInfo& info) void LLToolPie::handleSelect() { // tool is reselected when app gets focus, etc. + mBlockClickToWalk = true; } void LLToolPie::handleDeselect() @@ -1447,7 +1466,7 @@ void LLToolPie::stopCameraSteering() bool LLToolPie::inCameraSteerMode() { - return mMouseButtonDown && mMouseOutsideSlop; + return mMouseButtonDown && mMouseOutsideSlop && gSavedSettings.getBOOL("ClickToWalk"); } // true if x,y outside small box around start_x,start_y @@ -1898,6 +1917,7 @@ void LLToolPie::startCameraSteering() { LLFirstUse::notMoving(false); mMouseOutsideSlop = true; + mBlockClickToWalk = true; if (gAgentCamera.getFocusOnAvatar()) { diff --git a/indra/newview/lltoolpie.h b/indra/newview/lltoolpie.h index ac6751aac0..fe0acfe473 100644 --- a/indra/newview/lltoolpie.h +++ b/indra/newview/lltoolpie.h @@ -42,7 +42,7 @@ class LLToolPie : public LLTool, public LLSingleton public: // Virtual functions inherited from LLMouseHandler - virtual BOOL handleAnyMouseClick(S32 x, S32 y, MASK mask, EMouseClickType clicktype, BOOL down); + virtual BOOL handleAnyMouseClick(S32 x, S32 y, MASK mask, EClickType clicktype, BOOL down); virtual BOOL handleMouseDown(S32 x, S32 y, MASK mask); virtual BOOL handleRightMouseDown(S32 x, S32 y, MASK mask); virtual BOOL handleMouseUp(S32 x, S32 y, MASK mask); @@ -67,8 +67,8 @@ public: LLViewerObject* getClickActionObject() { return mClickActionObject; } LLObjectSelection* getLeftClickSelection() { return (LLObjectSelection*)mLeftClickSelection; } void resetSelection(); - bool walkToClickedLocation(); - bool teleportToClickedLocation(); + void walkToClickedLocation(); + void blockClickToWalk() { mBlockClickToWalk = true; } void stopClickToWalk(); static void selectionPropertiesReceived(); @@ -110,6 +110,7 @@ private: LLPointer mAutoPilotDestination; LLPointer mMouseSteerGrabPoint; bool mClockwise; + bool mBlockClickToWalk; LLUUID mMediaMouseCaptureID; LLPickInfo mPick; LLPickInfo mHoverPick; diff --git a/indra/newview/llviewerinput.cpp b/indra/newview/llviewerinput.cpp deleted file mode 100644 index ad4b9d4215..0000000000 --- a/indra/newview/llviewerinput.cpp +++ /dev/null @@ -1,1528 +0,0 @@ -/** - * @file llviewerinput.cpp - * @brief LLViewerInput class implementation - * - * $LicenseInfo:firstyear=2005&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, Linden Research, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; - * version 2.1 of the License only. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA - * $/LicenseInfo$ - */ - -#include "llviewerprecompiledheaders.h" - -#include "llviewerinput.h" - -#include "llappviewer.h" -#include "llfloaterreg.h" -#include "llmath.h" -#include "llagent.h" -#include "llagentcamera.h" -#include "llfloaterimnearbychat.h" -#include "llfocusmgr.h" -#include "llkeybind.h" // LLKeyData -#include "llmorphview.h" -#include "llmoveview.h" -#include "lltoolfocus.h" -#include "lltoolpie.h" -#include "llviewercontrol.h" -#include "llviewerwindow.h" -#include "llvoavatarself.h" -#include "llfloatercamera.h" -#include "llinitparam.h" -#include "llselectmgr.h" - -// -// Constants -// - -const F32 FLY_TIME = 0.5f; -const F32 FLY_FRAMES = 4; - -const F32 NUDGE_TIME = 0.25f; // in seconds -const S32 NUDGE_FRAMES = 2; -const F32 ORBIT_NUDGE_RATE = 0.05f; // fraction of normal speed - -const LLKeyData agent_control_lbutton(CLICK_LEFT, KEY_NONE, MASK_NONE, true); - -struct LLKeyboardActionRegistry -: public LLRegistrySingleton, LLKeyboardActionRegistry> -{ - LLSINGLETON_EMPTY_CTOR(LLKeyboardActionRegistry); -}; - -LLViewerInput gViewerInput; - -bool agent_jump( EKeystate s ) -{ - static BOOL first_fly_attempt(TRUE); - if (KEYSTATE_UP == s) - { - first_fly_attempt = TRUE; - return true; - } - F32 time = gKeyboard->getCurKeyElapsedTime(); - S32 frame_count = ll_round(gKeyboard->getCurKeyElapsedFrameCount()); - - if( time < FLY_TIME - || frame_count <= FLY_FRAMES - || gAgent.upGrabbed() - || !gSavedSettings.getBOOL("AutomaticFly")) - { - gAgent.moveUp(1); - } - else - { - gAgent.setFlying(TRUE, first_fly_attempt); - first_fly_attempt = FALSE; - gAgent.moveUp(1); - } - return true; -} - -bool agent_push_down( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return true; - gAgent.moveUp(-1); - return true; -} - -static void agent_check_temporary_run(LLAgent::EDoubleTapRunMode mode) -{ - if (gAgent.mDoubleTapRunMode == mode && - gAgent.getRunning() && - !gAgent.getAlwaysRun()) - { - // Turn off temporary running. - gAgent.clearRunning(); - gAgent.sendWalkRun(gAgent.getRunning()); - } -} - -static void agent_handle_doubletap_run(EKeystate s, LLAgent::EDoubleTapRunMode mode) -{ - if (KEYSTATE_UP == s) - { - // Note: in case shift is already released, slide left/right run - // will be released in agent_turn_left()/agent_turn_right() - agent_check_temporary_run(mode); - } - else if (gSavedSettings.getBOOL("AllowTapTapHoldRun") && - KEYSTATE_DOWN == s && - !gAgent.getRunning()) - { - if (gAgent.mDoubleTapRunMode == mode && - gAgent.mDoubleTapRunTimer.getElapsedTimeF32() < NUDGE_TIME) - { - // Same walk-key was pushed again quickly; this is a - // double-tap so engage temporary running. - gAgent.setRunning(); - gAgent.sendWalkRun(gAgent.getRunning()); - } - - // Pressing any walk-key resets the double-tap timer - gAgent.mDoubleTapRunTimer.reset(); - gAgent.mDoubleTapRunMode = mode; - } -} - -static void agent_push_forwardbackward( EKeystate s, S32 direction, LLAgent::EDoubleTapRunMode mode ) -{ - agent_handle_doubletap_run(s, mode); - if (KEYSTATE_UP == s) return; - - F32 time = gKeyboard->getCurKeyElapsedTime(); - S32 frame_count = ll_round(gKeyboard->getCurKeyElapsedFrameCount()); - - if( time < NUDGE_TIME || frame_count <= NUDGE_FRAMES) - { - gAgent.moveAtNudge(direction); - } - else - { - gAgent.moveAt(direction); - } -} - -bool camera_move_forward( EKeystate s ); - -bool agent_push_forward( EKeystate s ) -{ - if(gAgent.isMovementLocked()) return true; - - //in free camera control mode we need to intercept keyboard events for avatar movements - if (LLFloaterCamera::inFreeCameraMode()) - { - camera_move_forward(s); - } - else - { - agent_push_forwardbackward(s, 1, LLAgent::DOUBLETAP_FORWARD); - } - return true; -} - -bool camera_move_backward( EKeystate s ); - -bool agent_push_backward( EKeystate s ) -{ - if(gAgent.isMovementLocked()) return true; - - //in free camera control mode we need to intercept keyboard events for avatar movements - if (LLFloaterCamera::inFreeCameraMode()) - { - camera_move_backward(s); - } - else if (!gAgent.backwardGrabbed() && gAgentAvatarp->isSitting() && gSavedSettings.getBOOL("LeaveMouselook")) - { - gAgentCamera.changeCameraToThirdPerson(); - } - else - { - agent_push_forwardbackward(s, -1, LLAgent::DOUBLETAP_BACKWARD); - } - return true; -} - -static void agent_slide_leftright( EKeystate s, S32 direction, LLAgent::EDoubleTapRunMode mode ) -{ - agent_handle_doubletap_run(s, mode); - if( KEYSTATE_UP == s ) return; - F32 time = gKeyboard->getCurKeyElapsedTime(); - S32 frame_count = ll_round(gKeyboard->getCurKeyElapsedFrameCount()); - - if( time < NUDGE_TIME || frame_count <= NUDGE_FRAMES) - { - gAgent.moveLeftNudge(direction); - } - else - { - gAgent.moveLeft(direction); - } -} - - -bool agent_slide_left( EKeystate s ) -{ - if(gAgent.isMovementLocked()) return true; - agent_slide_leftright(s, 1, LLAgent::DOUBLETAP_SLIDELEFT); - return true; -} - - -bool agent_slide_right( EKeystate s ) -{ - if(gAgent.isMovementLocked()) return true; - agent_slide_leftright(s, -1, LLAgent::DOUBLETAP_SLIDERIGHT); - return true; -} - -bool camera_spin_around_cw( EKeystate s ); - -bool agent_turn_left(EKeystate s) -{ - //in free camera control mode we need to intercept keyboard events for avatar movements - if (LLFloaterCamera::inFreeCameraMode()) - { - camera_spin_around_cw(s); - return true; - } - - if(gAgent.isMovementLocked()) return false; - - if (LLToolCamera::getInstance()->mouseSteerMode()) - { - agent_slide_left(s); - } - else - { - if (KEYSTATE_UP == s) - { - // Check temporary running. In case user released 'left' key with shift already released. - agent_check_temporary_run(LLAgent::DOUBLETAP_SLIDELEFT); - return true; - } - F32 time = gKeyboard->getCurKeyElapsedTime(); - gAgent.moveYaw( LLFloaterMove::getYawRate( time ) ); - } - return true; -} - -bool camera_spin_around_ccw( EKeystate s ); - -bool agent_turn_right( EKeystate s ) -{ - //in free camera control mode we need to intercept keyboard events for avatar movements - if (LLFloaterCamera::inFreeCameraMode()) - { - camera_spin_around_ccw(s); - return true; - } - - if(gAgent.isMovementLocked()) return false; - - if (LLToolCamera::getInstance()->mouseSteerMode()) - { - agent_slide_right(s); - } - else - { - if (KEYSTATE_UP == s) - { - // Check temporary running. In case user released 'right' key with shift already released. - agent_check_temporary_run(LLAgent::DOUBLETAP_SLIDERIGHT); - return true; - } - F32 time = gKeyboard->getCurKeyElapsedTime(); - gAgent.moveYaw( -LLFloaterMove::getYawRate( time ) ); - } - return true; -} - -bool agent_look_up( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return true; - gAgent.movePitch(-1); - //gAgent.rotate(-2.f * DEG_TO_RAD, gAgent.getFrame().getLeftAxis() ); - return true; -} - - -bool agent_look_down( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return true; - gAgent.movePitch(1); - //gAgent.rotate(2.f * DEG_TO_RAD, gAgent.getFrame().getLeftAxis() ); - return true; -} - -bool agent_toggle_fly( EKeystate s ) -{ - // Only catch the edge - if (KEYSTATE_DOWN == s ) - { - LLAgent::toggleFlying(); - } - return true; -} - -F32 get_orbit_rate() -{ - F32 time = gKeyboard->getCurKeyElapsedTime(); - if( time < NUDGE_TIME ) - { - F32 rate = ORBIT_NUDGE_RATE + time * (1 - ORBIT_NUDGE_RATE)/ NUDGE_TIME; - //LL_INFOS() << rate << LL_ENDL; - return rate; - } - else - { - return 1; - } -} - -bool camera_spin_around_ccw( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return true; - gAgentCamera.unlockView(); - gAgentCamera.setOrbitLeftKey( get_orbit_rate() ); - return true; -} - - -bool camera_spin_around_cw( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return true; - gAgentCamera.unlockView(); - gAgentCamera.setOrbitRightKey( get_orbit_rate() ); - return true; -} - -bool camera_spin_around_ccw_sitting( EKeystate s ) -{ - if( KEYSTATE_UP == s && gAgent.mDoubleTapRunMode != LLAgent::DOUBLETAP_SLIDERIGHT ) return true; - if (gAgent.rotateGrabbed() || gAgentCamera.sitCameraEnabled() || gAgent.getRunning()) - { - //send keystrokes, but do not change camera - agent_turn_right(s); - } - else - { - //change camera but do not send keystrokes - gAgentCamera.unlockView(); - gAgentCamera.setOrbitLeftKey( get_orbit_rate() ); - } - return true; -} - - -bool camera_spin_around_cw_sitting( EKeystate s ) -{ - if( KEYSTATE_UP == s && gAgent.mDoubleTapRunMode != LLAgent::DOUBLETAP_SLIDELEFT ) return true; - if (gAgent.rotateGrabbed() || gAgentCamera.sitCameraEnabled() || gAgent.getRunning()) - { - //send keystrokes, but do not change camera - agent_turn_left(s); - } - else - { - //change camera but do not send keystrokes - gAgentCamera.unlockView(); - gAgentCamera.setOrbitRightKey( get_orbit_rate() ); - } - return true; -} - - -bool camera_spin_over( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return true; - gAgentCamera.unlockView(); - gAgentCamera.setOrbitUpKey( get_orbit_rate() ); - return true; -} - - -bool camera_spin_under( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return true; - gAgentCamera.unlockView(); - gAgentCamera.setOrbitDownKey( get_orbit_rate() ); - return true; -} - -bool camera_spin_over_sitting( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return true; - if (gAgent.upGrabbed() || gAgentCamera.sitCameraEnabled()) - { - //send keystrokes, but do not change camera - agent_jump(s); - } - else - { - //change camera but do not send keystrokes - gAgentCamera.setOrbitUpKey( get_orbit_rate() ); - } - return true; -} - - -bool camera_spin_under_sitting( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return true; - if (gAgent.downGrabbed() || gAgentCamera.sitCameraEnabled()) - { - //send keystrokes, but do not change camera - agent_push_down(s); - } - else - { - //change camera but do not send keystrokes - gAgentCamera.setOrbitDownKey( get_orbit_rate() ); - } - return true; -} - -bool camera_move_forward( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return true; - gAgentCamera.unlockView(); - gAgentCamera.setOrbitInKey( get_orbit_rate() ); - return true; -} - - -bool camera_move_backward( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return true; - gAgentCamera.unlockView(); - gAgentCamera.setOrbitOutKey( get_orbit_rate() ); - return true; -} - -bool camera_move_forward_sitting( EKeystate s ) -{ - if( KEYSTATE_UP == s && gAgent.mDoubleTapRunMode != LLAgent::DOUBLETAP_FORWARD ) return true; - if (gAgent.forwardGrabbed() || gAgentCamera.sitCameraEnabled() || (gAgent.getRunning() && !gAgent.getAlwaysRun())) - { - agent_push_forward(s); - } - else - { - gAgentCamera.setOrbitInKey( get_orbit_rate() ); - } - return true; -} - -bool camera_move_backward_sitting( EKeystate s ) -{ - if( KEYSTATE_UP == s && gAgent.mDoubleTapRunMode != LLAgent::DOUBLETAP_BACKWARD ) return true; - - if (gAgent.backwardGrabbed() || gAgentCamera.sitCameraEnabled() || (gAgent.getRunning() && !gAgent.getAlwaysRun())) - { - agent_push_backward(s); - } - else - { - gAgentCamera.setOrbitOutKey( get_orbit_rate() ); - } - return true; -} - -bool camera_pan_up( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return true; - gAgentCamera.unlockView(); - gAgentCamera.setPanUpKey( get_orbit_rate() ); - return true; -} - -bool camera_pan_down( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return true; - gAgentCamera.unlockView(); - gAgentCamera.setPanDownKey( get_orbit_rate() ); - return true; -} - -bool camera_pan_left( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return true; - gAgentCamera.unlockView(); - gAgentCamera.setPanLeftKey( get_orbit_rate() ); - return true; -} - -bool camera_pan_right( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return true; - gAgentCamera.unlockView(); - gAgentCamera.setPanRightKey( get_orbit_rate() ); - return true; -} - -bool camera_pan_in( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return true; - gAgentCamera.unlockView(); - gAgentCamera.setPanInKey( get_orbit_rate() ); - return true; -} - -bool camera_pan_out( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return true; - gAgentCamera.unlockView(); - gAgentCamera.setPanOutKey( get_orbit_rate() ); - return true; -} - -bool camera_move_forward_fast( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return true; - gAgentCamera.unlockView(); - gAgentCamera.setOrbitInKey(2.5f); - return true; -} - -bool camera_move_backward_fast( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return true; - gAgentCamera.unlockView(); - gAgentCamera.setOrbitOutKey(2.5f); - return true; -} - - -bool edit_avatar_spin_ccw( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return true; - gMorphView->setCameraDrivenByKeys( TRUE ); - gAgentCamera.setOrbitLeftKey( get_orbit_rate() ); - //gMorphView->orbitLeft( get_orbit_rate() ); - return true; -} - - -bool edit_avatar_spin_cw( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return true; - gMorphView->setCameraDrivenByKeys( TRUE ); - gAgentCamera.setOrbitRightKey( get_orbit_rate() ); - //gMorphView->orbitRight( get_orbit_rate() ); - return true; -} - -bool edit_avatar_spin_over( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return true; - gMorphView->setCameraDrivenByKeys( TRUE ); - gAgentCamera.setOrbitUpKey( get_orbit_rate() ); - //gMorphView->orbitUp( get_orbit_rate() ); - return true; -} - - -bool edit_avatar_spin_under( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return true; - gMorphView->setCameraDrivenByKeys( TRUE ); - gAgentCamera.setOrbitDownKey( get_orbit_rate() ); - //gMorphView->orbitDown( get_orbit_rate() ); - return true; -} - -bool edit_avatar_move_forward( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return true; - gMorphView->setCameraDrivenByKeys( TRUE ); - gAgentCamera.setOrbitInKey( get_orbit_rate() ); - //gMorphView->orbitIn(); - return true; -} - - -bool edit_avatar_move_backward( EKeystate s ) -{ - if( KEYSTATE_UP == s ) return true; - gMorphView->setCameraDrivenByKeys( TRUE ); - gAgentCamera.setOrbitOutKey( get_orbit_rate() ); - //gMorphView->orbitOut(); - return true; -} - -bool stop_moving( EKeystate s ) -{ - if( KEYSTATE_DOWN != s ) return true; - // stop agent - gAgent.setControlFlags(AGENT_CONTROL_STOP); - - // cancel autopilot - gAgent.stopAutoPilot(); - return true; -} - -bool start_chat( EKeystate s ) -{ - if (LLAppViewer::instance()->quitRequested()) - { - return true; // can't talk, gotta go, kthxbye! - } - if (KEYSTATE_DOWN != s) return true; - - // start chat - LLFloaterIMNearbyChat::startChat(NULL); - return true; -} - -bool start_gesture( EKeystate s ) -{ - LLUICtrl* focus_ctrlp = dynamic_cast(gFocusMgr.getKeyboardFocus()); - if (KEYSTATE_UP == s && - ! (focus_ctrlp && focus_ctrlp->acceptsTextInput())) - { - if ((LLFloaterReg::getTypedInstance("nearby_chat"))->getCurrentChat().empty()) - { - // No existing chat in chat editor, insert '/' - LLFloaterIMNearbyChat::startChat("/"); - } - else - { - // Don't overwrite existing text in chat editor - LLFloaterIMNearbyChat::startChat(NULL); - } - } - return true; -} - -bool run_forward(EKeystate s) -{ - if (KEYSTATE_UP != s) - { - if (gAgent.mDoubleTapRunMode != LLAgent::DOUBLETAP_FORWARD) - { - gAgent.mDoubleTapRunMode = LLAgent::DOUBLETAP_FORWARD; - } - if (!gAgent.getRunning()) - { - gAgent.setRunning(); - gAgent.sendWalkRun(true); - } - } - else if(KEYSTATE_UP == s) - { - if (gAgent.mDoubleTapRunMode == LLAgent::DOUBLETAP_FORWARD) - gAgent.mDoubleTapRunMode = LLAgent::DOUBLETAP_NONE; - gAgent.clearRunning(); - gAgent.sendWalkRun(false); - } - agent_push_forward(s); - return true; -} - -bool run_backward(EKeystate s) -{ - if (KEYSTATE_UP != s) - { - if (gAgent.mDoubleTapRunMode != LLAgent::DOUBLETAP_BACKWARD) - { - gAgent.mDoubleTapRunMode = LLAgent::DOUBLETAP_BACKWARD; - } - if (!gAgent.getRunning()) - { - gAgent.setRunning(); - gAgent.sendWalkRun(true); - } - } - else if (KEYSTATE_UP == s) - { - if (gAgent.mDoubleTapRunMode == LLAgent::DOUBLETAP_BACKWARD) - gAgent.mDoubleTapRunMode = LLAgent::DOUBLETAP_NONE; - gAgent.clearRunning(); - gAgent.sendWalkRun(false); - } - agent_push_backward(s); - return true; -} - -bool run_left(EKeystate s) -{ - if (KEYSTATE_UP != s) - { - if (gAgent.mDoubleTapRunMode != LLAgent::DOUBLETAP_SLIDELEFT) - { - gAgent.mDoubleTapRunMode = LLAgent::DOUBLETAP_SLIDELEFT; - } - if (!gAgent.getRunning()) - { - gAgent.setRunning(); - gAgent.sendWalkRun(true); - } - } - else if (KEYSTATE_UP == s) - { - if (gAgent.mDoubleTapRunMode == LLAgent::DOUBLETAP_SLIDELEFT) - gAgent.mDoubleTapRunMode = LLAgent::DOUBLETAP_NONE; - gAgent.clearRunning(); - gAgent.sendWalkRun(false); - } - agent_slide_left(s); - return true; -} - -bool run_right(EKeystate s) -{ - if (KEYSTATE_UP != s) - { - if (gAgent.mDoubleTapRunMode != LLAgent::DOUBLETAP_SLIDERIGHT) - { - gAgent.mDoubleTapRunMode = LLAgent::DOUBLETAP_SLIDERIGHT; - } - if (!gAgent.getRunning()) - { - gAgent.setRunning(); - gAgent.sendWalkRun(true); - } - } - else if (KEYSTATE_UP == s) - { - if (gAgent.mDoubleTapRunMode == LLAgent::DOUBLETAP_SLIDERIGHT) - gAgent.mDoubleTapRunMode = LLAgent::DOUBLETAP_NONE; - gAgent.clearRunning(); - gAgent.sendWalkRun(false); - } - agent_slide_right(s); - return true; -} - -bool toggle_run(EKeystate s) -{ - if (KEYSTATE_DOWN != s) return true; - bool run = gAgent.getAlwaysRun(); - if (run) - { - gAgent.clearAlwaysRun(); - gAgent.clearRunning(); - } - else - { - gAgent.setAlwaysRun(); - gAgent.setRunning(); - } - gAgent.sendWalkRun(!run); - return true; -} - -bool toggle_sit(EKeystate s) -{ - if (KEYSTATE_DOWN != s) return true; - if (gAgent.isSitting()) - { - gAgent.standUp(); - } - else - { - gAgent.sitDown(); - } - return true; -} - -bool toggle_pause_media(EKeystate s) // analogue of play/pause button in top bar -{ - if (KEYSTATE_DOWN != s) return true; - bool pause = LLViewerMedia::getInstance()->isAnyMediaPlaying(); - LLViewerMedia::getInstance()->setAllMediaPaused(pause); - return true; -} - -bool toggle_enable_media(EKeystate s) -{ - if (KEYSTATE_DOWN != s) return true; - bool pause = LLViewerMedia::getInstance()->isAnyMediaPlaying() || LLViewerMedia::getInstance()->isAnyMediaShowing(); - LLViewerMedia::getInstance()->setAllMediaEnabled(!pause); - return true; -} - -bool walk_to(EKeystate s) -{ - if (KEYSTATE_DOWN != s) return true; - return LLToolPie::getInstance()->walkToClickedLocation(); -} - -bool teleport_to(EKeystate s) -{ - if (KEYSTATE_DOWN != s) return true; - return LLToolPie::getInstance()->teleportToClickedLocation(); -} - -bool toggle_voice(EKeystate s) -{ - if (KEYSTATE_DOWN != s) return true; - if (!LLAgent::isActionAllowed("speak")) return false; - LLVoiceClient::getInstance()->toggleUserPTTState(); - return true; -} - -bool voice_follow_key(EKeystate s) -{ - if (KEYSTATE_DOWN == s) - { - if (!LLAgent::isActionAllowed("speak")) return false; - LLVoiceClient::getInstance()->setUserPTTState(true); - return true; - } - else if (KEYSTATE_UP == s && LLVoiceClient::getInstance()->getUserPTTState()) - { - LLVoiceClient::getInstance()->setUserPTTState(false); - return true; - } - return false; -} - -bool agen_control_lbutton_handle(EKeystate s) -{ - switch (s) - { - case KEYSTATE_DOWN: - gAgent.setControlFlags(AGENT_CONTROL_LBUTTON_DOWN); - break; - case KEYSTATE_UP: - gAgent.setControlFlags(AGENT_CONTROL_LBUTTON_UP); - break; - default: - break; - } - return true; -} - -#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); -REGISTER_KEYBOARD_ACTION("push_forward", agent_push_forward); -REGISTER_KEYBOARD_ACTION("push_backward", agent_push_backward); -REGISTER_KEYBOARD_ACTION("look_up", agent_look_up); -REGISTER_KEYBOARD_ACTION("look_down", agent_look_down); -REGISTER_KEYBOARD_ACTION("toggle_fly", agent_toggle_fly); -REGISTER_KEYBOARD_ACTION("turn_left", agent_turn_left); -REGISTER_KEYBOARD_ACTION("turn_right", agent_turn_right); -REGISTER_KEYBOARD_ACTION("slide_left", agent_slide_left); -REGISTER_KEYBOARD_ACTION("slide_right", agent_slide_right); -REGISTER_KEYBOARD_ACTION("spin_around_ccw", camera_spin_around_ccw); -REGISTER_KEYBOARD_ACTION("spin_around_cw", camera_spin_around_cw); -REGISTER_KEYBOARD_ACTION("spin_around_ccw_sitting", camera_spin_around_ccw_sitting); -REGISTER_KEYBOARD_ACTION("spin_around_cw_sitting", camera_spin_around_cw_sitting); -REGISTER_KEYBOARD_ACTION("spin_over", camera_spin_over); -REGISTER_KEYBOARD_ACTION("spin_under", camera_spin_under); -REGISTER_KEYBOARD_ACTION("spin_over_sitting", camera_spin_over_sitting); -REGISTER_KEYBOARD_ACTION("spin_under_sitting", camera_spin_under_sitting); -REGISTER_KEYBOARD_ACTION("move_forward", camera_move_forward); -REGISTER_KEYBOARD_ACTION("move_backward", camera_move_backward); -REGISTER_KEYBOARD_ACTION("move_forward_sitting", camera_move_forward_sitting); -REGISTER_KEYBOARD_ACTION("move_backward_sitting", camera_move_backward_sitting); -REGISTER_KEYBOARD_ACTION("pan_up", camera_pan_up); -REGISTER_KEYBOARD_ACTION("pan_down", camera_pan_down); -REGISTER_KEYBOARD_ACTION("pan_left", camera_pan_left); -REGISTER_KEYBOARD_ACTION("pan_right", camera_pan_right); -REGISTER_KEYBOARD_ACTION("pan_in", camera_pan_in); -REGISTER_KEYBOARD_ACTION("pan_out", camera_pan_out); -REGISTER_KEYBOARD_ACTION("move_forward_fast", camera_move_forward_fast); -REGISTER_KEYBOARD_ACTION("move_backward_fast", camera_move_backward_fast); -REGISTER_KEYBOARD_ACTION("edit_avatar_spin_ccw", edit_avatar_spin_ccw); -REGISTER_KEYBOARD_ACTION("edit_avatar_spin_cw", edit_avatar_spin_cw); -REGISTER_KEYBOARD_ACTION("edit_avatar_spin_over", edit_avatar_spin_over); -REGISTER_KEYBOARD_ACTION("edit_avatar_spin_under", edit_avatar_spin_under); -REGISTER_KEYBOARD_ACTION("edit_avatar_move_forward", edit_avatar_move_forward); -REGISTER_KEYBOARD_ACTION("edit_avatar_move_backward", edit_avatar_move_backward); -REGISTER_KEYBOARD_ACTION("stop_moving", stop_moving); -REGISTER_KEYBOARD_ACTION("start_chat", start_chat); -REGISTER_KEYBOARD_ACTION("start_gesture", start_gesture); -REGISTER_KEYBOARD_ACTION("run_forward", run_forward); -REGISTER_KEYBOARD_ACTION("run_backward", run_backward); -REGISTER_KEYBOARD_ACTION("run_left", run_left); -REGISTER_KEYBOARD_ACTION("run_right", run_right); -REGISTER_KEYBOARD_ACTION("toggle_run", toggle_run); -REGISTER_KEYBOARD_ACTION("toggle_sit", toggle_sit); -REGISTER_KEYBOARD_ACTION("toggle_pause_media", toggle_pause_media); -REGISTER_KEYBOARD_ACTION("toggle_enable_media", toggle_enable_media); -REGISTER_KEYBOARD_ACTION("teleport_to", teleport_to); -REGISTER_KEYBOARD_ACTION("walk_to", walk_to); -REGISTER_KEYBOARD_ACTION("toggle_voice", toggle_voice); -REGISTER_KEYBOARD_ACTION("voice_follow_key", voice_follow_key); -#undef REGISTER_KEYBOARD_ACTION - -LLViewerInput::LLViewerInput() -{ - resetBindings(); - - for (S32 i = 0; i < KEY_COUNT; i++) - { - mKeyHandledByUI[i] = FALSE; - } - for (S32 i = 0; i < CLICK_COUNT; i++) - { - mMouseLevel[i] = MOUSE_STATE_SILENT; - } - // we want the UI to never see these keys so that they can always control the avatar/camera - for(KEY k = KEY_PAD_UP; k <= KEY_PAD_DIVIDE; k++) - { - mKeysSkippedByUI.insert(k); - } -} - -// static -BOOL LLViewerInput::modeFromString(const std::string& string, S32 *mode) -{ - if (string == "FIRST_PERSON") - { - *mode = MODE_FIRST_PERSON; - return TRUE; - } - else if (string == "THIRD_PERSON") - { - *mode = MODE_THIRD_PERSON; - return TRUE; - } - else if (string == "EDIT_AVATAR") - { - *mode = MODE_EDIT_AVATAR; - return TRUE; - } - else if (string == "SITTING") - { - *mode = MODE_SITTING; - return TRUE; - } - else - { - *mode = MODE_THIRD_PERSON; - return FALSE; - } -} - -// static -BOOL LLViewerInput::mouseFromString(const std::string& string, EMouseClickType *mode) -{ - if (string == "LMB") - { - *mode = CLICK_LEFT; - return TRUE; - } - else if (string == "Double LMB") - { - *mode = CLICK_DOUBLELEFT; - return TRUE; - } - else if (string == "MMB") - { - *mode = CLICK_MIDDLE; - return TRUE; - } - else if (string == "MB4") - { - *mode = CLICK_BUTTON4; - return TRUE; - } - else if (string == "MB5") - { - *mode = CLICK_BUTTON5; - return TRUE; - } - else - { - *mode = CLICK_NONE; - return FALSE; - } -} - -BOOL LLViewerInput::handleKey(KEY translated_key, MASK translated_mask, BOOL repeated) -{ - // check for re-map - EKeyboardMode mode = gViewerInput.getMode(); - U32 keyidx = (translated_mask<<16) | translated_key; - key_remap_t::iterator iter = mRemapKeys[mode].find(keyidx); - if (iter != mRemapKeys[mode].end()) - { - translated_key = (iter->second) & 0xff; - translated_mask = (iter->second)>>16; - } - - // No repeats of F-keys - BOOL repeatable_key = (translated_key < KEY_F1 || translated_key > KEY_F12); - if (!repeatable_key && repeated) - { - return FALSE; - } - - LL_DEBUGS("UserInput") << "keydown -" << translated_key << "-" << LL_ENDL; - // skip skipped keys - if(mKeysSkippedByUI.find(translated_key) != mKeysSkippedByUI.end()) - { - mKeyHandledByUI[translated_key] = FALSE; - LL_INFOS("KeyboardHandling") << "Key wasn't handled by UI!" << LL_ENDL; - } - else - { - // it is sufficient to set this value once per call to handlekey - // without clearing it, as it is only used in the subsequent call to scanKey - mKeyHandledByUI[translated_key] = gViewerWindow->handleKey(translated_key, translated_mask); - // mKeyHandledByUI is not what you think ... this indicates whether the UI has handled this keypress yet (any keypress) - // NOT whether some UI shortcut wishes to handle the keypress - - } - return mKeyHandledByUI[translated_key]; -} - -BOOL LLViewerInput::handleKeyUp(KEY translated_key, MASK translated_mask) -{ - return gViewerWindow->handleKeyUp(translated_key, translated_mask); -} - -BOOL LLViewerInput::bindKey(const S32 mode, const KEY key, const MASK mask, const std::string& function_name) -{ - S32 index; - typedef boost::function function_t; - function_t function = NULL; - std::string name; - - // Allow remapping of F2-F12 - if (function_name[0] == 'F') - { - int c1 = function_name[1] - '0'; - int c2 = function_name[2] ? function_name[2] - '0' : -1; - if (c1 >= 0 && c1 <= 9 && c2 >= -1 && c2 <= 9) - { - int idx = c1; - if (c2 >= 0) - idx = idx*10 + c2; - if (idx >=2 && idx <= 12) - { - U32 keyidx = ((mask<<16)|key); - (mRemapKeys[mode])[keyidx] = ((0<<16)|(KEY_F1+(idx-1))); - return TRUE; - } - } - } - - // Not remapped, look for a function - - function_t* result = LLKeyboardActionRegistry::getValue(function_name); - if (result) - { - function = *result; - } - - if (!function) - { - LL_ERRS() << "Can't bind key to function " << function_name << ", no function with this name found" << LL_ENDL; - return FALSE; - } - - // check for duplicate first and overwrite - S32 size = mKeyBindings[mode].size(); - for (index = 0; index < size; index++) - { - if (key == mKeyBindings[mode][index].mKey && mask == mKeyBindings[mode][index].mMask) - break; - } - - if (mode >= MODE_COUNT) - { - LL_ERRS() << "LLKeyboard::bindKey() - unknown mode passed" << mode << LL_ENDL; - return FALSE; - } - - LLKeyboardBinding bind; - bind.mKey = key; - bind.mMask = mask; - bind.mFunction = function; - - mKeyBindings[mode].push_back(bind); - - return TRUE; -} - -BOOL LLViewerInput::bindMouse(const S32 mode, const EMouseClickType mouse, const MASK mask, const std::string& function_name) -{ - S32 index; - typedef boost::function function_t; - function_t function = NULL; - - function_t* result = LLKeyboardActionRegistry::getValue(function_name); - if (result) - { - function = *result; - } - - if (!function) - { - LL_ERRS() << "Can't bind key to function " << function_name << ", no function with this name found" << LL_ENDL; - return FALSE; - } - - // check for duplicate first and overwrite - S32 size = mMouseBindings[mode].size(); - for (index = 0; index < size; index++) - { - if (mouse == mMouseBindings[mode][index].mMouse && mask == mMouseBindings[mode][index].mMask) - break; - } - - if (mode >= MODE_COUNT) - { - LL_ERRS() << "LLKeyboard::bindKey() - unknown mode passed" << mode << LL_ENDL; - return FALSE; - } - - LLMouseBinding bind; - bind.mMouse = mouse; - bind.mMask = mask; - bind.mFunction = function; - - mMouseBindings[mode].push_back(bind); - - return TRUE; -} - -LLViewerInput::KeyBinding::KeyBinding() -: key("key"), - mouse("mouse"), - mask("mask"), - command("command") -{} - -LLViewerInput::KeyMode::KeyMode() -: bindings("binding") -{} - -LLViewerInput::Keys::Keys() -: first_person("first_person"), - third_person("third_person"), - sitting("sitting"), - edit_avatar("edit_avatar") -{} - -void LLViewerInput::resetBindings() -{ - for (S32 i = 0; i < MODE_COUNT; i++) - { - mKeyBindings[i].clear(); - mMouseBindings[i].clear(); - } -} - -S32 LLViewerInput::loadBindingsXML(const std::string& filename) -{ - resetBindings(); - - S32 binding_count = 0; - Keys keys; - LLSimpleXUIParser parser; - - if (parser.readXUI(filename, keys) - && keys.validateBlock()) - { - binding_count += loadBindingMode(keys.first_person, MODE_FIRST_PERSON); - binding_count += loadBindingMode(keys.third_person, MODE_THIRD_PERSON); - binding_count += loadBindingMode(keys.sitting, MODE_SITTING); - binding_count += loadBindingMode(keys.edit_avatar, MODE_EDIT_AVATAR); - } - return binding_count; -} - -S32 count_masks(const MASK &mask) -{ - S32 res = 0; - if (mask & MASK_CONTROL) - { - res++; - } - if (mask & MASK_SHIFT) - { - res++; - } - if (mask & MASK_ALT) - { - res++; - } - return res; -} - -bool compare_key_by_mask(LLKeyboardBinding i1, LLKeyboardBinding i2) -{ - return (count_masks(i1.mMask) > count_masks(i2.mMask)); -} - -bool compare_mouse_by_mask(LLMouseBinding i1, LLMouseBinding i2) -{ - return (count_masks(i1.mMask) > count_masks(i2.mMask)); -} - -S32 LLViewerInput::loadBindingMode(const LLViewerInput::KeyMode& keymode, S32 mode) -{ - S32 binding_count = 0; - for (LLInitParam::ParamIterator::const_iterator it = keymode.bindings.begin(), - end_it = keymode.bindings.end(); - it != end_it; - ++it) - { - bool processed = false; - std::string key_str = it->key.getValue(); - if (!key_str.empty() && key_str != "NONE") - { - KEY key; - LLKeyboard::keyFromString(key_str, &key); - if (key != KEY_NONE) - { - MASK mask; - LLKeyboard::maskFromString(it->mask, &mask); - bindKey(mode, key, mask, it->command); - processed = true; - } - else - { - LL_WARNS_ONCE() << "There might be issues in keybindings' file" << LL_ENDL; - } - } - if (!processed && it->mouse.isProvided() && !it->mouse.getValue().empty()) - { - EMouseClickType mouse; - mouseFromString(it->mouse.getValue(), &mouse); - if (mouse != CLICK_NONE) - { - MASK mask; - LLKeyboard::maskFromString(it->mask, &mask); - bindMouse(mode, mouse, mask, it->command); - processed = true; - } - else - { - LL_WARNS_ONCE() << "There might be issues in keybindings' file" << LL_ENDL; - } - } - if (processed) - { - // total - binding_count++; - } - } - - // sort lists by mask (so that Shift+W is executed before W, if both are assigned, but if Shift+W is not assigned W should be executed) - std::sort(mKeyBindings[mode].begin(), mKeyBindings[mode].end(), compare_key_by_mask); - std::sort(mMouseBindings[mode].begin(), mMouseBindings[mode].end(), compare_mouse_by_mask); - - return binding_count; -} - -EKeyboardMode LLViewerInput::getMode() const -{ - if ( gAgentCamera.cameraMouselook() ) - { - return MODE_FIRST_PERSON; - } - else if ( gMorphView && gMorphView->getVisible()) - { - return MODE_EDIT_AVATAR; - } - else if (isAgentAvatarValid() && gAgentAvatarp->isSitting()) - { - return MODE_SITTING; - } - else - { - return MODE_THIRD_PERSON; - } -} - -bool LLViewerInput::scanKey(const std::vector &binding, - S32 binding_count, - KEY key, - MASK mask, - BOOL key_down, - BOOL key_up, - BOOL key_level, - bool repeat) const -{ - for (S32 i = 0; i < binding_count; i++) - { - if (binding[i].mKey == key) - { - if ((binding[i].mMask & mask) == binding[i].mMask) - { - bool res = false; - if (key_down && !repeat) - { - // ...key went down this frame, call function - res = binding[i].mFunction( KEYSTATE_DOWN ); - return true; - } - else if (key_up) - { - // ...key went down this frame, call function - res = binding[i].mFunction( KEYSTATE_UP ); - } - else if (key_level) - { - // ...key held down from previous frame - // Not windows, just call the function. - res = binding[i].mFunction( KEYSTATE_LEVEL ); - }//if - // Key+Mask combinations are supposed to be unique, so we won't find anything else - return res; - }//if - }//if - }//for - return false; -} - -// Called from scanKeyboard. -bool LLViewerInput::scanKey(KEY key, BOOL key_down, BOOL key_up, BOOL key_level) const -{ - if (LLApp::isExiting()) - { - return false; - } - - S32 mode = getMode(); - // Consider keyboard scanning as NOT mouse event. JC - MASK mask = gKeyboard->currentMask(FALSE); - - if (mKeyHandledByUI[key]) - { - return false; - } - - // don't process key down on repeated keys - BOOL repeat = gKeyboard->getKeyRepeated(key); - - bool res = scanKey(mKeyBindings[mode], mKeyBindings[mode].size(), key, mask, key_down, key_up, key_level, repeat); - - if (!res && agent_control_lbutton.canHandle(CLICK_NONE, key, mask)) - { - if (key_down && !repeat) - { - res = agen_control_lbutton_handle(KEYSTATE_DOWN); - } - if (key_up) - { - res = agen_control_lbutton_handle(KEYSTATE_UP); - } - } - return res; -} - -BOOL LLViewerInput::handleMouse(LLWindow *window_impl, LLCoordGL pos, MASK mask, EMouseClickType clicktype, BOOL down) -{ - BOOL handled = gViewerWindow->handleAnyMouseClick(window_impl, pos, mask, clicktype, down); - - if (clicktype != CLICK_NONE) - { - // Special case - // If UI doesn't handle double click, LMB click is issued, so supres LMB 'down' when doubleclick is set - // handle !down as if we are handling doubleclick - - bool double_click_sp = (clicktype == CLICK_LEFT - && (mMouseLevel[CLICK_DOUBLELEFT] != MOUSE_STATE_SILENT) - && mMouseLevel[CLICK_LEFT] == MOUSE_STATE_SILENT); - if (double_click_sp && !down) - { - // Process doubleclick instead - clicktype = CLICK_DOUBLELEFT; - } - - - if (double_click_sp && down) - { - // Consume click. - // Due to handling, double click that is not handled will be immediately followed by LMB click - } - // If UI handled 'down', it should handle 'up' as well - // If we handle 'down' not by UI, then we should handle 'up'/'level' regardless of UI - else if (handled) - { - // UI handled new 'down' so iterupt whatever state we were in. - if (mMouseLevel[clicktype] != MOUSE_STATE_SILENT) - { - if (mMouseLevel[clicktype] == MOUSE_STATE_DOWN) - { - mMouseLevel[clicktype] = MOUSE_STATE_CLICK; - } - else - { - mMouseLevel[clicktype] = MOUSE_STATE_UP; - } - } - } - else if (down) - { - if (mMouseLevel[clicktype] == MOUSE_STATE_DOWN) - { - // this is repeated hit (mouse does not repeat event until release) - // for now treat rapid clicking like mouse being held - mMouseLevel[clicktype] = MOUSE_STATE_LEVEL; - } - else - { - mMouseLevel[clicktype] = MOUSE_STATE_DOWN; - } - } - else if (mMouseLevel[clicktype] != MOUSE_STATE_SILENT) - { - // Released mouse key - if (mMouseLevel[clicktype] == MOUSE_STATE_DOWN) - { - mMouseLevel[clicktype] = MOUSE_STATE_CLICK; - } - else - { - mMouseLevel[clicktype] = MOUSE_STATE_UP; - } - } - } - - return handled; -} - -bool LLViewerInput::scanMouse(const std::vector &binding, S32 binding_count, EMouseClickType mouse, MASK mask, EMouseState state) const -{ - for (S32 i = 0; i < binding_count; i++) - { - if (binding[i].mMouse == mouse && (binding[i].mMask & mask) == binding[i].mMask) - { - bool res = false; - switch (state) - { - case MOUSE_STATE_DOWN: - res = binding[i].mFunction(KEYSTATE_DOWN); - break; - case MOUSE_STATE_CLICK: - // Button went down and up in scope of single frame - // might not work best with some functions, - // but some function need specific states specifically - res = binding[i].mFunction(KEYSTATE_DOWN); - res |= binding[i].mFunction(KEYSTATE_UP); - break; - case MOUSE_STATE_LEVEL: - res = binding[i].mFunction(KEYSTATE_LEVEL); - break; - case MOUSE_STATE_UP: - res = binding[i].mFunction(KEYSTATE_UP); - break; - default: - break; - } - // Key+Mask combinations are supposed to be unique, no need to continue - return res; - } - } - return false; -} - -// todo: this recods key, scanMouse() triggers functions with EKeystate -bool LLViewerInput::scanMouse(EMouseClickType click, EMouseState state) const -{ - bool res = false; - S32 mode = getMode(); - MASK mask = gKeyboard->currentMask(TRUE); - res = scanMouse(mMouseBindings[mode], mMouseBindings[mode].size(), click, mask, state); - // no user defined actions found or those actions can't handle the key/button, handle control if nessesary - if (!res && agent_control_lbutton.canHandle(click, KEY_NONE, mask)) - { - switch (state) - { - case MOUSE_STATE_DOWN: - agen_control_lbutton_handle(KEYSTATE_DOWN); - res = true; - break; - case MOUSE_STATE_CLICK: - // might not work best with some functions, - // but some function need specific states too specifically - agen_control_lbutton_handle(KEYSTATE_DOWN); - agen_control_lbutton_handle(KEYSTATE_UP); - res = true; - break; - case MOUSE_STATE_UP: - agen_control_lbutton_handle(KEYSTATE_UP); - res = true; - break; - default: - break; - } - } - return res; -} - -void LLViewerInput::scanMouse() -{ - for (S32 i = 0; i < CLICK_COUNT; i++) - { - if (mMouseLevel[i] != MOUSE_STATE_SILENT) - { - scanMouse((EMouseClickType)i, mMouseLevel[i]); - if (mMouseLevel[i] == MOUSE_STATE_DOWN) - { - // mouse doesn't support 'continued' state, so after handling, switch to LEVEL - mMouseLevel[i] = MOUSE_STATE_LEVEL; - } - else if (mMouseLevel[i] == MOUSE_STATE_UP || mMouseLevel[i] == MOUSE_STATE_CLICK) - { - mMouseLevel[i] = MOUSE_STATE_SILENT; - } - } - } -} diff --git a/indra/newview/llviewerinput.h b/indra/newview/llviewerinput.h deleted file mode 100644 index 1fe55bd585..0000000000 --- a/indra/newview/llviewerinput.h +++ /dev/null @@ -1,177 +0,0 @@ -/** - * @file llviewerinput.h - * @brief LLViewerInput class header file - * - * $LicenseInfo:firstyear=2005&license=viewerlgpl$ - * Second Life Viewer Source Code - * Copyright (C) 2010, 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_LLVIEWERINPUT_H -#define LL_LLVIEWERINPUT_H - -#include "llkeyboard.h" // For EKeystate -#include "llinitparam.h" - -const S32 MAX_KEY_BINDINGS = 128; // was 60 - -class LLNamedFunction -{ -public: - LLNamedFunction() : mFunction(NULL) { }; - ~LLNamedFunction() { }; - - std::string mName; - LLKeyFunc mFunction; -}; - -class LLKeyboardBinding -{ -public: - KEY mKey; - MASK mMask; - - LLKeyFunc mFunction; -}; - -class LLMouseBinding -{ -public: - EMouseClickType mMouse; - MASK mMask; - - LLKeyFunc mFunction; -}; - - -typedef enum e_keyboard_mode -{ - MODE_FIRST_PERSON, - MODE_THIRD_PERSON, - MODE_EDIT_AVATAR, - MODE_SITTING, - MODE_COUNT -} EKeyboardMode; - -class LLWindow; - -void bind_keyboard_functions(); - -class LLViewerInput -{ -public: - struct KeyBinding : public LLInitParam::Block - { - Mandatory key, - mask, - command; - Optional mouse; // Note, not mandatory for the sake of backward campatibility with keys.xml - - KeyBinding(); - }; - - struct KeyMode : public LLInitParam::Block - { - Multiple bindings; - - KeyMode(); - }; - - struct Keys : public LLInitParam::Block - { - Optional first_person, - third_person, - sitting, - edit_avatar; - - Keys(); - }; - - LLViewerInput(); - - BOOL handleKey(KEY key, MASK mask, BOOL repeated); - BOOL handleKeyUp(KEY key, MASK mask); - - S32 loadBindingsXML(const std::string& filename); // returns number bound, 0 on error - EKeyboardMode getMode() const; - - static BOOL modeFromString(const std::string& string, S32 *mode); // False on failure - static BOOL mouseFromString(const std::string& string, EMouseClickType *mode);// False on failure - - bool scanKey(KEY key, - BOOL key_down, - BOOL key_up, - BOOL key_level) const; - - // handleMouse() records state, scanMouse() goes through states, scanMouse(click) processes individual saved states after UI is done with them - BOOL handleMouse(LLWindow *window_impl, LLCoordGL pos, MASK mask, EMouseClickType clicktype, BOOL down); - void scanMouse(); - -private: - bool scanKey(const std::vector &binding, - S32 binding_count, - KEY key, - MASK mask, - BOOL key_down, - BOOL key_up, - BOOL key_level, - bool repeat) const; - - enum EMouseState - { - MOUSE_STATE_DOWN, // key down this frame - MOUSE_STATE_CLICK, // key went up and down in scope of same frame - MOUSE_STATE_LEVEL, // clicked again fast, or never released - MOUSE_STATE_UP, // went up this frame - MOUSE_STATE_SILENT // notified about 'up', do not notify again - }; - bool scanMouse(EMouseClickType click, EMouseState state) const; - bool scanMouse(const std::vector &binding, - S32 binding_count, - EMouseClickType mouse, - MASK mask, - EMouseState state) const; - - S32 loadBindingMode(const LLViewerInput::KeyMode& keymode, S32 mode); - BOOL bindKey(const S32 mode, const KEY key, const MASK mask, const std::string& function_name); - BOOL bindMouse(const S32 mode, const EMouseClickType mouse, const MASK mask, const std::string& function_name); - void resetBindings(); - - // Hold all the ugly stuff torn out to make LLKeyboard non-viewer-specific here - - // TODO: at some point it is better to remake this, especially keyaboard part - // would be much better to send to functions actual state of the button than - // to send what we think function wants based on collection of bools (mKeyRepeated, mKeyLevel, mKeyDown) - std::vector mKeyBindings[MODE_COUNT]; - std::vector mMouseBindings[MODE_COUNT]; - - typedef std::map key_remap_t; - key_remap_t mRemapKeys[MODE_COUNT]; - std::set mKeysSkippedByUI; - BOOL mKeyHandledByUI[KEY_COUNT]; // key processed successfully by UI - - // This is indentical to what llkeyboard does (mKeyRepeated, mKeyLevel, mKeyDown e t c), - // just instead of remembering individually as bools, we record state as enum - EMouseState mMouseLevel[CLICK_COUNT]; // records of key state -}; - -extern LLViewerInput gViewerInput; - -#endif // LL_LLVIEWERINPUT_H diff --git a/indra/newview/llviewerkeyboard.cpp b/indra/newview/llviewerkeyboard.cpp new file mode 100644 index 0000000000..6914e0fc2b --- /dev/null +++ b/indra/newview/llviewerkeyboard.cpp @@ -0,0 +1,1039 @@ +/** + * @file llviewerkeyboard.cpp + * @brief LLViewerKeyboard class implementation + * + * $LicenseInfo:firstyear=2005&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA + * $/LicenseInfo$ + */ + +#include "llviewerprecompiledheaders.h" + +#include "llappviewer.h" +#include "llfloaterreg.h" +#include "llviewerkeyboard.h" +#include "llmath.h" +#include "llagent.h" +#include "llagentcamera.h" +#include "llfloaterimnearbychat.h" +#include "llviewercontrol.h" +#include "llfocusmgr.h" +#include "llmorphview.h" +#include "llmoveview.h" +#include "lltoolfocus.h" +#include "llviewerwindow.h" +#include "llvoavatarself.h" +#include "llfloatercamera.h" +#include "llinitparam.h" + +// +// Constants +// + +const F32 FLY_TIME = 0.5f; +const F32 FLY_FRAMES = 4; + +const F32 NUDGE_TIME = 0.25f; // in seconds +const S32 NUDGE_FRAMES = 2; +const F32 ORBIT_NUDGE_RATE = 0.05f; // fraction of normal speed + +struct LLKeyboardActionRegistry +: public LLRegistrySingleton, LLKeyboardActionRegistry> +{ + LLSINGLETON_EMPTY_CTOR(LLKeyboardActionRegistry); +}; + +LLViewerKeyboard gViewerKeyboard; + +void agent_jump( EKeystate s ) +{ + static BOOL first_fly_attempt(TRUE); + if (KEYSTATE_UP == s) + { + first_fly_attempt = TRUE; + return; + } + F32 time = gKeyboard->getCurKeyElapsedTime(); + S32 frame_count = ll_round(gKeyboard->getCurKeyElapsedFrameCount()); + + if( time < FLY_TIME + || frame_count <= FLY_FRAMES + || gAgent.upGrabbed() + || !gSavedSettings.getBOOL("AutomaticFly")) + { + gAgent.moveUp(1); + } + else + { + gAgent.setFlying(TRUE, first_fly_attempt); + first_fly_attempt = FALSE; + gAgent.moveUp(1); + } +} + +void agent_push_down( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return; + gAgent.moveUp(-1); +} + +static void agent_check_temporary_run(LLAgent::EDoubleTapRunMode mode) +{ + if (gAgent.mDoubleTapRunMode == mode && + gAgent.getRunning() && + !gAgent.getAlwaysRun()) + { + // Turn off temporary running. + gAgent.clearRunning(); + gAgent.sendWalkRun(gAgent.getRunning()); + } +} + +static void agent_handle_doubletap_run(EKeystate s, LLAgent::EDoubleTapRunMode mode) +{ + if (KEYSTATE_UP == s) + { + // Note: in case shift is already released, slide left/right run + // will be released in agent_turn_left()/agent_turn_right() + agent_check_temporary_run(mode); + } + else if (gSavedSettings.getBOOL("AllowTapTapHoldRun") && + KEYSTATE_DOWN == s && + !gAgent.getRunning()) + { + if (gAgent.mDoubleTapRunMode == mode && + gAgent.mDoubleTapRunTimer.getElapsedTimeF32() < NUDGE_TIME) + { + // Same walk-key was pushed again quickly; this is a + // double-tap so engage temporary running. + gAgent.setRunning(); + gAgent.sendWalkRun(gAgent.getRunning()); + } + + // Pressing any walk-key resets the double-tap timer + gAgent.mDoubleTapRunTimer.reset(); + gAgent.mDoubleTapRunMode = mode; + } +} + +static void agent_push_forwardbackward( EKeystate s, S32 direction, LLAgent::EDoubleTapRunMode mode ) +{ + agent_handle_doubletap_run(s, mode); + if (KEYSTATE_UP == s) return; + + F32 time = gKeyboard->getCurKeyElapsedTime(); + S32 frame_count = ll_round(gKeyboard->getCurKeyElapsedFrameCount()); + + if( time < NUDGE_TIME || frame_count <= NUDGE_FRAMES) + { + gAgent.moveAtNudge(direction); + } + else + { + gAgent.moveAt(direction); + } +} + +void camera_move_forward( EKeystate s ); + +void agent_push_forward( EKeystate s ) +{ + if(gAgent.isMovementLocked()) return; + + //in free camera control mode we need to intercept keyboard events for avatar movements + if (LLFloaterCamera::inFreeCameraMode()) + { + camera_move_forward(s); + } + else + { + agent_push_forwardbackward(s, 1, LLAgent::DOUBLETAP_FORWARD); + } +} + +void camera_move_backward( EKeystate s ); + +void agent_push_backward( EKeystate s ) +{ + if(gAgent.isMovementLocked()) return; + + //in free camera control mode we need to intercept keyboard events for avatar movements + if (LLFloaterCamera::inFreeCameraMode()) + { + camera_move_backward(s); + } + else if (!gAgent.backwardGrabbed() && gAgentAvatarp->isSitting() && gSavedSettings.getBOOL("LeaveMouselook")) + { + gAgentCamera.changeCameraToThirdPerson(); + } + else + { + agent_push_forwardbackward(s, -1, LLAgent::DOUBLETAP_BACKWARD); + } +} + +static void agent_slide_leftright( EKeystate s, S32 direction, LLAgent::EDoubleTapRunMode mode ) +{ + agent_handle_doubletap_run(s, mode); + if( KEYSTATE_UP == s ) return; + F32 time = gKeyboard->getCurKeyElapsedTime(); + S32 frame_count = ll_round(gKeyboard->getCurKeyElapsedFrameCount()); + + if( time < NUDGE_TIME || frame_count <= NUDGE_FRAMES) + { + gAgent.moveLeftNudge(direction); + } + else + { + gAgent.moveLeft(direction); + } +} + + +void agent_slide_left( EKeystate s ) +{ + if(gAgent.isMovementLocked()) return; + agent_slide_leftright(s, 1, LLAgent::DOUBLETAP_SLIDELEFT); +} + + +void agent_slide_right( EKeystate s ) +{ + if(gAgent.isMovementLocked()) return; + agent_slide_leftright(s, -1, LLAgent::DOUBLETAP_SLIDERIGHT); +} + +void camera_spin_around_cw( EKeystate s ); + +void agent_turn_left( EKeystate s ) +{ + //in free camera control mode we need to intercept keyboard events for avatar movements + if (LLFloaterCamera::inFreeCameraMode()) + { + camera_spin_around_cw(s); + return; + } + + if(gAgent.isMovementLocked()) return; + + if (LLToolCamera::getInstance()->mouseSteerMode()) + { + agent_slide_left(s); + } + else + { + if (KEYSTATE_UP == s) + { + // Check temporary running. In case user released 'left' key with shift already released. + agent_check_temporary_run(LLAgent::DOUBLETAP_SLIDELEFT); + return; + } + F32 time = gKeyboard->getCurKeyElapsedTime(); + gAgent.moveYaw( LLFloaterMove::getYawRate( time ) ); + } +} + +void camera_spin_around_ccw( EKeystate s ); + +void agent_turn_right( EKeystate s ) +{ + //in free camera control mode we need to intercept keyboard events for avatar movements + if (LLFloaterCamera::inFreeCameraMode()) + { + camera_spin_around_ccw(s); + return; + } + + if(gAgent.isMovementLocked()) return; + + if (LLToolCamera::getInstance()->mouseSteerMode()) + { + agent_slide_right(s); + } + else + { + if (KEYSTATE_UP == s) + { + // Check temporary running. In case user released 'right' key with shift already released. + agent_check_temporary_run(LLAgent::DOUBLETAP_SLIDERIGHT); + return; + } + F32 time = gKeyboard->getCurKeyElapsedTime(); + gAgent.moveYaw( -LLFloaterMove::getYawRate( time ) ); + } +} + +void agent_look_up( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return; + gAgent.movePitch(-1); + //gAgent.rotate(-2.f * DEG_TO_RAD, gAgent.getFrame().getLeftAxis() ); +} + + +void agent_look_down( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return; + gAgent.movePitch(1); + //gAgent.rotate(2.f * DEG_TO_RAD, gAgent.getFrame().getLeftAxis() ); +} + +void agent_toggle_fly( EKeystate s ) +{ + // Only catch the edge + if (KEYSTATE_DOWN == s ) + { + LLAgent::toggleFlying(); + } +} + +F32 get_orbit_rate() +{ + F32 time = gKeyboard->getCurKeyElapsedTime(); + if( time < NUDGE_TIME ) + { + F32 rate = ORBIT_NUDGE_RATE + time * (1 - ORBIT_NUDGE_RATE)/ NUDGE_TIME; + //LL_INFOS() << rate << LL_ENDL; + return rate; + } + else + { + return 1; + } +} + +void camera_spin_around_ccw( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return; + gAgentCamera.unlockView(); + gAgentCamera.setOrbitLeftKey( get_orbit_rate() ); +} + + +void camera_spin_around_cw( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return; + gAgentCamera.unlockView(); + gAgentCamera.setOrbitRightKey( get_orbit_rate() ); +} + +void camera_spin_around_ccw_sitting( EKeystate s ) +{ + if( KEYSTATE_UP == s && gAgent.mDoubleTapRunMode != LLAgent::DOUBLETAP_SLIDERIGHT ) return; + if (gAgent.rotateGrabbed() || gAgentCamera.sitCameraEnabled() || gAgent.getRunning()) + { + //send keystrokes, but do not change camera + agent_turn_right(s); + } + else + { + //change camera but do not send keystrokes + gAgentCamera.unlockView(); + gAgentCamera.setOrbitLeftKey( get_orbit_rate() ); + } +} + + +void camera_spin_around_cw_sitting( EKeystate s ) +{ + if( KEYSTATE_UP == s && gAgent.mDoubleTapRunMode != LLAgent::DOUBLETAP_SLIDELEFT ) return; + if (gAgent.rotateGrabbed() || gAgentCamera.sitCameraEnabled() || gAgent.getRunning()) + { + //send keystrokes, but do not change camera + agent_turn_left(s); + } + else + { + //change camera but do not send keystrokes + gAgentCamera.unlockView(); + gAgentCamera.setOrbitRightKey( get_orbit_rate() ); + } +} + + +void camera_spin_over( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return; + gAgentCamera.unlockView(); + gAgentCamera.setOrbitUpKey( get_orbit_rate() ); +} + + +void camera_spin_under( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return; + gAgentCamera.unlockView(); + gAgentCamera.setOrbitDownKey( get_orbit_rate() ); +} + +void camera_spin_over_sitting( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return; + if (gAgent.upGrabbed() || gAgentCamera.sitCameraEnabled()) + { + //send keystrokes, but do not change camera + agent_jump(s); + } + else + { + //change camera but do not send keystrokes + gAgentCamera.setOrbitUpKey( get_orbit_rate() ); + } +} + + +void camera_spin_under_sitting( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return; + if (gAgent.downGrabbed() || gAgentCamera.sitCameraEnabled()) + { + //send keystrokes, but do not change camera + agent_push_down(s); + } + else + { + //change camera but do not send keystrokes + gAgentCamera.setOrbitDownKey( get_orbit_rate() ); + } +} + +void camera_move_forward( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return; + gAgentCamera.unlockView(); + gAgentCamera.setOrbitInKey( get_orbit_rate() ); +} + + +void camera_move_backward( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return; + gAgentCamera.unlockView(); + gAgentCamera.setOrbitOutKey( get_orbit_rate() ); +} + +void camera_move_forward_sitting( EKeystate s ) +{ + if( KEYSTATE_UP == s && gAgent.mDoubleTapRunMode != LLAgent::DOUBLETAP_FORWARD ) return; + if (gAgent.forwardGrabbed() || gAgentCamera.sitCameraEnabled() || (gAgent.getRunning() && !gAgent.getAlwaysRun())) + { + agent_push_forward(s); + } + else + { + gAgentCamera.setOrbitInKey( get_orbit_rate() ); + } +} + + +void camera_move_backward_sitting( EKeystate s ) +{ + if( KEYSTATE_UP == s && gAgent.mDoubleTapRunMode != LLAgent::DOUBLETAP_BACKWARD ) return; + + if (gAgent.backwardGrabbed() || gAgentCamera.sitCameraEnabled() || (gAgent.getRunning() && !gAgent.getAlwaysRun())) + { + agent_push_backward(s); + } + else + { + gAgentCamera.setOrbitOutKey( get_orbit_rate() ); + } +} + +void camera_pan_up( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return; + gAgentCamera.unlockView(); + gAgentCamera.setPanUpKey( get_orbit_rate() ); +} + +void camera_pan_down( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return; + gAgentCamera.unlockView(); + gAgentCamera.setPanDownKey( get_orbit_rate() ); +} + +void camera_pan_left( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return; + gAgentCamera.unlockView(); + gAgentCamera.setPanLeftKey( get_orbit_rate() ); +} + +void camera_pan_right( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return; + gAgentCamera.unlockView(); + gAgentCamera.setPanRightKey( get_orbit_rate() ); +} + +void camera_pan_in( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return; + gAgentCamera.unlockView(); + gAgentCamera.setPanInKey( get_orbit_rate() ); +} + +void camera_pan_out( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return; + gAgentCamera.unlockView(); + gAgentCamera.setPanOutKey( get_orbit_rate() ); +} + +void camera_move_forward_fast( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return; + gAgentCamera.unlockView(); + gAgentCamera.setOrbitInKey(2.5f); +} + +void camera_move_backward_fast( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return; + gAgentCamera.unlockView(); + gAgentCamera.setOrbitOutKey(2.5f); +} + + +void edit_avatar_spin_ccw( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return; + gMorphView->setCameraDrivenByKeys( TRUE ); + gAgentCamera.setOrbitLeftKey( get_orbit_rate() ); + //gMorphView->orbitLeft( get_orbit_rate() ); +} + + +void edit_avatar_spin_cw( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return; + gMorphView->setCameraDrivenByKeys( TRUE ); + gAgentCamera.setOrbitRightKey( get_orbit_rate() ); + //gMorphView->orbitRight( get_orbit_rate() ); +} + +void edit_avatar_spin_over( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return; + gMorphView->setCameraDrivenByKeys( TRUE ); + gAgentCamera.setOrbitUpKey( get_orbit_rate() ); + //gMorphView->orbitUp( get_orbit_rate() ); +} + + +void edit_avatar_spin_under( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return; + gMorphView->setCameraDrivenByKeys( TRUE ); + gAgentCamera.setOrbitDownKey( get_orbit_rate() ); + //gMorphView->orbitDown( get_orbit_rate() ); +} + +void edit_avatar_move_forward( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return; + gMorphView->setCameraDrivenByKeys( TRUE ); + gAgentCamera.setOrbitInKey( get_orbit_rate() ); + //gMorphView->orbitIn(); +} + + +void edit_avatar_move_backward( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return; + gMorphView->setCameraDrivenByKeys( TRUE ); + gAgentCamera.setOrbitOutKey( get_orbit_rate() ); + //gMorphView->orbitOut(); +} + +void stop_moving( EKeystate s ) +{ + if( KEYSTATE_UP == s ) return; + // stop agent + gAgent.setControlFlags(AGENT_CONTROL_STOP); + + // cancel autopilot + gAgent.stopAutoPilot(); +} + +void start_chat( EKeystate s ) +{ + if (LLAppViewer::instance()->quitRequested()) + { + return; // can't talk, gotta go, kthxbye! + } + + // start chat + LLFloaterIMNearbyChat::startChat(NULL); +} + +void start_gesture( EKeystate s ) +{ + LLUICtrl* focus_ctrlp = dynamic_cast(gFocusMgr.getKeyboardFocus()); + if (KEYSTATE_UP == s && + ! (focus_ctrlp && focus_ctrlp->acceptsTextInput())) + { + if ((LLFloaterReg::getTypedInstance("nearby_chat"))->getCurrentChat().empty()) + { + // No existing chat in chat editor, insert '/' + LLFloaterIMNearbyChat::startChat("/"); + } + else + { + // Don't overwrite existing text in chat editor + LLFloaterIMNearbyChat::startChat(NULL); + } + } +} + +#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); +REGISTER_KEYBOARD_ACTION("push_forward", agent_push_forward); +REGISTER_KEYBOARD_ACTION("push_backward", agent_push_backward); +REGISTER_KEYBOARD_ACTION("look_up", agent_look_up); +REGISTER_KEYBOARD_ACTION("look_down", agent_look_down); +REGISTER_KEYBOARD_ACTION("toggle_fly", agent_toggle_fly); +REGISTER_KEYBOARD_ACTION("turn_left", agent_turn_left); +REGISTER_KEYBOARD_ACTION("turn_right", agent_turn_right); +REGISTER_KEYBOARD_ACTION("slide_left", agent_slide_left); +REGISTER_KEYBOARD_ACTION("slide_right", agent_slide_right); +REGISTER_KEYBOARD_ACTION("spin_around_ccw", camera_spin_around_ccw); +REGISTER_KEYBOARD_ACTION("spin_around_cw", camera_spin_around_cw); +REGISTER_KEYBOARD_ACTION("spin_around_ccw_sitting", camera_spin_around_ccw_sitting); +REGISTER_KEYBOARD_ACTION("spin_around_cw_sitting", camera_spin_around_cw_sitting); +REGISTER_KEYBOARD_ACTION("spin_over", camera_spin_over); +REGISTER_KEYBOARD_ACTION("spin_under", camera_spin_under); +REGISTER_KEYBOARD_ACTION("spin_over_sitting", camera_spin_over_sitting); +REGISTER_KEYBOARD_ACTION("spin_under_sitting", camera_spin_under_sitting); +REGISTER_KEYBOARD_ACTION("move_forward", camera_move_forward); +REGISTER_KEYBOARD_ACTION("move_backward", camera_move_backward); +REGISTER_KEYBOARD_ACTION("move_forward_sitting", camera_move_forward_sitting); +REGISTER_KEYBOARD_ACTION("move_backward_sitting", camera_move_backward_sitting); +REGISTER_KEYBOARD_ACTION("pan_up", camera_pan_up); +REGISTER_KEYBOARD_ACTION("pan_down", camera_pan_down); +REGISTER_KEYBOARD_ACTION("pan_left", camera_pan_left); +REGISTER_KEYBOARD_ACTION("pan_right", camera_pan_right); +REGISTER_KEYBOARD_ACTION("pan_in", camera_pan_in); +REGISTER_KEYBOARD_ACTION("pan_out", camera_pan_out); +REGISTER_KEYBOARD_ACTION("move_forward_fast", camera_move_forward_fast); +REGISTER_KEYBOARD_ACTION("move_backward_fast", camera_move_backward_fast); +REGISTER_KEYBOARD_ACTION("edit_avatar_spin_ccw", edit_avatar_spin_ccw); +REGISTER_KEYBOARD_ACTION("edit_avatar_spin_cw", edit_avatar_spin_cw); +REGISTER_KEYBOARD_ACTION("edit_avatar_spin_over", edit_avatar_spin_over); +REGISTER_KEYBOARD_ACTION("edit_avatar_spin_under", edit_avatar_spin_under); +REGISTER_KEYBOARD_ACTION("edit_avatar_move_forward", edit_avatar_move_forward); +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); +#undef REGISTER_KEYBOARD_ACTION + +LLViewerKeyboard::LLViewerKeyboard() +{ + for (S32 i = 0; i < MODE_COUNT; i++) + { + mBindingCount[i] = 0; + } + + for (S32 i = 0; i < KEY_COUNT; i++) + { + mKeyHandledByUI[i] = FALSE; + } + // we want the UI to never see these keys so that they can always control the avatar/camera + for(KEY k = KEY_PAD_UP; k <= KEY_PAD_DIVIDE; k++) + { + mKeysSkippedByUI.insert(k); + } +} + +BOOL LLViewerKeyboard::modeFromString(const std::string& string, S32 *mode) +{ + if (string == "FIRST_PERSON") + { + *mode = MODE_FIRST_PERSON; + return TRUE; + } + else if (string == "THIRD_PERSON") + { + *mode = MODE_THIRD_PERSON; + return TRUE; + } + else if (string == "EDIT") + { + *mode = MODE_EDIT; + return TRUE; + } + else if (string == "EDIT_AVATAR") + { + *mode = MODE_EDIT_AVATAR; + return TRUE; + } + else if (string == "SITTING") + { + *mode = MODE_SITTING; + return TRUE; + } + else + { + *mode = MODE_THIRD_PERSON; + return FALSE; + } +} + +BOOL LLViewerKeyboard::handleKey(KEY translated_key, MASK translated_mask, BOOL repeated) +{ + // check for re-map + EKeyboardMode mode = gViewerKeyboard.getMode(); + U32 keyidx = (translated_mask<<16) | translated_key; + key_remap_t::iterator iter = mRemapKeys[mode].find(keyidx); + if (iter != mRemapKeys[mode].end()) + { + translated_key = (iter->second) & 0xff; + translated_mask = (iter->second)>>16; + } + + // No repeats of F-keys + BOOL repeatable_key = (translated_key < KEY_F1 || translated_key > KEY_F12); + if (!repeatable_key && repeated) + { + return FALSE; + } + + LL_DEBUGS("UserInput") << "keydown -" << translated_key << "-" << LL_ENDL; + // skip skipped keys + if(mKeysSkippedByUI.find(translated_key) != mKeysSkippedByUI.end()) + { + mKeyHandledByUI[translated_key] = FALSE; + LL_INFOS("KeyboardHandling") << "Key wasn't handled by UI!" << LL_ENDL; + } + else + { + // it is sufficient to set this value once per call to handlekey + // without clearing it, as it is only used in the subsequent call to scanKey + mKeyHandledByUI[translated_key] = gViewerWindow->handleKey(translated_key, translated_mask); + // mKeyHandledByUI is not what you think ... this indicates whether the UI has handled this keypress yet (any keypress) + // NOT whether some UI shortcut wishes to handle the keypress + + } + return mKeyHandledByUI[translated_key]; +} + +BOOL LLViewerKeyboard::handleKeyUp(KEY translated_key, MASK translated_mask) +{ + return gViewerWindow->handleKeyUp(translated_key, translated_mask); +} + +BOOL LLViewerKeyboard::bindKey(const S32 mode, const KEY key, const MASK mask, const std::string& function_name) +{ + S32 index; + typedef boost::function function_t; + function_t function = NULL; + std::string name; + + // Allow remapping of F2-F12 + if (function_name[0] == 'F') + { + int c1 = function_name[1] - '0'; + int c2 = function_name[2] ? function_name[2] - '0' : -1; + if (c1 >= 0 && c1 <= 9 && c2 >= -1 && c2 <= 9) + { + int idx = c1; + if (c2 >= 0) + idx = idx*10 + c2; + if (idx >=2 && idx <= 12) + { + U32 keyidx = ((mask<<16)|key); + (mRemapKeys[mode])[keyidx] = ((0<<16)|(KEY_F1+(idx-1))); + return TRUE; + } + } + } + + // Not remapped, look for a function + + function_t* result = LLKeyboardActionRegistry::getValue(function_name); + if (result) + { + function = *result; + } + + if (!function) + { + LL_ERRS() << "Can't bind key to function " << function_name << ", no function with this name found" << LL_ENDL; + return FALSE; + } + + // check for duplicate first and overwrite + for (index = 0; index < mBindingCount[mode]; index++) + { + if (key == mBindings[mode][index].mKey && mask == mBindings[mode][index].mMask) + break; + } + + if (index >= MAX_KEY_BINDINGS) + { + LL_ERRS() << "LLKeyboard::bindKey() - too many keys for mode " << mode << LL_ENDL; + return FALSE; + } + + if (mode >= MODE_COUNT) + { + LL_ERRS() << "LLKeyboard::bindKey() - unknown mode passed" << mode << LL_ENDL; + return FALSE; + } + + mBindings[mode][index].mKey = key; + mBindings[mode][index].mMask = mask; + mBindings[mode][index].mFunction = function; + + if (index == mBindingCount[mode]) + mBindingCount[mode]++; + + return TRUE; +} + +LLViewerKeyboard::KeyBinding::KeyBinding() +: key("key"), + mask("mask"), + command("command") +{} + +LLViewerKeyboard::KeyMode::KeyMode(EKeyboardMode _mode) +: bindings("binding"), + mode(_mode) +{} + +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)) +{} + +S32 LLViewerKeyboard::loadBindingsXML(const std::string& filename) +{ + S32 binding_count = 0; + Keys keys; + LLSimpleXUIParser parser; + + 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); + } + return binding_count; +} + +S32 LLViewerKeyboard::loadBindingMode(const LLViewerKeyboard::KeyMode& keymode) +{ + S32 binding_count = 0; + for (LLInitParam::ParamIterator::const_iterator it = keymode.bindings.begin(), + end_it = keymode.bindings.end(); + 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++; + } + + return binding_count; +} + +S32 LLViewerKeyboard::loadBindings(const std::string& filename) +{ + LLFILE *fp; + const S32 BUFFER_SIZE = 2048; + char buffer[BUFFER_SIZE]; /* Flawfinder: ignore */ + // *NOTE: This buffer size is hard coded into scanf() below. + char mode_string[MAX_STRING] = ""; /* Flawfinder: ignore */ + char key_string[MAX_STRING] = ""; /* Flawfinder: ignore */ + char mask_string[MAX_STRING] = ""; /* Flawfinder: ignore */ + char function_string[MAX_STRING] = ""; /* Flawfinder: ignore */ + S32 mode = MODE_THIRD_PERSON; + KEY key = 0; + MASK mask = 0; + S32 tokens_read; + S32 binding_count = 0; + S32 line_count = 0; + + if(filename.empty()) + { + LL_ERRS() << " No filename specified" << LL_ENDL; + return 0; + } + + fp = LLFile::fopen(filename, "r"); + + if (!fp) + { + return 0; + } + + + while (!feof(fp)) + { + line_count++; + if (!fgets(buffer, BUFFER_SIZE, fp)) + break; + + // skip over comments, blank lines + if (buffer[0] == '#' || buffer[0] == '\n') continue; + + // grab the binding strings + tokens_read = sscanf( /* Flawfinder: ignore */ + buffer, + "%254s %254s %254s %254s", + mode_string, + key_string, + mask_string, + function_string); + + if (tokens_read == EOF) + { + LL_INFOS() << "Unexpected end-of-file at line " << line_count << " of key binding file " << filename << LL_ENDL; + fclose(fp); + return 0; + } + else if (tokens_read < 4) + { + LL_INFOS() << "Can't read line " << line_count << " of key binding file " << filename << LL_ENDL; + continue; + } + + // convert mode + if (!modeFromString(mode_string, &mode)) + { + LL_INFOS() << "Unknown mode on line " << line_count << " of key binding file " << filename << LL_ENDL; + LL_INFOS() << "Mode must be one of FIRST_PERSON, THIRD_PERSON, EDIT, EDIT_AVATAR" << LL_ENDL; + continue; + } + + // convert key + if (!LLKeyboard::keyFromString(key_string, &key)) + { + LL_INFOS() << "Can't interpret key on line " << line_count << " of key binding file " << filename << LL_ENDL; + continue; + } + + // convert mask + if (!LLKeyboard::maskFromString(mask_string, &mask)) + { + LL_INFOS() << "Can't interpret mask on line " << line_count << " of key binding file " << filename << LL_ENDL; + continue; + } + + // bind key + if (bindKey(mode, key, mask, function_string)) + { + binding_count++; + } + } + + fclose(fp); + + return binding_count; +} + + +EKeyboardMode LLViewerKeyboard::getMode() +{ + if ( gAgentCamera.cameraMouselook() ) + { + return MODE_FIRST_PERSON; + } + else if ( gMorphView && gMorphView->getVisible()) + { + return MODE_EDIT_AVATAR; + } + else if (isAgentAvatarValid() && gAgentAvatarp->isSitting()) + { + return MODE_SITTING; + } + else + { + return MODE_THIRD_PERSON; + } +} + + +// Called from scanKeyboard. +void LLViewerKeyboard::scanKey(KEY key, BOOL key_down, BOOL key_up, BOOL key_level) +{ + if (LLApp::isExiting()) + { + return; + } + + S32 mode = getMode(); + // Consider keyboard scanning as NOT mouse event. JC + MASK mask = gKeyboard->currentMask(FALSE); + + LLKeyBinding* binding = mBindings[mode]; + S32 binding_count = mBindingCount[mode]; + + + if (mKeyHandledByUI[key]) + { + return; + } + + // don't process key down on repeated keys + BOOL repeat = gKeyboard->getKeyRepeated(key); + + for (S32 i = 0; i < binding_count; i++) + { + //for (S32 key = 0; key < KEY_COUNT; key++) + if (binding[i].mKey == key) + { + //if (binding[i].mKey == key && binding[i].mMask == mask) + if (binding[i].mMask == mask) + { + if (key_down && !repeat) + { + // ...key went down this frame, call function + binding[i].mFunction( KEYSTATE_DOWN ); + } + else if (key_up) + { + // ...key went down this frame, call function + binding[i].mFunction( KEYSTATE_UP ); + } + else if (key_level) + { + // ...key held down from previous frame + // Not windows, just call the function. + binding[i].mFunction( KEYSTATE_LEVEL ); + }//if + }//if + }//for + }//for +} diff --git a/indra/newview/llviewerkeyboard.h b/indra/newview/llviewerkeyboard.h new file mode 100644 index 0000000000..110dc89d28 --- /dev/null +++ b/indra/newview/llviewerkeyboard.h @@ -0,0 +1,118 @@ +/** + * @file llviewerkeyboard.h + * @brief LLViewerKeyboard class header file + * + * $LicenseInfo:firstyear=2005&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, 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_LLVIEWERKEYBOARD_H +#define LL_LLVIEWERKEYBOARD_H + +#include "llkeyboard.h" // For EKeystate +#include "llinitparam.h" + +const S32 MAX_NAMED_FUNCTIONS = 100; +const S32 MAX_KEY_BINDINGS = 128; // was 60 + +class LLNamedFunction +{ +public: + LLNamedFunction() : mFunction(NULL) { }; + ~LLNamedFunction() { }; + + std::string mName; + LLKeyFunc mFunction; +}; + +typedef enum e_keyboard_mode +{ + MODE_FIRST_PERSON, + MODE_THIRD_PERSON, + MODE_EDIT, + MODE_EDIT_AVATAR, + MODE_SITTING, + MODE_COUNT +} EKeyboardMode; + + +void bind_keyboard_functions(); + +class LLViewerKeyboard +{ +public: + struct KeyBinding : public LLInitParam::Block + { + Mandatory key, + mask, + command; + + KeyBinding(); + }; + + struct KeyMode : public LLInitParam::Block + { + Multiple bindings; + EKeyboardMode mode; + KeyMode(EKeyboardMode mode); + }; + + struct Keys : public LLInitParam::Block + { + Optional first_person, + third_person, + edit, + sitting, + edit_avatar; + + Keys(); + }; + + LLViewerKeyboard(); + + BOOL handleKey(KEY key, MASK mask, BOOL repeated); + BOOL handleKeyUp(KEY key, MASK mask); + + S32 loadBindings(const std::string& filename); // returns number bound, 0 on error + S32 loadBindingsXML(const std::string& filename); // returns number bound, 0 on error + EKeyboardMode getMode(); + + BOOL modeFromString(const std::string& string, S32 *mode); // False on failure + + void scanKey(KEY key, BOOL key_down, BOOL key_up, BOOL key_level); + +private: + S32 loadBindingMode(const LLViewerKeyboard::KeyMode& keymode); + 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 + S32 mBindingCount[MODE_COUNT]; + LLKeyBinding mBindings[MODE_COUNT][MAX_KEY_BINDINGS]; + + typedef std::map key_remap_t; + key_remap_t mRemapKeys[MODE_COUNT]; + std::set mKeysSkippedByUI; + BOOL mKeyHandledByUI[KEY_COUNT]; // key processed successfully by UI +}; + +extern LLViewerKeyboard gViewerKeyboard; + +#endif // LL_LLVIEWERKEYBOARD_H diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index ab054fabde..b6c7be2ed3 100644 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -8031,6 +8031,7 @@ BOOL LLViewerMenuHolderGL::hideMenus() if (LLMenuHolderGL::hideMenus()) { + LLToolPie::instance().blockClickToWalk(); handled = TRUE; } diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index 19c0c2d285..1fbb32ac5e 100644 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -45,8 +45,7 @@ #include "llmeshrepository.h" #include "llnotificationhandler.h" #include "llpanellogin.h" -#include "llsetkeybinddialog.h" -#include "llviewerinput.h" +#include "llviewerkeyboard.h" #include "llviewermenu.h" #include "llviewquery.h" @@ -174,7 +173,7 @@ #include "llviewergesture.h" #include "llviewertexturelist.h" #include "llviewerinventory.h" -#include "llviewerinput.h" +#include "llviewerkeyboard.h" #include "llviewermedia.h" #include "llviewermediafocus.h" #include "llviewermenu.h" @@ -915,18 +914,7 @@ LLViewerWindow::Params::Params() {} -void LLViewerWindow::handlePieMenu(S32 x, S32 y, MASK mask) -{ - if (CAMERA_MODE_CUSTOMIZE_AVATAR != gAgentCamera.getCameraMode() && LLToolMgr::getInstance()->getCurrentTool() != LLToolPie::getInstance() && gAgent.isInitialized()) - { - // If the current tool didn't process the click, we should show - // the pie menu. This can be done by passing the event to the pie - // menu tool. - LLToolPie::getInstance()->handleRightMouseDown(x, y, mask); - } -} - -BOOL LLViewerWindow::handleAnyMouseClick(LLWindow *window, LLCoordGL pos, MASK mask, EMouseClickType clicktype, BOOL down) +BOOL LLViewerWindow::handleAnyMouseClick(LLWindow *window, LLCoordGL pos, MASK mask, LLMouseHandler::EClickType clicktype, BOOL down) { const char* buttonname = ""; const char* buttonstatestr = ""; @@ -950,30 +938,28 @@ BOOL LLViewerWindow::handleAnyMouseClick(LLWindow *window, LLCoordGL pos, MASK m switch (clicktype) { - case CLICK_LEFT: + case LLMouseHandler::CLICK_LEFT: mLeftMouseDown = down; buttonname = "Left"; break; - case CLICK_RIGHT: + case LLMouseHandler::CLICK_RIGHT: mRightMouseDown = down; buttonname = "Right"; break; - case CLICK_MIDDLE: + case LLMouseHandler::CLICK_MIDDLE: mMiddleMouseDown = down; buttonname = "Middle"; break; - case CLICK_DOUBLELEFT: + case LLMouseHandler::CLICK_DOUBLELEFT: mLeftMouseDown = down; buttonname = "Left Double Click"; break; - case CLICK_BUTTON4: + case LLMouseHandler::CLICK_BUTTON4: buttonname = "Button 4"; break; - case CLICK_BUTTON5: + case LLMouseHandler::CLICK_BUTTON5: buttonname = "Button 5"; break; - default: - break; // COUNT and NONE } LLView::sMouseHandlerMessage.clear(); @@ -1024,11 +1010,6 @@ BOOL LLViewerWindow::handleAnyMouseClick(LLWindow *window, LLCoordGL pos, MASK m LLViewerEventRecorder::instance().logMouseEvent(std::string(buttonstatestr),std::string(buttonname)); } - else if (down && clicktype == CLICK_RIGHT) - { - handlePieMenu(x, y, mask); - r = TRUE; - } return r; } @@ -1075,12 +1056,7 @@ BOOL LLViewerWindow::handleAnyMouseClick(LLWindow *window, LLCoordGL pos, MASK m return TRUE; } - if (down && clicktype == CLICK_RIGHT) - { - handlePieMenu(x, y, mask); - return TRUE; - } - + // If we got this far on a down-click, it wasn't handled. // Up-clicks, though, are always handled as far as the OS is concerned. BOOL default_rtn = !down; @@ -1099,8 +1075,7 @@ BOOL LLViewerWindow::handleMouseDown(LLWindow *window, LLCoordGL pos, MASK mask mMouseDownTimer.reset(); } BOOL down = TRUE; - //handleMouse() loops back to LLViewerWindow::handleAnyMouseClick - return gViewerInput.handleMouse(window, pos, mask, CLICK_LEFT, down); + return handleAnyMouseClick(window,pos,mask,LLMouseHandler::CLICK_LEFT,down); } BOOL LLViewerWindow::handleDoubleClick(LLWindow *window, LLCoordGL pos, MASK mask) @@ -1108,7 +1083,8 @@ BOOL LLViewerWindow::handleDoubleClick(LLWindow *window, LLCoordGL pos, MASK ma // try handling as a double-click first, then a single-click if that // wasn't handled. BOOL down = TRUE; - if (gViewerInput.handleMouse(window, pos, mask, CLICK_DOUBLELEFT, down)) + if (handleAnyMouseClick(window, pos, mask, + LLMouseHandler::CLICK_DOUBLELEFT, down)) { return TRUE; } @@ -1122,24 +1098,47 @@ BOOL LLViewerWindow::handleMouseUp(LLWindow *window, LLCoordGL pos, MASK mask) mMouseDownTimer.stop(); } BOOL down = FALSE; - return gViewerInput.handleMouse(window, pos, mask, CLICK_LEFT, down); + return handleAnyMouseClick(window,pos,mask,LLMouseHandler::CLICK_LEFT,down); } + + BOOL LLViewerWindow::handleRightMouseDown(LLWindow *window, LLCoordGL pos, MASK mask) { + S32 x = pos.mX; + S32 y = pos.mY; + x = ll_round((F32)x / mDisplayScale.mV[VX]); + y = ll_round((F32)y / mDisplayScale.mV[VY]); + BOOL down = TRUE; - return gViewerInput.handleMouse(window, pos, mask, CLICK_RIGHT, down); + BOOL handle = handleAnyMouseClick(window,pos,mask,LLMouseHandler::CLICK_RIGHT,down); + if (handle) + return handle; + + // *HACK: this should be rolled into the composite tool logic, not + // hardcoded at the top level. + if (CAMERA_MODE_CUSTOMIZE_AVATAR != gAgentCamera.getCameraMode() && LLToolMgr::getInstance()->getCurrentTool() != LLToolPie::getInstance() && gAgent.isInitialized()) + { + // If the current tool didn't process the click, we should show + // the pie menu. This can be done by passing the event to the pie + // menu tool. + LLToolPie::getInstance()->handleRightMouseDown(x, y, mask); + // show_context_menu( x, y, mask ); + } + + return TRUE; } BOOL LLViewerWindow::handleRightMouseUp(LLWindow *window, LLCoordGL pos, MASK mask) { BOOL down = FALSE; - return gViewerInput.handleMouse(window, pos, mask, CLICK_RIGHT, down); + return handleAnyMouseClick(window,pos,mask,LLMouseHandler::CLICK_RIGHT,down); } BOOL LLViewerWindow::handleMiddleMouseDown(LLWindow *window, LLCoordGL pos, MASK mask) { BOOL down = TRUE; - gViewerInput.handleMouse(window, pos, mask, CLICK_MIDDLE, down); + LLVoiceClient::getInstance()->updateMouseState(LLMouseHandler::CLICK_MIDDLE, true); + handleAnyMouseClick(window,pos,mask,LLMouseHandler::CLICK_MIDDLE,down); // Always handled as far as the OS is concerned. return TRUE; @@ -1294,7 +1293,8 @@ LLWindowCallbacks::DragNDropResult LLViewerWindow::handleDragNDrop( LLWindow *wi BOOL LLViewerWindow::handleMiddleMouseUp(LLWindow *window, LLCoordGL pos, MASK mask) { BOOL down = FALSE; - gViewerInput.handleMouse(window, pos, mask, CLICK_MIDDLE, down); + LLVoiceClient::getInstance()->updateMouseState(LLMouseHandler::CLICK_MIDDLE, false); + handleAnyMouseClick(window,pos,mask,LLMouseHandler::CLICK_MIDDLE,down); // Always handled as far as the OS is concerned. return TRUE; @@ -1305,10 +1305,12 @@ BOOL LLViewerWindow::handleOtherMouse(LLWindow *window, LLCoordGL pos, MASK mask switch (button) { case 4: - gViewerInput.handleMouse(window, pos, mask, CLICK_BUTTON4, down); + LLVoiceClient::getInstance()->updateMouseState(LLMouseHandler::CLICK_BUTTON4, down); + handleAnyMouseClick(window, pos, mask, LLMouseHandler::CLICK_BUTTON4, down); break; case 5: - gViewerInput.handleMouse(window, pos, mask, CLICK_BUTTON5, down); + LLVoiceClient::getInstance()->updateMouseState(LLMouseHandler::CLICK_BUTTON5, down); + handleAnyMouseClick(window, pos, mask, LLMouseHandler::CLICK_BUTTON5, down); break; default: break; @@ -1454,6 +1456,9 @@ void LLViewerWindow::handleFocusLost(LLWindow *window) BOOL LLViewerWindow::handleTranslatedKeyDown(KEY key, MASK mask, BOOL repeated) { + // Let the voice chat code check for its PTT key. Note that this never affects event processing. + LLVoiceClient::getInstance()->keyDown(key, mask); + if (gAwayTimer.getElapsedTimeF32() > LLAgent::MIN_AFK_TIME) { gAgent.clearAFK(); @@ -1472,12 +1477,14 @@ BOOL LLViewerWindow::handleTranslatedKeyDown(KEY key, MASK mask, BOOL repeated) return FALSE; } - // remaps, handles ignored cases and returns back to viewer window. - return gViewerInput.handleKey(key, mask, repeated); + return gViewerKeyboard.handleKey(key, mask, repeated); } BOOL LLViewerWindow::handleTranslatedKeyUp(KEY key, MASK mask) { + // Let the voice chat code check for its PTT key. Note that this never affects event processing. + LLVoiceClient::getInstance()->keyUp(key, mask); + // Let the inspect tool code check for ALT key to set LLToolSelectRect active instead LLToolCamera LLToolCompInspect * tool_inspectp = LLToolCompInspect::getInstance(); if (LLToolMgr::getInstance()->getCurrentTool() == tool_inspectp) @@ -1485,13 +1492,13 @@ BOOL LLViewerWindow::handleTranslatedKeyUp(KEY key, MASK mask) tool_inspectp->keyUp(key, mask); } - return gViewerInput.handleKeyUp(key, mask); + return gViewerKeyboard.handleKeyUp(key, mask); } void LLViewerWindow::handleScanKey(KEY key, BOOL key_down, BOOL key_up, BOOL key_level) { LLViewerJoystick::getInstance()->setCameraNeedsUpdate(true); - gViewerInput.scanKey(key, key_down, key_up, key_level); + gViewerKeyboard.scanKey(key, key_down, key_up, key_level); return; // Be clear this function returns nothing } @@ -2731,14 +2738,6 @@ BOOL LLViewerWindow::handleKey(KEY key, MASK mask) // hide tooltips on keypress LLToolTipMgr::instance().blockToolTips(); - // let menus handle navigation keys for navigation - if (LLSetKeyBindDialog::recordKey(key, mask)) - { - LL_DEBUGS() << "Key handled by LLSetKeyBindDialog" << LL_ENDL; - LLViewerEventRecorder::instance().logKeyEvent(key,mask); - return TRUE; - } - LLFocusableElement* keyboard_focus = gFocusMgr.getKeyboardFocus(); if (keyboard_focus @@ -2954,8 +2953,7 @@ BOOL LLViewerWindow::handleUnicodeChar(llwchar uni_char, MASK mask) { if (mask != MASK_ALT) { - // remaps, handles ignored cases and returns back to viewer window. - return gViewerInput.handleKey(KEY_RETURN, mask, gKeyboard->getKeyRepeated(KEY_RETURN)); + return gViewerKeyboard.handleKey(KEY_RETURN, mask, gKeyboard->getKeyRepeated(KEY_RETURN)); } } diff --git a/indra/newview/llviewerwindow.h b/indra/newview/llviewerwindow.h index 67e5e4b4d6..e901245f92 100644 --- a/indra/newview/llviewerwindow.h +++ b/indra/newview/llviewerwindow.h @@ -175,9 +175,8 @@ public: void initWorldUI(); void setUIVisibility(bool); bool getUIVisibility(); - void handlePieMenu(S32 x, S32 y, MASK mask); - BOOL handleAnyMouseClick(LLWindow *window, LLCoordGL pos, MASK mask, EMouseClickType clicktype, BOOL down); + BOOL handleAnyMouseClick(LLWindow *window, LLCoordGL pos, MASK mask, LLMouseHandler::EClickType clicktype, BOOL down); // // LLWindowCallback interface implementation diff --git a/indra/newview/llvoiceclient.cpp b/indra/newview/llvoiceclient.cpp index fca8de7410..cc590fc947 100644 --- a/indra/newview/llvoiceclient.cpp +++ b/indra/newview/llvoiceclient.cpp @@ -200,6 +200,8 @@ const LLVoiceVersionInfo LLVoiceClient::getVersion() void LLVoiceClient::updateSettings() { setUsePTT(gSavedSettings.getBOOL("PTTCurrentlyEnabled")); + std::string keyString = gSavedSettings.getString("PushToTalkButton"); + setPTTKey(keyString); setPTTIsToggle(gSavedSettings.getBOOL("PushToTalkToggle")); mDisableMic = gSavedSettings.getBOOL("VoiceDisableMic"); @@ -635,6 +637,32 @@ bool LLVoiceClient::getPTTIsToggle() return mPTTIsToggle; } +void LLVoiceClient::setPTTKey(std::string &key) +{ + // Value is stored as text for readability + if(key == "MiddleMouse") + { + mPTTMouseButton = LLMouseHandler::CLICK_MIDDLE; + } + else if(key == "MouseButton4") + { + mPTTMouseButton = LLMouseHandler::CLICK_BUTTON4; + } + else if (key == "MouseButton5") + { + mPTTMouseButton = LLMouseHandler::CLICK_BUTTON5; + } + else + { + mPTTMouseButton = 0; + if(!LLKeyboard::keyFromString(key, &mPTTKey)) + { + // If the call failed, don't match any key. + key = KEY_NONE; + } + } +} + void LLVoiceClient::inputUserControlState(bool down) { if(mPTTIsToggle) @@ -655,6 +683,43 @@ void LLVoiceClient::toggleUserPTTState(void) setUserPTTState(!getUserPTTState()); } +void LLVoiceClient::keyDown(KEY key, MASK mask) +{ + if (gKeyboard->getKeyRepeated(key)) + { + // ignore auto-repeat keys + return; + } + + if (mPTTMouseButton == 0 && LLAgent::isActionAllowed("speak") && (key == mPTTKey)) + { + bool down = gKeyboard->getKeyDown(mPTTKey); + if (down) + { + inputUserControlState(down); + } + } + +} +void LLVoiceClient::keyUp(KEY key, MASK mask) +{ + if (mPTTMouseButton == 0 && (key == mPTTKey)) + { + bool down = gKeyboard->getKeyDown(mPTTKey); + if (!down) + { + inputUserControlState(down); + } + } +} +void LLVoiceClient::updateMouseState(S32 click, bool down) +{ + if(mPTTMouseButton == click && LLAgent::isActionAllowed("speak")) + { + inputUserControlState(down); + } +} + //------------------------------------------- // nearby speaker accessors diff --git a/indra/newview/llvoiceclient.h b/indra/newview/llvoiceclient.h index cecaac7f85..3d04e1f0db 100644 --- a/indra/newview/llvoiceclient.h +++ b/indra/newview/llvoiceclient.h @@ -408,10 +408,16 @@ public: void setUsePTT(bool usePTT); void setPTTIsToggle(bool PTTIsToggle); bool getPTTIsToggle(); - + void setPTTKey(std::string &key); + void updateMicMuteLogic(); - + BOOL lipSyncEnabled(); + + // PTT key triggering + void keyDown(KEY key, MASK mask); + void keyUp(KEY key, MASK mask); + void updateMouseState(S32 click, bool down); boost::signals2::connection MicroChangedCallback(const micro_changed_signal_t::slot_type& cb ) { return mMicroChangedSignal.connect(cb); } diff --git a/indra/newview/llwindowlistener.cpp b/indra/newview/llwindowlistener.cpp index aa8c79b0d2..9e4297baaf 100644 --- a/indra/newview/llwindowlistener.cpp +++ b/indra/newview/llwindowlistener.cpp @@ -37,7 +37,7 @@ #include "llview.h" #include "llviewinject.h" #include "llviewerwindow.h" -#include "llviewerinput.h" +#include "llviewerkeyboard.h" #include "llrootview.h" #include "llsdutil.h" #include "stringize.h" @@ -279,7 +279,7 @@ void LLWindowListener::keyDown(LLSD const & evt) response.setResponse(target_view->getInfo()); gFocusMgr.setKeyboardFocus(target_view); - gViewerInput.handleKey(key, mask, false); + gViewerKeyboard.handleKey(key, mask, false); if(key < 0x80) mWindow->handleUnicodeChar(key, mask); } else @@ -291,7 +291,7 @@ void LLWindowListener::keyDown(LLSD const & evt) } else { - gViewerInput.handleKey(key, mask, false); + gViewerKeyboard.handleKey(key, mask, false); if(key < 0x80) mWindow->handleUnicodeChar(key, mask); } } diff --git a/indra/newview/skins/default/xui/en/control_table_contents_camera.xml b/indra/newview/skins/default/xui/en/control_table_contents_camera.xml deleted file mode 100644 index aba81e3134..0000000000 --- a/indra/newview/skins/default/xui/en/control_table_contents_camera.xml +++ /dev/null @@ -1,178 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/indra/newview/skins/default/xui/en/control_table_contents_camera_sitting.xml b/indra/newview/skins/default/xui/en/control_table_contents_camera_sitting.xml deleted file mode 100644 index 9334c6e179..0000000000 --- a/indra/newview/skins/default/xui/en/control_table_contents_camera_sitting.xml +++ /dev/null @@ -1,65 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/indra/newview/skins/default/xui/en/control_table_contents_columns_basic.xml b/indra/newview/skins/default/xui/en/control_table_contents_columns_basic.xml deleted file mode 100644 index e707aaf22c..0000000000 --- a/indra/newview/skins/default/xui/en/control_table_contents_columns_basic.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - diff --git a/indra/newview/skins/default/xui/en/control_table_contents_editing.xml b/indra/newview/skins/default/xui/en/control_table_contents_editing.xml deleted file mode 100644 index 2a3314840a..0000000000 --- a/indra/newview/skins/default/xui/en/control_table_contents_editing.xml +++ /dev/null @@ -1,80 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/indra/newview/skins/default/xui/en/control_table_contents_media.xml b/indra/newview/skins/default/xui/en/control_table_contents_media.xml deleted file mode 100644 index ce5d3556b6..0000000000 --- a/indra/newview/skins/default/xui/en/control_table_contents_media.xml +++ /dev/null @@ -1,76 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/indra/newview/skins/default/xui/en/control_table_contents_movement.xml b/indra/newview/skins/default/xui/en/control_table_contents_movement.xml deleted file mode 100644 index b410d2dc1c..0000000000 --- a/indra/newview/skins/default/xui/en/control_table_contents_movement.xml +++ /dev/null @@ -1,198 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/indra/newview/skins/default/xui/en/floater_preferences.xml b/indra/newview/skins/default/xui/en/floater_preferences.xml index ee730dcb01..0e62d50072 100644 --- a/indra/newview/skins/default/xui/en/floater_preferences.xml +++ b/indra/newview/skins/default/xui/en/floater_preferences.xml @@ -169,13 +169,6 @@ https://accounts.secondlife.com/change_email/ layout="topleft" help_topic="preferences_uploads_tab" name="uploads" /> - 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 48d9eee4cd..4e89df5a73 100644 --- a/indra/newview/skins/default/xui/en/floater_select_key.xml +++ b/indra/newview/skins/default/xui/en/floater_select_key.xml @@ -4,27 +4,10 @@ border="false" can_close="false" can_minimize="false" - height="116" + height="90" layout="topleft" name="modal container" - width="272"> - - Keyboard - - - Mouse Buttons - - -Press a key to set your trigger. -Allowed input: [INPUT]. - - -Combination [KEYSTR] is reserved by menu. - + width="240"> -Press a key to set your trigger. -Allowed input: [INPUT]. + width="180"> + Press a key to set your Speak button trigger. - - - +