diff options
author | Vadim ProductEngine <vsavchuk@productengine.com> | 2011-05-30 22:34:56 +0300 |
---|---|---|
committer | Vadim ProductEngine <vsavchuk@productengine.com> | 2011-05-30 22:34:56 +0300 |
commit | 657e434fd59139436e8b97e5ecd01ca686e82269 (patch) | |
tree | bebf0c4c8adff3587cec47f6403757f841f5c872 /indra | |
parent | 6f1cdd4926444567d21b1d6e0735a445b981e56b (diff) |
STORM-1253 WIP New day cycle editor.
Done:
* Creating new local day cycles.
* Editing existing local day cycles.
* Deleting day cycles.
To do:
* Editing region day cycle, dealing with skies in region scope.
* Handle teleport while editing a day cycle.
* Update UI when a day cycle or sky preset gets deleted.
* Make the time ctrl increase/decrease consistently.
Diffstat (limited to 'indra')
-rw-r--r-- | indra/llui/lltimectrl.cpp | 86 | ||||
-rw-r--r-- | indra/llui/lltimectrl.h | 21 | ||||
-rw-r--r-- | indra/newview/lldaycyclemanager.cpp | 62 | ||||
-rw-r--r-- | indra/newview/lldaycyclemanager.h | 9 | ||||
-rw-r--r-- | indra/newview/llenvmanager.cpp | 8 | ||||
-rw-r--r-- | indra/newview/llfloaterdeleteenvpreset.cpp | 68 | ||||
-rw-r--r-- | indra/newview/llfloaterdeleteenvpreset.h | 2 | ||||
-rw-r--r-- | indra/newview/llfloatereditdaycycle.cpp | 599 | ||||
-rw-r--r-- | indra/newview/llfloatereditdaycycle.h | 80 | ||||
-rw-r--r-- | indra/newview/llfloaterwindlight.cpp | 8 | ||||
-rw-r--r-- | indra/newview/llwldaycycle.cpp | 11 | ||||
-rw-r--r-- | indra/newview/llwldaycycle.h | 5 | ||||
-rw-r--r-- | indra/newview/llwlparammanager.h | 8 | ||||
-rw-r--r-- | indra/newview/skins/default/xui/en/floater_delete_env_preset.xml | 2 | ||||
-rw-r--r-- | indra/newview/skins/default/xui/en/floater_edit_day_cycle.xml | 30 |
15 files changed, 941 insertions, 58 deletions
diff --git a/indra/llui/lltimectrl.cpp b/indra/llui/lltimectrl.cpp index a77842a392..ce376f001d 100644 --- a/indra/llui/lltimectrl.cpp +++ b/indra/llui/lltimectrl.cpp @@ -108,7 +108,7 @@ LLTimeCtrl::LLTimeCtrl(const LLTimeCtrl::Params& p) mEditor = LLUICtrlFactory::create<LLLineEditor> (params); mEditor->setPrevalidateInput(LLTextValidate::validateNonNegativeS32NoSpace); mEditor->setPrevalidate(boost::bind(&LLTimeCtrl::isTimeStringValid, this, _1)); - mEditor->setText(LLStringExplicit("0:00 AM")); + mEditor->setText(LLStringExplicit("12:00 AM")); addChild(mEditor); //================= Spin Buttons ==========// @@ -130,6 +130,66 @@ LLTimeCtrl::LLTimeCtrl(const LLTimeCtrl::Params& p) setUseBoundingRect( TRUE ); } +F32 LLTimeCtrl::getTime24() const +{ + return getHours24() + getMinutes() / 60.0f; +} + +U32 LLTimeCtrl::getHours24() const +{ + U32 h = mHours; + + if (h == 12) + { + h = 0; + } + + if (mCurrentDayPeriod == PM) + { + h += 12; + } + + return h; +} + +U32 LLTimeCtrl::getMinutes() const +{ + return mMinutes; +} + +void LLTimeCtrl::setTime24(F32 time) +{ + time = llclamp(time, 0.0f, 23.99f); // fix out of range values + + U32 h = time; + U32 m = llround((time - h) * 60); // fixes values like 4.99999 + + // fix rounding error + if (m == 60) + { + m = 0; + ++h; + } + + mCurrentDayPeriod = (h >= 12 ? PM : AM); + + if (h >= 12) + { + h -= 12; + } + + if (h == 0) + { + h = 12; + } + + + mHours = h; + mMinutes = m; + + updateText(); +} + BOOL LLTimeCtrl::handleKeyHere(KEY key, MASK mask) { if (mEditor->hasFocus()) @@ -144,6 +204,11 @@ BOOL LLTimeCtrl::handleKeyHere(KEY key, MASK mask) onDownBtn(); return TRUE; } + if (key == KEY_RETURN) + { + onCommit(); + return TRUE; + } } return FALSE; } @@ -165,8 +230,8 @@ void LLTimeCtrl::onUpBtn() break; } - buildTimeString(); - mEditor->setText(mTimeString); + updateText(); + onCommit(); } void LLTimeCtrl::onDownBtn() @@ -186,15 +251,14 @@ void LLTimeCtrl::onDownBtn() break; } - buildTimeString(); - mEditor->setText(mTimeString); + updateText(); + onCommit(); } void LLTimeCtrl::onFocusLost() { - buildTimeString(); - mEditor->setText(mTimeString); - + updateText(); + onCommit(); LLUICtrl::onFocusLost(); } @@ -300,6 +364,7 @@ LLWString LLTimeCtrl::getMinutesWString(const LLWString& wstr) void LLTimeCtrl::increaseMinutes() { + // *TODO: snap to 5 min if (++mMinutes > MINUTES_MAX) { mMinutes = MINUTES_MIN; @@ -316,6 +381,7 @@ void LLTimeCtrl::increaseHours() void LLTimeCtrl::decreaseMinutes() { + // *TODO: snap to 5 min if (mMinutes-- == MINUTES_MIN) { mMinutes = MINUTES_MAX; @@ -344,7 +410,7 @@ void LLTimeCtrl::switchDayPeriod() } } -void LLTimeCtrl::buildTimeString() +void LLTimeCtrl::updateText() { std::stringstream time_buf; time_buf << mHours << ":"; @@ -367,7 +433,7 @@ void LLTimeCtrl::buildTimeString() break; } - mTimeString = time_buf.str(); + mEditor->setText(time_buf.str()); } LLTimeCtrl::EEditingPart LLTimeCtrl::getEditingPart() diff --git a/indra/llui/lltimectrl.h b/indra/llui/lltimectrl.h index 81d4477da4..aebc5b6eab 100644 --- a/indra/llui/lltimectrl.h +++ b/indra/llui/lltimectrl.h @@ -37,6 +37,7 @@ class LLLineEditor; class LLTimeCtrl : public LLUICtrl { + LOG_CLASS(LLTimeCtrl); public: struct Params : public LLInitParam::Block<Params, LLUICtrl::Params> { @@ -51,13 +52,17 @@ public: Params(); }; + + F32 getTime24() const; // 0.0 - 24.0 + U32 getHours24() const; // 0 - 23 + U32 getMinutes() const; // 0 - 59 + + void setTime24(F32 time); // 0.0 - 23.98(3) + protected: LLTimeCtrl(const Params&); friend class LLUICtrlFactory; - U32 getHours() const { return mHours; } - U32 getMinutes() const { return mMinutes; } - private: enum EDayPeriod @@ -101,7 +106,7 @@ private: void switchDayPeriod(); - void buildTimeString(); + void updateText(); EEditingPart getEditingPart(); @@ -114,11 +119,9 @@ private: class LLButton* mUpBtn; class LLButton* mDownBtn; - U32 mHours; - U32 mMinutes; - EDayPeriod mCurrentDayPeriod; - - std::string mTimeString; + U32 mHours; // 1 - 12 + U32 mMinutes; // 0 - 59 + EDayPeriod mCurrentDayPeriod; // AM/PM BOOL mAllowEdit; }; diff --git a/indra/newview/lldaycyclemanager.cpp b/indra/newview/lldaycyclemanager.cpp index c0eae8cd3c..e80da5ba11 100644 --- a/indra/newview/lldaycyclemanager.cpp +++ b/indra/newview/lldaycyclemanager.cpp @@ -38,7 +38,7 @@ const LLDayCycleManager::dc_map_t& LLDayCycleManager::getPresets() return mDayCycleMap; } -bool LLDayCycleManager::getPreset(const std::string name, LLWLDayCycle& day_cycle) +bool LLDayCycleManager::getPreset(const std::string name, LLWLDayCycle& day_cycle) const { dc_map_t::const_iterator it = mDayCycleMap.find(name); if (it == mDayCycleMap.end()) @@ -50,7 +50,7 @@ bool LLDayCycleManager::getPreset(const std::string name, LLWLDayCycle& day_cycl return true; } -bool LLDayCycleManager::getPreset(const std::string name, LLSD& day_cycle) +bool LLDayCycleManager::getPreset(const std::string name, LLSD& day_cycle) const { LLWLDayCycle dc; if (!getPreset(name, dc)) @@ -62,6 +62,46 @@ bool LLDayCycleManager::getPreset(const std::string name, LLSD& day_cycle) return true; } +bool LLDayCycleManager::presetExists(const std::string name) const +{ + LLWLDayCycle dummy; + return getPreset(name, dummy); +} + +bool LLDayCycleManager::isSystemPreset(const std::string& name) const +{ + return gDirUtilp->fileExists(getSysDir() + LLURI::escape(name) + ".xml"); +} + +bool LLDayCycleManager::savePreset(const std::string& name, const LLSD& data) +{ + // Save given preset. + LLWLDayCycle day; + day.loadDayCycle(data, LLEnvKey::SCOPE_LOCAL); + day.save(getUserDir() + LLURI::escape(name) + ".xml"); + + // Add it to our map. + addPreset(name, data); + return true; +} + +bool LLDayCycleManager::deletePreset(const std::string& name) +{ + std::string path; + std::string filename = LLURI::escape(name) + ".xml"; + + // Try removing specified user preset. + path = getUserDir() + filename; + if (gDirUtilp->fileExists(path)) + { + gDirUtilp->deleteFilesInDir(getUserDir(), filename); + return true; + } + + // Invalid or system preset. + return false; +} + // virtual void LLDayCycleManager::initSingleton() { @@ -102,13 +142,25 @@ bool LLDayCycleManager::loadPreset(const std::string& path) } std::string name(gDirUtilp->getBaseFileName(LLURI::unescape(path), /*strip_exten = */ true)); - LLWLDayCycle day_cycle; - day_cycle.loadDayCycle(data, LLEnvKey::SCOPE_LOCAL); - mDayCycleMap[name] = day_cycle; + addPreset(name, data); return true; } +bool LLDayCycleManager::addPreset(const std::string& name, const LLSD& data) +{ + if (name.empty()) + { + llassert(name.empty()); + return false; + } + + LLWLDayCycle day; + day.loadDayCycle(data, LLEnvKey::SCOPE_LOCAL); + mDayCycleMap[name] = day; + return false; +} + // static std::string LLDayCycleManager::getSysDir() { diff --git a/indra/newview/lldaycyclemanager.h b/indra/newview/lldaycyclemanager.h index e49e4986fe..b33c0a0d39 100644 --- a/indra/newview/lldaycyclemanager.h +++ b/indra/newview/lldaycyclemanager.h @@ -46,8 +46,12 @@ public: typedef std::map<std::string, LLWLDayCycle> dc_map_t; const dc_map_t& getPresets(); - bool getPreset(const std::string name, LLWLDayCycle& day_cycle); - bool getPreset(const std::string name, LLSD& day_cycle); + bool getPreset(const std::string name, LLWLDayCycle& day_cycle) const; + bool getPreset(const std::string name, LLSD& day_cycle) const; + bool presetExists(const std::string name) const; + bool isSystemPreset(const std::string& name) const; + bool savePreset(const std::string& name, const LLSD& data); + bool deletePreset(const std::string& name); private: friend class LLSingleton<LLDayCycleManager>; @@ -56,6 +60,7 @@ private: void loadAllPresets(); void loadPresets(const std::string& dir); bool loadPreset(const std::string& path); + bool addPreset(const std::string& name, const LLSD& data); static std::string getSysDir(); static std::string getUserDir(); diff --git a/indra/newview/llenvmanager.cpp b/indra/newview/llenvmanager.cpp index d5d03ff1e5..48d84fcffc 100644 --- a/indra/newview/llenvmanager.cpp +++ b/indra/newview/llenvmanager.cpp @@ -746,6 +746,7 @@ void LLEnvManagerNew::setUseRegionSettings(bool val) void LLEnvManagerNew::setUseWaterPreset(const std::string& name) { + // *TODO: make sure the preset exists. if (name.empty()) { llwarns << "Empty water preset name passed" << llendl; @@ -759,6 +760,7 @@ void LLEnvManagerNew::setUseWaterPreset(const std::string& name) void LLEnvManagerNew::setUseSkyPreset(const std::string& name) { + // *TODO: make sure the preset exists. if (name.empty()) { llwarns << "Empty sky preset name passed" << llendl; @@ -772,9 +774,9 @@ void LLEnvManagerNew::setUseSkyPreset(const std::string& name) void LLEnvManagerNew::setUseDayCycle(const std::string& name) { - if (name.empty()) + if (!LLDayCycleManager::instance().presetExists(name)) { - llwarns << "Empty day cycle name passed" << llendl; + llwarns << "Invalid day cycle name passed" << llendl; return; } @@ -939,6 +941,8 @@ void LLEnvManagerNew::onRegionChange(bool interpolate) return; } + // *TODO: clear environment settings of the previous region? + // Request environment settings of the new region. LL_DEBUGS("Windlight") << "New viewer region: " << region_uuid << LL_ENDL; mCurRegionUUID = region_uuid; diff --git a/indra/newview/llfloaterdeleteenvpreset.cpp b/indra/newview/llfloaterdeleteenvpreset.cpp index e014eedeb1..d2f0f6e520 100644 --- a/indra/newview/llfloaterdeleteenvpreset.cpp +++ b/indra/newview/llfloaterdeleteenvpreset.cpp @@ -31,11 +31,23 @@ // libs #include "llbutton.h" #include "llcombobox.h" +#include "llnotificationsutil.h" // newview #include "lldaycyclemanager.h" #include "llwaterparammanager.h" +static bool confirmation_callback(const LLSD& notification, const LLSD& response, boost::function<void()> cb) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + if (option == 0) + { + cb(); + } + return false; + +} + LLFloaterDeleteEnvPreset::LLFloaterDeleteEnvPreset(const LLSD &key) : LLFloater(key) , mPresetCombo(NULL) @@ -50,9 +62,6 @@ BOOL LLFloaterDeleteEnvPreset::postBuild() getChild<LLButton>("delete")->setCommitCallback(boost::bind(&LLFloaterDeleteEnvPreset::onBtnDelete, this)); getChild<LLButton>("cancel")->setCommitCallback(boost::bind(&LLFloaterDeleteEnvPreset::onBtnCancel, this)); - // Deletion is not implemented yet, so disable the button for now. - getChild<LLButton>("delete")->setEnabled(FALSE); - return TRUE; } @@ -70,14 +79,15 @@ void LLFloaterDeleteEnvPreset::onOpen(const LLSD& key) getChild<LLUICtrl>("label")->setValue(combo_label); // Populate the combobox. - mPresetCombo->removeall(); if (param == "water") { populateWaterPresetsList(); + getChild<LLButton>("delete")->setEnabled(FALSE); // not implemented yet } else if (param == "sky") { populateSkyPresetsList(); + getChild<LLButton>("delete")->setEnabled(FALSE); // not implemented yet } else if (param == "day_cycle") { @@ -91,7 +101,42 @@ void LLFloaterDeleteEnvPreset::onOpen(const LLSD& key) void LLFloaterDeleteEnvPreset::onBtnDelete() { - closeFloater(); + std::string param = mKey.asString(); + boost::function<void()> confirm_cb; + + if (param == "water") + { + llwarns << "Deleting water presets not implemented" << llendl; + return; + } + else if (param == "sky") + { + llwarns << "Deleting sky presets not implemented" << llendl; + return; + } + else if (param == "day_cycle") + { + LLDayCycleManager& day_mgr = LLDayCycleManager::instance(); + std::string preset_name = mPresetCombo->getValue().asString(); + + // Don't allow deleting system presets. + if (day_mgr.isSystemPreset(preset_name)) + { + LLNotificationsUtil::add("WLNoEditDefault"); + return; + } + + confirm_cb = boost::bind(&LLFloaterDeleteEnvPreset::onDeleteDayCycleConfirmation, this); + } + else + { + llwarns << "Unrecognized key" << llendl; + } + + LLSD args; + args["MESSAGE"] = getString("msg_confirm_deletion"); + LLNotificationsUtil::add("GenericAlertYesCancel", args, LLSD(), + boost::bind(&confirmation_callback, _1, _2, confirm_cb)); } void LLFloaterDeleteEnvPreset::onBtnCancel() @@ -101,6 +146,8 @@ void LLFloaterDeleteEnvPreset::onBtnCancel() void LLFloaterDeleteEnvPreset::populateWaterPresetsList() { + mPresetCombo->removeall(); + // *TODO: Reload the list when user preferences change. LLWaterParamManager& water_mgr = LLWaterParamManager::instance(); LL_DEBUGS("Windlight") << "Current water preset: " << water_mgr.mCurParams.mName << LL_ENDL; @@ -116,6 +163,9 @@ void LLFloaterDeleteEnvPreset::populateWaterPresetsList() void LLFloaterDeleteEnvPreset::populateSkyPresetsList() { + mPresetCombo->removeall(); + + // *TODO: Reload the list when user preferences change. LLWLParamManager& sky_mgr = LLWLParamManager::instance(); LL_DEBUGS("Windlight") << "Current sky preset: " << sky_mgr.mCurParams.mName << LL_ENDL; @@ -130,6 +180,8 @@ void LLFloaterDeleteEnvPreset::populateSkyPresetsList() void LLFloaterDeleteEnvPreset::populateDayCyclesList() { + mPresetCombo->removeall(); + // *TODO: Disable current day cycle. const LLDayCycleManager::dc_map_t& map = LLDayCycleManager::instance().getPresets(); for (LLDayCycleManager::dc_map_t::const_iterator it = map.begin(); it != map.end(); ++it) @@ -137,3 +189,9 @@ void LLFloaterDeleteEnvPreset::populateDayCyclesList() mPresetCombo->add(it->first); } } + +void LLFloaterDeleteEnvPreset::onDeleteDayCycleConfirmation() +{ + LLDayCycleManager::instance().deletePreset(mPresetCombo->getValue().asString()); + populateDayCyclesList(); +} diff --git a/indra/newview/llfloaterdeleteenvpreset.h b/indra/newview/llfloaterdeleteenvpreset.h index 08fd11a16d..76b23feae8 100644 --- a/indra/newview/llfloaterdeleteenvpreset.h +++ b/indra/newview/llfloaterdeleteenvpreset.h @@ -49,6 +49,8 @@ private: void populateSkyPresetsList(); void populateDayCyclesList(); + void onDeleteDayCycleConfirmation(); + LLComboBox* mPresetCombo; }; diff --git a/indra/newview/llfloatereditdaycycle.cpp b/indra/newview/llfloatereditdaycycle.cpp index 6275b48203..91809e0707 100644 --- a/indra/newview/llfloatereditdaycycle.cpp +++ b/indra/newview/llfloatereditdaycycle.cpp @@ -30,22 +30,53 @@ // libs #include "llbutton.h" +#include "llcheckboxctrl.h" +#include "llcombobox.h" +#include "llmultisliderctrl.h" +#include "llnotifications.h" +#include "llnotificationsutil.h" +#include "llspinctrl.h" +#include "lltimectrl.h" // newview +#include "llagent.h" +#include "lldaycyclemanager.h" +#include "llenvmanager.h" +#include "llviewerregion.h" +#include "llwlparammanager.h" + +const F32 LLFloaterEditDayCycle::sHoursPerDay = 24.0f; LLFloaterEditDayCycle::LLFloaterEditDayCycle(const LLSD &key) : LLFloater(key) +, mDayCycleNameEditor(NULL) +, mDayCyclesCombo(NULL) +, mTimeSlider(NULL) +, mKeysSlider(NULL) +, mSkyPresetsCombo(NULL) +, mTimeCtrl(NULL) +, mMakeDefaultCheckBox(NULL) +, mSaveButton(NULL) { } // virtual BOOL LLFloaterEditDayCycle::postBuild() { - getChild<LLButton>("cancel")->setCommitCallback(boost::bind(&LLFloaterEditDayCycle::onBtnCancel, this)); + mDayCycleNameEditor = getChild<LLLineEditor>("day_cycle_name"); + mDayCyclesCombo = getChild<LLComboBox>("day_cycle_combo"); + + mTimeSlider = getChild<LLMultiSliderCtrl>("WLTimeSlider"); + mKeysSlider = getChild<LLMultiSliderCtrl>("WLDayCycleKeys"); + mSkyPresetsCombo = getChild<LLComboBox>("WLSkyPresets"); + mTimeCtrl = getChild<LLTimeCtrl>("time"); + mSaveButton = getChild<LLButton>("save"); + mMakeDefaultCheckBox = getChild<LLCheckBoxCtrl>("make_default_cb"); - // Disable some non-functional controls. - getChildView("day_cycle_combo")->setEnabled(FALSE); - getChildView("save")->setEnabled(FALSE); + initCallbacks(); + + // add the time slider + mTimeSlider->addSlider(); return TRUE; } @@ -53,6 +84,7 @@ BOOL LLFloaterEditDayCycle::postBuild() // virtual void LLFloaterEditDayCycle::onOpen(const LLSD& key) { + bool new_day = isNewDay(); std::string param = key.asString(); std::string floater_title = getString(std::string("title_") + param); std::string hint = getString(std::string("hint_" + param)); @@ -64,10 +96,567 @@ void LLFloaterEditDayCycle::onOpen(const LLSD& key) getChild<LLUICtrl>("hint")->setValue(hint); // Hide the hint to the right of the combo if we're invoked to create a new preset. - getChildView("note")->setVisible(param == "edit"); + getChildView("note")->setVisible(!new_day); + + // Switch between the day cycle presets combobox and day cycle name input field. + mDayCyclesCombo->setVisible(!new_day); + mDayCycleNameEditor->setVisible(new_day); + + // TODO: Make sure only one instance of the floater exists? + + reset(); +} + +// virtual +void LLFloaterEditDayCycle::onClose(bool app_quitting) +{ + if (!app_quitting) // there's no point to change environment if we're quitting + { + LLEnvManagerNew::instance().usePrefs(); // revert changes made to current day cycle + } +} + +// virtual +void LLFloaterEditDayCycle::draw() +{ + syncTimeSlider(); + LLFloater::draw(); +} + +void LLFloaterEditDayCycle::initCallbacks(void) +{ + mDayCycleNameEditor->setKeystrokeCallback(boost::bind(&LLFloaterEditDayCycle::onDayCycleNameEdited, this), NULL); + mDayCyclesCombo->setCommitCallback(boost::bind(&LLFloaterEditDayCycle::onDayCycleSelected, this)); + mTimeSlider->setCommitCallback(boost::bind(&LLFloaterEditDayCycle::onTimeSliderMoved, this)); + mKeysSlider->setCommitCallback(boost::bind(&LLFloaterEditDayCycle::onKeyTimeMoved, this)); + mTimeCtrl->setCommitCallback(boost::bind(&LLFloaterEditDayCycle::onKeyTimeChanged, this)); + mSkyPresetsCombo->setCommitCallback(boost::bind(&LLFloaterEditDayCycle::onKeyPresetChanged, this)); + + getChild<LLButton>("WLAddKey")->setClickedCallback(boost::bind(&LLFloaterEditDayCycle::onAddKey, this)); + getChild<LLButton>("WLDeleteKey")->setClickedCallback(boost::bind(&LLFloaterEditDayCycle::onDeleteKey, this)); + + mSaveButton->setCommitCallback(boost::bind(&LLFloaterEditDayCycle::onBtnSave, this)); + mSaveButton->setRightMouseDownCallback(boost::bind(&LLFloaterEditDayCycle::dumpTrack, this)); + getChild<LLButton>("cancel")->setCommitCallback(boost::bind(&LLFloaterEditDayCycle::onBtnCancel, this)); +} + +void LLFloaterEditDayCycle::syncTimeSlider() +{ + // set time + mTimeSlider->setCurSliderValue((F32)LLWLParamManager::getInstance()->mAnimator.getDayTime() * sHoursPerDay); +} + +void LLFloaterEditDayCycle::loadTrack() +{ + // clear the slider + mKeysSlider->clear(); + mSliderToKey.clear(); + + // add sliders + + lldebugs << "Adding " << LLWLParamManager::getInstance()->mDay.mTimeMap.size() << " keys to slider" << llendl; + + LLWLDayCycle& cur_dayp = LLWLParamManager::instance().mDay; + for (std::map<F32, LLWLParamKey>::iterator it = cur_dayp.mTimeMap.begin(); it != cur_dayp.mTimeMap.end(); ++it) + { + addSliderKey(it->first * sHoursPerDay, it->second); + } + + // set drop-down menu to match preset of currently-selected keyframe (one is automatically selected initially) + const std::string& cur_sldr = mKeysSlider->getCurSlider(); + if (strlen(cur_sldr.c_str()) > 0) // only do this if there is a curSldr, otherwise we put an invalid entry into the map + { + mSkyPresetsCombo->selectByValue(mSliderToKey[cur_sldr].keyframe.toStringVal()); + } + + syncTimeSlider(); +} + +void LLFloaterEditDayCycle::applyTrack() +{ + lldebugs << "Applying track (" << mSliderToKey.size() << ")" << llendl; + + // if no keys, do nothing + if (mSliderToKey.size() == 0) + { + lldebugs << "No keys, not syncing" << llendl; + return; + } + + llassert_always(mSliderToKey.size() == mKeysSlider->getValue().size()); + + // create a new animation track + LLWLParamManager::getInstance()->mDay.clearKeyframes(); + + // add the keys one by one + for (std::map<std::string, SliderKey>::iterator it = mSliderToKey.begin(); + it != mSliderToKey.end(); ++it) + { + LLWLParamManager::getInstance()->mDay.addKeyframe(it->second.time / sHoursPerDay, + it->second.keyframe); + } + + // set the param manager's track to the new one + LL_DEBUGS("Windlight") << "Time slider val: " << mTimeSlider->getCurSliderValue() / sHoursPerDay << " (" << mTimeSlider->getCurSliderValue() << ")" << LL_ENDL; + LLWLParamManager::getInstance()->resetAnimator( + mTimeSlider->getCurSliderValue() / sHoursPerDay, false); + + LLWLParamManager::getInstance()->mAnimator.update( + LLWLParamManager::getInstance()->mCurParams); +} + +void LLFloaterEditDayCycle::refreshSkyPresetsList() +{ + LLWLParamManager& sky_mgr = LLWLParamManager::instance(); + for (std::map<LLWLParamKey, LLWLParamSet>::iterator it = sky_mgr.mParamList.begin(); + it != sky_mgr.mParamList.end(); ++it) + { + const LLWLParamKey& key = it->first; + + std::string item_title = key.name; + if (key.scope == LLEnvKey::SCOPE_REGION) + { + item_title += " (" + getRegionName() + ")"; + } + + mSkyPresetsCombo->add(item_title, LLSD(key.toStringVal())); + } + + // set defaults on combo boxes + mSkyPresetsCombo->selectFirstItem(); +} + +void LLFloaterEditDayCycle::refreshDayCyclesList() +{ + llassert(isNewDay() == false); + + mDayCyclesCombo->removeall(); + + const LLSD& region_day = LLEnvManagerNew::instance().getRegionSettings().getWLDayCycle(); + if (region_day.size() > 0) + { + LLWLParamKey key(getRegionName(), LLEnvKey::SCOPE_REGION); + mDayCyclesCombo->add(key.name, key.toLLSD()); + mDayCyclesCombo->addSeparator(); + } + + const LLDayCycleManager::dc_map_t& map = LLDayCycleManager::instance().getPresets(); + for (LLDayCycleManager::dc_map_t::const_iterator it = map.begin(); it != map.end(); ++it) + { + LLWLParamKey key(it->first, LLEnvKey::SCOPE_LOCAL); + mDayCyclesCombo->add(key.name, key.toLLSD()); + } + + mDayCyclesCombo->setLabel(getString("combo_label")); +} + +void LLFloaterEditDayCycle::onTimeSliderMoved() +{ + /// get the slider value + F32 val = mTimeSlider->getCurSliderValue() / sHoursPerDay; + LL_DEBUGS("Windlight") << "Time slider val: " << val << " (" << mTimeSlider->getCurSliderValue() << ")" << LL_ENDL; + + // set the value, turn off animation + LLWLParamManager::getInstance()->mAnimator.setDayTime((F64)val); + LLWLParamManager::getInstance()->mAnimator.deactivate(); + + // then call update once + LLWLParamManager::getInstance()->mAnimator.update( + LLWLParamManager::getInstance()->mCurParams); +} + +void LLFloaterEditDayCycle::onKeyTimeMoved() +{ + if (mKeysSlider->getValue().size() == 0) + { + return; + } + + // make sure we have a slider + const std::string& cur_sldr = mKeysSlider->getCurSlider(); + if (cur_sldr == "") + { + return; + } + + F32 time24 = mKeysSlider->getCurSliderValue(); + + // check to see if a key exists + LLWLParamKey key = mSliderToKey[cur_sldr].keyframe; + lldebugs << "Setting key time: " << time24 << LL_ENDL; + mSliderToKey[cur_sldr].time = time24; + + // if it exists, turn on check box + mSkyPresetsCombo->selectByValue(key.toStringVal()); + + mTimeCtrl->setTime24(time24); + + applyTrack(); +} + +void LLFloaterEditDayCycle::onKeyTimeChanged() +{ + // if no keys, skipped + if (mSliderToKey.size() == 0) + { + return; + } + + F32 time24 = mTimeCtrl->getTime24(); + + const std::string& cur_sldr = mKeysSlider->getCurSlider(); + mKeysSlider->setCurSliderValue(time24, TRUE); + F32 time = mKeysSlider->getCurSliderValue() / sHoursPerDay; + + // now set the key's time in the sliderToKey map + lldebugs << "Setting key time: " << time << LL_ENDL; + mSliderToKey[cur_sldr].time = time; + + applyTrack(); +} + +void LLFloaterEditDayCycle::onKeyPresetChanged() +{ + // do nothing if no sliders + if (mKeysSlider->getValue().size() == 0) + { + return; + } + + // change the map + + std::string stringVal = mSkyPresetsCombo->getSelectedValue().asString(); + LLWLParamKey new_key(stringVal); + const std::string& cur_sldr = mKeysSlider->getCurSlider(); + + // if null, don't use + if (cur_sldr == "") + { + return; + } + + mSliderToKey[cur_sldr].keyframe = new_key; + + // Apply changes to current day cycle. + applyTrack(); +} + +void LLFloaterEditDayCycle::onAddKey() +{ + llassert_always(mSliderToKey.size() == mKeysSlider->getValue().size()); + + S32 max_sliders; + LLEnvKey::EScope scope = LLEnvKey::SCOPE_LOCAL; // *TODO: editing region day cycle + switch (scope) + { + case LLEnvKey::SCOPE_LOCAL: + max_sliders = 20; // *HACK this should be LLWLPacketScrubber::MAX_LOCAL_KEY_FRAMES; + break; + case LLEnvKey::SCOPE_REGION: + max_sliders = 12; // *HACK this should be LLWLPacketScrubber::MAX_REGION_KEY_FRAMES; + break; + default: + max_sliders = (S32) mKeysSlider->getMaxValue(); + break; + } + + if ((S32)mSliderToKey.size() >= max_sliders) + { + LLSD args; + args["SCOPE"] = LLEnvManager::getScopeString(scope); + args["MAX"] = max_sliders; + LLNotificationsUtil::add("DayCycleTooManyKeyframes", args, LLSD(), LLNotificationFunctorRegistry::instance().DONOTHING); + return; + } + + // add the slider key + LLWLParamKey sky_params(mSkyPresetsCombo->getSelectedValue()); + F32 time = mTimeSlider->getCurSliderValue(); + addSliderKey(time, sky_params); + + // apply the change to current day cycles + applyTrack(); +} + +void LLFloaterEditDayCycle::addSliderKey(F32 time, LLWLParamKey keyframe) +{ + // make a slider + const std::string& sldr_name = mKeysSlider->addSlider(time); + if (sldr_name.empty()) + { + return; + } + + // set the key + SliderKey newKey(keyframe, mKeysSlider->getCurSliderValue()); + + llassert_always(sldr_name != LLStringUtil::null); + + // add to map + mSliderToKey.insert(std::pair<std::string, SliderKey>(sldr_name, newKey)); + + llassert_always(mSliderToKey.size() == mKeysSlider->getValue().size()); +} + +LLWLParamKey LLFloaterEditDayCycle::getSelectedDayCycle() +{ + LLWLParamKey dc_key; + + if (mDayCycleNameEditor->getVisible()) + { + dc_key.name = mDayCycleNameEditor->getText(); + dc_key.scope = LLEnvKey::SCOPE_LOCAL; + } + else + { + LLSD combo_val = mDayCyclesCombo->getValue(); + + if (!combo_val.isArray()) // manually typed text + { + dc_key.name = combo_val.asString(); + dc_key.scope = LLEnvKey::SCOPE_LOCAL; + } + else + { + dc_key.fromLLSD(combo_val); + } + } + + return dc_key; +} + +void LLFloaterEditDayCycle::deletePreset(LLWLParamKey keyframe) +{ + // *TODO: This should be called when a sky preset gets deleted. + + /// delete any reference + std::map<std::string, SliderKey>::iterator curr_preset, next_preset; + for (curr_preset = mSliderToKey.begin(); curr_preset != mSliderToKey.end(); curr_preset = next_preset) + { + next_preset = curr_preset; + ++next_preset; + if (curr_preset->second.keyframe == keyframe) + { + mKeysSlider->deleteSlider(curr_preset->first); + mSliderToKey.erase(curr_preset); + } + } +} + +bool LLFloaterEditDayCycle::isNewDay() const +{ + return mKey.asString() == "new"; +} + +void LLFloaterEditDayCycle::dumpTrack() +{ + LL_DEBUGS("Windlight") << "Dumping day cycle" << LL_ENDL; + + LLWLDayCycle& cur_dayp = LLWLParamManager::instance().mDay; + for (std::map<F32, LLWLParamKey>::iterator it = cur_dayp.mTimeMap.begin(); it != cur_dayp.mTimeMap.end(); ++it) + { + F32 time = it->first * 24.0f; + S32 h = time; + S32 m = (time - h) * 60.0f; + LL_DEBUGS("Windlight") << llformat("(%.3f) %02d:%02d", time, h, m) << " => " << it->second.name << LL_ENDL; + } +} + +void LLFloaterEditDayCycle::enableEditing(bool enable) +{ + mSkyPresetsCombo->setEnabled(enable); + mTimeCtrl->setEnabled(enable); + getChild<LLPanel>("day_cycle_slider_panel")->setCtrlsEnabled(enable); + mSaveButton->setEnabled(enable); + mMakeDefaultCheckBox->setEnabled(enable); +} + +void LLFloaterEditDayCycle::reset() +{ + // clear the slider + mKeysSlider->clear(); + mSliderToKey.clear(); + + refreshSkyPresetsList(); + + if (isNewDay()) + { + mDayCycleNameEditor->setValue(LLSD()); + F32 time = 0.5f * sHoursPerDay; + mSaveButton->setEnabled(FALSE); // will be enabled as soon as users enters a name + mTimeSlider->setCurSliderValue(time); + + addSliderKey(time, LLWLParamKey("Default", LLEnvKey::SCOPE_LOCAL)); + onKeyTimeMoved(); // update the time control and sky sky combo + + applyTrack(); + } + else + { + refreshDayCyclesList(); + + // Disable controls until a day cycle to edit is selected. + enableEditing(false); + } +} + +void LLFloaterEditDayCycle::onDeleteKey() +{ + if (mSliderToKey.size() == 0) + { + return; + } + else if (mSliderToKey.size() == 1) + { + LLNotifications::instance().add("EnvCannotDeleteLastDayCycleKey", LLSD(), LLSD()); + return; + } + + // delete from map + const std::string& sldr_name = mKeysSlider->getCurSlider(); + std::map<std::string, SliderKey>::iterator mIt = mSliderToKey.find(sldr_name); + mSliderToKey.erase(mIt); + + mKeysSlider->deleteCurSlider(); + + if (mSliderToKey.size() == 0) + { + return; + } + + const std::string& name = mKeysSlider->getCurSlider(); + mSkyPresetsCombo->selectByValue(mSliderToKey[name].keyframe.toStringVal()); + F32 time24 = mSliderToKey[name].time; + + mTimeCtrl->setTime24(time24); + + applyTrack(); +} + +void LLFloaterEditDayCycle::onDayCycleNameEdited() +{ + // Disable saving a day cycle having empty name. + LLWLParamKey key = getSelectedDayCycle(); + mSaveButton->setEnabled(!key.name.empty()); +} + +void LLFloaterEditDayCycle::onDayCycleSelected() +{ + LLSD day_data; + LLWLParamKey dc_key = getSelectedDayCycle(); + + if (dc_key.scope == LLEnvKey::SCOPE_LOCAL) + { + if (!LLDayCycleManager::instance().getPreset(dc_key.name, day_data)) + { + llwarns << "No day cycle named " << dc_key.name << llendl; + return; + } + } + else + { + day_data = LLEnvManagerNew::instance().getRegionSettings().getWLDayCycle(); + if (day_data.size() == 0) + { + llwarns << "Empty region day cycle" << llendl; + llassert(day_data.size() > 0); + return; + } + } + + F32 slider_time = mTimeSlider->getCurSliderValue() / sHoursPerDay; + LL_DEBUGS("Windlight") << "Time slider val: " << slider_time << " (" << mTimeSlider->getCurSliderValue() << ")" << LL_ENDL; + LLWLParamManager::instance().applyDayCycleParams(day_data, dc_key.scope, slider_time); + loadTrack(); + + enableEditing(true); +} + +void LLFloaterEditDayCycle::onBtnSave() +{ + LLDayCycleManager& day_mgr = LLDayCycleManager::instance(); + LLWLParamKey selected_day = getSelectedDayCycle(); + + if (selected_day.scope == LLEnvKey::SCOPE_REGION) + { + llwarns << "Saving to a local day cycle" << llendl; + } + + std::string name = selected_day.name; + if (name.empty()) + { + // *TODO: show an alert + llwarns << "Empty day cycle name" << llendl; + return; + } + + // Don't allow overwriting system presets. + if (day_mgr.isSystemPreset(name)) + { + LLNotificationsUtil::add("WLNoEditDefault"); + return; + } + + // Save, ask for confirmation for overwriting an existing preset. + if (day_mgr.presetExists(name)) + { + LLNotificationsUtil::add("WLSavePresetAlert", LLSD(), LLSD(), boost::bind(&LLFloaterEditDayCycle::onSaveAnswer, this, _1, _2)); + } + else + { + // new preset, hence no confirmation needed + onSaveConfirmed(); + } } void LLFloaterEditDayCycle::onBtnCancel() { +#if 0 // temporary + onClose(false); + reset(); +#else + closeFloater(); +#endif +} + +bool LLFloaterEditDayCycle::onSaveAnswer(const LLSD& notification, const LLSD& response) +{ + S32 option = LLNotificationsUtil::getSelectedOption(notification, response); + + // If they choose save, do it. Otherwise, don't do anything + if (option == 0) + { + onSaveConfirmed(); + } + + return false; +} + +void LLFloaterEditDayCycle::onSaveConfirmed() +{ + std::string name = getSelectedDayCycle().name; + + // Save preset. + LLSD data = LLWLParamManager::instance().mDay.asLLSD(); + LL_DEBUGS("Windlight") << "Saving day cycle " << name << ": " << data << LL_ENDL; + LLDayCycleManager::instance().savePreset(name, data); + + // Change preference if requested. + if (mMakeDefaultCheckBox->getValue()) + { + LL_DEBUGS("Windlight") << name << " is now the new preferred day cycle" << llendl; + LLEnvManagerNew::instance().setUseDayCycle(name); + } + +#if 0 // temporary + reset(); +#else closeFloater(); +#endif +} + +// static +std::string LLFloaterEditDayCycle::getRegionName() +{ + return gAgent.getRegion() ? gAgent.getRegion()->getName() : LLTrans::getString("Unknown"); } diff --git a/indra/newview/llfloatereditdaycycle.h b/indra/newview/llfloatereditdaycycle.h index d1caa05888..742608712b 100644 --- a/indra/newview/llfloatereditdaycycle.h +++ b/indra/newview/llfloatereditdaycycle.h @@ -29,6 +29,16 @@ #include "llfloater.h" +#include "llwlparammanager.h" // for LLWLParamKey + +class LLCheckBoxCtrl; +class LLComboBox; +class LLLineEditor; +class LLMultiSliderCtrl; +class LLTimeCtrl; + +/// Menu for all of windlight's functionality. +/// Menuing system for adjusting the atmospheric settings of the world. class LLFloaterEditDayCycle : public LLFloater { LOG_CLASS(LLFloaterEditDayCycle); @@ -38,8 +48,78 @@ public: /*virtual*/ BOOL postBuild(); /*virtual*/ void onOpen(const LLSD& key); + /*virtual*/ void onClose(bool app_quitting); + /*virtual*/ void draw(); + +private: + + /// sync the time slider with day cycle structure + void syncTimeSlider(); + + // makes sure key slider has what's in day cycle + void loadTrack(); + + /// makes sure day cycle data structure has what's in menu + void applyTrack(); + + /// refresh the sky presets combobox + void refreshSkyPresetsList(); + + /// refresh the day cycle combobox + void refreshDayCyclesList(); + + /// add a slider to the track + void addSliderKey(F32 time, LLWLParamKey keyframe); + void initCallbacks(); + LLWLParamKey getSelectedDayCycle(); + void deletePreset(LLWLParamKey keyframe); + bool isNewDay() const; + void dumpTrack(); + void enableEditing(bool enable); + void reset(); + + void onTimeSliderMoved(); /// time slider moved + void onKeyTimeMoved(); /// a key frame moved + void onKeyTimeChanged(); /// a key frame's time changed + void onKeyPresetChanged(); /// sky preset selected + void onAddKey(); /// new key added on slider + void onDeleteKey(); /// a key frame deleted + + void onDayCycleNameEdited(); + void onDayCycleSelected(); + void onBtnSave(); void onBtnCancel(); + + bool onSaveAnswer(const LLSD& notification, const LLSD& response); + void onSaveConfirmed(); + + static std::string getRegionName(); + + /// convenience class for holding keyframes mapped to sliders + struct SliderKey + { + public: + SliderKey(LLWLParamKey kf, F32 t) : keyframe(kf), time(t) {} + SliderKey() : keyframe(), time(0.f) {} // Don't use this default constructor + + LLWLParamKey keyframe; + F32 time; + }; + + static const F32 sHoursPerDay; + + LLLineEditor* mDayCycleNameEditor; + LLComboBox* mDayCyclesCombo; + LLMultiSliderCtrl* mTimeSlider; + LLMultiSliderCtrl* mKeysSlider; + LLComboBox* mSkyPresetsCombo; + LLTimeCtrl* mTimeCtrl; + LLCheckBoxCtrl* mMakeDefaultCheckBox; + LLButton* mSaveButton; + + // map of sliders to parameters + std::map<std::string, SliderKey> mSliderToKey; }; #endif // LL_LLFLOATEREDITDAYCYCLE_H diff --git a/indra/newview/llfloaterwindlight.cpp b/indra/newview/llfloaterwindlight.cpp index fb69e6f594..16cb0f5e57 100644 --- a/indra/newview/llfloaterwindlight.cpp +++ b/indra/newview/llfloaterwindlight.cpp @@ -82,7 +82,13 @@ BOOL LLFloaterWindLight::postBuild() LLWLParamManager::getInstance()->mParamList.begin(); for(; mIt != LLWLParamManager::getInstance()->mParamList.end(); mIt++) { - comboBox->add(mIt->first.toString(), mIt->first.toLLSD()); + const LLWLParamKey& key = mIt->first; + std::string item_title = key.name; + if (key.scope == LLEnvKey::SCOPE_REGION) + { + item_title += std::string(" (") + LLTrans::getString("Region") + std::string(")"); + } + comboBox->add(item_title, key.toLLSD()); } // entry for when we're in estate time diff --git a/indra/newview/llwldaycycle.cpp b/indra/newview/llwldaycycle.cpp index 6ac63fb99e..91d2173d6d 100644 --- a/indra/newview/llwldaycycle.cpp +++ b/indra/newview/llwldaycycle.cpp @@ -127,12 +127,17 @@ LLSD LLWLDayCycle::loadDayCycleFromPath(const std::string& file_path) void LLWLDayCycle::saveDayCycle(const std::string & fileName) { - LLSD day_data = asLLSD(); - std::string pathName(gDirUtilp->getExpandedFilename(LL_PATH_APP_SETTINGS, "windlight/days", fileName)); //llinfos << "Saving WindLight settings to " << pathName << llendl; - llofstream day_cycle_xml(pathName); + save(pathName); +} + +void LLWLDayCycle::save(const std::string& file_path) +{ + LLSD day_data = asLLSD(); + + llofstream day_cycle_xml(file_path); LLPointer<LLSDFormatter> formatter = new LLSDXMLFormatter(); formatter->format(day_data, day_cycle_xml, LLSDFormatter::OPTIONS_PRETTY); day_cycle_xml.close(); diff --git a/indra/newview/llwldaycycle.h b/indra/newview/llwldaycycle.h index 56bd66f114..e5bc82bb6c 100644 --- a/indra/newview/llwldaycycle.h +++ b/indra/newview/llwldaycycle.h @@ -62,9 +62,12 @@ public: /// load a day cycle void loadDayCycleFromFile(const std::string & fileName); - /// load a day cycle + /// save a day cycle void saveDayCycle(const std::string & fileName); + /// save a day cycle + void save(const std::string& file_path); + /// load the LLSD data from a file (returns the undefined LLSD if not found) static LLSD loadCycleDataFromFile(const std::string & fileName); diff --git a/indra/newview/llwlparammanager.h b/indra/newview/llwlparammanager.h index 7903661a7e..30fc8cd68f 100644 --- a/indra/newview/llwlparammanager.h +++ b/indra/newview/llwlparammanager.h @@ -139,7 +139,7 @@ public: } inline LLWLParamKey() // NOT really valid, just so std::maps can return a default of some sort - : name(NULL), scope(SCOPE_LOCAL) + : name(""), scope(SCOPE_LOCAL) { } @@ -168,6 +168,12 @@ public: return llsd; } + inline void fromLLSD(const LLSD& llsd) + { + name = llsd[NAME_IDX].asString(); + scope = EScope(llsd[SCOPE_IDX].asInteger()); + } + inline bool operator <(const LLWLParamKey other) const { if (name < other.name) diff --git a/indra/newview/skins/default/xui/en/floater_delete_env_preset.xml b/indra/newview/skins/default/xui/en/floater_delete_env_preset.xml index 1539c6448e..82a541d40d 100644 --- a/indra/newview/skins/default/xui/en/floater_delete_env_preset.xml +++ b/indra/newview/skins/default/xui/en/floater_delete_env_preset.xml @@ -16,6 +16,8 @@ <string name="label_water">Preset:</string> <string name="label_sky">Preset:</string> <string name="label_day_cycle">Day cycle:</string> + + <string name="msg_confirm_deletion">Are you sure you want to delete the selected preset?</string> <text follows="top|left|right" diff --git a/indra/newview/skins/default/xui/en/floater_edit_day_cycle.xml b/indra/newview/skins/default/xui/en/floater_edit_day_cycle.xml index 7572efea05..5b72d295e5 100644 --- a/indra/newview/skins/default/xui/en/floater_edit_day_cycle.xml +++ b/indra/newview/skins/default/xui/en/floater_edit_day_cycle.xml @@ -13,6 +13,7 @@ <string name="title_edit">Edit Day Cycle</string> <string name="hint_new">Name your day cycle, adjust the controls to create it, and click "Save".</string> <string name="hint_edit">To edit your day cycle, adjust the controls below and click "Save".</string> + <string name="combo_label">-Select a preset-</string> <text follows="top|left|right" @@ -42,12 +43,14 @@ left_pad="10" name="day_cycle_combo" top_delta="-5" - width="200"> - <combo_box.item - label="-Select a preset-" - name="item0" - value="any" /> - </combo_box> + width="200" /> + <line_editor + height="20" + left_delta="0" + name="day_cycle_name" + top_delta="0" + visible="true" + width="200" /> <text follows="top|left|right" height="40" @@ -110,7 +113,7 @@ <panel follows="top|left" height="100" - name="day_cycle_slider" + name="day_cycle_slider_panel" layout="topleft" left_delta="25" top_pad="25" @@ -428,7 +431,7 @@ label="Preset" layout="topleft" left_pad="5" - name="WLKeyPresets" + name="WLSkyPresets" width="205" /> <text type="string" @@ -444,24 +447,23 @@ width="35"> Time: </text> - <text_editor - allow_scroll="false" - follows="left|top|right" - font="SansSerif" + <time + follows="left|top" height="16" + label_width="0" layout="topleft" left_pad="3" name="time" top_delta="-1" value="6:00 AM" - width="60"/> + width="75"/> <check_box follows="top|left" height="10" label="Make this my new day cycle" layout="topleft" left="330" - name="new_water_preset_chb" + name="make_default_cb" top="430" width="280"/> <button |