summaryrefslogtreecommitdiff
path: root/indra/newview
diff options
context:
space:
mode:
authorAlexander Gavriliuk <alexandrgproductengine@lindenlab.com>2024-05-08 23:32:58 +0200
committerAndrew Meadows <andrew.l.meadows@gmail.com>2024-10-03 09:02:09 -0700
commit2daf175650cdda7cc8f820b6cb17b1475496e7ac (patch)
tree0ece9bb592a922fbcb3f4532aee10941e307f44f /indra/newview
parentec39ac89e8529da206dafd519d75ad5944888076 (diff)
Add GameControl UI for per device settings
Diffstat (limited to 'indra/newview')
-rw-r--r--indra/newview/app_settings/settings.xml54
-rw-r--r--indra/newview/llappviewer.cpp61
-rw-r--r--indra/newview/llfloaterpreference.cpp1178
-rw-r--r--indra/newview/llfloaterpreference.h103
-rw-r--r--indra/newview/skins/default/xui/en/game_control_table_columns.xml15
-rw-r--r--indra/newview/skins/default/xui/en/game_control_table_rows.xml1
-rw-r--r--indra/newview/skins/default/xui/en/panel_preferences_game_control.xml332
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>