From 2daf175650cdda7cc8f820b6cb17b1475496e7ac Mon Sep 17 00:00:00 2001 From: Alexander Gavriliuk Date: Wed, 8 May 2024 23:32:58 +0200 Subject: Add GameControl UI for per device settings --- indra/newview/app_settings/settings.xml | 54 +- indra/newview/llappviewer.cpp | 61 +- indra/newview/llfloaterpreference.cpp | 1178 ++++++++++++++------ indra/newview/llfloaterpreference.h | 103 +- .../default/xui/en/game_control_table_columns.xml | 15 - .../default/xui/en/game_control_table_rows.xml | 1 - .../xui/en/panel_preferences_game_control.xml | 332 ++++-- 7 files changed, 1235 insertions(+), 509 deletions(-) delete mode 100644 indra/newview/skins/default/xui/en/game_control_table_columns.xml (limited to 'indra/newview') 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 @@ AFKTimeout Comment - - Time before automatically setting AFK (away from keyboard) mode (seconds, 0=never). - Valid values are: 0, 120, 300, 600, 1800 - + + Time before automatically setting AFK (away from keyboard) mode (seconds, 0=never). + Valid values are: 0, 120, 300, 600, 1800 + Persist 1 Type @@ -379,7 +379,7 @@ Value 0 - AutoAcceptNewInventory + AutoAcceptNewInventory Comment Automatically accept new notecards/textures/landmarks @@ -2645,6 +2645,50 @@ Value 0 + AnalogChannelMappings + + Comment + GameControl analog button to channel mappings + Persist + 1 + Type + String + Value + push:AXIS_1+,slide:AXIS_0+,jump:AXIS_4+,turn:AXIS_2+,look:AXIS_3+ + + BinaryChannelMappings + + Comment + GameControl binary button to channel mappings + Persist + 1 + Type + String + Value + toggle_run:BUTTON_9,toggle_fly:BUTTON_11,toggle_flycam:BUTTON_10,stop:BUTTON_7 + + FlycamChannelMappings + + Comment + GameControl flycam button to channel mappings + Persist + 1 + Type + String + Value + advance:AXIS_1+,pan:AXIS_0+,rise:AXIS_5+,pitch:AXIS_3-,yaw:AXIS_2+ + + KnownGameControllers + + Comment + Map of controller device-specific options. + Persist + 1 + Type + LLSD + Value + + EnableGestureSounds Comment 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::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("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(view); - if (panel) + if (LLPanelPreference* panel = dynamic_cast(view)) + { panel->saveSettings(); + } } saveIgnoredNotifications(); } void LLFloaterPreference::apply() { - LLAvatarPropertiesProcessor::getInstance()->addObserver( gAgent.getID(), this ); + LLAvatarPropertiesProcessor::getInstance()->addObserver(gAgent.getID(), this); LLTabContainer* tabcontainer = getChild("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(view); - if (panel) + if (LLPanelPreference* panel = dynamic_cast(view)) + { panel->apply(); + } } gViewerWindow->requestResolutionUpdate(); // for UIScaleFactor @@ -606,7 +604,7 @@ void LLFloaterPreference::apply() LLViewerMedia::getInstance()->setCookiesEnabled(getChild("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("web_proxy_enabled")->getValue(); std::string proxy_address = getChild("web_proxy_editor")->getValue(); @@ -643,13 +641,12 @@ void LLFloaterPreference::cancel(const std::vector settings_to_skip { LLTabContainer* tabcontainer = getChild("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(view); - if (panel) - panel->cancel(settings_to_skip); + if (LLPanelPreference* panel = dynamic_cast(view)) + { + panel->cancel(); + } } // hide joystick pref floater LLFloaterReg::hideInstance("pref_joystick"); @@ -678,7 +675,7 @@ void LLFloaterPreference::cancel(const std::vector settings_to_skip } //Need to reload the navmesh if the pathing console is up LLHandle 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 settings_to_skip void LLFloaterPreference::onOpen(const LLSD& key) { + LLTabContainer* tabcontainer = getChild("pref core"); + for (LLView* view : *tabcontainer->getChildList()) + { + if (LLPanelPreference* panel = dynamic_cast(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("preferences"); - if (instance) + if (LLFloaterPreference* instance = LLFloaterReg::findTypedInstance("preferences")) { instance->setPersonalInfo(visibility); } @@ -1054,14 +1059,12 @@ void LLFloaterPreference::updateUserInfo(const std::string& visibility) void LLFloaterPreference::refreshEnabledGraphics() { - LLFloaterPreference* instance = LLFloaterReg::findTypedInstance("preferences"); - if (instance) + if (LLFloaterPreference* instance = LLFloaterReg::findTypedInstance("preferences")) { instance->refresh(); } - LLFloater* advanced = LLFloaterReg::findTypedInstance("prefs_graphics_advanced"); - if (advanced) + if (LLFloater* advanced = LLFloaterReg::findTypedInstance("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("background"); - if (color_swatch) + if (LLColorSwatchCtrl* color_swatch = findChild("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("IndirectMaxNonImpostorsText", true)); @@ -1298,8 +1300,7 @@ void LLFloaterPreference::refresh() gSavedSettings.getU32("RenderAvatarMaxComplexity"), getChild("IndirectMaxComplexityText", true)); refreshEnabledState(); - LLFloater* advanced = LLFloaterReg::findTypedInstance("prefs_graphics_advanced"); - if (advanced) + if (LLFloater* advanced = LLFloaterReg::findTypedInstance("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("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("UseLightShaders"); - if(ctrl_alm) + if (LLCheckBoxCtrl* ctrl_alm = getChild("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("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(view); - if (panel) + if (LLPanelPreferenceControls* panel = dynamic_cast(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("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(view); - if (panel) + if (LLPanelPreferenceControls* panel = dynamic_cast(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("media_enabled")->set(media_enabled); getChild("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("prefs_graphics_advanced"); - // Save the value of all controls in the hierarchy mSavedValues.clear(); std::list view_stack; view_stack.push_back(this); - if (advanced) + // Search for 'Advanced' panel and add it if found + if (LLFloater* advanced = LLFloaterReg::findTypedInstance("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(curview); - if (color_swatch) + if (LLColorSwatchCtrl* color_swatch = dynamic_cast(curview)) { mSavedColors[color_swatch->getName()] = color_swatch->get(); } else { - LLUICtrl* ctrl = dynamic_cast(curview); - if (ctrl) + if (LLUICtrl* ctrl = dynamic_cast(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 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 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::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" / > // - 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 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 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(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(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(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 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(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& 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(value, 0, LLGameControl::MAX_AXIS_DEAD_ZONE); + deviceOptions.getAxisOptions()[row_index].mDeadZone = (U16)value; + } + else // column_index == 3 + { + value = std::clamp(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("game_control_to_server"); - mCheckGameControlToServer->setCommitCallback(boost::bind(&LLPanelPreferenceGameControl::onClickGameControlToServer, this, _1)); - //mCheckGameControlToServer->setEnabled(gSavedSettings.getBOOL( "GameControlToServer")); - mCheckGameControlToAgent = getChild("game_control_to_agent"); - mCheckGameControlToAgent->setCommitCallback(boost::bind(&LLPanelPreferenceGameControl::onClickGameControlToAgent, this, _1)); - //mCheckGameControlToAgent->setEnabled(gSavedSettings.getBOOL( "GameControlToAgent")); + mCheckAgentToGameControl = getChild("agent_to_game_control"); + + mCheckGameControlToAgent->setCommitCallback([this](LLUICtrl*, const LLSD&) { updateActionTableState(); }); + mCheckAgentToGameControl->setCommitCallback([this](LLUICtrl*, const LLSD&) { updateActionTableState(); }); - mCheckAgentToGameControl= getChild("agent_to_game_control"); - mCheckAgentToGameControl->setCommitCallback(boost::bind(&LLPanelPreferenceGameControl::onClickAgentToGameControl, this, _1)); - //mCheckAgentToGameControl->setEnabled(gSavedSettings.getBOOL( "AgentToGameControl")); + getChild("game_control_tabs")->setCommitCallback([this](LLUICtrl*, const LLSD&) { clearSelectionState(); }); + getChild("device_settings_tabs")->setCommitCallback([this](LLUICtrl*, const LLSD&) { clearSelectionState(); }); + // 1st tab "Channel mappings" + mTabChannelMappings = getChild("tab_channel_mappings"); mActionTable = getChild("action_table"); - mActionTable->setCommitCallback(boost::bind(&LLPanelPreferenceGameControl::onActionSelect, this)); + mActionTable->setCommitCallback([this](LLUICtrl* ctrl, const LLSD&) { onGridSelect(ctrl); }); + + // 2nd tab "Device settings" + mTabDeviceSettings = getChild("tab_device_settings"); + mNoDeviceMessage = getChild("nodevice_message"); + mDevicePrompt = getChild("device_prompt"); + mSingleDevice = getChild("single_device"); + mDeviceList = getChild("device_list"); + mCheckShowAllDevices = getChild("show_all_known_devices"); + mPanelDeviceSettings = getChild("device_settings"); + + mCheckShowAllDevices->setCommitCallback([this](LLUICtrl*, const LLSD&) { populateDeviceTitle(); }); + mDeviceList->setCommitCallback([this](LLUICtrl*, const LLSD& value) { populateDeviceSettings(value); }); + + mTabAxisOptions = getChild("tab_axis_options"); + mAxisOptions = getChild("axis_options"); + mAxisOptions->setCommitCallback([this](LLUICtrl*, const LLSD&) { onAxisOptionsSelect(); }); + + mTabAxisMappings = getChild("tab_axis_mappings"); + mAxisMappings = getChild("axis_mappings"); + mAxisMappings->setCommitCallback([this](LLUICtrl* ctrl, const LLSD&) { onGridSelect(ctrl); }); + + mTabButtonMappings = getChild("tab_button_mappings"); + mButtonMappings = getChild("button_mappings"); + mButtonMappings->setCommitCallback([this](LLUICtrl* ctrl, const LLSD&) { onGridSelect(ctrl); }); + + mResetToDefaults = getChild("reset_to_defaults"); + mResetToDefaults->setCommitCallback([this](LLUICtrl* ctrl, const LLSD&) { onResetToDefaults(); }); + + // Numeric value editor + mNumericValueEditor = getChild("numeric_value_editor"); + mNumericValueEditor->setCommitCallback([this](LLUICtrl*, const LLSD&) { onCommitNumericValue(); }); + + // Channel selectors + mAnalogChannelSelector = getChild("analog_channel_selector"); + mAnalogChannelSelector->setCommitCallback([this](LLUICtrl* ctrl, const LLSD&) { onCommitInputChannel(ctrl); }); + + mBinaryChannelSelector = getChild("binary_channel_selector"); + mBinaryChannelSelector->setCommitCallback([this](LLUICtrl* ctrl, const LLSD&) { onCommitInputChannel(ctrl); }); + + mAxisSelector = getChild("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("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 rows = mActionTable->getAllData(); + std::vector axes = mAnalogChannelSelector->getAllData(); + std::vector 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::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& 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 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 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 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::const_iterator row_it = contents.rows.begin(); - row_it != contents.rows.end(); - ++row_it) +void LLPanelPreferenceGameControl::populateMappingTableCells(LLScrollListCtrl* target, + const std::vector& mappings, const LLComboBox* source) +{ + std::vector rows = target->getAllData(); + std::vector 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& 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: - // - // - // - 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> mappings; + LLGameControl::getDefaultMappings(mappings); + std::vector rows = mActionTable->getAllData(); + std::vector axes = mAnalogChannelSelector->getAllData(); + std::vector 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 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 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 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 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("other_http_proxy_type"); if ((otherHttpProxy->getSelectedValue().asString() == "Socks" && - !getChild("socks_proxy_enabled")->get())||( + getChild("socks_proxy_enabled")->get() == false )||( otherHttpProxy->getSelectedValue().asString() == "Web" && - !getChild("web_proxy_enabled")->get())) + getChild("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(pView); - LLTabContainer const *pTabContainer = dynamic_cast(pView); - ll::ui::SearchableControl const *pSCtrl = dynamic_cast( 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& mappings, const LLComboBox* source); + LLGameControl::Options& getSelectedDeviceOptions(); + + static std::string getChannelLabel(const std::string& channelName, + const std::vector& 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 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 @@ - - - - - - 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" /> - 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"> + top="10"/> + top="30"/> - + + + + + + + + + No device + + Device to customize: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + follows="top|left" + top="405" + height="41" + left="1" + right="-1"> +