/** * @file llfloaterpreference.cpp * @brief Global preferences with and without persistence. * * $LicenseInfo:firstyear=2002&license=viewerlgpl$ * Second Life Viewer Source Code * Copyright (C) 2010, Linden Research, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License only. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA * $/LicenseInfo$ */ /* * App-wide preferences. Note that these are not per-user, * because we need to load many preferences before we have * a login name. */ #include "llviewerprecompiledheaders.h" #include "llfloaterpreference.h" #include "message.h" #include "llfloaterautoreplacesettings.h" #include "llagent.h" #include "llagentcamera.h" #include "llcheckboxctrl.h" #include "llcolorswatch.h" #include "llcombobox.h" #include "llcommandhandler.h" #include "lldirpicker.h" #include "lleventtimer.h" #include "llfeaturemanager.h" #include "llfocusmgr.h" //#include "llfirstuse.h" #include "llfloaterreg.h" #include "llfloaterabout.h" #include "llfavoritesbar.h" #include "llfloaterpreferencesgraphicsadvanced.h" #include "llfloaterperformance.h" #include "llfloatersidepanelcontainer.h" #include "llfloaterimsession.h" #include "llgamecontrol.h" #include "llkeyboard.h" #include "llmodaldialog.h" #include "llnavigationbar.h" #include "llfloaterimnearbychat.h" #include "llnotifications.h" #include "llnotificationsutil.h" #include "llnotificationtemplate.h" #include "llpanellogin.h" #include "llpanelvoicedevicesettings.h" #include "llradiogroup.h" #include "llsearchcombobox.h" #include "llsky.h" #include "llscrolllistctrl.h" #include "llscrolllistitem.h" #include "llsliderctrl.h" #include "lltabcontainer.h" #include "lltrans.h" #include "llviewercontrol.h" #include "llviewercamera.h" #include "llviewereventrecorder.h" #include "llviewermessage.h" #include "llviewerwindow.h" #include "llviewerthrottle.h" #include "llvoavatarself.h" #include "llvotree.h" #include "llfloaterpathfindingconsole.h" // linden library includes #include "llavatarnamecache.h" #include "llerror.h" #include "llfontgl.h" #include "llrect.h" #include "llstring.h" // project includes #include "llbutton.h" #include "llflexibleobject.h" #include "lllineeditor.h" #include "llresmgr.h" #include "llspinctrl.h" #include "llstartup.h" #include "lltextbox.h" #include "llui.h" #include "llviewerobjectlist.h" #include "llvovolume.h" #include "llwindow.h" #include "llworld.h" #include "lluictrlfactory.h" #include "llviewermedia.h" #include "llpluginclassmedia.h" #include "llteleporthistorystorage.h" #include "llproxy.h" #include "llweb.h" #include "lllogininstance.h" // to check if logged in yet #include "llsdserialize.h" #include "llpresetsmanager.h" #include "llviewercontrol.h" #include "llpresetsmanager.h" #include "llinventoryfunctions.h" #include "llsearchableui.h" #include "llperfstats.h" const F32 BANDWIDTH_UPDATER_TIMEOUT = 0.5f; char const* const VISIBILITY_DEFAULT = "default"; char const* const VISIBILITY_HIDDEN = "hidden"; //control value for middle mouse as talk2push button const static std::string MIDDLE_MOUSE_CV = "MiddleMouse"; // for voice client and redability const static std::string MOUSE_BUTTON_4_CV = "MouseButton4"; const static std::string MOUSE_BUTTON_5_CV = "MouseButton5"; /// This must equal the maximum value set for the IndirectMaxComplexity slider in panel_preferences_graphics1.xml static const U32 INDIRECT_MAX_ARC_OFF = 101; // all the way to the right == disabled static const U32 MIN_INDIRECT_ARC_LIMIT = 1; // must match minimum of IndirectMaxComplexity in panel_preferences_graphics1.xml static const U32 MAX_INDIRECT_ARC_LIMIT = INDIRECT_MAX_ARC_OFF-1; // one short of all the way to the right... /// These are the effective range of values for RenderAvatarMaxComplexity static const F32 MIN_ARC_LIMIT = 20000.0f; static const F32 MAX_ARC_LIMIT = 350000.0f; static const F32 MIN_ARC_LOG = log(MIN_ARC_LIMIT); static const F32 MAX_ARC_LOG = log(MAX_ARC_LIMIT); static const F32 ARC_LIMIT_MAP_SCALE = (MAX_ARC_LOG - MIN_ARC_LOG) / (MAX_INDIRECT_ARC_LIMIT - MIN_INDIRECT_ARC_LIMIT); struct LabelDef : public LLInitParam::Block { Mandatory name; Mandatory value; LabelDef() : name("name"), value("value") {} }; struct LabelTable : public LLInitParam::Block { Multiple labels; LabelTable() : labels("label") {} }; // global functions // helper functions for getting/freeing the web browser media // if creating/destroying these is too slow, we'll need to create // a static member and update all our static callbacks void handleNameTagOptionChanged(const LLSD& newvalue); void handleDisplayNamesOptionChanged(const LLSD& newvalue); bool callback_clear_browser_cache(const LLSD& notification, const LLSD& response); bool callback_clear_cache(const LLSD& notification, const LLSD& response); //bool callback_skip_dialogs(const LLSD& notification, const LLSD& response, LLFloaterPreference* floater); //bool callback_reset_dialogs(const LLSD& notification, const LLSD& response, LLFloaterPreference* floater); void fractionFromDecimal(F32 decimal_val, S32& numerator, S32& denominator); bool callback_clear_cache(const LLSD& notification, const LLSD& response) { S32 option = LLNotificationsUtil::getSelectedOption(notification, response); if ( option == 0 ) // YES { // flag client texture cache for clearing next time the client runs gSavedSettings.setBOOL("PurgeCacheOnNextStartup", true); LLNotificationsUtil::add("CacheWillClear"); } return false; } bool callback_clear_browser_cache(const LLSD& notification, const LLSD& response) { S32 option = LLNotificationsUtil::getSelectedOption(notification, response); if ( option == 0 ) // YES { // clean web LLViewerMedia::getInstance()->clearAllCaches(); LLViewerMedia::getInstance()->clearAllCookies(); // clean nav bar history LLNavigationBar::getInstance()->clearHistoryCache(); // flag client texture cache for clearing next time the client runs gSavedSettings.setBOOL("PurgeCacheOnNextStartup", true); LLNotificationsUtil::add("CacheWillClear"); LLSearchHistory::getInstance()->clearHistory(); LLSearchHistory::getInstance()->save(); LLSearchComboBox* search_ctrl = LLNavigationBar::getInstance()->getChild("search_combo_box"); search_ctrl->clearHistory(); LLTeleportHistoryStorage::getInstance()->purgeItems(); LLTeleportHistoryStorage::getInstance()->save(); } return false; } void handleNameTagOptionChanged(const LLSD& newvalue) { LLAvatarNameCache::getInstance()->setUseUsernames(gSavedSettings.getBOOL("NameTagShowUsernames")); LLVOAvatar::invalidateNameTags(); } void handleDisplayNamesOptionChanged(const LLSD& newvalue) { LLAvatarNameCache::getInstance()->setUseDisplayNames(newvalue.asBoolean()); LLVOAvatar::invalidateNameTags(); } void handleAppearanceCameraMovementChanged(const LLSD& newvalue) { if(!newvalue.asBoolean() && gAgentCamera.getCameraMode() == CAMERA_MODE_CUSTOMIZE_AVATAR) { gAgentCamera.changeCameraToDefault(); gAgentCamera.resetView(); } } void fractionFromDecimal(F32 decimal_val, S32& numerator, S32& denominator) { numerator = 0; denominator = 0; for (F32 test_denominator = 1.f; test_denominator < 30.f; test_denominator += 1.f) { if (fmodf((decimal_val * test_denominator) + 0.01f, 1.f) < 0.02f) { numerator = ll_round(decimal_val * test_denominator); denominator = ll_round(test_denominator); break; } } } // handle secondlife:///app/worldmap/{NAME}/{COORDS} URLs // Also see LLUrlEntryKeybinding, the value of this command type // is ability to show up to date value in chat class LLKeybindingHandler: public LLCommandHandler { public: // requires trusted browser to trigger LLKeybindingHandler(): LLCommandHandler("keybinding", UNTRUSTED_CLICK_ONLY) { } bool handle(const LLSD& params, const LLSD& query_map, const std::string& grid, LLMediaCtrl* web) { if (params.size() < 1) return false; LLFloaterPreference* prefsfloater = dynamic_cast (LLFloaterReg::showInstance("preferences")); if (prefsfloater) { // find 'controls' panel and bring it the front LLTabContainer* tabcontainer = prefsfloater->getChild("pref core"); LLPanel* panel = prefsfloater->getChild("controls"); if (tabcontainer && panel) { tabcontainer->selectTabPanel(panel); } } return true; } }; LLKeybindingHandler gKeybindHandler; ////////////////////////////////////////////// // LLFloaterPreference // static std::string LLFloaterPreference::sSkin = ""; LLFloaterPreference::LLFloaterPreference(const LLSD& key) : LLFloater(key), mGotPersonalInfo(false), mLanguageChanged(false), mAvatarDataInitialized(false), mSearchDataDirty(true) { LLConversationLog::instance().addObserver(this); //Build Floater is now Called from LLFloaterReg::add("preferences", "floater_preferences.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); static bool registered_dialog = false; if (!registered_dialog) { LLFloaterReg::add("keybind_dialog", "floater_select_key.xml", (LLFloaterBuildFunc)&LLFloaterReg::build); registered_dialog = true; } mCommitCallbackRegistrar.add("Pref.Cancel", { boost::bind(&LLFloaterPreference::onBtnCancel, this, _2), cb_info::UNTRUSTED_BLOCK }); mCommitCallbackRegistrar.add("Pref.OK", { boost::bind(&LLFloaterPreference::onBtnOK, this, _2), cb_info::UNTRUSTED_BLOCK }); mCommitCallbackRegistrar.add("Pref.ClearCache", { boost::bind(&LLFloaterPreference::onClickClearCache, this), cb_info::UNTRUSTED_BLOCK }); mCommitCallbackRegistrar.add("Pref.WebClearCache", { boost::bind(&LLFloaterPreference::onClickBrowserClearCache, this), cb_info::UNTRUSTED_BLOCK }); mCommitCallbackRegistrar.add("Pref.SetCache", { boost::bind(&LLFloaterPreference::onClickSetCache, this), cb_info::UNTRUSTED_BLOCK }); mCommitCallbackRegistrar.add("Pref.ResetCache", { boost::bind(&LLFloaterPreference::onClickResetCache, this), cb_info::UNTRUSTED_BLOCK }); mCommitCallbackRegistrar.add("Pref.ClickSkin", { boost::bind(&LLFloaterPreference::onClickSkin, this,_1, _2), cb_info::UNTRUSTED_BLOCK }); mCommitCallbackRegistrar.add("Pref.SelectSkin", { boost::bind(&LLFloaterPreference::onSelectSkin, this), cb_info::UNTRUSTED_BLOCK }); mCommitCallbackRegistrar.add("Pref.SetSounds", { boost::bind(&LLFloaterPreference::onClickSetSounds, this), cb_info::UNTRUSTED_BLOCK }); mCommitCallbackRegistrar.add("Pref.ClickEnablePopup", { boost::bind(&LLFloaterPreference::onClickEnablePopup, this), cb_info::UNTRUSTED_BLOCK }); mCommitCallbackRegistrar.add("Pref.ClickDisablePopup", { boost::bind(&LLFloaterPreference::onClickDisablePopup, this), cb_info::UNTRUSTED_BLOCK }); mCommitCallbackRegistrar.add("Pref.LogPath", { boost::bind(&LLFloaterPreference::onClickLogPath, this), cb_info::UNTRUSTED_BLOCK }); mCommitCallbackRegistrar.add("Pref.RenderExceptions", { boost::bind(&LLFloaterPreference::onClickRenderExceptions, this), cb_info::UNTRUSTED_BLOCK }); mCommitCallbackRegistrar.add("Pref.AutoAdjustments", { boost::bind(&LLFloaterPreference::onClickAutoAdjustments, this), cb_info::UNTRUSTED_BLOCK }); mCommitCallbackRegistrar.add("Pref.HardwareDefaults", { boost::bind(&LLFloaterPreference::setHardwareDefaults, this), cb_info::UNTRUSTED_BLOCK }); mCommitCallbackRegistrar.add("Pref.AvatarImpostorsEnable", { boost::bind(&LLFloaterPreference::onAvatarImpostorsEnable, this), cb_info::UNTRUSTED_BLOCK }); mCommitCallbackRegistrar.add("Pref.UpdateIndirectMaxNonImpostors", { boost::bind(&LLFloaterPreference::updateMaxNonImpostors, this), cb_info::UNTRUSTED_BLOCK }); mCommitCallbackRegistrar.add("Pref.UpdateIndirectMaxComplexity", { boost::bind(&LLFloaterPreference::updateMaxComplexity, this), cb_info::UNTRUSTED_BLOCK }); mCommitCallbackRegistrar.add("Pref.RenderOptionUpdate", { boost::bind(&LLFloaterPreference::onRenderOptionEnable, this), cb_info::UNTRUSTED_BLOCK }); mCommitCallbackRegistrar.add("Pref.WindowedMod", { boost::bind(&LLFloaterPreference::onCommitWindowedMode, this), cb_info::UNTRUSTED_BLOCK }); mCommitCallbackRegistrar.add("Pref.UpdateSliderText", { boost::bind(&LLFloaterPreference::refreshUI,this), cb_info::UNTRUSTED_BLOCK }); mCommitCallbackRegistrar.add("Pref.QualityPerformance", { boost::bind(&LLFloaterPreference::onChangeQuality, this, _2), cb_info::UNTRUSTED_BLOCK }); mCommitCallbackRegistrar.add("Pref.applyUIColor", { boost::bind(&LLFloaterPreference::applyUIColor, this ,_1, _2), cb_info::UNTRUSTED_BLOCK }); mCommitCallbackRegistrar.add("Pref.getUIColor", { boost::bind(&LLFloaterPreference::getUIColor, this ,_1, _2), cb_info::UNTRUSTED_BLOCK }); mCommitCallbackRegistrar.add("Pref.MaturitySettings", { boost::bind(&LLFloaterPreference::onChangeMaturity, this), cb_info::UNTRUSTED_BLOCK }); mCommitCallbackRegistrar.add("Pref.BlockList", { boost::bind(&LLFloaterPreference::onClickBlockList, this), cb_info::UNTRUSTED_BLOCK }); mCommitCallbackRegistrar.add("Pref.Proxy", { boost::bind(&LLFloaterPreference::onClickProxySettings, this), cb_info::UNTRUSTED_BLOCK }); mCommitCallbackRegistrar.add("Pref.TranslationSettings", { boost::bind(&LLFloaterPreference::onClickTranslationSettings, this), cb_info::UNTRUSTED_BLOCK }); mCommitCallbackRegistrar.add("Pref.AutoReplace", { boost::bind(&LLFloaterPreference::onClickAutoReplace, this), cb_info::UNTRUSTED_BLOCK }); mCommitCallbackRegistrar.add("Pref.PermsDefault", { boost::bind(&LLFloaterPreference::onClickPermsDefault, this), cb_info::UNTRUSTED_BLOCK }); mCommitCallbackRegistrar.add("Pref.RememberedUsernames", { boost::bind(&LLFloaterPreference::onClickRememberedUsernames, this), cb_info::UNTRUSTED_BLOCK }); mCommitCallbackRegistrar.add("Pref.SpellChecker", { boost::bind(&LLFloaterPreference::onClickSpellChecker, this), cb_info::UNTRUSTED_BLOCK }); mCommitCallbackRegistrar.add("Pref.Advanced", { boost::bind(&LLFloaterPreference::onClickAdvanced, this), cb_info::UNTRUSTED_BLOCK }); sSkin = gSavedSettings.getString("SkinCurrent"); mCommitCallbackRegistrar.add("Pref.ClickActionChange", { boost::bind(&LLFloaterPreference::onClickActionChange, this), cb_info::UNTRUSTED_BLOCK }); gSavedSettings.getControl("NameTagShowUsernames")->getCommitSignal()->connect(boost::bind(&handleNameTagOptionChanged, _2)); gSavedSettings.getControl("NameTagShowFriends")->getCommitSignal()->connect(boost::bind(&handleNameTagOptionChanged, _2)); gSavedSettings.getControl("UseDisplayNames")->getCommitSignal()->connect(boost::bind(&handleDisplayNamesOptionChanged, _2)); gSavedSettings.getControl("AppearanceCameraMovement")->getCommitSignal()->connect(boost::bind(&handleAppearanceCameraMovementChanged, _2)); gSavedSettings.getControl("WindLightUseAtmosShaders")->getCommitSignal()->connect(boost::bind(&LLFloaterPreference::onAtmosShaderChange, this)); LLAvatarPropertiesProcessor::getInstance()->addObserver( gAgent.getID(), this ); mComplexityChangedSignal = gSavedSettings.getControl("RenderAvatarMaxComplexity")->getCommitSignal()->connect(boost::bind(&LLFloaterPreference::updateComplexityText, this)); mImpostorsChangedSignal = gSavedSettings.getControl("RenderAvatarMaxNonImpostors")->getSignal()->connect(boost::bind(&LLFloaterPreference::updateIndirectMaxNonImpostors, this, _2)); mCommitCallbackRegistrar.add("Pref.ClearLog", { boost::bind(&LLConversationLog::onClearLog, &LLConversationLog::instance()), cb_info::UNTRUSTED_BLOCK }); mCommitCallbackRegistrar.add("Pref.DeleteTranscripts", { boost::bind(&LLFloaterPreference::onDeleteTranscripts, this), cb_info::UNTRUSTED_BLOCK }); mCommitCallbackRegistrar.add("UpdateFilter", { boost::bind(&LLFloaterPreference::onUpdateFilterTerm, this, false), cb_info::UNTRUSTED_BLOCK }); // Hook up for filtering } void LLFloaterPreference::processProperties( void* pData, EAvatarProcessorType type ) { if ( APT_PROPERTIES_LEGACY == type ) { const LLAvatarLegacyData* pAvatarData = static_cast( pData ); if (pAvatarData && (gAgent.getID() == pAvatarData->avatar_id) && (pAvatarData->avatar_id != LLUUID::null)) { mAllowPublish = (bool)(pAvatarData->flags & AVATAR_ALLOW_PUBLISH); mAvatarDataInitialized = true; getChild("online_searchresults")->setValue(mAllowPublish); } } } void LLFloaterPreference::saveAvatarProperties( void ) { const bool allowPublish = getChild("online_searchresults")->getValue(); if ((LLStartUp::getStartupState() == STATE_STARTED) && mAvatarDataInitialized && (allowPublish != mAllowPublish)) { std::string cap_url = gAgent.getRegionCapability("AgentProfile"); if (!cap_url.empty()) { mAllowPublish = allowPublish; LLCoros::instance().launch("requestAgentUserInfoCoro", boost::bind(saveAvatarPropertiesCoro, cap_url, allowPublish)); } } } // static void LLFloaterPreference::saveAvatarPropertiesCoro(const std::string cap_url, bool allow_publish) { LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID); LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("put_avatar_properties_coro", httpPolicy)); LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest); LLCore::HttpHeaders::ptr_t httpHeaders; LLCore::HttpOptions::ptr_t httpOpts(new LLCore::HttpOptions); httpOpts->setFollowRedirects(true); std::string finalUrl = cap_url + "/" + gAgentID.asString(); LLSD data; data["allow_publish"] = allow_publish; LLSD result = httpAdapter->putAndSuspend(httpRequest, finalUrl, data, httpOpts, httpHeaders); LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS]; LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults); if (!status) { LL_WARNS("Preferences") << "Failed to put agent information " << data << " for id " << gAgentID << LL_ENDL; return; } LL_DEBUGS("Preferences") << "Agent id: " << gAgentID << " Data: " << data << " Result: " << httpResults << LL_ENDL; } bool LLFloaterPreference::postBuild() { mDeleteTranscriptsBtn = getChild("delete_transcripts"); mEnabledPopups = getChild("enabled_popups"); mDisabledPopups = getChild("disabled_popups"); mEnablePopupBtn = getChild("enable_this_popup"); mDisablePopupBtn = getChild("disable_this_popup"); setPanelVisibility("game_control", LLGameControl::isEnabled()); gSavedSettings.getControl("ChatFontSize")->getSignal()->connect(boost::bind(&LLFloaterIMSessionTab::processChatHistoryStyleUpdate, false)); gSavedSettings.getControl("ChatFontSize")->getSignal()->connect(boost::bind(&LLViewerChat::signalChatFontChanged)); gSavedSettings.getControl("ChatBubbleOpacity")->getSignal()->connect(boost::bind(&LLFloaterPreference::onNameTagOpacityChange, this, _2)); gSavedSettings.getControl("PreferredMaturity")->getSignal()->connect(boost::bind(&LLFloaterPreference::onChangeMaturity, this)); gSavedSettings.getControl("RenderAvatarComplexityMode")->getSignal()->connect( [this](LLControlVariable* control, const LLSD& new_val, const LLSD& old_val) { onChangeComplexityMode(new_val); }); gSavedPerAccountSettings.getControl("ModelUploadFolder")->getSignal()->connect(boost::bind(&LLFloaterPreference::onChangeModelFolder, this)); gSavedPerAccountSettings.getControl("PBRUploadFolder")->getSignal()->connect(boost::bind(&LLFloaterPreference::onChangePBRFolder, this)); gSavedPerAccountSettings.getControl("TextureUploadFolder")->getSignal()->connect(boost::bind(&LLFloaterPreference::onChangeTextureFolder, this)); gSavedPerAccountSettings.getControl("SoundUploadFolder")->getSignal()->connect(boost::bind(&LLFloaterPreference::onChangeSoundFolder, this)); gSavedPerAccountSettings.getControl("AnimationUploadFolder")->getSignal()->connect(boost::bind(&LLFloaterPreference::onChangeAnimationFolder, this)); LLTabContainer* tabcontainer = getChild("pref core"); if (!tabcontainer->selectTab(gSavedSettings.getS32("LastPrefTab"))) tabcontainer->selectFirstTab(); getChild("cache_location")->setEnabled(false); // make it read-only but selectable (STORM-227) std::string cache_location = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, ""); setCacheLocation(cache_location); getChild("log_path_string")->setEnabled(false); // make it read-only but selectable getChild("language_combobox")->setCommitCallback(boost::bind(&LLFloaterPreference::onLanguageChange, this)); getChild("FriendIMOptions")->setCommitCallback(boost::bind(&LLFloaterPreference::onNotificationsChange, this,"FriendIMOptions")); getChild("NonFriendIMOptions")->setCommitCallback(boost::bind(&LLFloaterPreference::onNotificationsChange, this,"NonFriendIMOptions")); getChild("ConferenceIMOptions")->setCommitCallback(boost::bind(&LLFloaterPreference::onNotificationsChange, this,"ConferenceIMOptions")); getChild("GroupChatOptions")->setCommitCallback(boost::bind(&LLFloaterPreference::onNotificationsChange, this,"GroupChatOptions")); getChild("NearbyChatOptions")->setCommitCallback(boost::bind(&LLFloaterPreference::onNotificationsChange, this,"NearbyChatOptions")); getChild("ObjectIMOptions")->setCommitCallback(boost::bind(&LLFloaterPreference::onNotificationsChange, this,"ObjectIMOptions")); // if floater is opened before login set default localized do not disturb message if (LLStartUp::getStartupState() < STATE_STARTED) { gSavedPerAccountSettings.setString("DoNotDisturbModeResponse", LLTrans::getString("DoNotDisturbModeResponseDefault")); } // set 'enable' property for 'Clear log...' button changed(); LLLogChat::getInstance()->setSaveHistorySignal(boost::bind(&LLFloaterPreference::onLogChatHistorySaved, this)); LLSliderCtrl* fov_slider = getChild("camera_fov"); fov_slider->setMinValue(LLViewerCamera::getInstance()->getMinView()); fov_slider->setMaxValue(LLViewerCamera::getInstance()->getMaxView()); bool enable_complexity = gSavedSettings.getS32("RenderAvatarComplexityMode") != LLVOAvatar::AV_RENDER_ONLY_SHOW_FRIENDS; getChild("IndirectMaxComplexity")->setEnabled(enable_complexity); // Hook up and init for filtering mFilterEdit = getChild("search_prefs_edit"); mFilterEdit->setKeystrokeCallback(boost::bind(&LLFloaterPreference::onUpdateFilterTerm, this, false)); // Load and assign label for 'default language' std::string user_filename = gDirUtilp->getExpandedFilename(LL_PATH_DEFAULT_SKIN, "default_languages.xml"); std::map labels; if (loadFromFilename(user_filename, labels)) { std::string system_lang = gSavedSettings.getString("SystemLanguage"); std::map::iterator iter = labels.find(system_lang); if (iter != labels.end()) { getChild("language_combobox")->add(iter->second, LLSD("default"), ADD_TOP, true); } else { LL_WARNS() << "Language \"" << system_lang << "\" is not in default_languages.xml" << LL_ENDL; getChild("language_combobox")->add("System default", LLSD("default"), ADD_TOP, true); } } else { LL_WARNS() << "Failed to load labels from " << user_filename << ". Using default." << LL_ENDL; getChild("language_combobox")->add("System default", LLSD("default"), ADD_TOP, true); } return true; } void LLFloaterPreference::updateDeleteTranscriptsButton() { mDeleteTranscriptsBtn->setEnabled(LLLogChat::transcriptFilesExist()); } void LLFloaterPreference::onDoNotDisturbResponseChanged() { // set "DoNotDisturbResponseChanged" true if user edited message differs from default, false otherwise bool response_changed_flag = LLTrans::getString("DoNotDisturbModeResponseDefault") != getChild("do_not_disturb_response")->getValue().asString(); gSavedPerAccountSettings.setBOOL("DoNotDisturbResponseChanged", response_changed_flag ); } LLFloaterPreference::~LLFloaterPreference() { LLConversationLog::instance().removeObserver(this); mComplexityChangedSignal.disconnect(); mImpostorsChangedSignal.disconnect(); } void LLFloaterPreference::draw() { bool has_first_selected = (mDisabledPopups->getFirstSelected()!=NULL); mEnablePopupBtn->setEnabled(has_first_selected); has_first_selected = (mEnabledPopups->getFirstSelected()!=NULL); mDisablePopupBtn->setEnabled(has_first_selected); LLFloater::draw(); } void LLFloaterPreference::saveSettings() { LLTabContainer* tabcontainer = getChild("pref core"); for (LLView* view : *tabcontainer->getChildList()) { if (LLPanelPreference* panel = dynamic_cast(view)) { panel->saveSettings(); } } saveIgnoredNotifications(); } void LLFloaterPreference::apply() { LLAvatarPropertiesProcessor::getInstance()->addObserver(gAgent.getID(), this); LLTabContainer* tabcontainer = getChild("pref core"); if (sSkin != gSavedSettings.getString("SkinCurrent")) { LLNotificationsUtil::add("ChangeSkin"); refreshSkin(this); } // Call apply() on all panels that derive from LLPanelPreference for (LLView* view : *tabcontainer->getChildList()) { if (LLPanelPreference* panel = dynamic_cast(view)) { panel->apply(); } } gViewerWindow->requestResolutionUpdate(); // for UIScaleFactor LLSliderCtrl* fov_slider = getChild("camera_fov"); fov_slider->setMinValue(LLViewerCamera::getInstance()->getMinView()); fov_slider->setMaxValue(LLViewerCamera::getInstance()->getMaxView()); std::string cache_location = gDirUtilp->getExpandedFilename(LL_PATH_CACHE, ""); setCacheLocation(cache_location); LLViewerMedia::getInstance()->setCookiesEnabled(getChild("cookies_enabled")->getValue()); 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(); int proxy_port = getChild("web_proxy_port")->getValue(); LLViewerMedia::getInstance()->setProxyConfig(proxy_enable, proxy_address, proxy_port); } if (mGotPersonalInfo) { bool new_hide_online = getChild("online_visibility")->getValue().asBoolean(); if (new_hide_online != mOriginalHideOnlineStatus) { // This hack is because we are representing several different // possible strings with a single checkbox. Since most users // can only select between 2 values, we represent it as a // checkbox. This breaks down a little bit for liaisons, but // works out in the end. if (new_hide_online != mOriginalHideOnlineStatus) { if (new_hide_online) mDirectoryVisibility = VISIBILITY_HIDDEN; else mDirectoryVisibility = VISIBILITY_DEFAULT; //Update showonline value, otherwise multiple applys won't work mOriginalHideOnlineStatus = new_hide_online; } gAgent.sendAgentUpdateUserInfo(mDirectoryVisibility); } } saveAvatarProperties(); } void LLFloaterPreference::cancel(const std::vector settings_to_skip) { LLTabContainer* tabcontainer = getChild("pref core"); // Call cancel() on all panels that derive from LLPanelPreference for (LLView* view : *tabcontainer->getChildList()) { if (LLPanelPreference* panel = dynamic_cast(view)) { panel->cancel(settings_to_skip); } } // hide joystick pref floater LLFloaterReg::hideInstance("pref_joystick"); // hide translation settings floater LLFloaterReg::hideInstance("prefs_translation"); // hide autoreplace settings floater LLFloaterReg::hideInstance("prefs_autoreplace"); // hide spellchecker settings folder LLFloaterReg::hideInstance("prefs_spellchecker"); // hide advanced graphics floater LLFloaterReg::hideInstance("prefs_graphics_advanced"); // reverts any changes to current skin gSavedSettings.setString("SkinCurrent", sSkin); updateClickActionViews(); LLFloaterPreferenceProxy * advanced_proxy_settings = LLFloaterReg::findTypedInstance("prefs_proxy"); if (advanced_proxy_settings) { advanced_proxy_settings->cancel(); } //Need to reload the navmesh if the pathing console is up LLHandle pathfindingConsoleHandle = LLFloaterPathfindingConsole::getInstanceHandle(); if (!pathfindingConsoleHandle.isDead()) { LLFloaterPathfindingConsole* pPathfindingConsole = pathfindingConsoleHandle.get(); pPathfindingConsole->onRegionBoundaryCross(); } if (!mSavedGraphicsPreset.empty()) { gSavedSettings.setString("PresetGraphicActive", mSavedGraphicsPreset); LLPresetsManager::getInstance()->triggerChangeSignal(); } restoreIgnoredNotifications(); } 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 if (!initialized && LLStartUp::getStartupState() == STATE_STARTED) { // Special approach is used for do not disturb response localization, because "DoNotDisturbModeResponse" is // in non-localizable xml, and also because it may be changed by user and in this case it shouldn't be localized. // To keep track of whether do not disturb response is default or changed by user additional setting DoNotDisturbResponseChanged // was added into per account settings. // initialization should happen once,so setting variable to true initialized = true; // this connection is needed to properly set "DoNotDisturbResponseChanged" setting when user makes changes in // do not disturb response message. gSavedPerAccountSettings.getControl("DoNotDisturbModeResponse")->getSignal()->connect(boost::bind(&LLFloaterPreference::onDoNotDisturbResponseChanged, this)); } gAgent.sendAgentUserInfoRequest(); /////////////////////////// From LLPanelGeneral ////////////////////////// // if we have no agent, we can't let them choose anything // if we have an agent, then we only let them choose if they have a choice bool can_choose_maturity = gAgent.getID().notNull() && (gAgent.isMature() || gAgent.isGodlike()); LLComboBox* maturity_combo = getChild("maturity_desired_combobox"); LLAvatarPropertiesProcessor::getInstance()->sendAvatarLegacyPropertiesRequest( gAgent.getID() ); if (can_choose_maturity) { // if they're not adult or a god, they shouldn't see the adult selection, so delete it if (!gAgent.isAdult() && !gAgent.isGodlikeWithoutAdminMenuFakery()) { // we're going to remove the adult entry from the combo LLScrollListCtrl* maturity_list = maturity_combo->findChild("ComboBox"); if (maturity_list) { maturity_list->deleteItems(LLSD(SIM_ACCESS_ADULT)); } } getChildView("maturity_desired_combobox")->setEnabled( true); getChildView("maturity_desired_textbox")->setVisible( false); } else { getChild("maturity_desired_textbox")->setValue(maturity_combo->getSelectedItemLabel()); getChildView("maturity_desired_combobox")->setEnabled( false); } // Forget previous language changes. mLanguageChanged = false; // Display selected maturity icons. onChangeMaturity(); onChangeModelFolder(); onChangePBRFolder(); onChangeTextureFolder(); onChangeSoundFolder(); onChangeAnimationFolder(); // Load (double-)click to walk/teleport settings. updateClickActionViews(); // Enabled/disabled popups, might have been changed by user actions // while preferences floater was closed. buildPopupLists(); // get the options that were checked onNotificationsChange("FriendIMOptions"); onNotificationsChange("NonFriendIMOptions"); onNotificationsChange("ConferenceIMOptions"); onNotificationsChange("GroupChatOptions"); onNotificationsChange("NearbyChatOptions"); onNotificationsChange("ObjectIMOptions"); LLPanelLogin::setAlwaysRefresh(true); refresh(); // Make sure the current state of prefs are saved away when // the floater is opened. That will make cancel() do its job saveSettings(); // Make sure there is a default preference file LLPresetsManager::getInstance()->createMissingDefault(PRESETS_CAMERA); LLPresetsManager::getInstance()->createMissingDefault(PRESETS_GRAPHIC); bool started = (LLStartUp::getStartupState() == STATE_STARTED); LLButton* load_btn = findChild("PrefLoadButton"); LLButton* save_btn = findChild("PrefSaveButton"); LLButton* delete_btn = findChild("PrefDeleteButton"); LLButton* exceptions_btn = findChild("RenderExceptionsButton"); LLButton* auto_adjustments_btn = findChild("AutoAdjustmentsButton"); if (load_btn && save_btn && delete_btn && exceptions_btn && auto_adjustments_btn) { load_btn->setEnabled(started); save_btn->setEnabled(started); delete_btn->setEnabled(started); exceptions_btn->setEnabled(started); auto_adjustments_btn->setEnabled(started); } collectSearchableItems(); if (!mFilterEdit->getText().empty()) { mFilterEdit->setText(LLStringExplicit("")); onUpdateFilterTerm(true); } } void LLFloaterPreference::onRenderOptionEnable() { refreshEnabledGraphics(); } void LLFloaterPreference::onAvatarImpostorsEnable() { refreshEnabledGraphics(); } //static void LLFloaterPreference::initDoNotDisturbResponse() { if (!gSavedPerAccountSettings.getBOOL("DoNotDisturbResponseChanged")) { //LLTrans::getString("DoNotDisturbModeResponseDefault") is used here for localization (EXT-5885) gSavedPerAccountSettings.setString("DoNotDisturbModeResponse", LLTrans::getString("DoNotDisturbModeResponseDefault")); } } //static void LLFloaterPreference::updateShowFavoritesCheckbox(bool val) { LLFloaterPreference* instance = LLFloaterReg::findTypedInstance("preferences"); if (instance) { instance->getChild("favorites_on_login_check")->setValue(val); } } void LLFloaterPreference::setHardwareDefaults() { std::string preset_graphic_active = gSavedSettings.getString("PresetGraphicActive"); if (!preset_graphic_active.empty()) { saveGraphicsPreset(preset_graphic_active); saveSettings(); // save here to be able to return to the previous preset by Cancel } setRecommendedSettings(); } void LLFloaterPreference::setRecommendedSettings() { resetAutotuneSettings(); gSavedSettings.getControl("RenderVSyncEnable")->resetToDefault(true); LLFeatureManager::getInstance()->applyRecommendedSettings(); // reset indirects before refresh because we may have changed what they control LLAvatarComplexityControls::setIndirectControls(); refreshEnabledGraphics(); gSavedSettings.setString("PresetGraphicActive", ""); LLPresetsManager::getInstance()->triggerChangeSignal(); 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) { LLView* view = *iter; LLPanelPreference* panel = dynamic_cast(view); if (panel) { panel->setHardwareDefaults(); } } } void LLFloaterPreference::resetAutotuneSettings() { gSavedSettings.setBOOL("AutoTuneFPS", false); const std::string autotune_settings[] = { "AutoTuneLock", "KeepAutoTuneLock", "TargetFPS", "TuningFPSStrategy", "AutoTuneImpostorByDistEnabled", "AutoTuneImpostorFarAwayDistance" , "AutoTuneRenderFarClipMin", "AutoTuneRenderFarClipTarget", "RenderAvatarMaxART" }; for (auto it : autotune_settings) { gSavedSettings.getControl(it)->resetToDefault(true); } } void LLFloaterPreference::getControlNames(std::vector& names) { LLView* view = findChild("display"); LLFloater* advanced = LLFloaterReg::findTypedInstance("prefs_graphics_advanced"); if (view && advanced) { std::list stack; stack.push_back(view); stack.push_back(advanced); while(!stack.empty()) { // Process view on top of the stack LLView* curview = stack.front(); stack.pop_front(); LLUICtrl* ctrl = dynamic_cast(curview); if (ctrl) { LLControlVariable* control = ctrl->getControlVariable(); if (control) { std::string control_name = control->getName(); if (std::find(names.begin(), names.end(), control_name) == names.end()) { names.push_back(control_name); } } } for (child_list_t::const_iterator iter = curview->getChildList()->begin(); iter != curview->getChildList()->end(); ++iter) { stack.push_back(*iter); } } } } //virtual void LLFloaterPreference::onClose(bool app_quitting) { gSavedSettings.setS32("LastPrefTab", getChild("pref core")->getCurrentPanelIndex()); LLPanelLogin::setAlwaysRefresh(false); if (!app_quitting) { cancel(); } } // static void LLFloaterPreference::onBtnOK(const LLSD& userdata) { // commit any outstanding text entry if (hasFocus()) { LLUICtrl* cur_focus = dynamic_cast(gFocusMgr.getKeyboardFocus()); if (cur_focus && cur_focus->acceptsTextInput()) { cur_focus->onCommit(); } } if (canClose()) { saveSettings(); apply(); if (userdata.asString() == "closeadvanced") { LLFloaterReg::hideInstance("prefs_graphics_advanced"); } else { closeFloater(false); } //Conversation transcript and log path changed so reload conversations based on new location if (mPriorInstantMessageLogPath.length()) { if (moveTranscriptsAndLog()) { //When floaters are empty but have a chat history files, reload chat history into them LLFloaterIMSessionTab::reloadEmptyFloaters(); } //Couldn't move files so restore the old path and show a notification else { gSavedPerAccountSettings.setString("InstantMessageLogPath", mPriorInstantMessageLogPath); LLNotificationsUtil::add("PreferenceChatPathChanged"); } mPriorInstantMessageLogPath.clear(); } LLUIColorTable::instance().saveUserSettings(); gSavedSettings.saveToFile(gSavedSettings.getString("ClientSettingsFile"), true); // save current config to settings LLGameControl::saveToSettings(); // Only save once logged in and loaded per account settings if (mGotPersonalInfo) { gSavedPerAccountSettings.saveToFile(gSavedSettings.getString("PerAccountSettingsFile"), true); } } else { // Show beep, pop up dialog, etc. LL_INFOS("Preferences") << "Can't close preferences!" << LL_ENDL; } LLPanelLogin::updateLocationSelectorsVisibility(); //Need to reload the navmesh if the pathing console is up LLHandle pathfindingConsoleHandle = LLFloaterPathfindingConsole::getInstanceHandle(); if ( !pathfindingConsoleHandle.isDead() ) { LLFloaterPathfindingConsole* pPathfindingConsole = pathfindingConsoleHandle.get(); pPathfindingConsole->onRegionBoundaryCross(); } } // static void LLFloaterPreference::onBtnCancel(const LLSD& userdata) { if (hasFocus()) { LLUICtrl* cur_focus = dynamic_cast(gFocusMgr.getKeyboardFocus()); if (cur_focus && cur_focus->acceptsTextInput()) { cur_focus->onCommit(); } refresh(); } if (userdata.asString() == "closeadvanced") { cancel({"RenderQualityPerformance"}); LLFloaterReg::hideInstance("prefs_graphics_advanced"); } else { cancel(); closeFloater(); } // restore config from settings LLGameControl::loadFromSettings(); } //static void LLFloaterPreference::refreshInstance() { if (LLFloaterPreference* instance = LLFloaterReg::findTypedInstance("preferences")) { instance->refresh(); } } // static void LLFloaterPreference::updateUserInfo(const std::string& visibility) { if (LLFloaterPreference* instance = LLFloaterReg::findTypedInstance("preferences")) { instance->setPersonalInfo(visibility); } } // static void LLFloaterPreference::refreshEnabledGraphics() { if (LLFloaterPreference* instance = LLFloaterReg::findTypedInstance("preferences")) { instance->refresh(); } if (LLFloater* advanced = LLFloaterReg::findTypedInstance("prefs_graphics_advanced")) { advanced->refresh(); } } void LLFloaterPreference::onClickClearCache() { LLNotificationsUtil::add("ConfirmClearCache", LLSD(), LLSD(), callback_clear_cache); } void LLFloaterPreference::onClickBrowserClearCache() { LLNotificationsUtil::add("ConfirmClearBrowserCache", LLSD(), LLSD(), callback_clear_browser_cache); } // Called when user changes language via the combobox. void LLFloaterPreference::onLanguageChange() { // Let the user know that the change will only take effect after restart. // Do it only once so that we're not too irritating. if (!mLanguageChanged) { LLNotificationsUtil::add("ChangeLanguage"); mLanguageChanged = true; } } void LLFloaterPreference::onNotificationsChange(const std::string& OptionName) { mNotificationOptions[OptionName] = getChild(OptionName)->getSelectedItemLabel(); 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") { show_notifications_alert = false; break; } } getChild("notifications_alert")->setVisible(show_notifications_alert); } void LLFloaterPreference::onNameTagOpacityChange(const LLSD& newvalue) { if (LLColorSwatchCtrl* color_swatch = findChild("background")) { LLColor4 new_color = color_swatch->get(); color_swatch->set(new_color.setAlpha((F32)newvalue.asReal())); } } void LLFloaterPreference::onClickSetCache() { std::string cur_name(gSavedSettings.getString("CacheLocation")); // std::string cur_top_folder(gDirUtilp->getBaseFileName(cur_name)); std::string proposed_name(cur_name); (new LLDirPickerThread(boost::bind(&LLFloaterPreference::changeCachePath, this, _1, _2), proposed_name))->getFile(); } void LLFloaterPreference::changeCachePath(const std::vector& filenames, std::string proposed_name) { std::string dir_name = filenames[0]; if (!dir_name.empty() && dir_name != proposed_name) { std::string new_top_folder(gDirUtilp->getBaseFileName(dir_name)); LLNotificationsUtil::add("CacheWillBeMoved"); gSavedSettings.setString("NewCacheLocation", dir_name); gSavedSettings.setString("NewCacheLocationTopFolder", new_top_folder); } else { std::string cache_location = gDirUtilp->getCacheDir(); gSavedSettings.setString("CacheLocation", cache_location); std::string top_folder(gDirUtilp->getBaseFileName(cache_location)); gSavedSettings.setString("CacheLocationTopFolder", top_folder); } } void LLFloaterPreference::onClickResetCache() { if (gDirUtilp->getCacheDir(false) == gDirUtilp->getCacheDir(true)) { // The cache location was already the default. return; } gSavedSettings.setString("NewCacheLocation", ""); gSavedSettings.setString("NewCacheLocationTopFolder", ""); LLNotificationsUtil::add("CacheWillBeMoved"); std::string cache_location = gDirUtilp->getCacheDir(false); gSavedSettings.setString("CacheLocation", cache_location); std::string top_folder(gDirUtilp->getBaseFileName(cache_location)); gSavedSettings.setString("CacheLocationTopFolder", top_folder); } void LLFloaterPreference::onClickSkin(LLUICtrl* ctrl, const LLSD& userdata) { gSavedSettings.setString("SkinCurrent", userdata.asString()); ctrl->setValue(userdata.asString()); } void LLFloaterPreference::onSelectSkin() { std::string skin_selection = getChild("skin_selection")->getValue().asString(); gSavedSettings.setString("SkinCurrent", skin_selection); } void LLFloaterPreference::refreshSkin(void* data) { LLPanel*self = (LLPanel*)data; sSkin = gSavedSettings.getString("SkinCurrent"); self->getChild("skin_selection", true)->setValue(sSkin); } void LLFloaterPreference::buildPopupLists() { mDisabledPopups->deleteAllItems(); mEnabledPopups->deleteAllItems(); for (LLNotifications::TemplateMap::const_iterator iter = LLNotifications::instance().templatesBegin(); iter != LLNotifications::instance().templatesEnd(); ++iter) { LLNotificationTemplatePtr templatep = iter->second; LLNotificationFormPtr formp = templatep->mForm; LLNotificationForm::EIgnoreType ignore = formp->getIgnoreType(); if (ignore <= LLNotificationForm::IGNORE_NO) continue; LLSD row; row["columns"][0]["value"] = formp->getIgnoreMessage(); row["columns"][0]["font"] = "SANSSERIF_SMALL"; row["columns"][0]["width"] = 400; LLScrollListItem* item = NULL; bool show_popup = !formp->getIgnored(); if (!show_popup) { if (ignore == LLNotificationForm::IGNORE_WITH_LAST_RESPONSE) { LLSD last_response = LLUI::getInstance()->mSettingGroups["config"]->getLLSD("Default" + templatep->mName); if (!last_response.isUndefined()) { for (LLSD::map_const_iterator it = last_response.beginMap(); it != last_response.endMap(); ++it) { if (it->second.asBoolean()) { row["columns"][1]["value"] = formp->getElement(it->first)["ignore"].asString(); row["columns"][1]["font"] = "SANSSERIF_SMALL"; row["columns"][1]["width"] = 360; break; } } } } item = mDisabledPopups->addElement(row); } else { item = mEnabledPopups->addElement(row); } if (item) { item->setUserdata((void*)&iter->first); } } } void LLFloaterPreference::refreshEnabledState() { // Cannot have floater active until caps have been received getChild("default_creation_permissions")->setEnabled(LLStartUp::getStartupState() >= STATE_STARTED); getChildView("block_list")->setEnabled(LLLoginInstance::getInstance()->authSuccess()); } void LLAvatarComplexityControls::setIndirectControls() { /* * We have controls that have an indirect relationship between the control * values and adjacent text and the underlying setting they influence. * In each case, the control and its associated setting are named Indirect * This method interrogates the controlled setting and establishes the * appropriate value for the indirect control. It must be called whenever the * underlying setting may have changed other than through the indirect control, * such as when the 'Reset all to recommended settings' button is used... */ setIndirectMaxNonImpostors(); setIndirectMaxArc(); } // static void LLAvatarComplexityControls::setIndirectMaxNonImpostors() { U32 max_non_impostors = gSavedSettings.getU32("RenderAvatarMaxNonImpostors"); // for this one, we just need to make zero, which means off, the max value of the slider U32 indirect_max_non_impostors = (0 == max_non_impostors) ? LLVOAvatar::NON_IMPOSTORS_MAX_SLIDER : max_non_impostors; gSavedSettings.setU32("IndirectMaxNonImpostors", indirect_max_non_impostors); } void LLAvatarComplexityControls::setIndirectMaxArc() { U32 max_arc = gSavedSettings.getU32("RenderAvatarMaxComplexity"); U32 indirect_max_arc; if (0 == max_arc) { // the off position is all the way to the right, so set to control max indirect_max_arc = INDIRECT_MAX_ARC_OFF; } else { // This is the inverse of the calculation in updateMaxComplexity indirect_max_arc = (U32)ll_round(((log(F32(max_arc)) - MIN_ARC_LOG) / ARC_LIMIT_MAP_SCALE)) + MIN_INDIRECT_ARC_LIMIT; } gSavedSettings.setU32("IndirectMaxComplexity", indirect_max_arc); } void LLFloaterPreference::refresh() { setPanelVisibility("game_control", LLGameControl::isEnabled()); LLTabContainer* tabcontainer = getChild("pref core"); for (LLView* view : *tabcontainer->getChildList()) { if (LLPanelPreferenceControls* panel = dynamic_cast(view)) { panel->refresh(); break; } } LLFloater::refresh(); setMaxNonImpostorsText( gSavedSettings.getU32("RenderAvatarMaxNonImpostors"), getChild("IndirectMaxNonImpostorsText", true)); LLAvatarComplexityControls::setText( gSavedSettings.getU32("RenderAvatarMaxComplexity"), getChild("IndirectMaxComplexityText", true)); refreshEnabledState(); if (LLFloater* advanced = LLFloaterReg::findTypedInstance("prefs_graphics_advanced")) { advanced->refresh(); } updateClickActionViews(); } void LLFloaterPreference::onCommitWindowedMode() { refresh(); } void LLFloaterPreference::onChangeQuality(const LLSD& data) { U32 level = (U32)data.asReal(); LLFeatureManager::getInstance()->setGraphicsLevel(level, true); refreshEnabledGraphics(); refresh(); } void LLFloaterPreference::onClickSetSounds() { // Disable Enable gesture sounds checkbox if the master sound is disabled // or if sound effects are disabled. getChild("gesture_audio_play_btn")->setEnabled(!gSavedSettings.getBOOL("MuteSounds")); } void LLFloaterPreference::onClickEnablePopup() { std::vector items = mDisabledPopups->getAllSelected(); std::vector::iterator itor; for (itor = items.begin(); itor != items.end(); ++itor) { LLNotificationTemplatePtr templatep = LLNotifications::instance().getTemplate(*(std::string*)((*itor)->getUserdata())); //gSavedSettings.setWarning(templatep->mName, true); std::string notification_name = templatep->mName; LLUI::getInstance()->mSettingGroups["ignores"]->setBOOL(notification_name, true); } buildPopupLists(); if (!mFilterEdit->getText().empty()) { filterIgnorableNotifications(); } } void LLFloaterPreference::onClickDisablePopup() { std::vector items = mEnabledPopups->getAllSelected(); std::vector::iterator itor; for (itor = items.begin(); itor != items.end(); ++itor) { LLNotificationTemplatePtr templatep = LLNotifications::instance().getTemplate(*(std::string*)((*itor)->getUserdata())); templatep->mForm->setIgnored(true); } buildPopupLists(); if (!mFilterEdit->getText().empty()) { filterIgnorableNotifications(); } } void LLFloaterPreference::resetAllIgnored() { for (LLNotifications::TemplateMap::const_iterator iter = LLNotifications::instance().templatesBegin(); iter != LLNotifications::instance().templatesEnd(); ++iter) { if (iter->second->mForm->getIgnoreType() > LLNotificationForm::IGNORE_NO) { iter->second->mForm->setIgnored(false); } } } void LLFloaterPreference::setAllIgnored() { for (LLNotifications::TemplateMap::const_iterator iter = LLNotifications::instance().templatesBegin(); iter != LLNotifications::instance().templatesEnd(); ++iter) { if (iter->second->mForm->getIgnoreType() > LLNotificationForm::IGNORE_NO) { iter->second->mForm->setIgnored(true); } } } void LLFloaterPreference::onClickLogPath() { std::string proposed_name(gSavedPerAccountSettings.getString("InstantMessageLogPath")); mPriorInstantMessageLogPath.clear(); (new LLDirPickerThread(boost::bind(&LLFloaterPreference::changeLogPath, this, _1, _2), proposed_name))->getFile(); } void LLFloaterPreference::changeLogPath(const std::vector& filenames, std::string proposed_name) { //Path changed if (proposed_name != filenames[0]) { gSavedPerAccountSettings.setString("InstantMessageLogPath", filenames[0]); mPriorInstantMessageLogPath = proposed_name; // enable/disable 'Delete transcripts button updateDeleteTranscriptsButton(); } } bool LLFloaterPreference::moveTranscriptsAndLog() { std::string instantMessageLogPath(gSavedPerAccountSettings.getString("InstantMessageLogPath")); std::string chatLogPath = gDirUtilp->add(instantMessageLogPath, gDirUtilp->getUserName()); bool madeDirectory = false; //Does the directory really exist, if not then make it if (!LLFile::isdir(chatLogPath)) { //mkdir success is defined as zero if (LLFile::mkdir(chatLogPath) != 0) { return false; } madeDirectory = true; } 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)) { //Couldn't move the log and created a new directory so remove the new directory if (madeDirectory) { LLFile::rmdir(chatLogPath); } return false; } //Attempt to move transcripts std::vector listOfTranscripts; std::vector listOfFilesMoved; LLLogChat::getListOfTranscriptFiles(listOfTranscripts); if (!LLLogChat::moveTranscripts(gDirUtilp->getChatLogsDir(), instantMessageLogPath, listOfTranscripts, listOfFilesMoved)) { //Couldn't move all the transcripts so restore those that moved back to their old location LLLogChat::moveTranscripts(instantMessageLogPath, gDirUtilp->getChatLogsDir(), listOfFilesMoved); //Move the conversation log back LLConversationLog::instance().moveLog(targetConversationLogDir, originalConversationLogDir); if (madeDirectory) { LLFile::rmdir(chatLogPath); } return false; } gDirUtilp->setChatLogsDir(instantMessageLogPath); gDirUtilp->updatePerAccountChatLogsDir(); return true; } void LLFloaterPreference::setPersonalInfo(const std::string& visibility) { mGotPersonalInfo = true; mDirectoryVisibility = visibility; if (visibility == VISIBILITY_DEFAULT) { mOriginalHideOnlineStatus = false; getChildView("online_visibility")->setEnabled(true); } else if (visibility == VISIBILITY_HIDDEN) { mOriginalHideOnlineStatus = true; getChildView("online_visibility")->setEnabled(true); } else { mOriginalHideOnlineStatus = true; } getChild("online_searchresults")->setEnabled(true); getChildView("friends_online_notify_checkbox")->setEnabled(true); getChild("online_visibility")->setValue(mOriginalHideOnlineStatus); getChild("online_visibility")->setLabelArg("[DIR_VIS]", mDirectoryVisibility); getChildView("favorites_on_login_check")->setEnabled(true); getChildView("log_path_button")->setEnabled(true); getChildView("chat_font_size")->setEnabled(true); getChildView("conversation_log_combo")->setEnabled(true); getChild("voice_call_friends_only_check")->setEnabled(true); getChild("voice_call_friends_only_check")->setValue(gSavedPerAccountSettings.getBOOL("VoiceCallsFriendsOnly")); } void LLFloaterPreference::refreshUI() { refresh(); } void LLAvatarComplexityControls::updateMax(LLSliderCtrl* slider, LLTextBox* value_label, bool short_val) { // Called when the IndirectMaxComplexity control changes // Responsible for fixing the slider label (IndirectMaxComplexityText) and setting RenderAvatarMaxComplexity U32 indirect_value = slider->getValue().asInteger(); U32 max_arc; if (INDIRECT_MAX_ARC_OFF == indirect_value) { // The 'off' position is when the slider is all the way to the right, // which is a value of INDIRECT_MAX_ARC_OFF, // so it is necessary to set max_arc to 0 disable muted avatars. max_arc = 0; } else { // if this is changed, the inverse calculation in setIndirectMaxArc // must be changed to match max_arc = (U32)ll_round(exp(MIN_ARC_LOG + (ARC_LIMIT_MAP_SCALE * (indirect_value - MIN_INDIRECT_ARC_LIMIT)))); } gSavedSettings.setU32("RenderAvatarMaxComplexity", (U32)max_arc); setText(max_arc, value_label, short_val); } void LLAvatarComplexityControls::setText(U32 value, LLTextBox* text_box, bool short_val) { if (0 == value) { text_box->setText(LLTrans::getString("no_limit")); } else { std::string text_value = short_val ? llformat("%d", value / 1000) : llformat("%d", value); text_box->setText(text_value); } } void LLAvatarComplexityControls::updateMaxRenderTime(LLSliderCtrl* slider, LLTextBox* value_label, bool short_val) { setRenderTimeText((F32)(LLPerfStats::renderAvatarMaxART_ns/1000), value_label, short_val); } void LLAvatarComplexityControls::setRenderTimeText(F32 value, LLTextBox* text_box, bool short_val) { if (0 == value) { text_box->setText(LLTrans::getString("no_limit")); } else { text_box->setText(llformat("%.0f", value)); } } void LLFloaterPreference::updateMaxNonImpostors() { // Called when the IndirectMaxNonImpostors control changes // Responsible for fixing the slider label (IndirectMaxNonImpostorsText) and setting RenderAvatarMaxNonImpostors LLSliderCtrl* ctrl = getChild("IndirectMaxNonImpostors", true); U32 value = ctrl->getValue().asInteger(); if (0 == value || LLVOAvatar::NON_IMPOSTORS_MAX_SLIDER <= value) { value = 0; } gSavedSettings.setU32("RenderAvatarMaxNonImpostors", value); LLVOAvatar::updateImpostorRendering(value); // make it effective immediately setMaxNonImpostorsText(value, getChild("IndirectMaxNonImpostorsText")); } void LLFloaterPreference::updateIndirectMaxNonImpostors(const LLSD& newvalue) { U32 value = newvalue.asInteger(); if ((value != 0) && (value != gSavedSettings.getU32("IndirectMaxNonImpostors"))) { gSavedSettings.setU32("IndirectMaxNonImpostors", value); } setMaxNonImpostorsText(value, getChild("IndirectMaxNonImpostorsText")); } void LLFloaterPreference::setMaxNonImpostorsText(U32 value, LLTextBox* text_box) { if (0 == value) { text_box->setText(LLTrans::getString("no_limit")); } else { text_box->setText(llformat("%d", value)); } } void LLFloaterPreference::updateMaxComplexity() { // Called when the IndirectMaxComplexity control changes LLAvatarComplexityControls::updateMax( getChild("IndirectMaxComplexity"), getChild("IndirectMaxComplexityText")); } void LLFloaterPreference::updateComplexityText() { LLAvatarComplexityControls::setText(gSavedSettings.getU32("RenderAvatarMaxComplexity"), getChild("IndirectMaxComplexityText", true)); } bool LLFloaterPreference::loadFromFilename(const std::string& filename, std::map &label_map) { LLXMLNodePtr root; if (!LLXMLNode::parseFile(filename, root, NULL)) { LL_WARNS("Preferences") << "Unable to parse file " << filename << LL_ENDL; return false; } if (!root->hasName("labels")) { LL_WARNS("Preferences") << filename << " is not a valid definition file" << LL_ENDL; return false; } LabelTable params; LLXUIParser parser; parser.readXUI(root, params, filename); if (params.validateBlock()) { for (LLInitParam::ParamIterator::const_iterator it = params.labels.begin(); it != params.labels.end(); ++it) { LabelDef label_entry = *it; label_map[label_entry.name] = label_entry.value; } } else { LL_WARNS("Preferences") << filename << " failed to load" << LL_ENDL; return false; } return true; } void LLFloaterPreference::onChangeMaturity() { U8 sim_access = gSavedSettings.getU32("PreferredMaturity"); getChild("rating_icon_general")->setVisible(sim_access == SIM_ACCESS_PG || sim_access == SIM_ACCESS_MATURE || sim_access == SIM_ACCESS_ADULT); getChild("rating_icon_moderate")->setVisible(sim_access == SIM_ACCESS_MATURE || sim_access == SIM_ACCESS_ADULT); getChild("rating_icon_adult")->setVisible(sim_access == SIM_ACCESS_ADULT); } void LLFloaterPreference::onChangeComplexityMode(const LLSD& newvalue) { bool enable_complexity = newvalue.asInteger() != LLVOAvatar::AV_RENDER_ONLY_SHOW_FRIENDS; getChild("IndirectMaxComplexity")->setEnabled(enable_complexity); } std::string get_category_path(LLFolderType::EType cat_type) { LLUUID cat_id = gInventory.findUserDefinedCategoryUUIDForType(cat_type); return get_category_path(cat_id); } void LLFloaterPreference::onChangeModelFolder() { if (gInventory.isInventoryUsable()) { getChild("upload_models")->setText(get_category_path(LLFolderType::FT_OBJECT)); } } void LLFloaterPreference::onChangePBRFolder() { if (gInventory.isInventoryUsable()) { getChild("upload_pbr")->setText(get_category_path(LLFolderType::FT_MATERIAL)); } } void LLFloaterPreference::onChangeTextureFolder() { if (gInventory.isInventoryUsable()) { getChild("upload_textures")->setText(get_category_path(LLFolderType::FT_TEXTURE)); } } void LLFloaterPreference::onChangeSoundFolder() { if (gInventory.isInventoryUsable()) { getChild("upload_sounds")->setText(get_category_path(LLFolderType::FT_SOUND)); } } void LLFloaterPreference::onChangeAnimationFolder() { if (gInventory.isInventoryUsable()) { getChild("upload_animation")->setText(get_category_path(LLFolderType::FT_ANIMATION)); } } // FIXME: this will stop you from spawning the sidetray from preferences dialog on login screen // but the UI for this will still be enabled void LLFloaterPreference::onClickBlockList() { LLFloaterSidePanelContainer::showPanel("people", "panel_people", LLSD().with("people_panel_tab_name", "blocked_panel")); } void LLFloaterPreference::onClickProxySettings() { LLFloaterReg::showInstance("prefs_proxy"); } void LLFloaterPreference::onClickTranslationSettings() { LLFloaterReg::showInstance("prefs_translation"); } void LLFloaterPreference::onClickAutoReplace() { LLFloaterReg::showInstance("prefs_autoreplace"); } void LLFloaterPreference::onClickSpellChecker() { LLFloaterReg::showInstance("prefs_spellchecker"); } void LLFloaterPreference::onClickRenderExceptions() { LLFloaterReg::showInstance("avatar_render_settings"); } void LLFloaterPreference::onClickAutoAdjustments() { LLFloaterPerformance* performance_floater = LLFloaterReg::showTypedInstance("performance"); if (performance_floater) { performance_floater->showAutoadjustmentsPanel(); } } void LLFloaterPreference::onClickAdvanced() { LLFloaterReg::showInstance("prefs_graphics_advanced"); LLTabContainer* tabcontainer = getChild("pref core"); for (child_list_t::const_iterator iter = tabcontainer->getChildList()->begin(); iter != tabcontainer->getChildList()->end(); ++iter) { LLView* view = *iter; LLPanelPreferenceGraphics* panel = dynamic_cast(view); if (panel) { panel->resetDirtyChilds(); } } } void LLFloaterPreference::onClickActionChange() { updateClickActionControls(); } void LLFloaterPreference::onAtmosShaderChange() { if (LLCheckBoxCtrl* ctrl_alm = getChild("UseLightShaders")) { //Deferred/SSAO/Shadows bool bumpshiny = LLCubeMap::sUseCubeMaps && LLFeatureManager::getInstance()->isFeatureAvailable("RenderObjectBump") && gSavedSettings.getBOOL("RenderObjectBump"); bool shaders = gSavedSettings.getBOOL("WindLightUseAtmosShaders"); bool enabled = LLFeatureManager::getInstance()->isFeatureAvailable("RenderDeferred") && bumpshiny && shaders; ctrl_alm->setEnabled(enabled); } } void LLFloaterPreference::onClickPermsDefault() { LLFloaterReg::showInstance("perms_default"); } void LLFloaterPreference::onClickRememberedUsernames() { LLFloaterReg::showInstance("forget_username"); } void LLFloaterPreference::onDeleteTranscripts() { LLSD args; args["FOLDER"] = gDirUtilp->getUserName(); LLNotificationsUtil::add("PreferenceChatDeleteTranscripts", args, LLSD(), boost::bind(&LLFloaterPreference::onDeleteTranscriptsResponse, this, _1, _2)); } void LLFloaterPreference::onDeleteTranscriptsResponse(const LLSD& notification, const LLSD& response) { if (0 == LLNotificationsUtil::getSelectedOption(notification, response)) { LLLogChat::deleteTranscripts(); updateDeleteTranscriptsButton(); } } void LLFloaterPreference::onLogChatHistorySaved() { if (!mDeleteTranscriptsBtn->getEnabled()) { mDeleteTranscriptsBtn->setEnabled(true); } } void LLFloaterPreference::updateClickActionControls() { const int single_clk_action = getChild("single_click_action_combo")->getValue().asInteger(); const int double_clk_action = getChild("double_click_action_combo")->getValue().asInteger(); // Todo: This is a very ugly way to get access to keybindings. // Reconsider possible options. // Potential option: make constructor of LLKeyConflictHandler private // but add a getter that will return shared pointer for specific // mode, pointer should only exist so long as there are external users. // In such case we won't need to do this 'dynamic_cast' nightmare. // updateTable() can also be avoided LLTabContainer* tabcontainer = getChild("pref core"); for (LLView* view : *tabcontainer->getChildList()) { if (LLPanelPreferenceControls* panel = dynamic_cast(view)) { panel->setKeyBind("walk_to", EMouseClickType::CLICK_LEFT, KEY_NONE, MASK_NONE, single_clk_action == 1); panel->setKeyBind("walk_to", EMouseClickType::CLICK_DOUBLELEFT, KEY_NONE, MASK_NONE, double_clk_action == 1); panel->setKeyBind("teleport_to", EMouseClickType::CLICK_DOUBLELEFT, KEY_NONE, MASK_NONE, double_clk_action == 2); panel->updateAndApply(); } } } void LLFloaterPreference::updateClickActionViews() { bool click_to_walk = false; bool dbl_click_to_walk = false; bool dbl_click_to_teleport = false; // Todo: This is a very ugly way to get access to keybindings. // Reconsider possible options. LLTabContainer* tabcontainer = getChild("pref core"); for (LLView* view : *tabcontainer->getChildList()) { if (LLPanelPreferenceControls* panel = dynamic_cast(view)) { click_to_walk = panel->canKeyBindHandle("walk_to", EMouseClickType::CLICK_LEFT, KEY_NONE, MASK_NONE); dbl_click_to_walk = panel->canKeyBindHandle("walk_to", EMouseClickType::CLICK_DOUBLELEFT, KEY_NONE, MASK_NONE); dbl_click_to_teleport = panel->canKeyBindHandle("teleport_to", EMouseClickType::CLICK_DOUBLELEFT, KEY_NONE, MASK_NONE); } } getChild("single_click_action_combo")->setValue((int)click_to_walk); getChild("double_click_action_combo")->setValue(dbl_click_to_teleport ? 2 : (int)dbl_click_to_walk); } void LLFloaterPreference::updateSearchableItems() { mSearchDataDirty = true; } void LLFloaterPreference::applyUIColor(LLUICtrl* ctrl, const LLSD& param) { LLUIColorTable::instance().setColor(param.asString(), LLColor4(ctrl->getValue())); } void LLFloaterPreference::getUIColor(LLUICtrl* ctrl, const LLSD& param) { LLColorSwatchCtrl* color_swatch = (LLColorSwatchCtrl*) ctrl; color_swatch->setOriginal(LLUIColorTable::instance().getColor(param.asString())); } void LLFloaterPreference::setCacheLocation(const LLStringExplicit& location) { LLUICtrl* cache_location_editor = getChild("cache_location"); cache_location_editor->setValue(location); cache_location_editor->setToolTip(location); } void LLFloaterPreference::selectPanel(const LLSD& name) { LLTabContainer * tab_containerp = getChild("pref core"); LLPanel * panel = tab_containerp->getPanelByName(name.asStringRef()); if (NULL != panel) { tab_containerp->selectTabPanel(panel); } } void LLFloaterPreference::setPanelVisibility(const LLSD& name, bool visible) { LLTabContainer * tab_containerp = getChild("pref core"); LLPanel * panel = tab_containerp->getPanelByName(name.asStringRef()); if (NULL != panel) { tab_containerp->setTabVisibility(panel, visible); } } void LLFloaterPreference::selectPrivacyPanel() { selectPanel("im"); } void LLFloaterPreference::selectChatPanel() { selectPanel("chat"); } void LLFloaterPreference::changed() { getChild("clear_log")->setEnabled(LLConversationLog::instance().getConversations().size() > 0); // set 'enable' property for 'Delete transcripts...' button updateDeleteTranscriptsButton(); } void LLFloaterPreference::saveGraphicsPreset(std::string& preset) { mSavedGraphicsPreset = preset; } //------------------------------Updater--------------------------------------- static bool handleBandwidthChanged(const LLSD& newvalue) { gViewerThrottle.setMaxBandwidth((F32) newvalue.asReal()); return true; } class LLPanelPreference::Updater : public LLEventTimer { public: typedef boost::function callback_t; Updater(callback_t cb, F32 period) :LLEventTimer(period), mCallback(cb) { stop(); } virtual ~Updater(){} void update(const LLSD& new_value) { mNewValue = new_value; start(); } protected: bool tick() override { mCallback(mNewValue); stop(); return false; } private: LLSD mNewValue; callback_t mCallback; }; //---------------------------------------------------------------------------- static LLPanelInjector t_places("panel_preference"); LLPanelPreference::LLPanelPreference() : LLPanel(), mBandWidthUpdater(NULL) { mCommitCallbackRegistrar.add("Pref.setControlFalse", { boost::bind(&LLPanelPreference::setControlFalse,this, _2), cb_info::UNTRUSTED_BLOCK }); mCommitCallbackRegistrar.add("Pref.updateMediaAutoPlayCheckbox", { boost::bind(&LLPanelPreference::updateMediaAutoPlayCheckbox, this, _1), cb_info::UNTRUSTED_BLOCK }); mCommitCallbackRegistrar.add("Pref.PrefDelete", { boost::bind(&LLPanelPreference::deletePreset, this, _2), cb_info::UNTRUSTED_BLOCK }); mCommitCallbackRegistrar.add("Pref.PrefSave", { boost::bind(&LLPanelPreference::savePreset, this, _2), cb_info::UNTRUSTED_BLOCK }); mCommitCallbackRegistrar.add("Pref.PrefLoad", { boost::bind(&LLPanelPreference::loadPreset, this, _2), cb_info::UNTRUSTED_BLOCK }); } //virtual bool LLPanelPreference::postBuild() { ////////////////////// PanelGeneral /////////////////// if (hasChild("display_names_check", true)) { bool use_people_api = gSavedSettings.getBOOL("UsePeopleAPI"); LLCheckBoxCtrl* ctrl_display_name = getChild("display_names_check"); ctrl_display_name->setEnabled(use_people_api); if (!use_people_api) { ctrl_display_name->setValue(false); } } ////////////////////// PanelVoice /////////////////// if (hasChild("voice_unavailable", true)) { bool voice_disabled = gSavedSettings.getBOOL("CmdLineDisableVoice"); getChildView("voice_unavailable")->setVisible( voice_disabled); getChildView("enable_voice_check")->setVisible( !voice_disabled); } //////////////////////PanelSkins /////////////////// if (hasChild("skin_selection", true)) { LLFloaterPreference::refreshSkin(this); // if skin is set to a skin that no longer exists (silver) set back to default if (getChild("skin_selection")->getSelectedIndex() < 0) { gSavedSettings.setString("SkinCurrent", "default"); LLFloaterPreference::refreshSkin(this); } } //////////////////////PanelPrivacy /////////////////// if (hasChild("media_enabled", true)) { bool media_enabled = gSavedSettings.getBOOL("AudioStreamingMedia"); getChild("media_enabled")->set(media_enabled); getChild("autoplay_enabled")->setEnabled(media_enabled); } if (hasChild("music_enabled", true)) { getChild("music_enabled")->set(gSavedSettings.getBOOL("AudioStreamingMusic")); } if (hasChild("voice_call_friends_only_check", true)) { getChild("voice_call_friends_only_check")->setCommitCallback(boost::bind(&showFriendsOnlyWarning, _1, _2)); } if (hasChild("allow_multiple_viewer_check", true)) { getChild("allow_multiple_viewer_check")->setCommitCallback(boost::bind(&showMultipleViewersWarning, _1, _2)); } if (hasChild("favorites_on_login_check", true)) { getChild("favorites_on_login_check")->setCommitCallback(boost::bind(&handleFavoritesOnLoginChanged, _1, _2)); bool show_favorites_at_login = LLPanelLogin::getShowFavorites(); getChild("favorites_on_login_check")->setValue(show_favorites_at_login); } if (hasChild("mute_chb_label", true)) { getChild("mute_chb_label")->setShowCursorHand(false); getChild("mute_chb_label")->setSoundFlags(LLView::MOUSE_UP); getChild("mute_chb_label")->setClickedCallback(boost::bind(&toggleMuteWhenMinimized)); } //////////////////////PanelSetup /////////////////// if (hasChild("max_bandwidth", true)) { mBandWidthUpdater = new LLPanelPreference::Updater(boost::bind(&handleBandwidthChanged, _1), BANDWIDTH_UPDATER_TIMEOUT); gSavedSettings.getControl("ThrottleBandwidthKBPS")->getSignal()->connect(boost::bind(&LLPanelPreference::Updater::update, mBandWidthUpdater, _2)); } #ifdef EXTERNAL_TOS LLRadioGroup* ext_browser_settings = getChild("preferred_browser_behavior"); if (ext_browser_settings) { // turn off ability to set external/internal browser ext_browser_settings->setSelectedByValue(LLWeb::BROWSER_EXTERNAL_ONLY, true); ext_browser_settings->setEnabled(false); } #endif apply(); return true; } LLPanelPreference::~LLPanelPreference() { if (mBandWidthUpdater) { delete mBandWidthUpdater; } } // virtual void LLPanelPreference::apply() { // no-op } // virtual void LLPanelPreference::saveSettings() { // Save the value of all controls in the hierarchy mSavedValues.clear(); std::list view_stack; view_stack.push_back(this); // 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()) { // Process view on top of the stack LLView* curview = view_stack.front(); view_stack.pop_front(); if (LLColorSwatchCtrl* color_swatch = dynamic_cast(curview)) { mSavedColors[color_swatch->getName()] = color_swatch->get(); } else { if (LLUICtrl* ctrl = dynamic_cast(curview)) { if (LLControlVariable* control = ctrl->getControlVariable()) { mSavedValues[control] = control->getValue(); } } } // Push children onto the end of the work stack for (LLView* view : *curview->getChildList()) { view_stack.push_back(view); } } if (LLStartUp::getStartupState() == STATE_STARTED) { LLControlVariable* control = gSavedPerAccountSettings.getControl("VoiceCallsFriendsOnly"); if (control) { mSavedValues[control] = control->getValue(); } } } void LLPanelPreference::showMultipleViewersWarning(LLUICtrl* checkbox, const LLSD& value) { if (checkbox && checkbox->getValue()) { LLNotificationsUtil::add("AllowMultipleViewers"); } } void LLPanelPreference::showFriendsOnlyWarning(LLUICtrl* checkbox, const LLSD& value) { if (checkbox) { gSavedPerAccountSettings.setBOOL("VoiceCallsFriendsOnly", checkbox->getValue().asBoolean()); if (checkbox->getValue()) { LLNotificationsUtil::add("FriendsAndGroupsOnly"); } } } void LLPanelPreference::handleFavoritesOnLoginChanged(LLUICtrl* checkbox, const LLSD& value) { if (checkbox) { LLFavoritesOrderStorage::instance().showFavoritesOnLoginChanged(checkbox->getValue().asBoolean()); if(checkbox->getValue()) { LLNotificationsUtil::add("FavoritesOnLogin"); } } } void LLPanelPreference::toggleMuteWhenMinimized() { std::string mute("MuteWhenMinimized"); gSavedSettings.setBOOL(mute, !gSavedSettings.getBOOL(mute)); LLFloaterPreference* instance = LLFloaterReg::findTypedInstance("preferences"); if (instance) { instance->getChild("mute_when_minimized")->setBtnFocus(); } } 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; } auto found = std::find(settings_to_skip.begin(), settings_to_skip.end(), control->getName()); if (found != settings_to_skip.end()) { continue; } control->set(ctrl_value); } for (string_color_map_t::iterator iter = mSavedColors.begin(); iter != mSavedColors.end(); ++iter) { LLColorSwatchCtrl* color_swatch = findChild(iter->first); if (color_swatch) { color_swatch->set(iter->second); color_swatch->onCommit(); } } } void LLPanelPreference::setControlFalse(const LLSD& user_data) { std::string control_name = user_data.asString(); LLControlVariable* control = findControl(control_name); if (control) control->set(LLSD(false)); } void LLPanelPreference::updateMediaAutoPlayCheckbox(LLUICtrl* ctrl) { std::string name = ctrl->getName(); // Disable "Allow Media to auto play" only when both // "Streaming Music" and "Media" are unchecked. STORM-513. if ((name == "enable_music") || (name == "enable_media")) { bool music_enabled = getChild("enable_music")->get(); bool media_enabled = getChild("enable_media")->get(); getChild("media_auto_play_combo")->setEnabled(music_enabled || media_enabled); } } void LLPanelPreference::deletePreset(const LLSD& user_data) { LLFloaterReg::showInstance("delete_pref_preset", user_data.asString()); } void LLPanelPreference::savePreset(const LLSD& user_data) { LLFloaterReg::showInstance("save_pref_preset", user_data.asString()); } void LLPanelPreference::loadPreset(const LLSD& user_data) { LLFloaterReg::showInstance("load_pref_preset", user_data.asString()); } void LLPanelPreference::setHardwareDefaults() { } class LLPanelPreferencePrivacy : public LLPanelPreference { public: LLPanelPreferencePrivacy() { mAccountIndependentSettings.push_back("AutoDisengageMic"); } void saveSettings() override { LLPanelPreference::saveSettings(); // Don't save (=erase from the saved values map) per-account privacy settings // if we're not logged in, otherwise they will be reset to defaults on log off. if (LLStartUp::getStartupState() != STATE_STARTED) { // Erase only common settings, assuming there are no color settings on Privacy page. for (control_values_map_t::iterator it = mSavedValues.begin(); it != mSavedValues.end(); ) { const std::string setting = it->first->getName(); if (find(mAccountIndependentSettings.begin(), mAccountIndependentSettings.end(), setting) == mAccountIndependentSettings.end()) { it = mSavedValues.erase(it); } else { ++it; } } } } private: std::list mAccountIndependentSettings; }; static LLPanelInjector t_pref_graph("panel_preference_graphics"); static LLPanelInjector t_pref_privacy("panel_preference_privacy"); bool LLPanelPreferenceGraphics::postBuild() { LLFloaterReg::showInstance("prefs_graphics_advanced"); LLFloaterReg::hideInstance("prefs_graphics_advanced"); resetDirtyChilds(); setPresetText(); LLPresetsManager* presetsMgr = LLPresetsManager::getInstance(); presetsMgr->setPresetListChangeCallback(boost::bind(&LLPanelPreferenceGraphics::onPresetsListChange, this)); presetsMgr->createMissingDefault(PRESETS_GRAPHIC); // a no-op after the first time, but that's ok return LLPanelPreference::postBuild(); } void LLPanelPreferenceGraphics::draw() { LLPanelPreference::draw(); } void LLPanelPreferenceGraphics::onPresetsListChange() { resetDirtyChilds(); setPresetText(); LLFloaterPreference* instance = LLFloaterReg::findTypedInstance("preferences"); if (instance && !gSavedSettings.getString("PresetGraphicActive").empty()) { instance->saveSettings(); //make cancel work correctly after changing the preset } } void LLPanelPreferenceGraphics::setPresetText() { LLTextBox* preset_text = getChild("preset_text"); std::string preset_graphic_active = gSavedSettings.getString("PresetGraphicActive"); if (!preset_graphic_active.empty() && preset_graphic_active != preset_text->getText()) { LLFloaterPreference* instance = LLFloaterReg::findTypedInstance("preferences"); if (instance) { instance->saveGraphicsPreset(preset_graphic_active); } } if (hasDirtyChilds() && !preset_graphic_active.empty()) { gSavedSettings.setString("PresetGraphicActive", ""); preset_graphic_active.clear(); // This doesn't seem to cause an infinite recursion. This trigger is needed to cause the pulldown // panel to update. LLPresetsManager::getInstance()->triggerChangeSignal(); } if (!preset_graphic_active.empty()) { if (preset_graphic_active == PRESETS_DEFAULT) { preset_graphic_active = LLTrans::getString(PRESETS_DEFAULT); } preset_text->setText(preset_graphic_active); } else { preset_text->setText(LLTrans::getString("none_paren_cap")); } preset_text->resetDirty(); } bool LLPanelPreferenceGraphics::hasDirtyChilds() { LLFloater* advanced = LLFloaterReg::findTypedInstance("prefs_graphics_advanced"); std::list view_stack; view_stack.push_back(this); if (advanced) { view_stack.push_back(advanced); } while(!view_stack.empty()) { // Process view on top of the stack LLView* curview = view_stack.front(); view_stack.pop_front(); LLUICtrl* ctrl = dynamic_cast(curview); if (ctrl) { if (ctrl->isDirty()) { LLControlVariable* control = ctrl->getControlVariable(); if (control) { std::string control_name = control->getName(); if (!control_name.empty()) { return true; } } } } // Push children onto the end of the work stack for (child_list_t::const_iterator iter = curview->getChildList()->begin(); iter != curview->getChildList()->end(); ++iter) { view_stack.push_back(*iter); } } return false; } void LLPanelPreferenceGraphics::resetDirtyChilds() { LLFloater* advanced = LLFloaterReg::findTypedInstance("prefs_graphics_advanced"); std::list view_stack; view_stack.push_back(this); if (advanced) { view_stack.push_back(advanced); } while(!view_stack.empty()) { // Process view on top of the stack LLView* curview = view_stack.front(); view_stack.pop_front(); LLUICtrl* ctrl = dynamic_cast(curview); if (ctrl) { ctrl->resetDirty(); } // Push children onto the end of the work stack for (child_list_t::const_iterator iter = curview->getChildList()->begin(); iter != curview->getChildList()->end(); ++iter) { view_stack.push_back(*iter); } } } void LLPanelPreferenceGraphics::cancel(const std::vector settings_to_skip) { LLPanelPreference::cancel(settings_to_skip); } void LLPanelPreferenceGraphics::saveSettings() { resetDirtyChilds(); std::string preset_graphic_active = gSavedSettings.getString("PresetGraphicActive"); if (preset_graphic_active.empty()) { LLFloaterPreference* instance = LLFloaterReg::findTypedInstance("preferences"); if (instance) { //don't restore previous preset after closing Preferences instance->saveGraphicsPreset(preset_graphic_active); } } LLPanelPreference::saveSettings(); } void LLPanelPreferenceGraphics::setHardwareDefaults() { resetDirtyChilds(); } //------------------------LLPanelPreferenceControls-------------------------------- static LLPanelInjector t_pref_contrls("panel_preference_controls"); LLPanelPreferenceControls::LLPanelPreferenceControls() :LLPanelPreference(), mEditingColumn(-1), mEditingMode(0) { // MODE_COUNT - 1 because there are currently no settings assigned to 'saved settings'. for (U32 i = 0; i < LLKeyConflictHandler::MODE_COUNT - 1; ++i) { mConflictHandler[i].setLoadMode((LLKeyConflictHandler::ESourceMode)i); } } LLPanelPreferenceControls::~LLPanelPreferenceControls() { } void LLPanelPreferenceControls::refresh() { populateControlTable(); LLPanelPreference::refresh(); } bool LLPanelPreferenceControls::postBuild() { // populate list of controls pControlsTable = getChild("controls_list"); pKeyModeBox = getChild("key_mode"); pControlsTable->setCommitCallback(boost::bind(&LLPanelPreferenceControls::onListCommit, this)); pKeyModeBox->setCommitCallback(boost::bind(&LLPanelPreferenceControls::onModeCommit, this)); getChild("restore_defaults")->setCommitCallback(boost::bind(&LLPanelPreferenceControls::onRestoreDefaultsBtn, this)); return true; } void LLPanelPreferenceControls::regenerateControls() { mEditingMode = pKeyModeBox->getValue().asInteger(); mConflictHandler[mEditingMode].loadFromSettings((LLKeyConflictHandler::ESourceMode)mEditingMode); populateControlTable(); } bool LLPanelPreferenceControls::addControlTableColumns(const std::string &filename) { LLXMLNodePtr xmlNode; LLScrollListCtrl::Contents contents; if (!LLUICtrlFactory::getLayeredXMLNode(filename, xmlNode)) { LL_WARNS("Preferences") << "Failed to load " << filename << LL_ENDL; return false; } LLXUIParser parser; parser.readXUI(xmlNode, contents, filename); if (!contents.validateBlock()) { return false; } for (LLInitParam::ParamIterator::const_iterator col_it = contents.columns.begin(); col_it != contents.columns.end(); ++col_it) { pControlsTable->addColumn(*col_it); } return true; } bool LLPanelPreferenceControls::addControlTableRows(const std::string &filename) { LLXMLNodePtr xmlNode; LLScrollListCtrl::Contents contents; if (!LLUICtrlFactory::getLayeredXMLNode(filename, xmlNode)) { LL_WARNS("Preferences") << "Failed to load " << filename << LL_ENDL; return false; } LLXUIParser parser; parser.readXUI(xmlNode, contents, filename); if (!contents.validateBlock()) { return false; } LLScrollListCell::Params cell_params; // init basic cell params cell_params.font = LLFontGL::getFontSansSerif(); cell_params.font_halign = LLFontGL::LEFT; cell_params.column = ""; cell_params.value = ""; for (LLScrollListItem::Params& row_params : contents.rows) { std::string control = row_params.value.getValue().asString(); if (!control.empty() && control != "menu_separator") { bool show = true; bool enabled = mConflictHandler[mEditingMode].canAssignControl(control); if (!enabled) { // If empty: this is a placeholder to make sure user won't assign // value by accident, don't show it // If not empty: predefined control combination user should see // to know that combination is reserved show = !mConflictHandler[mEditingMode].isControlEmpty(control); // example: teleport_to and walk_to in first person view, and // sitting related functions, see generatePlaceholders() } if (show) { // At the moment viewer is hardcoded to assume that columns are named as lst_ctrl%d LLScrollListItem::Params item_params(row_params); item_params.enabled.setValue(enabled); S32 num_columns = pControlsTable->getNumColumns(); for (S32 col = 1; col < num_columns; col++) { cell_params.column = llformat("lst_ctrl%d", col); cell_params.value = mConflictHandler[mEditingMode].getControlString(control, col - 1); item_params.columns.add(cell_params); } pControlsTable->addRow(item_params, EAddPosition::ADD_BOTTOM); } } else { // Separator example: // // // pControlsTable->addRow(row_params, EAddPosition::ADD_BOTTOM); } } return true; } void LLPanelPreferenceControls::addControlTableSeparator() { LLScrollListItem::Params separator_params; separator_params.enabled(false); LLScrollListCell::Params column_params; column_params.type = "icon"; column_params.value = "menu_separator"; column_params.column = "lst_action"; column_params.color = LLColor4(0.f, 0.f, 0.f, 0.7f); column_params.font_halign = LLFontGL::HCENTER; separator_params.columns.add(column_params); pControlsTable->addRow(separator_params, EAddPosition::ADD_BOTTOM); } void LLPanelPreferenceControls::populateControlTable() { pControlsTable->clearRows(); pControlsTable->clearColumns(); // Add columns std::string filename; switch ((LLKeyConflictHandler::ESourceMode)mEditingMode) { case LLKeyConflictHandler::MODE_THIRD_PERSON: case LLKeyConflictHandler::MODE_FIRST_PERSON: case LLKeyConflictHandler::MODE_EDIT_AVATAR: case LLKeyConflictHandler::MODE_SITTING: filename = "control_table_contents_columns_basic.xml"; break; default: { // Either unknown mode or MODE_SAVED_SETTINGS // It doesn't have UI or actual settings yet LL_WARNS("Preferences") << "Unimplemented mode" << LL_ENDL; // Searchable columns were removed, mark searchables for an update LLFloaterPreference* instance = LLFloaterReg::findTypedInstance("preferences"); if (instance) { instance->updateSearchableItems(); } return; } } addControlTableColumns(filename); // Add rows. // Each file represents individual visual group (movement/camera/media...) if (mEditingMode == LLKeyConflictHandler::MODE_FIRST_PERSON) { // Don't display whole camera and editing groups addControlTableRows("control_table_contents_movement.xml"); addControlTableSeparator(); addControlTableRows("control_table_contents_media.xml"); addControlTableSeparator(); if (LLGameControl::isEnabled()) { addControlTableRows("control_table_contents_game_control.xml"); } } // MODE_THIRD_PERSON; MODE_EDIT_AVATAR; MODE_SITTING else if (mEditingMode < LLKeyConflictHandler::MODE_SAVED_SETTINGS) { // In case of 'sitting' mode, movements still apply due to vehicles // but walk_to is not supported and will be hidden by addControlTableRows addControlTableRows("control_table_contents_movement.xml"); addControlTableSeparator(); addControlTableRows("control_table_contents_camera.xml"); addControlTableSeparator(); addControlTableRows("control_table_contents_editing.xml"); addControlTableSeparator(); addControlTableRows("control_table_contents_media.xml"); addControlTableSeparator(); if (LLGameControl::isEnabled()) { addControlTableRows("control_table_contents_game_control.xml"); } } else { LL_WARNS("Preferences") << "Unimplemented mode" << LL_ENDL; } // explicit update to make sure table is ready for llsearchableui pControlsTable->updateColumns(); // Searchable columns were removed and readded, mark searchables for an update // Note: at the moment tables/lists lack proper llsearchableui support LLFloaterPreference* instance = LLFloaterReg::findTypedInstance("preferences"); if (instance) { instance->updateSearchableItems(); } } void LLPanelPreferenceControls::updateTable() { mEditingControl.clear(); std::vector list = pControlsTable->getAllData(); for (S32 i = 0; i < list.size(); ++i) { std::string control = list[i]->getValue(); if (!control.empty()) { LLScrollListCell* cell = NULL; S32 num_columns = pControlsTable->getNumColumns(); for (S32 col = 1; col < num_columns; col++) { cell = list[i]->getColumn(col); cell->setValue(mConflictHandler[mEditingMode].getControlString(control, col - 1)); } } } pControlsTable->deselectAllItems(); } void LLPanelPreferenceControls::apply() { for (U32 i = 0; i < LLKeyConflictHandler::MODE_COUNT - 1; ++i) { if (mConflictHandler[i].hasUnsavedChanges()) { mConflictHandler[i].saveToSettings(); } } } void LLPanelPreferenceControls::cancel(const std::vector settings_to_skip) { for (U32 i = 0; i < LLKeyConflictHandler::MODE_COUNT - 1; ++i) { if (mConflictHandler[i].hasUnsavedChanges()) { mConflictHandler[i].clear(); if (mEditingMode == i) { // cancel() can be called either when preferences floater closes // or when child floater closes (like advanced graphical settings) // in which case we need to clear and repopulate table regenerateControls(); } } } } void LLPanelPreferenceControls::saveSettings() { for (U32 i = 0; i < LLKeyConflictHandler::MODE_COUNT - 1; ++i) { if (mConflictHandler[i].hasUnsavedChanges()) { mConflictHandler[i].saveToSettings(); mConflictHandler[i].clear(); } } S32 mode = pKeyModeBox->getValue().asInteger(); if (mConflictHandler[mode].empty() || pControlsTable->isEmpty()) { regenerateControls(); } } void LLPanelPreferenceControls::resetDirtyChilds() { regenerateControls(); } void LLPanelPreferenceControls::onListCommit() { LLScrollListItem* item = pControlsTable->getFirstSelected(); if (item == NULL) { return; } std::string control = item->getValue(); if (control.empty()) { pControlsTable->deselectAllItems(); return; } if (!mConflictHandler[mEditingMode].canAssignControl(control)) { pControlsTable->deselectAllItems(); return; } S32 cell_ind = item->getSelectedCell(); if (cell_ind <= 0) { pControlsTable->deselectAllItems(); return; } // List does not tell us what cell was clicked, so we have to figure it out manually, but // fresh mouse coordinates are not yet accessible during onCommit() and there are other issues, // so we cheat: remember item user clicked at, trigger 'key dialog' on hover that comes next, // use coordinates from hover to calculate cell LLScrollListCell* cell = item->getColumn(cell_ind); if (cell) { LLSetKeyBindDialog* dialog = LLFloaterReg::getTypedInstance("keybind_dialog", LLSD()); if (dialog) { mEditingControl = control; mEditingColumn = cell_ind; dialog->setParent(this, pControlsTable, DEFAULT_KEY_FILTER); LLFloater* root_floater = gFloaterView->getParentFloater(this); if (root_floater) root_floater->addDependentFloater(dialog); dialog->openFloater(); dialog->setFocus(true); } } else { pControlsTable->deselectAllItems(); } } void LLPanelPreferenceControls::onModeCommit() { mEditingMode = pKeyModeBox->getValue().asInteger(); if (mConflictHandler[mEditingMode].empty()) { // opening for first time mConflictHandler[mEditingMode].loadFromSettings((LLKeyConflictHandler::ESourceMode)mEditingMode); } populateControlTable(); } void LLPanelPreferenceControls::onRestoreDefaultsBtn() { LLNotificationsUtil::add("PreferenceControlsDefaults", LLSD(), LLSD(), boost::bind(&LLPanelPreferenceControls::onRestoreDefaultsResponse, this, _1, _2)); } void LLPanelPreferenceControls::onRestoreDefaultsResponse(const LLSD& notification, const LLSD& response) { S32 option = LLNotificationsUtil::getSelectedOption(notification, response); switch(option) { case 0: // All for (U32 i = 0; i < LLKeyConflictHandler::MODE_COUNT - 1; ++i) { mConflictHandler[i].resetToDefaults(); // Apply changes to viewer as 'temporary' mConflictHandler[i].saveToSettings(true); // notify comboboxes in move&view about potential change LLFloaterPreference* instance = LLFloaterReg::findTypedInstance("preferences"); if (instance) { instance->updateClickActionViews(); } } updateTable(); break; case 1: // Current mConflictHandler[mEditingMode].resetToDefaults(); // Apply changes to viewer as 'temporary' mConflictHandler[mEditingMode].saveToSettings(true); if (mEditingMode == LLKeyConflictHandler::MODE_THIRD_PERSON) { // notify comboboxes in move&view about potential change LLFloaterPreference* instance = LLFloaterReg::findTypedInstance("preferences"); if (instance) { instance->updateClickActionViews(); } } updateTable(); break; case 2: // Cancel default: //exit; break; } } // Bypass to let Move & view read values without need to create own key binding handler // Assumes third person view // Might be better idea to just move whole mConflictHandler into LLFloaterPreference bool LLPanelPreferenceControls::canKeyBindHandle(const std::string &control, EMouseClickType click, KEY key, MASK mask) { S32 mode = LLKeyConflictHandler::MODE_THIRD_PERSON; if (mConflictHandler[mode].empty()) { // opening for first time mConflictHandler[mode].loadFromSettings(LLKeyConflictHandler::MODE_THIRD_PERSON); } return mConflictHandler[mode].canHandleControl(control, click, key, mask); } // Bypass to let Move & view modify values without need to create own key binding handler // Assumes third person view // Might be better idea to just move whole mConflictHandler into LLFloaterPreference void LLPanelPreferenceControls::setKeyBind(const std::string &control, EMouseClickType click, KEY key, MASK mask, bool set) { S32 mode = LLKeyConflictHandler::MODE_THIRD_PERSON; if (mConflictHandler[mode].empty()) { // opening for first time mConflictHandler[mode].loadFromSettings(LLKeyConflictHandler::MODE_THIRD_PERSON); } if (!mConflictHandler[mode].canAssignControl(mEditingControl)) { return; } bool already_recorded = mConflictHandler[mode].canHandleControl(control, click, key, mask); if (set) { if (already_recorded) { // nothing to do return; } // find free spot to add data, if no free spot, assign to first S32 index = 0; for (S32 i = 0; i < 3; i++) { if (mConflictHandler[mode].getControl(control, i).isEmpty()) { index = i; break; } } // At the moment 'ignore_mask' mask is mostly ignored, a placeholder // Todo: implement it since it's preferable for things like teleport to match // mask exactly but for things like running to ignore additional masks // Ideally this needs representation in keybindings UI bool ignore_mask = true; mConflictHandler[mode].registerControl(control, index, click, key, mask, ignore_mask); } else if (!set) { if (!already_recorded) { // nothing to do return; } // find specific control and reset it for (S32 i = 0; i < 3; i++) { LLKeyData data = mConflictHandler[mode].getControl(control, i); if (data.mMouse == click && data.mKey == key && data.mMask == mask) { mConflictHandler[mode].clearControl(control, i); } } } } void LLPanelPreferenceControls::updateAndApply() { S32 mode = LLKeyConflictHandler::MODE_THIRD_PERSON; mConflictHandler[mode].saveToSettings(true); updateTable(); } // from LLSetKeybindDialog's interface bool LLPanelPreferenceControls::onSetKeyBind(EMouseClickType click, KEY key, MASK mask, bool all_modes) { if (!mConflictHandler[mEditingMode].canAssignControl(mEditingControl)) { return true; } if ( mEditingColumn > 0) { if (all_modes) { for (U32 i = 0; i < LLKeyConflictHandler::MODE_COUNT - 1; ++i) { if (mConflictHandler[i].empty()) { mConflictHandler[i].loadFromSettings((LLKeyConflictHandler::ESourceMode)i); } mConflictHandler[i].registerControl(mEditingControl, mEditingColumn - 1, click, key, mask, true); // Apply changes to viewer as 'temporary' mConflictHandler[i].saveToSettings(true); } } else { mConflictHandler[mEditingMode].registerControl(mEditingControl, mEditingColumn - 1, click, key, mask, true); // Apply changes to viewer as 'temporary' mConflictHandler[mEditingMode].saveToSettings(true); } } updateTable(); if ((mEditingMode == LLKeyConflictHandler::MODE_THIRD_PERSON || all_modes) && (mEditingControl == "walk_to" || mEditingControl == "teleport_to" || click == CLICK_LEFT || click == CLICK_DOUBLELEFT)) { // notify comboboxes in move&view about potential change LLFloaterPreference* instance = LLFloaterReg::findTypedInstance("preferences"); if (instance) { instance->updateClickActionViews(); } } return true; } void LLPanelPreferenceControls::onDefaultKeyBind(bool all_modes) { if (!mConflictHandler[mEditingMode].canAssignControl(mEditingControl)) { return; } if (mEditingColumn > 0) { if (all_modes) { for (U32 i = 0; i < LLKeyConflictHandler::MODE_COUNT - 1; ++i) { if (mConflictHandler[i].empty()) { mConflictHandler[i].loadFromSettings((LLKeyConflictHandler::ESourceMode)i); } mConflictHandler[i].resetToDefault(mEditingControl, mEditingColumn - 1); // Apply changes to viewer as 'temporary' mConflictHandler[i].saveToSettings(true); } } else { mConflictHandler[mEditingMode].resetToDefault(mEditingControl, mEditingColumn - 1); // Apply changes to viewer as 'temporary' mConflictHandler[mEditingMode].saveToSettings(true); } } updateTable(); if (mEditingMode == LLKeyConflictHandler::MODE_THIRD_PERSON || all_modes) { // notify comboboxes in move&view about potential change LLFloaterPreference* instance = LLFloaterReg::findTypedInstance("preferences"); if (instance) { instance->updateClickActionViews(); } } } void LLPanelPreferenceControls::onCancelKeyBind() { pControlsTable->deselectAllItems(); } //------------------------LLPanelPreferenceGameControl-------------------------------- // LLPanelPreferenceGameControl is effectively a singleton, so we track its instance static LLPanelPreferenceGameControl* gGameControlPanel { nullptr }; static LLScrollListCtrl* gSelectedGrid { nullptr }; static LLScrollListItem* gSelectedItem { nullptr }; static LLScrollListCell* gSelectedCell { nullptr }; // static void LLPanelPreferenceGameControl::updateDeviceList() { if (gGameControlPanel) { gGameControlPanel->updateDeviceListInternal(); } } LLPanelPreferenceGameControl::LLPanelPreferenceGameControl() { gGameControlPanel = this; } LLPanelPreferenceGameControl::~LLPanelPreferenceGameControl() { gGameControlPanel = nullptr; } static LLPanelInjector t_pref_game_control("panel_preference_game_control"); // Collect all UI control values into mSavedValues void LLPanelPreferenceGameControl::saveSettings() { LLPanelPreference::saveSettings(); std::vector items = mActionTable->getAllData(); // 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(); }; // Use string formatting functions provided by class LLGameControl: if (LLControlVariable* analogMappings = gSavedSettings.getControl("AnalogChannelMappings")) { analogMappings->set(LLGameControl::stringifyAnalogMappings(getChannel)); mSavedValues[analogMappings] = analogMappings->getValue(); } if (LLControlVariable* binaryMappings = gSavedSettings.getControl("BinaryChannelMappings")) { binaryMappings->set(LLGameControl::stringifyBinaryMappings(getChannel)); mSavedValues[binaryMappings] = binaryMappings->getValue(); } 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::onGridSelect(LLUICtrl* ctrl) { clearSelectionState(); LLScrollListCtrl* table = dynamic_cast(ctrl); if (!table || !table->getEnabled()) return; if (LLScrollListItem* item = table->getFirstSelected()) { if (initCombobox(item, table)) return; table->deselectAllItems(); } } bool LLPanelPreferenceGameControl::initCombobox(LLScrollListItem* item, LLScrollListCtrl* grid) { if (item->getSelectedCell() != 1) return false; LLScrollListText* cell = dynamic_cast(item->getColumn(1)); if (!cell) return false; LLComboBox* combobox = nullptr; if (grid == mActionTable) { 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; } else if (grid == mAxisMappings) { combobox = mAxisSelector; } else if (grid == mButtonMappings) { combobox = mBinaryChannelSelector; } if (!combobox) return false; // compute new rect for combobox S32 row_index = grid->getItemIndex(item); fitInRect(combobox, grid, row_index, 1); 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; 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 = channel_name; } } if (value.empty()) { // 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(LLUICtrl* ctrl) { if (!gSelectedGrid || !gSelectedItem || !gSelectedCell) return; LLComboBox* combobox = dynamic_cast(ctrl); llassert(combobox); if (!combobox) return; 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) { 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()) { chosen_index = row_index; } std::string label = chosen_index == row_index ? LLStringUtil::null : combobox->getSelectedItemLabel(); gSelectedCell->setValue(label); map[row_index] = chosen_index; } } gSelectedGrid->deselectAllItems(); clearSelectionState(); } bool LLPanelPreferenceGameControl::isWaitingForInputChannel() { return gSelectedCell != nullptr; } // static 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() { clearSelectionState(); if (LLScrollListItem* row = mAxisOptions->getFirstSelected()) { LLGameControl::Options& options = getSelectedDeviceOptions(); S32 row_index = mAxisOptions->getItemIndex(row); { // always update invert checkbox value because even though it may have been clicked // the row does not know its cell has been selected constexpr S32 invert_checkbox_column = 1; bool invert = row->getColumn(invert_checkbox_column)->getValue().asBoolean(); options.getAxisOptions()[row_index].mMultiplier = invert ? -1 : 1; } S32 column_index = row->getSelectedCell(); if (column_index == 2 || column_index == 3) { fitInRect(mNumericValueEditor, mAxisOptions, row_index, column_index); if (column_index == 2) { mNumericValueEditor->setMinValue(0); mNumericValueEditor->setMaxValue(LLGameControl::MAX_AXIS_DEAD_ZONE); mNumericValueEditor->setValue(options.getAxisOptions()[row_index].mDeadZone); } else // column_index == 3 { mNumericValueEditor->setMinValue(-LLGameControl::MAX_AXIS_OFFSET); mNumericValueEditor->setMaxValue(LLGameControl::MAX_AXIS_OFFSET); mNumericValueEditor->setValue(options.getAxisOptions()[row_index].mOffset); } mNumericValueEditor->setVisible(true); } initCombobox(row, mAxisOptions); LLGameControl::setDeviceOptions(mSelectedDeviceGUID, options); } } 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); LLGameControl::setDeviceOptions(mSelectedDeviceGUID, deviceOptions); } } bool LLPanelPreferenceGameControl::postBuild() { // Above the tab container mCheckGameControlToServer = getChild("game_control_to_server"); mCheckGameControlToAgent = getChild("game_control_to_agent"); mCheckAgentToGameControl = getChild("agent_to_game_control"); mCheckGameControlToServer->setCommitCallback([this](LLUICtrl*, const LLSD&) { LLGameControl::setSendToServer(mCheckGameControlToServer->getValue()); updateActionTableState(); }); mCheckGameControlToAgent->setCommitCallback([this](LLUICtrl*, const LLSD&) { LLGameControl::setControlAgent(mCheckGameControlToAgent->getValue()); updateActionTableState(); }); mCheckAgentToGameControl->setCommitCallback([this](LLUICtrl*, const LLSD&) { LLGameControl::setTranslateAgentActions(mCheckAgentToGameControl->getValue()); updateActionTableState(); }); 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([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(); return true; } // 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()); clearSelectionState(); // Setup the 1st tab populateActionTableCells(); updateActionTableState(); updateDeviceListInternal(); updateEnable(); } void LLPanelPreferenceGameControl::updateDeviceListInternal() { // 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::populateActionTableCells() { 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); } } } } // static bool LLPanelPreferenceGameControl::parseXmlFile(LLScrollListCtrl::Contents& contents, const std::string& filename, const std::string& what) { LLXMLNodePtr xmlNode; if (!LLUICtrlFactory::getLayeredXMLNode(filename, xmlNode)) { 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 " << what << " from '" << filename << "'" << LL_ENDL; return false; } 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) { 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::populateDeviceSettings(const std::string& guid) { 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) { row_params.columns.add(cell_params); } 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) { LLScrollListItem* row = mAxisOptions->addRow(row_params); row->getColumn(0)->setValue(items[i]->getColumn(0)->getValue()); } } 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.mMultiplier == -1 ? true : false); 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::getFontMonospace(); for (size_t i = 0; i < target->getNumColumns(); ++i) { row_params.columns.add(cell_params); } for (size_t i = 0; i < row_count; ++i) { LLScrollListItem* row = target->addRow(row_params); row->getColumn(0)->setValue(items[i]->getColumn(0)->getValue()); } } 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) { 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()); } } LLGameControl::Options& LLPanelPreferenceGameControl::getSelectedDeviceOptions() { auto options_it = mDeviceOptions.find(mSelectedDeviceGUID); llassert_always(options_it != mDeviceOptions.end()); return options_it->second.options; } // 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; } } } 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) { childRect.mRight = rect.mRight; } child->setRect(childRect); } } void LLPanelPreferenceGameControl::clearSelectionState() { gSelectedGrid = nullptr; gSelectedItem = nullptr; gSelectedCell = nullptr; mNumericValueEditor->setVisible(false); mAnalogChannelSelector->setVisible(false); mBinaryChannelSelector->setVisible(false); mAxisSelector->setVisible(false); } void LLPanelPreferenceGameControl::addActionTableSeparator() { LLScrollListItem::Params separator_params; separator_params.enabled(false); LLScrollListCell::Params column_params; column_params.type = "icon"; column_params.value = "menu_separator"; column_params.column = "action"; column_params.color = LLColor4(0.f, 0.f, 0.f, 0.7f); column_params.font_halign = LLFontGL::HCENTER; separator_params.columns.add(column_params); mActionTable->addRow(separator_params, EAddPosition::ADD_BOTTOM); } void LLPanelPreferenceGameControl::updateEnable() { bool enabled = LLGameControl::isEnabled(); LLGameControl::setEnabled(enabled); mCheckGameControlToServer->setEnabled(enabled); mCheckGameControlToAgent->setEnabled(enabled); mCheckAgentToGameControl->setEnabled(enabled); mActionTable->setEnabled(enabled); mAxisOptions->setEnabled(enabled); mAxisMappings->setEnabled(enabled); mButtonMappings->setEnabled(enabled); mDeviceList->setEnabled(enabled); if (!enabled) { //mActionTable->deselectAllItems(); mAnalogChannelSelector->setVisible(false); mBinaryChannelSelector->setVisible(false); clearSelectionState(); } } void LLPanelPreferenceGameControl::updateActionTableState() { // Enable the table if at least one of the GameControl<-->Agent options is enabled bool enable_table = LLGameControl::isEnabled() && (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), mSocksSettingsDirty(false) { mCommitCallbackRegistrar.add("Proxy.OK", { boost::bind(&LLFloaterPreferenceProxy::onBtnOk, this), cb_info::UNTRUSTED_BLOCK }); mCommitCallbackRegistrar.add("Proxy.Cancel", { boost::bind(&LLFloaterPreferenceProxy::onBtnCancel, this), cb_info::UNTRUSTED_BLOCK }); mCommitCallbackRegistrar.add("Proxy.Change", { boost::bind(&LLFloaterPreferenceProxy::onChangeSocksSettings, this), cb_info::UNTRUSTED_BLOCK }); } LLFloaterPreferenceProxy::~LLFloaterPreferenceProxy() { } bool LLFloaterPreferenceProxy::postBuild() { LLRadioGroup* socksAuth = getChild("socks5_auth_type"); if (!socksAuth) { return false; } if (socksAuth->getSelectedValue().asString() == "None") { getChild("socks5_username")->setEnabled(false); getChild("socks5_password")->setEnabled(false); } else { // Populate the SOCKS 5 credential fields with protected values. LLPointer socks_cred = gSecAPIHandler->loadCredential("SOCKS5"); getChild("socks5_username")->setValue(socks_cred->getIdentifier()["username"].asString()); getChild("socks5_password")->setValue(socks_cred->getAuthenticator()["creds"].asString()); } return true; } void LLFloaterPreferenceProxy::onOpen(const LLSD& key) { saveSettings(); } void LLFloaterPreferenceProxy::onClose(bool app_quitting) { if (app_quitting) { cancel(); } if (mSocksSettingsDirty) { // If the user plays with the Socks proxy settings after login, it's only fair we let them know // it will not be updated until next restart. if (LLStartUp::getStartupState()>STATE_LOGIN_WAIT) { LLNotifications::instance().add("ChangeProxySettings", LLSD(), LLSD()); mSocksSettingsDirty = false; // we have notified the user now be quiet again } } } void LLFloaterPreferenceProxy::saveSettings() { // Save the value of all controls in the hierarchy mSavedValues.clear(); std::list view_stack; view_stack.push_back(this); while (!view_stack.empty()) { // Process view on top of the stack LLView* curview = view_stack.front(); view_stack.pop_front(); LLUICtrl* ctrl = dynamic_cast(curview); if (ctrl) { LLControlVariable* control = ctrl->getControlVariable(); if (control) { mSavedValues[control] = control->getValue(); } } // Push children onto the end of the work stack for (child_list_t::const_iterator iter = curview->getChildList()->begin(); iter != curview->getChildList()->end(); ++iter) { view_stack.push_back(*iter); } } } void LLFloaterPreferenceProxy::onBtnOk() { // commit any outstanding text entry if (hasFocus()) { LLUICtrl* cur_focus = dynamic_cast(gFocusMgr.getKeyboardFocus()); if (cur_focus && cur_focus->acceptsTextInput()) { cur_focus->onCommit(); } } // Save SOCKS proxy credentials securely if password auth is enabled LLRadioGroup* socksAuth = getChild("socks5_auth_type"); if (socksAuth->getSelectedValue().asString() == "UserPass") { LLSD socks_id = LLSD::emptyMap(); socks_id["type"] = "SOCKS5"; socks_id["username"] = getChild("socks5_username")->getValue().asString(); LLSD socks_authenticator = LLSD::emptyMap(); socks_authenticator["type"] = "SOCKS5"; socks_authenticator["creds"] = getChild("socks5_password")->getValue().asString(); // Using "SOCKS5" as the "grid" argument since the same proxy // settings will be used for all grids and because there is no // way to specify the type of credential. LLPointer socks_cred = gSecAPIHandler->createCredential("SOCKS5", socks_id, socks_authenticator); gSecAPIHandler->saveCredential(socks_cred, true); } else { // Clear SOCKS5 credentials since they are no longer needed. LLPointer socks_cred = new LLCredential("SOCKS5"); gSecAPIHandler->deleteCredential(socks_cred); } closeFloater(false); } void LLFloaterPreferenceProxy::onBtnCancel() { if (hasFocus()) { LLUICtrl* cur_focus = dynamic_cast(gFocusMgr.getKeyboardFocus()); if (cur_focus && cur_focus->acceptsTextInput()) { cur_focus->onCommit(); } refresh(); } cancel(); } void LLFloaterPreferenceProxy::onClickCloseBtn(bool app_quitting) { cancel(); } void LLFloaterPreferenceProxy::cancel() { for (const auto& iter : mSavedValues) { iter.first->set(iter.second); } mSocksSettingsDirty = false; closeFloater(); } void LLFloaterPreferenceProxy::onChangeSocksSettings() { mSocksSettingsDirty = true; LLRadioGroup* socksAuth = getChild("socks5_auth_type"); if (socksAuth->getSelectedValue().asString() == "None") { getChild("socks5_username")->setEnabled(false); getChild("socks5_password")->setEnabled(false); } else { getChild("socks5_username")->setEnabled(true); getChild("socks5_password")->setEnabled(true); } // 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() == false )||( otherHttpProxy->getSelectedValue().asString() == "Web" && getChild("web_proxy_enabled")->get() == false ) ) { otherHttpProxy->selectFirstItem(); } } void LLFloaterPreference::onUpdateFilterTerm(bool force) { LLWString seachValue = utf8str_to_wstring( mFilterEdit->getValue() ); LLWStringUtil::toLower( seachValue ); if( !mSearchData || (mSearchData->mLastFilter == seachValue && !force)) return; if (mSearchDataDirty) { // Data exists, but is obsolete, regenerate collectSearchableItems(); } mSearchData->mLastFilter = seachValue; if( !mSearchData->mRootTab ) return; mSearchData->mRootTab->hightlightAndHide( seachValue ); filterIgnorableNotifications(); if (LLTabContainer* pRoot = getChild("pref core")) pRoot->selectFirstTab(); } void LLFloaterPreference::filterIgnorableNotifications() { bool visible = mEnabledPopups->highlightMatchingItems(mFilterEdit->getValue()); visible |= mDisabledPopups->highlightMatchingItems(mFilterEdit->getValue()); if (visible) { getChildRef("pref core").setTabVisibility( getChild("msgs"), true ); } } void collectChildren( LLView const *aView, ll::prefs::PanelDataPtr aParentPanel, ll::prefs::TabContainerDataPtr aParentTabContainer ) { if( !aView ) return; llassert_always( aParentPanel || aParentTabContainer ); for (LLView* pView : *aView->getChildList()) { if (!pView) continue; 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 ); if( pTabContainer ) { pCurPanelData.reset(); 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 ); } else if( pPanel ) { pCurTabContainer.reset(); 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 ); } else if( pSCtrl && pSCtrl->getSearchText().size() ) { 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 ); llassert_always( aParentPanel || aParentTabContainer ); if( aParentPanel ) aParentPanel->mChildren.push_back( item ); if( aParentTabContainer ) aParentTabContainer->mChildren.push_back( item ); } collectChildren( pView, pCurPanelData, pCurTabContainer ); } } void LLFloaterPreference::collectSearchableItems() { mSearchData.reset( nullptr ); LLTabContainer *pRoot = getChild< LLTabContainer >( "pref core" ); if( mFilterEdit && pRoot ) { mSearchData.reset(new ll::prefs::SearchData() ); ll::prefs::TabContainerDataPtr pRootTabcontainer = ll::prefs::TabContainerDataPtr( new ll::prefs::TabContainerData ); pRootTabcontainer->mTabContainer = pRoot; pRootTabcontainer->mLabel = pRoot->getLabel(); mSearchData->mRootTab = pRootTabcontainer; collectChildren( this, ll::prefs::PanelDataPtr(), pRootTabcontainer ); } mSearchDataDirty = false; } void LLFloaterPreference::saveIgnoredNotifications() { for (LLNotifications::TemplateMap::const_iterator iter = LLNotifications::instance().templatesBegin(); iter != LLNotifications::instance().templatesEnd(); ++iter) { LLNotificationTemplatePtr templatep = iter->second; LLNotificationFormPtr formp = templatep->mForm; LLNotificationForm::EIgnoreType ignore = formp->getIgnoreType(); if (ignore <= LLNotificationForm::IGNORE_NO) continue; mIgnorableNotifs[templatep->mName] = !formp->getIgnored(); } } void LLFloaterPreference::restoreIgnoredNotifications() { for (std::map::iterator it = mIgnorableNotifs.begin(); it != mIgnorableNotifs.end(); ++it) { LLUI::getInstance()->mSettingGroups["ignores"]->setBOOL(it->first, it->second); } }