diff options
author | Alexander Gavriliuk <alexandrgproductengine@lindenlab.com> | 2024-05-08 23:32:58 +0200 |
---|---|---|
committer | Andrew Meadows <andrew.l.meadows@gmail.com> | 2024-10-03 09:02:09 -0700 |
commit | 2daf175650cdda7cc8f820b6cb17b1475496e7ac (patch) | |
tree | 0ece9bb592a922fbcb3f4532aee10941e307f44f /indra/newview | |
parent | ec39ac89e8529da206dafd519d75ad5944888076 (diff) |
Add GameControl UI for per device settings
Diffstat (limited to 'indra/newview')
-rw-r--r-- | indra/newview/app_settings/settings.xml | 54 | ||||
-rw-r--r-- | indra/newview/llappviewer.cpp | 61 | ||||
-rw-r--r-- | indra/newview/llfloaterpreference.cpp | 1178 | ||||
-rw-r--r-- | indra/newview/llfloaterpreference.h | 103 | ||||
-rw-r--r-- | indra/newview/skins/default/xui/en/game_control_table_columns.xml | 15 | ||||
-rw-r--r-- | indra/newview/skins/default/xui/en/game_control_table_rows.xml | 1 | ||||
-rw-r--r-- | indra/newview/skins/default/xui/en/panel_preferences_game_control.xml | 332 |
7 files changed, 1235 insertions, 509 deletions
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index ebe027689a..2e520037b3 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -82,10 +82,10 @@ <key>AFKTimeout</key> <map> <key>Comment</key> - <string> - Time before automatically setting AFK (away from keyboard) mode (seconds, 0=never). - Valid values are: 0, 120, 300, 600, 1800 -</string> + <string> + Time before automatically setting AFK (away from keyboard) mode (seconds, 0=never). + Valid values are: 0, 120, 300, 600, 1800 + </string> <key>Persist</key> <integer>1</integer> <key>Type</key> @@ -379,7 +379,7 @@ <key>Value</key> <integer>0</integer> </map> - <key>AutoAcceptNewInventory</key> + <key>AutoAcceptNewInventory</key> <map> <key>Comment</key> <string>Automatically accept new notecards/textures/landmarks</string> @@ -2645,6 +2645,50 @@ <key>Value</key> <integer>0</integer> </map> + <key>AnalogChannelMappings</key> + <map> + <key>Comment</key> + <string>GameControl analog button to channel mappings</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>String</string> + <key>Value</key> + <string>push:AXIS_1+,slide:AXIS_0+,jump:AXIS_4+,turn:AXIS_2+,look:AXIS_3+</string> + </map> + <key>BinaryChannelMappings</key> + <map> + <key>Comment</key> + <string>GameControl binary button to channel mappings</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>String</string> + <key>Value</key> + <string>toggle_run:BUTTON_9,toggle_fly:BUTTON_11,toggle_flycam:BUTTON_10,stop:BUTTON_7</string> + </map> + <key>FlycamChannelMappings</key> + <map> + <key>Comment</key> + <string>GameControl flycam button to channel mappings</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>String</string> + <key>Value</key> + <string>advance:AXIS_1+,pan:AXIS_0+,rise:AXIS_5+,pitch:AXIS_3-,yaw:AXIS_2+</string> + </map> + <key>KnownGameControllers</key> + <map> + <key>Comment</key> + <string>Map of controller device-specific options.</string> + <key>Persist</key> + <integer>1</integer> + <key>Type</key> + <string>LLSD</string> + <key>Value</key> + <map/> + </map> <key>EnableGestureSounds</key> <map> <key>Comment</key> diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index cb7606ac2e..dcd2a2f98d 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -1135,10 +1135,13 @@ bool LLAppViewer::init() LLViewerJoystick::getInstance()->init(false); } - LLGameControl::init(gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "gamecontrollerdb.txt")); - LLGameControl::enableSendToServer(gSavedSettings.getBOOL("GameControlToServer")); - LLGameControl::enableControlAgent(gSavedSettings.getBOOL("GameControlToAgent")); - LLGameControl::enableTranslateAgentActions(gSavedSettings.getBOOL("AgentToGameControl")); + LLGameControl::init(gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "gamecontrollerdb.txt"), + [&](const std::string& name) -> bool { return gSavedSettings.getBOOL(name); }, + [&](const std::string& name, bool value) { gSavedSettings.setBOOL(name, value); }, + [&](const std::string& name) -> std::string { return gSavedSettings.getString(name); }, + [&](const std::string& name, const std::string& value) { gSavedSettings.setString(name, value); }, + [&](const std::string& name) -> LLSD { return gSavedSettings.getLLSD(name); }, + [&](const std::string& name, const LLSD& value) { gSavedSettings.setLLSD(name, value); }); try { @@ -1423,46 +1426,6 @@ bool LLAppViewer::frame() return ret; } -// util for detecting most active input channel -LLGameControl::InputChannel get_active_input_channel(const LLGameControl::State& state) -{ - LLGameControl::InputChannel input; - if (state.mButtons > 0) - { - // check buttons - input.mType = LLGameControl::InputChannel::TYPE_BUTTON; - for (U8 i = 0; i < 32; ++i) - { - if ((0x1 << i) & state.mButtons) - { - input.mIndex = i; - break; - } - } - } - else - { - // scan axes - S16 threshold = std::numeric_limits<S16>::max() / 2; - for (U8 i = 0; i < 6; ++i) - { - if (abs(state.mAxes[i]) > threshold) - { - input.mType = LLGameControl::InputChannel::TYPE_AXIS; - // input.mIndex ultimately translates to a LLGameControl::KeyboardAxis - // which distinguishes between negative and positive directions - // so we must translate to axis index "i" according to the sign - // of the axis value. - input.mIndex = i; - input.mSign = state.mAxes[i] > 0 ? 1 : -1; - break; - } - } - } - return input; -} - - void sendGameControlInput() { LLMessageSystem* msg = gMessageSystem; @@ -4860,13 +4823,9 @@ void LLAppViewer::idle() bool should_send_game_control = LLGameControl::computeFinalStateAndCheckForChanges(); if (LLPanelPreferenceGameControl::isWaitingForInputChannel()) { - LLGameControl::InputChannel channel = get_active_input_channel(LLGameControl::getState()); - if (channel.mType != LLGameControl::InputChannel::TYPE_NONE) - { - LLPanelPreferenceGameControl::applyGameControlInput(channel); - // skip this send because input is being used to set preferences - should_send_game_control = false; - } + LLPanelPreferenceGameControl::applyGameControlInput(); + // skip this send because input is being used to set preferences + should_send_game_control = false; } if (should_send_game_control) { diff --git a/indra/newview/llfloaterpreference.cpp b/indra/newview/llfloaterpreference.cpp index 47777bf85c..2aa54eef57 100644 --- a/indra/newview/llfloaterpreference.cpp +++ b/indra/newview/llfloaterpreference.cpp @@ -563,21 +563,19 @@ void LLFloaterPreference::draw() void LLFloaterPreference::saveSettings() { LLTabContainer* tabcontainer = getChild<LLTabContainer>("pref core"); - child_list_t::const_iterator iter = tabcontainer->getChildList()->begin(); - child_list_t::const_iterator end = tabcontainer->getChildList()->end(); - for ( ; iter != end; ++iter) + for (LLView* view : *tabcontainer->getChildList()) { - LLView* view = *iter; - LLPanelPreference* panel = dynamic_cast<LLPanelPreference*>(view); - if (panel) + if (LLPanelPreference* panel = dynamic_cast<LLPanelPreference*>(view)) + { panel->saveSettings(); + } } saveIgnoredNotifications(); } void LLFloaterPreference::apply() { - LLAvatarPropertiesProcessor::getInstance()->addObserver( gAgent.getID(), this ); + LLAvatarPropertiesProcessor::getInstance()->addObserver(gAgent.getID(), this); LLTabContainer* tabcontainer = getChild<LLTabContainer>("pref core"); if (sSkin != gSavedSettings.getString("SkinCurrent")) @@ -585,14 +583,14 @@ void LLFloaterPreference::apply() LLNotificationsUtil::add("ChangeSkin"); refreshSkin(this); } + // Call apply() on all panels that derive from LLPanelPreference - for (child_list_t::const_iterator iter = tabcontainer->getChildList()->begin(); - iter != tabcontainer->getChildList()->end(); ++iter) + for (LLView* view : *tabcontainer->getChildList()) { - LLView* view = *iter; - LLPanelPreference* panel = dynamic_cast<LLPanelPreference*>(view); - if (panel) + if (LLPanelPreference* panel = dynamic_cast<LLPanelPreference*>(view)) + { panel->apply(); + } } gViewerWindow->requestResolutionUpdate(); // for UIScaleFactor @@ -606,7 +604,7 @@ void LLFloaterPreference::apply() LLViewerMedia::getInstance()->setCookiesEnabled(getChild<LLUICtrl>("cookies_enabled")->getValue()); - if (hasChild("web_proxy_enabled", true) &&hasChild("web_proxy_editor", true) && hasChild("web_proxy_port", true)) + if (hasChild("web_proxy_enabled", true) && hasChild("web_proxy_editor", true) && hasChild("web_proxy_port", true)) { bool proxy_enable = getChild<LLUICtrl>("web_proxy_enabled")->getValue(); std::string proxy_address = getChild<LLUICtrl>("web_proxy_editor")->getValue(); @@ -643,13 +641,12 @@ void LLFloaterPreference::cancel(const std::vector<std::string> settings_to_skip { LLTabContainer* tabcontainer = getChild<LLTabContainer>("pref core"); // Call cancel() on all panels that derive from LLPanelPreference - for (child_list_t::const_iterator iter = tabcontainer->getChildList()->begin(); - iter != tabcontainer->getChildList()->end(); ++iter) + for (LLView* view : *tabcontainer->getChildList()) { - LLView* view = *iter; - LLPanelPreference* panel = dynamic_cast<LLPanelPreference*>(view); - if (panel) - panel->cancel(settings_to_skip); + if (LLPanelPreference* panel = dynamic_cast<LLPanelPreference*>(view)) + { + panel->cancel(); + } } // hide joystick pref floater LLFloaterReg::hideInstance("pref_joystick"); @@ -678,7 +675,7 @@ void LLFloaterPreference::cancel(const std::vector<std::string> settings_to_skip } //Need to reload the navmesh if the pathing console is up LLHandle<LLFloaterPathfindingConsole> pathfindingConsoleHandle = LLFloaterPathfindingConsole::getInstanceHandle(); - if ( !pathfindingConsoleHandle.isDead() ) + if (!pathfindingConsoleHandle.isDead()) { LLFloaterPathfindingConsole* pPathfindingConsole = pathfindingConsoleHandle.get(); pPathfindingConsole->onRegionBoundaryCross(); @@ -695,6 +692,15 @@ void LLFloaterPreference::cancel(const std::vector<std::string> settings_to_skip void LLFloaterPreference::onOpen(const LLSD& key) { + LLTabContainer* tabcontainer = getChild<LLTabContainer>("pref core"); + for (LLView* view : *tabcontainer->getChildList()) + { + if (LLPanelPreference* panel = dynamic_cast<LLPanelPreference*>(view)) + { + panel->onOpen(key); + } + } + // this variable and if that follows it are used to properly handle do not disturb mode response message static bool initialized = false; // if user is logged in and we haven't initialized do not disturb mode response yet, do it @@ -762,8 +768,7 @@ void LLFloaterPreference::onOpen(const LLSD& key) // while preferences floater was closed. buildPopupLists(); - - //get the options that were checked + // get the options that were checked onNotificationsChange("FriendIMOptions"); onNotificationsChange("NonFriendIMOptions"); onNotificationsChange("ConferenceIMOptions"); @@ -775,8 +780,7 @@ void LLFloaterPreference::onOpen(const LLSD& key) refresh(); // Make sure the current state of prefs are saved away when - // when the floater is opened. That will make cancel do its - // job + // the floater is opened. That will make cancel() do its job saveSettings(); // Make sure there is a default preference file @@ -976,9 +980,9 @@ void LLFloaterPreference::onBtnOK(const LLSD& userdata) } //Conversation transcript and log path changed so reload conversations based on new location - if(mPriorInstantMessageLogPath.length()) + if (mPriorInstantMessageLogPath.length()) { - if(moveTranscriptsAndLog()) + if (moveTranscriptsAndLog()) { //When floaters are empty but have a chat history files, reload chat history into them LLFloaterIMSessionTab::reloadEmptyFloaters(); @@ -995,11 +999,13 @@ void LLFloaterPreference::onBtnOK(const LLSD& userdata) LLUIColorTable::instance().saveUserSettings(); gSavedSettings.saveToFile(gSavedSettings.getString("ClientSettingsFile"), true); - //Only save once logged in and loaded per account settings - if(mGotPersonalInfo) + LLGameControl::loadFromSettings(); + + // Only save once logged in and loaded per account settings + if (mGotPersonalInfo) { gSavedPerAccountSettings.saveToFile(gSavedSettings.getString("PerAccountSettingsFile"), true); - } + } } else { @@ -1045,8 +1051,7 @@ void LLFloaterPreference::onBtnCancel(const LLSD& userdata) // static void LLFloaterPreference::updateUserInfo(const std::string& visibility) { - LLFloaterPreference* instance = LLFloaterReg::findTypedInstance<LLFloaterPreference>("preferences"); - if (instance) + if (LLFloaterPreference* instance = LLFloaterReg::findTypedInstance<LLFloaterPreference>("preferences")) { instance->setPersonalInfo(visibility); } @@ -1054,14 +1059,12 @@ void LLFloaterPreference::updateUserInfo(const std::string& visibility) void LLFloaterPreference::refreshEnabledGraphics() { - LLFloaterPreference* instance = LLFloaterReg::findTypedInstance<LLFloaterPreference>("preferences"); - if (instance) + if (LLFloaterPreference* instance = LLFloaterReg::findTypedInstance<LLFloaterPreference>("preferences")) { instance->refresh(); } - LLFloater* advanced = LLFloaterReg::findTypedInstance<LLFloater>("prefs_graphics_advanced"); - if (advanced) + if (LLFloater* advanced = LLFloaterReg::findTypedInstance<LLFloater>("prefs_graphics_advanced")) { advanced->refresh(); } @@ -1096,7 +1099,7 @@ void LLFloaterPreference::onNotificationsChange(const std::string& OptionName) bool show_notifications_alert = true; for (notifications_map::iterator it_notification = mNotificationOptions.begin(); it_notification != mNotificationOptions.end(); it_notification++) { - if(it_notification->second != "No action") + if (it_notification->second != "No action") { show_notifications_alert = false; break; @@ -1108,8 +1111,7 @@ void LLFloaterPreference::onNotificationsChange(const std::string& OptionName) void LLFloaterPreference::onNameTagOpacityChange(const LLSD& newvalue) { - LLColorSwatchCtrl* color_swatch = findChild<LLColorSwatchCtrl>("background"); - if (color_swatch) + if (LLColorSwatchCtrl* color_swatch = findChild<LLColorSwatchCtrl>("background")) { LLColor4 new_color = color_swatch->get(); color_swatch->set(new_color.setAlpha((F32)newvalue.asReal())); @@ -1290,7 +1292,7 @@ void LLAvatarComplexityControls::setIndirectMaxArc() void LLFloaterPreference::refresh() { - LLPanel::refresh(); + LLFloater::refresh(); setMaxNonImpostorsText( gSavedSettings.getU32("RenderAvatarMaxNonImpostors"), getChild<LLTextBox>("IndirectMaxNonImpostorsText", true)); @@ -1298,8 +1300,7 @@ void LLFloaterPreference::refresh() gSavedSettings.getU32("RenderAvatarMaxComplexity"), getChild<LLTextBox>("IndirectMaxComplexityText", true)); refreshEnabledState(); - LLFloater* advanced = LLFloaterReg::findTypedInstance<LLFloater>("prefs_graphics_advanced"); - if (advanced) + if (LLFloater* advanced = LLFloaterReg::findTypedInstance<LLFloater>("prefs_graphics_advanced")) { advanced->refresh(); } @@ -1313,7 +1314,7 @@ void LLFloaterPreference::onCommitWindowedMode() void LLFloaterPreference::onChangeQuality(const LLSD& data) { - U32 level = (U32)(data.asReal()); + U32 level = (U32)data.asReal(); LLFeatureManager::getInstance()->setGraphicsLevel(level, true); refreshEnabledGraphics(); refresh(); @@ -1393,7 +1394,6 @@ void LLFloaterPreference::onClickLogPath() std::string proposed_name(gSavedPerAccountSettings.getString("InstantMessageLogPath")); mPriorInstantMessageLogPath.clear(); - (new LLDirPickerThread(boost::bind(&LLFloaterPreference::changeLogPath, this, _1, _2), proposed_name))->getFile(); } @@ -1418,10 +1418,10 @@ bool LLFloaterPreference::moveTranscriptsAndLog() bool madeDirectory = false; //Does the directory really exist, if not then make it - if(!LLFile::isdir(chatLogPath)) + if (!LLFile::isdir(chatLogPath)) { //mkdir success is defined as zero - if(LLFile::mkdir(chatLogPath) != 0) + if (LLFile::mkdir(chatLogPath) != 0) { return false; } @@ -1431,10 +1431,10 @@ bool LLFloaterPreference::moveTranscriptsAndLog() std::string originalConversationLogDir = LLConversationLog::instance().getFileName(); std::string targetConversationLogDir = gDirUtilp->add(chatLogPath, "conversation.log"); //Try to move the conversation log - if(!LLConversationLog::instance().moveLog(originalConversationLogDir, targetConversationLogDir)) + if (!LLConversationLog::instance().moveLog(originalConversationLogDir, targetConversationLogDir)) { //Couldn't move the log and created a new directory so remove the new directory - if(madeDirectory) + if (madeDirectory) { LLFile::rmdir(chatLogPath); } @@ -1447,7 +1447,7 @@ bool LLFloaterPreference::moveTranscriptsAndLog() LLLogChat::getListOfTranscriptFiles(listOfTranscripts); - if(!LLLogChat::moveTranscripts(gDirUtilp->getChatLogsDir(), + if (!LLLogChat::moveTranscripts(gDirUtilp->getChatLogsDir(), instantMessageLogPath, listOfTranscripts, listOfFilesMoved)) @@ -1460,7 +1460,7 @@ bool LLFloaterPreference::moveTranscriptsAndLog() //Move the conversation log back LLConversationLog::instance().moveLog(targetConversationLogDir, originalConversationLogDir); - if(madeDirectory) + if (madeDirectory) { LLFile::rmdir(chatLogPath); } @@ -1507,7 +1507,6 @@ void LLFloaterPreference::setPersonalInfo(const std::string& visibility) getChild<LLUICtrl>("voice_call_friends_only_check")->setValue(gSavedPerAccountSettings.getBOOL("VoiceCallsFriendsOnly")); } - void LLFloaterPreference::refreshUI() { refresh(); @@ -1791,8 +1790,7 @@ void LLFloaterPreference::onClickActionChange() void LLFloaterPreference::onAtmosShaderChange() { - LLCheckBoxCtrl* ctrl_alm = getChild<LLCheckBoxCtrl>("UseLightShaders"); - if(ctrl_alm) + if (LLCheckBoxCtrl* ctrl_alm = getChild<LLCheckBoxCtrl>("UseLightShaders")) { //Deferred/SSAO/Shadows bool bumpshiny = LLCubeMap::sUseCubeMaps && LLFeatureManager::getInstance()->isFeatureAvailable("RenderObjectBump") && gSavedSettings.getBOOL("RenderObjectBump"); @@ -1853,12 +1851,9 @@ void LLFloaterPreference::updateClickActionControls() // In such case we won't need to do this 'dynamic_cast' nightmare. // updateTable() can also be avoided LLTabContainer* tabcontainer = getChild<LLTabContainer>("pref core"); - for (child_list_t::const_iterator iter = tabcontainer->getChildList()->begin(); - iter != tabcontainer->getChildList()->end(); ++iter) + for (LLView* view : *tabcontainer->getChildList()) { - LLView* view = *iter; - LLPanelPreferenceControls* panel = dynamic_cast<LLPanelPreferenceControls*>(view); - if (panel) + if (LLPanelPreferenceControls* panel = dynamic_cast<LLPanelPreferenceControls*>(view)) { panel->setKeyBind("walk_to", EMouseClickType::CLICK_LEFT, @@ -1892,12 +1887,9 @@ void LLFloaterPreference::updateClickActionViews() // Todo: This is a very ugly way to get access to keybindings. // Reconsider possible options. LLTabContainer* tabcontainer = getChild<LLTabContainer>("pref core"); - for (child_list_t::const_iterator iter = tabcontainer->getChildList()->begin(); - iter != tabcontainer->getChildList()->end(); ++iter) + for (LLView* view : *tabcontainer->getChildList()) { - LLView* view = *iter; - LLPanelPreferenceControls* panel = dynamic_cast<LLPanelPreferenceControls*>(view); - if (panel) + if (LLPanelPreferenceControls* panel = dynamic_cast<LLPanelPreferenceControls*>(view)) { click_to_walk = panel->canKeyBindHandle("walk_to", EMouseClickType::CLICK_LEFT, @@ -1969,7 +1961,6 @@ void LLFloaterPreference::changed() // set 'enable' property for 'Delete transcripts...' button updateDeleteTranscriptsButton(); - } void LLFloaterPreference::saveGraphicsPreset(std::string& preset) @@ -2077,7 +2068,6 @@ bool LLPanelPreference::postBuild() if (hasChild("media_enabled", true)) { bool media_enabled = gSavedSettings.getBOOL("AudioStreamingMedia"); - getChild<LLCheckBoxCtrl>("media_enabled")->set(media_enabled); getChild<LLCheckBoxCtrl>("autoplay_enabled")->setEnabled(media_enabled); } @@ -2134,41 +2124,41 @@ LLPanelPreference::~LLPanelPreference() delete mBandWidthUpdater; } } + +// virtual void LLPanelPreference::apply() { // no-op } +// virtual void LLPanelPreference::saveSettings() { - LLFloater* advanced = LLFloaterReg::findTypedInstance<LLFloater>("prefs_graphics_advanced"); - // Save the value of all controls in the hierarchy mSavedValues.clear(); std::list<LLView*> view_stack; view_stack.push_back(this); - if (advanced) + // Search for 'Advanced' panel and add it if found + if (LLFloater* advanced = LLFloaterReg::findTypedInstance<LLFloater>("prefs_graphics_advanced")) { view_stack.push_back(advanced); } - while(!view_stack.empty()) + + while (!view_stack.empty()) { // Process view on top of the stack LLView* curview = view_stack.front(); view_stack.pop_front(); - LLColorSwatchCtrl* color_swatch = dynamic_cast<LLColorSwatchCtrl *>(curview); - if (color_swatch) + if (LLColorSwatchCtrl* color_swatch = dynamic_cast<LLColorSwatchCtrl*>(curview)) { mSavedColors[color_swatch->getName()] = color_swatch->get(); } else { - LLUICtrl* ctrl = dynamic_cast<LLUICtrl*>(curview); - if (ctrl) + if (LLUICtrl* ctrl = dynamic_cast<LLUICtrl*>(curview)) { - LLControlVariable* control = ctrl->getControlVariable(); - if (control) + if (LLControlVariable* control = ctrl->getControlVariable()) { mSavedValues[control] = control->getValue(); } @@ -2176,10 +2166,9 @@ void LLPanelPreference::saveSettings() } // Push children onto the end of the work stack - for (child_list_t::const_iterator iter = curview->getChildList()->begin(); - iter != curview->getChildList()->end(); ++iter) + for (LLView* view : *curview->getChildList()) { - view_stack.push_back(*iter); + view_stack.push_back(view); } } @@ -2240,12 +2229,12 @@ void LLPanelPreference::cancel(const std::vector<std::string> settings_to_skip) { for (control_values_map_t::iterator iter = mSavedValues.begin(); iter != mSavedValues.end(); ++iter) - { +{ LLControlVariable* control = iter->first; LLSD ctrl_value = iter->second; if((control->getName() == "InstantMessageLogPath") && (ctrl_value.asString() == "")) - { + { continue; } @@ -2336,7 +2325,7 @@ public: if (find(mAccountIndependentSettings.begin(), mAccountIndependentSettings.end(), setting) == mAccountIndependentSettings.end()) { - mSavedValues.erase(it++); + it = mSavedValues.erase(it); } else { @@ -2500,9 +2489,11 @@ void LLPanelPreferenceGraphics::cancel(const std::vector<std::string> settings_t { LLPanelPreference::cancel(settings_to_skip); } + void LLPanelPreferenceGraphics::saveSettings() { resetDirtyChilds(); + std::string preset_graphic_active = gSavedSettings.getString("PresetGraphicActive"); if (preset_graphic_active.empty()) { @@ -2513,8 +2504,10 @@ void LLPanelPreferenceGraphics::saveSettings() instance->saveGraphicsPreset(preset_graphic_active); } } + LLPanelPreference::saveSettings(); } + void LLPanelPreferenceGraphics::setHardwareDefaults() { resetDirtyChilds(); @@ -2610,12 +2603,9 @@ bool LLPanelPreferenceControls::addControlTableRows(const std::string &filename) cell_params.column = ""; cell_params.value = ""; - - for (LLInitParam::ParamIterator<LLScrollListItem::Params>::const_iterator row_it = contents.rows.begin(); - row_it != contents.rows.end(); - ++row_it) + for (LLScrollListItem::Params& row_params : contents.rows) { - std::string control = row_it->value.getValue().asString(); + std::string control = row_params.value.getValue().asString(); if (!control.empty() && control != "menu_separator") { bool show = true; @@ -2634,7 +2624,7 @@ bool LLPanelPreferenceControls::addControlTableRows(const std::string &filename) if (show) { // At the moment viewer is hardcoded to assume that columns are named as lst_ctrl%d - LLScrollListItem::Params item_params(*row_it); + LLScrollListItem::Params item_params(row_params); item_params.enabled.setValue(enabled); S32 num_columns = pControlsTable->getNumColumns(); @@ -2659,7 +2649,7 @@ bool LLPanelPreferenceControls::addControlTableRows(const std::string &filename) // value = "menu_separator" // column = "lst_action" / > //</rows> - pControlsTable->addRow(*row_it, EAddPosition::ADD_BOTTOM); + pControlsTable->addRow(row_params, EAddPosition::ADD_BOTTOM); } } return true; @@ -3115,6 +3105,7 @@ void LLPanelPreferenceControls::onDefaultKeyBind(bool all_modes) mConflictHandler[mEditingMode].saveToSettings(true); } } + updateTable(); if (mEditingMode == LLKeyConflictHandler::MODE_THIRD_PERSON || all_modes) @@ -3136,7 +3127,10 @@ void LLPanelPreferenceControls::onCancelKeyBind() //------------------------LLPanelPreferenceGameControl-------------------------------- // LLPanelPreferenceGameControl is effectively a singleton, so we track its instance -static LLPanelPreferenceGameControl* gGameControlPanel; +static LLPanelPreferenceGameControl* gGameControlPanel { nullptr }; +static LLScrollListCtrl* gSelectedGrid { nullptr }; +static LLScrollListItem* gSelectedItem { nullptr }; +static LLScrollListCell* gSelectedCell { nullptr }; LLPanelPreferenceGameControl::LLPanelPreferenceGameControl() { @@ -3150,332 +3144,724 @@ LLPanelPreferenceGameControl::~LLPanelPreferenceGameControl() static LLPanelInjector<LLPanelPreferenceGameControl> t_pref_game_control("panel_preference_game_control"); -void LLPanelPreferenceGameControl::apply() -{ -} - -void LLPanelPreferenceGameControl::loadDefaults() -{ - // TODO: implement this -} - -void LLPanelPreferenceGameControl::loadSettings() -{ - // TODO: implement this -} - +// Collect all UI control values into mSavedValues void LLPanelPreferenceGameControl::saveSettings() { - // TODO: implement this -} - -void LLPanelPreferenceGameControl::updateEnabledState() -{ - // TODO?: implement this -} + LLPanelPreference::saveSettings(); -static LLScrollListItem* gSelectedItem { nullptr }; -static LLScrollListCell* gSelectedCell { nullptr }; + std::vector<LLScrollListItem*> items = mActionTable->getAllData(); -void LLPanelPreferenceGameControl::onClickGameControlToServer(LLUICtrl* ctrl) -{ - BOOL checked = mCheckGameControlToServer->get(); - gSavedSettings.setBOOL( "GameControlToServer", checked ); - LLGameControl::enableSendToServer(checked); -} + // Find the channel visually associated with the specified action + LLGameControl::getChannel_t getChannel = + [&](const std::string& action) -> LLGameControl::InputChannel + { + for (LLScrollListItem* item : items) + { + if (action == item->getValue() && (item->getNumColumns() >= 2)) + { + return LLGameControl::getChannelByName(item->getColumn(1)->getValue()); + } + } + return LLGameControl::InputChannel(); + }; -void LLPanelPreferenceGameControl::onClickGameControlToAgent(LLUICtrl* ctrl) -{ - BOOL checked = mCheckGameControlToAgent->get(); - gSavedSettings.setBOOL( "GameControlToAgent", checked ); - LLGameControl::enableControlAgent(checked); + // Use string formatting functions provided by class LLGameControl: + // stringifyAnalogMappings(), stringifyBinaryMappings(), stringifyFlycamMappings() - mActionTable->deselectAllItems(); - bool table_enabled = checked || mCheckAgentToGameControl->get(); - mActionTable->setEnabled(table_enabled); - mChannelSelector->setEnabled(table_enabled); - LLGameControl::enableTranslateAgentActions(checked); -} + if (LLControlVariable* analogMappings = gSavedSettings.getControl("AnalogChannelMappings")) + { + analogMappings->set(LLGameControl::stringifyAnalogMappings(getChannel)); + mSavedValues[analogMappings] = analogMappings->getValue(); + } -void LLPanelPreferenceGameControl::onClickAgentToGameControl(LLUICtrl* ctrl) -{ - BOOL checked = mCheckAgentToGameControl->get(); - gSavedSettings.setBOOL( "AgentToGameControl", checked ); + if (LLControlVariable* binaryMappings = gSavedSettings.getControl("BinaryChannelMappings")) + { + binaryMappings->set(LLGameControl::stringifyBinaryMappings(getChannel)); + mSavedValues[binaryMappings] = binaryMappings->getValue(); + } - mActionTable->deselectAllItems(); - bool table_enabled = checked || mCheckGameControlToAgent->get(); - mActionTable->setEnabled(table_enabled); - mChannelSelector->setEnabled(table_enabled); - LLGameControl::enableTranslateAgentActions(checked); + if (LLControlVariable* flycamMappings = gSavedSettings.getControl("FlycamChannelMappings")) + { + flycamMappings->set(LLGameControl::stringifyFlycamMappings(getChannel)); + mSavedValues[flycamMappings] = flycamMappings->getValue(); + } + if (LLControlVariable* knownControllers = gSavedSettings.getControl("KnownGameControllers")) + { + LLSD deviceOptions(LLSD::emptyMap()); + for (auto& pair : mDeviceOptions) + { + pair.second.settings = pair.second.options.saveToString(pair.second.name); + if (!pair.second.settings.empty()) + { + deviceOptions.insert(pair.first, pair.second.settings); + } + } + knownControllers->set(deviceOptions); + mSavedValues[knownControllers] = deviceOptions; + } } -void LLPanelPreferenceGameControl::onActionSelect() +void LLPanelPreferenceGameControl::onGridSelect(LLUICtrl* ctrl) { clearSelectionState(); - LLScrollListItem* item = mActionTable->getFirstSelected(); - if (item == NULL) - { + LLScrollListCtrl* table = dynamic_cast<LLScrollListCtrl*>(ctrl); + if (!table || !table->getEnabled()) return; + + if (LLScrollListItem* item = table->getFirstSelected()) + { + if (initCombobox(item, table)) + return; + + table->deselectAllItems(); } +} - std::string action = item->getValue(); +bool LLPanelPreferenceGameControl::initCombobox(LLScrollListItem* item, LLScrollListCtrl* grid) +{ + if (item->getSelectedCell() != 1) + return false; + + LLScrollListText* cell = dynamic_cast<LLScrollListText*>(item->getColumn(1)); + if (!cell) + return false; - if (action.empty()) + LLComboBox* combobox = nullptr; + if (grid == mActionTable) { - mActionTable->deselectAllItems(); - return; + std::string action = item->getValue(); + LLGameControl::ActionNameType actionNameType = LLGameControl::getActionNameType(action); + combobox = + actionNameType == LLGameControl::ACTION_NAME_ANALOG ? mAnalogChannelSelector : + actionNameType == LLGameControl::ACTION_NAME_BINARY ? mBinaryChannelSelector : + actionNameType == LLGameControl::ACTION_NAME_FLYCAM ? mAnalogChannelSelector : + nullptr; } - - S32 cell_index = item->getSelectedCell(); - if (cell_index != 1) + else if (grid == mAxisMappings) { - mActionTable->deselectAllItems(); - return; + combobox = mAxisSelector; } - - LLScrollListText* cell = dynamic_cast<LLScrollListText*>(item->getColumn(cell_index)); - if (cell) + else if (grid == mButtonMappings) { - gSelectedItem = item; - gSelectedCell = cell; + combobox = mBinaryChannelSelector; + } + if (!combobox) + return false; - // compute new rect for mChannelSelector - S32 row = mActionTable->getFirstSelectedIndex(); - S32 column = item->getSelectedCell(); - LLRect cell_rect = mActionTable->getCellRect(row, column); + // compute new rect for combobox + S32 row_index = grid->getItemIndex(item); + fitInRect(combobox, grid, row_index, 1); - LLRect combo_rect = mChannelSelector->getRect(); - S32 width = combo_rect.getWidth(); - S32 height = combo_rect.getHeight(); - S32 left = cell_rect.mLeft + cell->getTextWidth(); - combo_rect.set(left, cell_rect.mTop, left + width, cell_rect.mTop - height); - mChannelSelector->setRect(combo_rect); + std::string channel_name = "NONE"; + std::string cell_value = cell->getValue(); + std::vector<LLScrollListItem*> items = combobox->getAllData(); + for (const LLScrollListItem* item : items) + { + if (item->getColumn(0)->getValue().asString() == cell_value) + { + channel_name = item->getValue().asString(); + break; + } + } - std::string value = gSelectedCell->getValue(); - if (value == " ") + std::string value; + LLGameControl::InputChannel channel = LLGameControl::getChannelByName(channel_name); + if (!channel.isNone()) + { + std::string channel_name = channel.getLocalName(); + std::string channel_label = getChannelLabel(channel_name, combobox->getAllData()); + if (combobox->itemExists(channel_label)) { - value = "NONE"; + value = channel_name; } - mChannelSelector->setValue(value); - mChannelSelector->setVisible(TRUE); - mChannelSelector->showList(); } - else + if (value.empty()) { - mActionTable->deselectAllItems(); + // Assign the last element in the dropdown list which is "NONE" + value = combobox->getAllData().back()->getValue().asString(); } + + combobox->setValue(value); + combobox->setVisible(TRUE); + combobox->showList(); + + gSelectedGrid = grid; + gSelectedItem = item; + gSelectedCell = cell; + + return true; } -void LLPanelPreferenceGameControl::onCommitInputChannel() +void LLPanelPreferenceGameControl::onCommitInputChannel(LLUICtrl* ctrl) { - if (gSelectedCell) - { - std::string channel_name = mChannelSelector->getSelectedItemLabel(); - LLGameControl::InputChannel channel = LLGameControl::getChannelByName(channel_name); + if (!gSelectedGrid || !gSelectedItem || !gSelectedCell) + return; + + LLComboBox* combobox = dynamic_cast<LLComboBox*>(ctrl); + llassert(combobox); + if (!combobox) + return; - std::string action_name = gSelectedItem->getValue(); - bool success = LLGameControl::updateActionMap(action_name, channel); - if (success) + if (gSelectedGrid == mActionTable) + { + std::string value = combobox->getValue(); + std::string label = (value == "NONE") ? + LLStringUtil::null : combobox->getSelectedItemLabel(); + gSelectedCell->setValue(label); + } + else + { + S32 chosen_index = combobox->getCurrentIndex(); + if (chosen_index >= 0) { - if (channel_name == "NONE") + int row_index = gSelectedGrid->getItemIndex(gSelectedItem); + llassert(row_index >= 0); + LLGameControl::Options& deviceOptions = getSelectedDeviceOptions(); + std::vector<U8>& map = gSelectedGrid == mAxisMappings ? + deviceOptions.getAxisMap() : deviceOptions.getButtonMap(); + if (chosen_index >= map.size()) { - gSelectedCell->setValue(" "); - // TODO?: also clear cell to the right with script-relevant name - } - else - { - gSelectedCell->setValue(channel_name); - // TODO?: also update the cell to the right with script-relevant name + chosen_index = row_index; } + std::string label = chosen_index == row_index ? + LLStringUtil::null : combobox->getSelectedItemLabel(); + gSelectedCell->setValue(label); + map[row_index] = chosen_index; } - gGameControlPanel->updateTable(); - clearSelectionState(); } + gSelectedGrid->deselectAllItems(); + clearSelectionState(); } bool LLPanelPreferenceGameControl::isWaitingForInputChannel() { - return gSelectedItem != nullptr; + return gSelectedCell != nullptr; } // static -void LLPanelPreferenceGameControl::applyGameControlInput(const LLGameControl::InputChannel& channel) +void LLPanelPreferenceGameControl::applyGameControlInput() +{ + if (!gGameControlPanel || !gSelectedGrid || !gSelectedCell) + return; + + LLComboBox* combobox; + LLGameControl::InputChannel::Type expectedType; + if (gGameControlPanel->mAnalogChannelSelector->getVisible()) + { + combobox = gGameControlPanel->mAnalogChannelSelector; + expectedType = LLGameControl::InputChannel::TYPE_AXIS; + } + else if (gGameControlPanel->mBinaryChannelSelector->getVisible()) + { + combobox = gGameControlPanel->mBinaryChannelSelector; + expectedType = LLGameControl::InputChannel::TYPE_BUTTON; + } + else + { + return; + } + + LLGameControl::InputChannel channel = LLGameControl::getActiveInputChannel(); + if (channel.mType == expectedType) + { + std::string channel_name = channel.getLocalName(); + std::string channel_label = LLPanelPreferenceGameControl::getChannelLabel(channel_name, combobox->getAllData()); + gSelectedCell->setValue(channel_label); + gSelectedGrid->deselectAllItems(); + gGameControlPanel->clearSelectionState(); + } +} + +void LLPanelPreferenceGameControl::onAxisOptionsSelect() { - if (gSelectedItem && channel.mType != (U8)(LLPanelPreferenceGameControl::TYPE_NONE)) + clearSelectionState(); + + if (LLScrollListItem* row = mAxisOptions->getFirstSelected()) { - S32 cell_index = gSelectedItem->getSelectedCell(); - if (cell_index > 0) + LLGameControl::Options& deviceOptions = getSelectedDeviceOptions(); + S32 row_index = mAxisOptions->getItemIndex(row); + S32 column_index = row->getSelectedCell(); + if (column_index == 1) { - LLScrollListCell* cell = gSelectedItem->getColumn(cell_index); - if (cell) + LLGameControl::Options& deviceOptions = getSelectedDeviceOptions(); + deviceOptions.getAxisOptions()[row_index].mInvert = + row->getColumn(column_index)->getValue().asBoolean(); + } + else if (column_index == 2 || column_index == 3) + { + fitInRect(mNumericValueEditor, mAxisOptions, row_index, column_index); + if (column_index == 2) { - bool success = LLGameControl::updateActionMap(gSelectedItem->getValue(), channel); - if (success) - { - cell->setValue(channel.getLocalName()); - // TODO?: also update the cell to the right with script-relevant name - gGameControlPanel->updateTable(); - } - + mNumericValueEditor->setMinValue(0); + mNumericValueEditor->setMaxValue(LLGameControl::MAX_AXIS_DEAD_ZONE); + mNumericValueEditor->setValue(deviceOptions.getAxisOptions()[row_index].mDeadZone); } + else // column_index == 3 + { + mNumericValueEditor->setMinValue(-LLGameControl::MAX_AXIS_OFFSET); + mNumericValueEditor->setMaxValue(LLGameControl::MAX_AXIS_OFFSET); + mNumericValueEditor->setValue(deviceOptions.getAxisOptions()[row_index].mOffset); + } + mNumericValueEditor->setVisible(TRUE); } - gGameControlPanel->clearSelectionState(); + + initCombobox(row, mAxisOptions); + } +} + +void LLPanelPreferenceGameControl::onCommitNumericValue() +{ + if (LLScrollListItem* row = mAxisOptions->getFirstSelected()) + { + LLGameControl::Options& deviceOptions = getSelectedDeviceOptions(); + S32 value = mNumericValueEditor->getValue().asInteger(); + S32 row_index = mAxisOptions->getItemIndex(row); + S32 column_index = row->getSelectedCell(); + llassert(column_index == 2 || column_index == 3); + if (column_index != 2 && column_index != 3) + return; + + if (column_index == 2) + { + value = std::clamp<S32>(value, 0, LLGameControl::MAX_AXIS_DEAD_ZONE); + deviceOptions.getAxisOptions()[row_index].mDeadZone = (U16)value; + } + else // column_index == 3 + { + value = std::clamp<S32>(value, -LLGameControl::MAX_AXIS_OFFSET, LLGameControl::MAX_AXIS_OFFSET); + deviceOptions.getAxisOptions()[row_index].mOffset = (S16)value; + } + setNumericLabel(row->getColumn(column_index), value); } } BOOL LLPanelPreferenceGameControl::postBuild() { + // Above the tab container mCheckGameControlToServer = getChild<LLCheckBoxCtrl>("game_control_to_server"); - mCheckGameControlToServer->setCommitCallback(boost::bind(&LLPanelPreferenceGameControl::onClickGameControlToServer, this, _1)); - //mCheckGameControlToServer->setEnabled(gSavedSettings.getBOOL( "GameControlToServer")); - mCheckGameControlToAgent = getChild<LLCheckBoxCtrl>("game_control_to_agent"); - mCheckGameControlToAgent->setCommitCallback(boost::bind(&LLPanelPreferenceGameControl::onClickGameControlToAgent, this, _1)); - //mCheckGameControlToAgent->setEnabled(gSavedSettings.getBOOL( "GameControlToAgent")); + mCheckAgentToGameControl = getChild<LLCheckBoxCtrl>("agent_to_game_control"); + + mCheckGameControlToAgent->setCommitCallback([this](LLUICtrl*, const LLSD&) { updateActionTableState(); }); + mCheckAgentToGameControl->setCommitCallback([this](LLUICtrl*, const LLSD&) { updateActionTableState(); }); - mCheckAgentToGameControl= getChild<LLCheckBoxCtrl>("agent_to_game_control"); - mCheckAgentToGameControl->setCommitCallback(boost::bind(&LLPanelPreferenceGameControl::onClickAgentToGameControl, this, _1)); - //mCheckAgentToGameControl->setEnabled(gSavedSettings.getBOOL( "AgentToGameControl")); + getChild<LLTabContainer>("game_control_tabs")->setCommitCallback([this](LLUICtrl*, const LLSD&) { clearSelectionState(); }); + getChild<LLTabContainer>("device_settings_tabs")->setCommitCallback([this](LLUICtrl*, const LLSD&) { clearSelectionState(); }); + // 1st tab "Channel mappings" + mTabChannelMappings = getChild<LLPanel>("tab_channel_mappings"); mActionTable = getChild<LLScrollListCtrl>("action_table"); - mActionTable->setCommitCallback(boost::bind(&LLPanelPreferenceGameControl::onActionSelect, this)); + mActionTable->setCommitCallback([this](LLUICtrl* ctrl, const LLSD&) { onGridSelect(ctrl); }); + + // 2nd tab "Device settings" + mTabDeviceSettings = getChild<LLPanel>("tab_device_settings"); + mNoDeviceMessage = getChild<LLTextBox>("nodevice_message"); + mDevicePrompt = getChild<LLTextBox>("device_prompt"); + mSingleDevice = getChild<LLTextBox>("single_device"); + mDeviceList = getChild<LLComboBox>("device_list"); + mCheckShowAllDevices = getChild<LLCheckBoxCtrl>("show_all_known_devices"); + mPanelDeviceSettings = getChild<LLPanel>("device_settings"); + + mCheckShowAllDevices->setCommitCallback([this](LLUICtrl*, const LLSD&) { populateDeviceTitle(); }); + mDeviceList->setCommitCallback([this](LLUICtrl*, const LLSD& value) { populateDeviceSettings(value); }); + + mTabAxisOptions = getChild<LLPanel>("tab_axis_options"); + mAxisOptions = getChild<LLScrollListCtrl>("axis_options"); + mAxisOptions->setCommitCallback([this](LLUICtrl*, const LLSD&) { onAxisOptionsSelect(); }); + + mTabAxisMappings = getChild<LLPanel>("tab_axis_mappings"); + mAxisMappings = getChild<LLScrollListCtrl>("axis_mappings"); + mAxisMappings->setCommitCallback([this](LLUICtrl* ctrl, const LLSD&) { onGridSelect(ctrl); }); + + mTabButtonMappings = getChild<LLPanel>("tab_button_mappings"); + mButtonMappings = getChild<LLScrollListCtrl>("button_mappings"); + mButtonMappings->setCommitCallback([this](LLUICtrl* ctrl, const LLSD&) { onGridSelect(ctrl); }); + + mResetToDefaults = getChild<LLButton>("reset_to_defaults"); + mResetToDefaults->setCommitCallback([this](LLUICtrl* ctrl, const LLSD&) { onResetToDefaults(); }); + + // Numeric value editor + mNumericValueEditor = getChild<LLSpinCtrl>("numeric_value_editor"); + mNumericValueEditor->setCommitCallback([this](LLUICtrl*, const LLSD&) { onCommitNumericValue(); }); + + // Channel selectors + mAnalogChannelSelector = getChild<LLComboBox>("analog_channel_selector"); + mAnalogChannelSelector->setCommitCallback([this](LLUICtrl* ctrl, const LLSD&) { onCommitInputChannel(ctrl); }); + + mBinaryChannelSelector = getChild<LLComboBox>("binary_channel_selector"); + mBinaryChannelSelector->setCommitCallback([this](LLUICtrl* ctrl, const LLSD&) { onCommitInputChannel(ctrl); }); + + mAxisSelector = getChild<LLComboBox>("axis_selector"); + mAxisSelector->setCommitCallback([this](LLUICtrl* ctrl, const LLSD&) { onCommitInputChannel(ctrl); }); + + // Setup the 1st tab + populateActionTableRows("game_control_table_rows.xml"); + addActionTableSeparator(); + populateActionTableRows("game_control_table_camera_rows.xml"); + + // Setup the 2nd tab + populateOptionsTableRows(); + populateMappingTableRows(mAxisMappings, mAxisSelector, LLGameControl::NUM_AXES); + populateMappingTableRows(mButtonMappings, mBinaryChannelSelector, LLGameControl::NUM_BUTTONS); + + // Workaround for the common bug: + // LLScrollListCtrl with draw_heading="true" initially has incorrect mTop (17 px higher) + LLRect rect = mAxisOptions->getRect(); + rect.mTop = mAxisOptions->getParent()->getRect().getHeight() - 1; + mAxisOptions->setRect(rect); + mAxisOptions->updateLayout(); - populateActionTable(); + return TRUE; +} - // enable the table if at least one of the GameControl<-->Avatar options is enabled - mActionTable->setEnabled(mCheckGameControlToAgent->get() || mCheckAgentToGameControl->get()); +// Update all UI control values from real objects +// This function is called before floater is shown +void LLPanelPreferenceGameControl::onOpen(const LLSD& key) +{ + mCheckGameControlToServer->setValue(LLGameControl::getSendToServer()); + mCheckGameControlToAgent->setValue(LLGameControl::getControlAgent()); + mCheckAgentToGameControl->setValue(LLGameControl::getTranslateAgentActions()); - mChannelSelector = getChild<LLComboBox>("input_channel_combo"); - mChannelSelector->setVisible(FALSE); - mChannelSelector->setCommitCallback(boost::bind(&LLPanelPreferenceGameControl::onCommitInputChannel, this)); + clearSelectionState(); - return TRUE; + // Setup the 1st tab + populateActionTableCells(); + updateActionTableState(); + + // Setup the 2nd tab + mDeviceOptions.clear(); + for (const auto& pair : LLGameControl::getDeviceOptions()) + { + DeviceOptions deviceOptions = { LLStringUtil::null, pair.second, LLGameControl::Options() }; + deviceOptions.options.loadFromString(deviceOptions.name, deviceOptions.settings); + mDeviceOptions.emplace(pair.first, deviceOptions); + } + // Add missing device settings/options even if they are default + for (const auto& device : LLGameControl::getDevices()) + { + if (mDeviceOptions.find(device.getGUID()) == mDeviceOptions.end()) + { + mDeviceOptions[device.getGUID()] = { device.getName(), device.saveOptionsToString(true), device.getOptions() }; + } + } + + mCheckShowAllDevices->setValue(false); + populateDeviceTitle(); +} + +void LLPanelPreferenceGameControl::populateActionTableRows(const std::string& filename) +{ + LLScrollListCtrl::Contents contents; + if (!parseXmlFile(contents, filename, "rows")) + return; + + // init basic cell params + LLScrollListCell::Params second_cell_params; + second_cell_params.font = LLFontGL::getFontSansSerif(); + second_cell_params.font_halign = LLFontGL::LEFT; + second_cell_params.column = mActionTable->getColumn(1)->mName; + second_cell_params.value = ""; // Actual value is assigned in populateActionTableCells + + for (const LLScrollListItem::Params& row_params : contents.rows) + { + std::string name = row_params.value.getValue().asString(); + if (!name.empty() && name != "menu_separator") + { + LLScrollListItem::Params new_params(row_params); + new_params.enabled.setValue(true); + // item_params should already have one column that was defined + // in XUI config file, and now we want to add one more + if (new_params.columns.size() == 1) + { + new_params.columns.add(second_cell_params); + } + mActionTable->addRow(new_params, EAddPosition::ADD_BOTTOM); + } + else + { + mActionTable->addRow(row_params, EAddPosition::ADD_BOTTOM); + } + } } -void LLPanelPreferenceGameControl::populateActionTable() +void LLPanelPreferenceGameControl::populateActionTableCells() { - loadSettings(); - populateColumns(); - populateRows("game_control_table_rows.xml"); - addTableSeparator(); - populateRows("game_control_table_camera_rows.xml"); + std::vector<LLScrollListItem*> rows = mActionTable->getAllData(); + std::vector<LLScrollListItem*> axes = mAnalogChannelSelector->getAllData(); + std::vector<LLScrollListItem*> btns = mBinaryChannelSelector->getAllData(); + + for (LLScrollListItem* row : rows) + { + if (row->getNumColumns() >= 2) // Skip separators + { + std::string name = row->getValue().asString(); + if (!name.empty() && name != "menu_separator") + { + LLGameControl::InputChannel channel = LLGameControl::getChannelByAction(name); + std::string channel_name = channel.getLocalName(); + std::string channel_label = + channel.isAxis() ? getChannelLabel(channel_name, axes) : + channel.isButton() ? getChannelLabel(channel_name, btns) : + LLStringUtil::null; + row->getColumn(1)->setValue(channel_label); + } + } + } } -void LLPanelPreferenceGameControl::populateColumns() +// static +bool LLPanelPreferenceGameControl::parseXmlFile(LLScrollListCtrl::Contents& contents, + const std::string& filename, const std::string& what) { - // populate columns - std::string filename = "game_control_table_columns.xml"; LLXMLNodePtr xmlNode; - LLScrollListCtrl::Contents contents; if (!LLUICtrlFactory::getLayeredXMLNode(filename, xmlNode)) { - LL_WARNS("Preferences") << "Failed to populate columns from '" << filename << "'" << LL_ENDL; - return; + LL_WARNS("Preferences") << "Failed to populate " << what << " from '" << filename << "'" << LL_ENDL; + return false; } + LLXUIParser parser; parser.readXUI(xmlNode, contents, filename); if (!contents.validateBlock()) { - LL_WARNS("Preferences") << "Failed to parse columns from '" << filename << "'" << LL_ENDL; - return; + LL_WARNS("Preferences") << "Failed to parse " << what << " from '" << filename << "'" << LL_ENDL; + return false; } - for (LLInitParam::ParamIterator<LLScrollListColumn::Params>::const_iterator col_it = contents.columns.begin(); - col_it != contents.columns.end(); - ++col_it) + + return true; +} + +void LLPanelPreferenceGameControl::populateDeviceTitle() +{ + mSelectedDeviceGUID.clear(); + + bool showAllDevices = mCheckShowAllDevices->getValue().asBoolean(); + std::size_t deviceCount = showAllDevices ? mDeviceOptions.size() : LLGameControl::getDevices().size(); + + mNoDeviceMessage->setVisible(!deviceCount); + mDevicePrompt->setVisible(deviceCount); + mSingleDevice->setVisible(deviceCount == 1); + mDeviceList->setVisible(deviceCount > 1); + mPanelDeviceSettings->setVisible(deviceCount); + + auto makeTitle = [](const std::string& guid, const std::string& name) -> std::string + { + return guid + ", " + name; + }; + + if (deviceCount == 1) { - mActionTable->addColumn(*col_it); + if (showAllDevices) + { + const std::pair<std::string, DeviceOptions>& pair = *mDeviceOptions.begin(); + mSingleDevice->setValue(makeTitle(pair.first, pair.second.name)); + populateDeviceSettings(pair.first); + } + else + { + const LLGameControl::Device& device = LLGameControl::getDevices().front(); + mSingleDevice->setValue(makeTitle(device.getGUID(), device.getName())); + populateDeviceSettings(device.getGUID()); + } + } + else if (deviceCount) + { + mDeviceList->clear(); + mDeviceList->clearRows(); + + auto makeListItem = [](const std::string& guid, const std::string& title) + { + return LLSD().with("value", guid).with("columns", LLSD().with("label", title)); + }; + + if (showAllDevices) + { + for (const auto& pair : mDeviceOptions) + { + mDeviceList->addElement(makeListItem(pair.first, makeTitle(pair.first, pair.second.name))); + } + } + else + { + for (const LLGameControl::Device& device : LLGameControl::getDevices()) + { + mDeviceList->addElement(makeListItem(device.getGUID(), makeTitle(device.getGUID(), device.getName()))); + } + } + + mDeviceList->selectNthItem(0); + populateDeviceSettings(mDeviceList->getValue()); } } -void LLPanelPreferenceGameControl::populateRows(const std::string& filename) +void LLPanelPreferenceGameControl::populateDeviceSettings(const std::string& guid) { - LLXMLNodePtr xmlNode; - if (!LLUICtrlFactory::getLayeredXMLNode(filename, xmlNode)) + LL_INFOS() << "guid: '" << guid << "'" << LL_ENDL; + + mSelectedDeviceGUID = guid; + auto options_it = mDeviceOptions.find(guid); + llassert_always(options_it != mDeviceOptions.end()); + const DeviceOptions& deviceOptions = options_it->second; + + populateOptionsTableCells(); + populateMappingTableCells(mAxisMappings, deviceOptions.options.getAxisMap(), mAxisSelector); + populateMappingTableCells(mButtonMappings, deviceOptions.options.getButtonMap(), mBinaryChannelSelector); +} + +void LLPanelPreferenceGameControl::populateOptionsTableRows() +{ + mAxisOptions->clearRows(); + + std::vector<LLScrollListItem*> items = mAnalogChannelSelector->getAllData(); + + LLScrollListItem::Params row_params; + LLScrollListCell::Params cell_params; + cell_params.font = LLFontGL::getFontMonospace(); + for (size_t i = 0; i < mAxisOptions->getNumColumns(); ++i) { - LL_WARNS("Preferences") << "Failed to populate rows from '" << filename << "'" << LL_ENDL; - return; + row_params.columns.add(cell_params); } - LLScrollListCtrl::Contents contents; - LLXUIParser parser; - parser.readXUI(xmlNode, contents, filename); - if (!contents.validateBlock()) + + row_params.columns(1).type = "checkbox"; + row_params.columns(2).font_halign = "right"; + row_params.columns(3).font_halign = "right"; + + for (size_t i = 0; i < LLGameControl::NUM_AXES; ++i) { - LL_WARNS("Preferences") << "Failed to parse rows from '" << filename << "'" << LL_ENDL; - return; + LLScrollListItem* row = mAxisOptions->addRow(row_params); + row->getColumn(0)->setValue(items[i]->getColumn(0)->getValue()); } +} - // init basic cell params +void LLPanelPreferenceGameControl::populateOptionsTableCells() +{ + std::vector<LLScrollListItem*> rows = mAxisOptions->getAllData(); + const auto& all_axis_options = getSelectedDeviceOptions().getAxisOptions(); + llassert(rows.size() == all_axis_options.size()); + + for (size_t i = 0; i < rows.size(); ++i) + { + LLScrollListItem* row = rows[i]; + const LLGameControl::Options::AxisOptions& axis_options = all_axis_options[i]; + row->getColumn(1)->setValue(axis_options.mInvert); + setNumericLabel(row->getColumn(2), axis_options.mDeadZone); + setNumericLabel(row->getColumn(3), axis_options.mOffset); + } +} + +void LLPanelPreferenceGameControl::populateMappingTableRows(LLScrollListCtrl* target, + const LLComboBox* source, size_t row_count) +{ + target->clearRows(); + + std::vector<LLScrollListItem*> items = source->getAllData(); + + LLScrollListItem::Params row_params; LLScrollListCell::Params cell_params; - cell_params.font = LLFontGL::getFontSansSerif(); - cell_params.font_halign = LLFontGL::LEFT; - cell_params.column = ""; - cell_params.value = ""; + cell_params.font = LLFontGL::getFontMonospace(); + for (size_t i = 0; i < target->getNumColumns(); ++i) + { + row_params.columns.add(cell_params); + } - // we expect the mActionTable to have at least three columns - if (mActionTable->getNumColumns() < 3) + for (size_t i = 0; i < row_count; ++i) { - LL_WARNS("Preferences") << "expected at least three columns in '" << filename << "'" << LL_ENDL; - return; + LLScrollListItem* row = target->addRow(row_params); + row->getColumn(0)->setValue(items[i]->getColumn(0)->getValue()); } - LLScrollListColumn* local_channel_column = mActionTable->getColumn(1); +} - for (LLInitParam::ParamIterator<LLScrollListItem::Params>::const_iterator row_it = contents.rows.begin(); - row_it != contents.rows.end(); - ++row_it) +void LLPanelPreferenceGameControl::populateMappingTableCells(LLScrollListCtrl* target, + const std::vector<U8>& mappings, const LLComboBox* source) +{ + std::vector<LLScrollListItem*> rows = target->getAllData(); + std::vector<LLScrollListItem*> items = source->getAllData(); + llassert(rows.size() == mappings.size()); + + for (size_t i = 0; i < rows.size(); ++i) { - std::string name = row_it->value.getValue().asString(); - if (!name.empty() && name != "menu_separator") - { - LLScrollListItem::Params item_params(*row_it); - item_params.enabled.setValue(true); - size_t num_columns = item_params.columns.size(); - // item_params should already have one column that was defined - // in XUI config file, and now we want to add two more - if (num_columns > 0) - { - LLGameControl::InputChannel channel = LLGameControl::getChannelByActionName(name); + U8 mapping = mappings[i]; + llassert(mapping < items.size()); + // Default values should look as empty cells + rows[i]->getColumn(1)->setValue(mapping == i ? LLSD() : + items[mapping]->getColumn(0)->getValue()); + } +} - cell_params.column = local_channel_column->mName; - cell_params.value = channel.getLocalName(); - item_params.columns.add(cell_params); +LLGameControl::Options& LLPanelPreferenceGameControl::getSelectedDeviceOptions() +{ + auto options_it = mDeviceOptions.find(mSelectedDeviceGUID); + llassert_always(options_it != mDeviceOptions.end()); + return options_it->second.options; +} - // TODO?: add a column with more human readable name - //cell_params.column = remote_channel_column->mName; - //cell_params.value = channel.getRemoteName(); - //item_params.columns.add(cell_params); +// static +std::string LLPanelPreferenceGameControl::getChannelLabel(const std::string& channel_name, + const std::vector<LLScrollListItem*>& items) +{ + if (!channel_name.empty() && channel_name != "NONE") + { + for (LLScrollListItem* item : items) + { + if (item->getValue().asString() == channel_name) + { + if (item->getNumColumns()) + { + return item->getColumn(0)->getValue().asString(); + } + break; } - mActionTable->addRow(item_params, EAddPosition::ADD_BOTTOM); } - else + } + return LLStringUtil::null; +} + +// static +void LLPanelPreferenceGameControl::setNumericLabel(LLScrollListCell* cell, S32 value) +{ + // Default values should look as empty cells + cell->setValue(value ? llformat("%d ", value) : LLStringUtil::null); +} + +void LLPanelPreferenceGameControl::fitInRect(LLUICtrl* ctrl, LLScrollListCtrl* grid, S32 row_index, S32 col_index) +{ + LLRect rect(grid->getCellRect(row_index, col_index)); + LLView* parent = grid->getParent(); + while (parent && parent != ctrl->getParent()) + { + rect.translate(parent->getRect().mLeft, parent->getRect().mBottom); + parent = parent->getParent(); + } + + ctrl->setRect(rect); + rect.translate(-rect.mLeft, -rect.mBottom); + for (LLView* child : *ctrl->getChildList()) + { + LLRect childRect(child->getRect()); + childRect.intersectWith(rect); + if (childRect.mRight < rect.mRight && + childRect.mRight > (rect.mLeft + rect.mRight) / 2) { - // Separator example: - // <rows - // enabled = "false"> - // <columns - // type = "icon" - // color = "0 0 0 0.7" - // halign = "center" - // value = "menu_separator" - // column = "action" / > - //</rows> - mActionTable->addRow(*row_it, EAddPosition::ADD_BOTTOM); + childRect.mRight = rect.mRight; } + child->setRect(childRect); } } void LLPanelPreferenceGameControl::clearSelectionState() { - if (gSelectedCell) - { - mChannelSelector->setVisible(FALSE); - gSelectedCell = nullptr; - } + gSelectedGrid = nullptr; gSelectedItem = nullptr; + gSelectedCell = nullptr; + mNumericValueEditor->setVisible(FALSE); + mAnalogChannelSelector->setVisible(FALSE); + mBinaryChannelSelector->setVisible(FALSE); + mAxisSelector->setVisible(FALSE); } -void LLPanelPreferenceGameControl::addTableSeparator() +void LLPanelPreferenceGameControl::addActionTableSeparator() { LLScrollListItem::Params separator_params; separator_params.enabled(false); @@ -3489,11 +3875,116 @@ void LLPanelPreferenceGameControl::addTableSeparator() mActionTable->addRow(separator_params, EAddPosition::ADD_BOTTOM); } -void LLPanelPreferenceGameControl::updateTable() +void LLPanelPreferenceGameControl::updateActionTableState() { + // Enable the table if at least one of the GameControl<-->Agent options is enabled + bool enable_table = mCheckGameControlToAgent->get() || mCheckAgentToGameControl->get(); + mActionTable->deselectAllItems(); + mActionTable->setEnabled(enable_table); + mAnalogChannelSelector->setVisible(FALSE); + mBinaryChannelSelector->setVisible(FALSE); } +void LLPanelPreferenceGameControl::onResetToDefaults() +{ + clearSelectionState(); + if (mTabChannelMappings->getVisible()) + { + resetChannelMappingsToDefaults(); + } + else if (mTabDeviceSettings->getVisible() && !mSelectedDeviceGUID.empty()) + { + if (mTabAxisOptions->getVisible()) + { + resetAxisOptionsToDefaults(); + } + else if (mTabAxisMappings->getVisible()) + { + resetAxisMappingsToDefaults(); + } + else if (mTabButtonMappings->getVisible()) + { + resetButtonMappingsToDefaults(); + } + } +} + +void LLPanelPreferenceGameControl::resetChannelMappingsToDefaults() +{ + std::vector<std::pair<std::string, LLGameControl::InputChannel>> mappings; + LLGameControl::getDefaultMappings(mappings); + std::vector<LLScrollListItem*> rows = mActionTable->getAllData(); + std::vector<LLScrollListItem*> axes = mAnalogChannelSelector->getAllData(); + std::vector<LLScrollListItem*> btns = mBinaryChannelSelector->getAllData(); + for (LLScrollListItem* row : rows) + { + if (row->getNumColumns() >= 2) // Skip separators + { + std::string action_name = row->getValue().asString(); + if (!action_name.empty() && action_name != "menu_separator") + { + std::string channel_label; + for (const auto& mapping : mappings) + { + if (mapping.first == action_name) + { + std::string channel_name = mapping.second.getLocalName(); + channel_label = + mapping.second.isAxis() ? getChannelLabel(channel_name, axes) : + mapping.second.isButton() ? getChannelLabel(channel_name, btns) : + LLStringUtil::null; + break; + } + } + row->getColumn(1)->setValue(channel_label); + } + } + } +} + +void LLPanelPreferenceGameControl::resetAxisOptionsToDefaults() +{ + std::vector<LLScrollListItem*> rows = mAxisOptions->getAllData(); + llassert(rows.size() == LLGameControl::NUM_AXES); + LLGameControl::Options& options = getSelectedDeviceOptions(); + llassert(options.getAxisOptions().size() == LLGameControl::NUM_AXES); + for (U8 i = 0; i < LLGameControl::NUM_AXES; ++i) + { + rows[i]->getColumn(1)->setValue(false); + rows[i]->getColumn(2)->setValue(LLStringUtil::null); + rows[i]->getColumn(3)->setValue(LLStringUtil::null); + options.getAxisOptions()[i].resetToDefaults(); + } +} + +void LLPanelPreferenceGameControl::resetAxisMappingsToDefaults() +{ + std::vector<LLScrollListItem*> rows = mAxisMappings->getAllData(); + llassert(rows.size() == LLGameControl::NUM_AXES); + LLGameControl::Options& options = getSelectedDeviceOptions(); + llassert(options.getAxisMap().size() == LLGameControl::NUM_AXES); + for (U8 i = 0; i < LLGameControl::NUM_AXES; ++i) + { + rows[i]->getColumn(1)->setValue(LLStringUtil::null); + options.getAxisMap()[i] = i; + } +} + +void LLPanelPreferenceGameControl::resetButtonMappingsToDefaults() +{ + std::vector<LLScrollListItem*> rows = mButtonMappings->getAllData(); + llassert(rows.size() == LLGameControl::NUM_BUTTONS); + LLGameControl::Options& options = getSelectedDeviceOptions(); + llassert(options.getButtonMap().size() == LLGameControl::NUM_BUTTONS); + for (U8 i = 0; i < LLGameControl::NUM_BUTTONS; ++i) + { + rows[i]->getColumn(1)->setValue(LLStringUtil::null); + options.getButtonMap()[i] = i; + } +} + +//------------------------LLFloaterPreferenceProxy-------------------------------- LLFloaterPreferenceProxy::LLFloaterPreferenceProxy(const LLSD& key) : LLFloater(key), @@ -3538,7 +4029,7 @@ void LLFloaterPreferenceProxy::onOpen(const LLSD& key) void LLFloaterPreferenceProxy::onClose(bool app_quitting) { - if(app_quitting) + if (app_quitting) { cancel(); } @@ -3562,7 +4053,7 @@ void LLFloaterPreferenceProxy::saveSettings() mSavedValues.clear(); std::list<LLView*> view_stack; view_stack.push_back(this); - while(!view_stack.empty()) + while (!view_stack.empty()) { // Process view on top of the stack LLView* curview = view_stack.front(); @@ -3649,13 +4140,9 @@ void LLFloaterPreferenceProxy::onClickCloseBtn(bool app_quitting) void LLFloaterPreferenceProxy::cancel() { - - for (control_values_map_t::iterator iter = mSavedValues.begin(); - iter != mSavedValues.end(); ++iter) + for (const auto& iter : mSavedValues) { - LLControlVariable* control = iter->first; - LLSD ctrl_value = iter->second; - control->set(ctrl_value); + iter.first->set(iter.second); } mSocksSettingsDirty = false; closeFloater(); @@ -3680,21 +4167,20 @@ void LLFloaterPreferenceProxy::onChangeSocksSettings() // Check for invalid states for the other HTTP proxy radio LLRadioGroup* otherHttpProxy = getChild<LLRadioGroup>("other_http_proxy_type"); if ((otherHttpProxy->getSelectedValue().asString() == "Socks" && - !getChild<LLCheckBoxCtrl>("socks_proxy_enabled")->get())||( + getChild<LLCheckBoxCtrl>("socks_proxy_enabled")->get() == false )||( otherHttpProxy->getSelectedValue().asString() == "Web" && - !getChild<LLCheckBoxCtrl>("web_proxy_enabled")->get())) + getChild<LLCheckBoxCtrl>("web_proxy_enabled")->get() == false ) ) { otherHttpProxy->selectFirstItem(); } - } void LLFloaterPreference::onUpdateFilterTerm(bool force) { - LLWString seachValue = utf8str_to_wstring(mFilterEdit->getValue()); - LLWStringUtil::toLower(seachValue); + LLWString seachValue = utf8str_to_wstring( mFilterEdit->getValue() ); + LLWStringUtil::toLower( seachValue ); - if (!mSearchData || (mSearchData->mLastFilter == seachValue && !force)) + if( !mSearchData || (mSearchData->mLastFilter == seachValue && !force)) return; if (mSearchDataDirty) @@ -3705,7 +4191,7 @@ void LLFloaterPreference::onUpdateFilterTerm(bool force) mSearchData->mLastFilter = seachValue; - if (!mSearchData->mRootTab) + if( !mSearchData->mRootTab ) return; mSearchData->mRootTab->hightlightAndHide( seachValue ); @@ -3728,10 +4214,10 @@ void LLFloaterPreference::filterIgnorableNotifications() void collectChildren( LLView const *aView, ll::prefs::PanelDataPtr aParentPanel, ll::prefs::TabContainerDataPtr aParentTabContainer ) { - if (!aView) + if( !aView ) return; - llassert_always(aParentPanel || aParentTabContainer); + llassert_always( aParentPanel || aParentTabContainer ); for (LLView* pView : *aView->getChildList()) { @@ -3741,56 +4227,56 @@ void collectChildren( LLView const *aView, ll::prefs::PanelDataPtr aParentPanel, ll::prefs::PanelDataPtr pCurPanelData = aParentPanel; ll::prefs::TabContainerDataPtr pCurTabContainer = aParentTabContainer; - LLPanel const *pPanel = dynamic_cast<LLPanel const*>(pView); - LLTabContainer const *pTabContainer = dynamic_cast<LLTabContainer const*>(pView); - ll::ui::SearchableControl const *pSCtrl = dynamic_cast<ll::ui::SearchableControl const*>( pView ); + LLPanel const *pPanel = dynamic_cast< LLPanel const *>( pView ); + LLTabContainer const *pTabContainer = dynamic_cast< LLTabContainer const *>( pView ); + ll::ui::SearchableControl const *pSCtrl = dynamic_cast< ll::ui::SearchableControl const *>( pView ); - if (pTabContainer) + if( pTabContainer ) { pCurPanelData.reset(); - pCurTabContainer = ll::prefs::TabContainerDataPtr(new ll::prefs::TabContainerData); - pCurTabContainer->mTabContainer = const_cast< LLTabContainer *>(pTabContainer); + pCurTabContainer = ll::prefs::TabContainerDataPtr( new ll::prefs::TabContainerData ); + pCurTabContainer->mTabContainer = const_cast< LLTabContainer *>( pTabContainer ); pCurTabContainer->mLabel = pTabContainer->getLabel(); pCurTabContainer->mPanel = 0; - if (aParentPanel) - aParentPanel->mChildPanel.push_back(pCurTabContainer); - if (aParentTabContainer) - aParentTabContainer->mChildPanel.push_back(pCurTabContainer); + if( aParentPanel ) + aParentPanel->mChildPanel.push_back( pCurTabContainer ); + if( aParentTabContainer ) + aParentTabContainer->mChildPanel.push_back( pCurTabContainer ); } - else if (pPanel) + else if( pPanel ) { pCurTabContainer.reset(); - pCurPanelData = ll::prefs::PanelDataPtr(new ll::prefs::PanelData); + pCurPanelData = ll::prefs::PanelDataPtr( new ll::prefs::PanelData ); pCurPanelData->mPanel = pPanel; pCurPanelData->mLabel = pPanel->getLabel(); llassert_always( aParentPanel || aParentTabContainer ); - if (aParentTabContainer) - aParentTabContainer->mChildPanel.push_back(pCurPanelData); - else if (aParentPanel) - aParentPanel->mChildPanel.push_back(pCurPanelData); + if( aParentTabContainer ) + aParentTabContainer->mChildPanel.push_back( pCurPanelData ); + else if( aParentPanel ) + aParentPanel->mChildPanel.push_back( pCurPanelData ); } - else if (pSCtrl && pSCtrl->getSearchText().size()) + else if( pSCtrl && pSCtrl->getSearchText().size() ) { - ll::prefs::SearchableItemPtr item = ll::prefs::SearchableItemPtr(new ll::prefs::SearchableItem()); + ll::prefs::SearchableItemPtr item = ll::prefs::SearchableItemPtr( new ll::prefs::SearchableItem() ); item->mView = pView; item->mCtrl = pSCtrl; - item->mLabel = utf8str_to_wstring(pSCtrl->getSearchText()); - LLWStringUtil::toLower(item->mLabel); + item->mLabel = utf8str_to_wstring( pSCtrl->getSearchText() ); + LLWStringUtil::toLower( item->mLabel ); - llassert_always(aParentPanel || aParentTabContainer); + llassert_always( aParentPanel || aParentTabContainer ); - if (aParentPanel) - aParentPanel->mChildren.push_back(item); - if (aParentTabContainer) - aParentTabContainer->mChildren.push_back(item); + if( aParentPanel ) + aParentPanel->mChildren.push_back( item ); + if( aParentTabContainer ) + aParentTabContainer->mChildren.push_back( item ); } - collectChildren(pView, pCurPanelData, pCurTabContainer); + collectChildren( pView, pCurPanelData, pCurTabContainer ); } } diff --git a/indra/newview/llfloaterpreference.h b/indra/newview/llfloaterpreference.h index 22421f296c..810bb7d6ac 100644 --- a/indra/newview/llfloaterpreference.h +++ b/indra/newview/llfloaterpreference.h @@ -36,10 +36,13 @@ #include "llfloater.h" #include "llavatarpropertiesprocessor.h" #include "llconversationlog.h" -#include "llgamecontroltranslator.h" +#include "llgamecontrol.h" +#include "llkeyconflict.h" +#include "llscrolllistcell.h" +#include "llscrolllistctrl.h" #include "llsearcheditor.h" #include "llsetkeybinddialog.h" -#include "llkeyconflict.h" +#include "llspinctrl.h" class LLConversationLogObserver; class LLPanelPreference; @@ -102,6 +105,7 @@ public: static void updateShowFavoritesCheckbox(bool val); void processProperties( void* pData, EAvatarProcessorType type ) override; + void saveAvatarProperties( void ); static void saveAvatarPropertiesCoro(const std::string url, bool allow_publish); void selectPrivacyPanel(); void selectChatPanel(); @@ -306,8 +310,6 @@ public: void setHardwareDefaults() override; void setPresetText(); - static const std::string getPresetsPath(); - protected: bool hasDirtyChilds(); @@ -372,6 +374,7 @@ private: class LLPanelPreferenceGameControl : public LLPanelPreference { + LOG_CLASS(LLPanelPreferenceGameControl); public: enum InputType @@ -384,41 +387,93 @@ public: LLPanelPreferenceGameControl(); ~LLPanelPreferenceGameControl(); - void apply() override; - void loadDefaults(); - void loadSettings(); + void onOpen(const LLSD& key) override; void saveSettings() override; - void updateEnabledState(); - void onClickGameControlToServer(LLUICtrl* ctrl); - // "Agent" in this context means either Avatar or Flycam - void onClickGameControlToAgent(LLUICtrl* ctrl); - void onClickAgentToGameControl(LLUICtrl* ctrl); - void onActionSelect(); - void onCommitInputChannel(); + void onGridSelect(LLUICtrl* ctrl); + void onCommitInputChannel(LLUICtrl* ctrl); + + void onAxisOptionsSelect(); + void onCommitNumericValue(); static bool isWaitingForInputChannel(); - static void applyGameControlInput(const LLGameControl::InputChannel& channel); + static void applyGameControlInput(); + protected: bool postBuild() override; - void populateActionTable(); - void populateColumns(); - void populateRows(const std::string& filename); + void populateActionTableRows(const std::string& filename); + void populateActionTableCells(); + static bool parseXmlFile(LLScrollListCtrl::Contents& contents, + const std::string& filename, const std::string& what); + + void populateDeviceTitle(); + void populateDeviceSettings(const std::string& guid); + void populateOptionsTableRows(); + void populateOptionsTableCells(); + void populateMappingTableRows(LLScrollListCtrl* target, + const LLComboBox* source, size_t row_count); + void populateMappingTableCells(LLScrollListCtrl* target, + const std::vector<U8>& mappings, const LLComboBox* source); + LLGameControl::Options& getSelectedDeviceOptions(); + + static std::string getChannelLabel(const std::string& channelName, + const std::vector<LLScrollListItem*>& items); + static void setNumericLabel(LLScrollListCell* cell, S32 value); + static void fitInRect(LLUICtrl* ctrl, LLScrollListCtrl* grid, S32 row_index, S32 col_index); private: + bool initCombobox(LLScrollListItem* item, LLScrollListCtrl* grid); void clearSelectionState(); - void addTableSeparator(); - void updateTable(); - LOG_CLASS(LLPanelPreferenceGameControl); - + void addActionTableSeparator(); + void updateActionTableState(); + void onResetToDefaults(); + void resetChannelMappingsToDefaults(); + void resetAxisOptionsToDefaults(); + void resetAxisMappingsToDefaults(); + void resetButtonMappingsToDefaults(); + + // Above the tab container LLCheckBoxCtrl *mCheckGameControlToServer; // send game_control data to server LLCheckBoxCtrl *mCheckGameControlToAgent; // use game_control data to move avatar LLCheckBoxCtrl *mCheckAgentToGameControl; // translate external avatar actions to game_control data + // 1st tab "Channel mappings" + LLPanel* mTabChannelMappings; LLScrollListCtrl* mActionTable; - LLComboBox* mChannelSelector; - LLGameControlTranslator mActionTranslator; + + // 2nd tab "Device settings" + LLPanel* mTabDeviceSettings; + LLTextBox* mNoDeviceMessage; + LLTextBox* mDevicePrompt; + LLTextBox* mSingleDevice; + LLComboBox* mDeviceList; + LLCheckBoxCtrl* mCheckShowAllDevices; + LLPanel* mPanelDeviceSettings; + LLPanel* mTabAxisOptions; + LLScrollListCtrl* mAxisOptions; + LLPanel* mTabAxisMappings; + LLScrollListCtrl* mAxisMappings; + LLPanel* mTabButtonMappings; + LLScrollListCtrl* mButtonMappings; + + LLButton* mResetToDefaults; + + // Numeric value editor + LLSpinCtrl* mNumericValueEditor; + + // Channel selectors + LLComboBox* mAnalogChannelSelector; + LLComboBox* mBinaryChannelSelector; + LLComboBox* mAxisSelector; + + struct DeviceOptions + { + std::string name, settings; + LLGameControl::Options options; + }; + std::map<std::string, DeviceOptions> mDeviceOptions; + std::string mSelectedDeviceGUID; }; class LLAvatarComplexityControls diff --git a/indra/newview/skins/default/xui/en/game_control_table_columns.xml b/indra/newview/skins/default/xui/en/game_control_table_columns.xml deleted file mode 100644 index f88fc8305c..0000000000 --- a/indra/newview/skins/default/xui/en/game_control_table_columns.xml +++ /dev/null @@ -1,15 +0,0 @@ -<?xml version="1.0" encoding="utf-8" standalone="yes" ?> -<contents> - <columns - relative_width="0.25" - label="Action" - name="action" /> - <columns - relative_width="0.25" - label="GameControl" - name="index" /> - <columns - relative_width="0.50" - label=" " - name="foo" /> -</contents> diff --git a/indra/newview/skins/default/xui/en/game_control_table_rows.xml b/indra/newview/skins/default/xui/en/game_control_table_rows.xml index b09346e83e..181bc8592c 100644 --- a/indra/newview/skins/default/xui/en/game_control_table_rows.xml +++ b/indra/newview/skins/default/xui/en/game_control_table_rows.xml @@ -63,7 +63,6 @@ name="action" value="Look Down" /> </rows> - <rows name="stop" value="stop"> diff --git a/indra/newview/skins/default/xui/en/panel_preferences_game_control.xml b/indra/newview/skins/default/xui/en/panel_preferences_game_control.xml index 074e27215d..1815d98ae0 100644 --- a/indra/newview/skins/default/xui/en/panel_preferences_game_control.xml +++ b/indra/newview/skins/default/xui/en/panel_preferences_game_control.xml @@ -10,186 +10,384 @@ top="1" width="517"> <check_box + name="game_control_to_server" control_name="GameControlToServer" - follows="top|left" - height="15" label="Send GameControl Data to server" layout="topleft" + height="15" left="10" - name="game_control_to_server" - top="6" - width="232"/> + top="10"/> <check_box + name="game_control_to_agent" control_name="GameControlToAgent" - follows="top|left" - height="15" label="GameControl moves avatar and flycam" layout="topleft" + height="15" left="10" - name="game_control_to_agent" - top="27" - width="232"/> + top="30"/> <check_box + name="agent_to_game_control" control_name="AgentToGameControl" - follows="top|left" - height="15" label="Avatar actions interpreted as GameControl" layout="topleft" + height="15" left="10" - name="agent_to_game_control" - top="48" - width="232"/> - <scroll_list - draw_heading="true" + top="50"/> + <tab_container + name="game_control_tabs" + layout="topleft" follows="all" + top="70" + left="2" + right="-2" + bottom="-32"> + <panel + name="tab_channel_mappings" + label="Channel mappings"> + <scroll_list + name="action_table" + layout="topleft" + follows="all" + top="1" + left="6" + right="-5" + bottom="-4" + can_sort="false" + column_padding="0" + multi_select="false" + selection_type="header" + fg_disable_color="ScrollUnselectedColor"> + <scroll_list.columns + name="action" + label="Action" + relative_width="0.25" /> + <scroll_list.columns + label="Channel" + relative_width="0.75" /> + </scroll_list> + </panel> + <panel + name="tab_device_settings" + label="Device settings"> + <text + type="string" + name="nodevice_message" + layout="topleft" + follows="all" + halign="center" + valign="center" + top="1" + left="1" + right="-1" + bottom="-1">No device</text> + <panel + layout="topleft" + follows="top|left|right" + top="1" + left="1" + right="-1" + height="60"> + <text + type="string" + name="device_prompt" + layout="topleft" + follows="top|left" + valign="center" + top="10" + left="2" + height="21" + width="200">Device to customize:</text> + <text + type="string" + name="single_device" + layout="topleft" + follows="top|left" + valign="center" + top="30" + left="2" + height="21" + width="500"/> + <combo_box + name="device_list" + layout="topleft" + follows="top|left" + top="30" + left="2" + width="500"/> + </panel> + <!-- This checkbox should be placed inside the above panel --> + <check_box + name="show_all_known_devices" + label="Show all known devices" + layout="topleft" + follows="top|left" + top_delta="10" + left="310"/> + <panel + name="device_settings" + layout="topleft" + follows="all" + top="61" + left="1" + right="-1" + bottom="-1"> + <tab_container + name="device_settings_tabs" + layout="topleft" + follows="all" + top="1" + left="1" + right="-1" + bottom="-1"> + <panel + name="tab_axis_options" + label="Axis Options"> + <scroll_list + name="axis_options" + follows="all" + top="41" + left="1" + right="-1" + bottom="-1" + can_sort="false" + column_padding="0" + draw_heading="true" + multi_select="false" + selection_type="header" + fg_disable_color="ScrollUnselectedColor"> + <scroll_list.columns + label="Axis" + relative_width="0.5"/> + <scroll_list.columns + label="Invert" + relative_width="0.1"/> + <scroll_list.columns + label="Dead Zone" + relative_width="0.2"/> + <scroll_list.columns + label="Offset" + relative_width="0.2"/> + </scroll_list> + </panel> + <panel + name="tab_axis_mappings" + label="Axis Mappings"> + <scroll_list + name="axis_mappings" + follows="all" + top="1" + left="1" + right="-1" + bottom="-1" + can_sort="false" + column_padding="0" + multi_select="false" + selection_type="header" + fg_disable_color="ScrollUnselectedColor"> + <scroll_list.columns relative_width="0.5"/> + <scroll_list.columns relative_width="0.5"/> + </scroll_list> + </panel> + <panel + name="tab_button_mappings" + label="Button Mappings"> + <scroll_list + name="button_mappings" + follows="all" + top="1" + left="1" + right="-1" + bottom="-1" + can_sort="false" + column_padding="0" + multi_select="false" + selection_type="header" + fg_disable_color="ScrollUnselectedColor"> + <scroll_list.columns relative_width="0.5"/> + <scroll_list.columns relative_width="0.5"/> + </scroll_list> + </panel> + </tab_container> + </panel> + </panel> + </tab_container> + <panel layout="topleft" - column_padding="0" - selection_type="header" - top="66" - left="3" - bottom="-3" - right="-3" - can_sort="false" - multi_select="false" - name="action_table" - fg_disable_color="ScrollUnselectedColor"/> + follows="top|left" + top="405" + height="41" + left="1" + right="-1"> + <button + layout="topleft" + follows="top|left" + name="reset_to_defaults" + label="Reset to Defaults" + top="1" + left="8" + width="200"/> + </panel> + <spinner + name="numeric_value_editor" + decimal_digits="0" + increment="1" + width="100" + height="18"/> <combo_box - height="23" - layout="topleft" - left="10" - name="input_channel_combo" - top_pad="5" - width="90"> + name="axis_selector" + width="250" + height="18"> + <combo_box.item label="AXIS_0 (Left stick: tilt left/right)"/> + <combo_box.item label="AXIS_1 (Left stick: tilt forward/back)"/> + <combo_box.item label="AXIS_2 (Right stick: tilt left/right)"/> + <combo_box.item label="AXIS_3 (Right stick: tilt forward/back)"/> + <combo_box.item label="AXIS_4 (Left trigger: push/release)"/> + <combo_box.item label="AXIS_5 (Right trigger: push/release)"/> + <combo_box.item label="NONE (No mapping)"/> + </combo_box> + <combo_box + name="analog_channel_selector" + width="380" + height="18"> <combo_box.item - label="AXIS_0-" + label="AXIS_0- (Left stick: tilt right)" name="AXIS_0-" value="AXIS_0-"/> <combo_box.item - label="AXIS_0+" + label="AXIS_0+ (Left stick: tilt left)" name="AXIS_0+" value="AXIS_0+"/> <combo_box.item - label="AXIS_1-" + label="AXIS_1- (Left stick: tilt back)" name="AXIS_1-" value="AXIS_1-"/> <combo_box.item - label="AXIS_1+" + label="AXIS_1+ (Left stick: tilt forward)" name="AXIS_1+" value="AXIS_1+"/> <combo_box.item - label="AXIS_2-" + label="AXIS_2- (Right stick: tilt right)" name="AXIS_2-" value="AXIS_2-"/> <combo_box.item - label="AXIS_2+" + label="AXIS_2+ (Right stick: tilt left)" name="AXIS_2+" value="AXIS_2+"/> <combo_box.item - label="AXIS_3-" + label="AXIS_3- (Right stick: tilt back)" name="AXIS_3-" value="AXIS_3-"/> <combo_box.item - label="AXIS_3+" + label="AXIS_3+ (Right stick: tilt forward)" name="AXIS_3+" value="AXIS_3+"/> <combo_box.item - label="AXIS_4-" + label="AXIS_4- (Left trigger: release)" name="AXIS_4-" value="AXIS_4-"/> <combo_box.item - label="AXIS_4+" + label="AXIS_4+ (Left trigger: push)" name="AXIS_4+" value="AXIS_4+"/> <combo_box.item - label="AXIS_5-" + label="AXIS_5- (Right trigger: release)" name="AXIS_5-" value="AXIS_5-"/> <combo_box.item - label="AXIS_5+" + label="AXIS_5+ (Right trigger: push)" name="AXIS_5+" value="AXIS_5+"/> <combo_box.item - label="BUTTON_0" + label="NONE (No mapping)" + name="NONE" + value="NONE"/> + </combo_box> + <combo_box + name="binary_channel_selector" + width="380" + height="18"> + <combo_box.item + label="BUTTON_0 ('A' button)" name="BUTTON_0" value="BUTTON_0"/> <combo_box.item - label="BUTTON_1" + label="BUTTON_1 ('B' button)" name="BUTTON_1" value="BUTTON_1"/> <combo_box.item - label="BUTTON_2" + label="BUTTON_2 ('X' button)" name="BUTTON_2" value="BUTTON_2"/> <combo_box.item - label="BUTTON_3" + label="BUTTON_3 ('Y' button)" name="BUTTON_3" value="BUTTON_3"/> <combo_box.item - label="BUTTON_4" + label="BUTTON_4 (Back button)" name="BUTTON_4" value="BUTTON_4"/> <combo_box.item - label="BUTTON_5" + label="BUTTON_5 (Guide button)" name="BUTTON_5" value="BUTTON_5"/> <combo_box.item - label="BUTTON_6" + label="BUTTON_6 (Home button)" name="BUTTON_6" value="BUTTON_6"/> <combo_box.item - label="BUTTON_7" + label="BUTTON_7 (Left stick)" name="BUTTON_7" value="BUTTON_7"/> <combo_box.item - label="BUTTON_8" + label="BUTTON_8 (Right stick)" name="BUTTON_8" value="BUTTON_8"/> <combo_box.item - label="BUTTON_9" + label="BUTTON_9 (Left shoulder)" name="BUTTON_9" value="BUTTON_9"/> <combo_box.item - label="BUTTON_10" + label="BUTTON_10 (Right shoulder)" name="BUTTON_10" value="BUTTON_10"/> <combo_box.item - label="BUTTON_11" + label="BUTTON_11 (DPad up)" name="BUTTON_11" value="BUTTON_11"/> <combo_box.item - label="BUTTON_12" + label="BUTTON_12 (DPad down)" name="BUTTON_12" value="BUTTON_12"/> <combo_box.item - label="BUTTON_13" + label="BUTTON_13 (DPad left)" name="BUTTON_13" value="BUTTON_13"/> <combo_box.item - label="BUTTON_14" + label="BUTTON_14 (DPad right)" name="BUTTON_14" value="BUTTON_14"/> <combo_box.item - label="BUTTON_15" + label="BUTTON_15 (Miscellaneous 1)" name="BUTTON_15" value="BUTTON_15"/> <combo_box.item - label="BUTTON_16" + label="BUTTON_16 (Paddle 1)" name="BUTTON_16" value="BUTTON_16"/> <combo_box.item - label="BUTTON_17" + label="BUTTON_17 (Paddle 2)" name="BUTTON_17" value="BUTTON_17"/> <combo_box.item - label="BUTTON_18" + label="BUTTON_18 (Paddle 3)" name="BUTTON_18" value="BUTTON_18"/> <combo_box.item - label="BUTTON_19" + label="BUTTON_19 (Paddle 4)" name="BUTTON_19" value="BUTTON_19"/> <combo_box.item - label="BUTTON_20" + label="BUTTON_20 (Touchpad)" name="BUTTON_20" value="BUTTON_20"/> <combo_box.item @@ -237,7 +435,7 @@ name="BUTTON_31" value="BUTTON_31"/> <combo_box.item - label="NONE" + label="NONE (No mapping)" name="NONE" value="NONE"/> </combo_box> |