From 657e434fd59139436e8b97e5ecd01ca686e82269 Mon Sep 17 00:00:00 2001 From: Vadim ProductEngine Date: Mon, 30 May 2011 22:34:56 +0300 Subject: 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. --- indra/llui/lltimectrl.cpp | 86 ++- indra/llui/lltimectrl.h | 21 +- indra/newview/lldaycyclemanager.cpp | 62 ++- indra/newview/lldaycyclemanager.h | 9 +- indra/newview/llenvmanager.cpp | 8 +- indra/newview/llfloaterdeleteenvpreset.cpp | 68 ++- indra/newview/llfloaterdeleteenvpreset.h | 2 + indra/newview/llfloatereditdaycycle.cpp | 599 ++++++++++++++++++++- indra/newview/llfloatereditdaycycle.h | 80 +++ indra/newview/llfloaterwindlight.cpp | 8 +- indra/newview/llwldaycycle.cpp | 11 +- indra/newview/llwldaycycle.h | 5 +- indra/newview/llwlparammanager.h | 8 +- .../default/xui/en/floater_delete_env_preset.xml | 2 + .../default/xui/en/floater_edit_day_cycle.xml | 30 +- 15 files changed, 941 insertions(+), 58 deletions(-) (limited to 'indra') 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 (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 { @@ -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 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; @@ -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 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("delete")->setCommitCallback(boost::bind(&LLFloaterDeleteEnvPreset::onBtnDelete, this)); getChild("cancel")->setCommitCallback(boost::bind(&LLFloaterDeleteEnvPreset::onBtnCancel, this)); - // Deletion is not implemented yet, so disable the button for now. - getChild("delete")->setEnabled(FALSE); - return TRUE; } @@ -70,14 +79,15 @@ void LLFloaterDeleteEnvPreset::onOpen(const LLSD& key) getChild("label")->setValue(combo_label); // Populate the combobox. - mPresetCombo->removeall(); if (param == "water") { populateWaterPresetsList(); + getChild("delete")->setEnabled(FALSE); // not implemented yet } else if (param == "sky") { populateSkyPresetsList(); + getChild("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 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("cancel")->setCommitCallback(boost::bind(&LLFloaterEditDayCycle::onBtnCancel, this)); + mDayCycleNameEditor = getChild("day_cycle_name"); + mDayCyclesCombo = getChild("day_cycle_combo"); + + mTimeSlider = getChild("WLTimeSlider"); + mKeysSlider = getChild("WLDayCycleKeys"); + mSkyPresetsCombo = getChild("WLSkyPresets"); + mTimeCtrl = getChild("time"); + mSaveButton = getChild("save"); + mMakeDefaultCheckBox = getChild("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("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("WLAddKey")->setClickedCallback(boost::bind(&LLFloaterEditDayCycle::onAddKey, this)); + getChild("WLDeleteKey")->setClickedCallback(boost::bind(&LLFloaterEditDayCycle::onDeleteKey, this)); + + mSaveButton->setCommitCallback(boost::bind(&LLFloaterEditDayCycle::onBtnSave, this)); + mSaveButton->setRightMouseDownCallback(boost::bind(&LLFloaterEditDayCycle::dumpTrack, this)); + getChild("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::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::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::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(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::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::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("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::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 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 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 @@ Preset: Preset: Day cycle: + + Are you sure you want to delete the selected preset? Edit Day Cycle Name your day cycle, adjust the controls to create it, and click "Save". To edit your day cycle, adjust the controls below and click "Save". + -Select a preset- - - + width="200" /> + Time: - + width="75"/>