From 778088b74c19773646a50b5c0aa40015f2b242f3 Mon Sep 17 00:00:00 2001
From: Vadim ProductEngine <vsavchuk@productengine.com>
Date: Tue, 31 May 2011 22:36:26 +0300
Subject: STORM-1253 WIP Implemented editing region day cycle.

---
 indra/newview/CMakeLists.txt                       |   2 +
 indra/newview/llfloatereditdaycycle.cpp            | 130 ++++++++++++++++++++-
 indra/newview/llfloatereditdaycycle.h              |  10 ++
 indra/newview/llfloaterregioninfo.cpp              |   6 +-
 indra/newview/llregioninfomodel.cpp                | 106 +++++++++++++++++
 indra/newview/llregioninfomodel.h                  |  82 +++++++++++++
 indra/newview/llviewerregion.cpp                   |   3 +
 indra/newview/llwldaycycle.cpp                     |  19 ++-
 indra/newview/llwldaycycle.h                       |   5 +-
 .../default/xui/en/floater_edit_day_cycle.xml      |  10 ++
 10 files changed, 362 insertions(+), 11 deletions(-)
 create mode 100644 indra/newview/llregioninfomodel.cpp
 create mode 100644 indra/newview/llregioninfomodel.h

(limited to 'indra')

diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index 8e6453e8db..4c88cd4561 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -414,6 +414,7 @@ set(viewer_SOURCE_FILES
     llproductinforequest.cpp
     llprogressview.cpp
     llrecentpeople.cpp
+    llregioninfomodel.cpp
     llregionposition.cpp
     llremoteparcelrequest.cpp
     llsavedsettingsglue.cpp
@@ -964,6 +965,7 @@ set(viewer_HEADER_FILES
     llproductinforequest.h
     llprogressview.h
     llrecentpeople.h
+    llregioninfomodel.h
     llregionposition.h
     llremoteparcelrequest.h
     llresourcedata.h
diff --git a/indra/newview/llfloatereditdaycycle.cpp b/indra/newview/llfloatereditdaycycle.cpp
index 2ff6901652..a62f348129 100644
--- a/indra/newview/llfloatereditdaycycle.cpp
+++ b/indra/newview/llfloatereditdaycycle.cpp
@@ -32,6 +32,7 @@
 #include "llbutton.h"
 #include "llcheckboxctrl.h"
 #include "llcombobox.h"
+#include "llloadingindicator.h"
 #include "llmultisliderctrl.h"
 #include "llnotifications.h"
 #include "llnotificationsutil.h"
@@ -42,6 +43,7 @@
 #include "llagent.h"
 #include "lldaycyclemanager.h"
 #include "llenvmanager.h"
+#include "llregioninfomodel.h"
 #include "llviewerregion.h"
 #include "llwlparammanager.h"
 
@@ -138,6 +140,15 @@ void LLFloaterEditDayCycle::initCallbacks(void)
 	mSaveButton->setCommitCallback(boost::bind(&LLFloaterEditDayCycle::onBtnSave, this));
 	mSaveButton->setRightMouseDownCallback(boost::bind(&LLFloaterEditDayCycle::dumpTrack, this));
 	getChild<LLButton>("cancel")->setCommitCallback(boost::bind(&LLFloaterEditDayCycle::onBtnCancel, this));
+
+	// Connect to env manager events.
+	LLEnvManagerNew& env_mgr = LLEnvManagerNew::instance();
+	env_mgr.setRegionSettingsChangeCallback(boost::bind(&LLFloaterEditDayCycle::onRegionSettingsChange, this));
+	env_mgr.setRegionChangeCallback(boost::bind(&LLFloaterEditDayCycle::onRegionChange, this));
+	env_mgr.setRegionSettingsAppliedCallback(boost::bind(&LLFloaterEditDayCycle::onRegionSettingsApplied, this, _1));
+
+	// Connect to region info updates.
+	LLRegionInfoModel::instance().setUpdateCallback(boost::bind(&LLFloaterEditDayCycle::onRegionInfoUpdate, this));
 }
 
 void LLFloaterEditDayCycle::syncTimeSlider()
@@ -500,6 +511,50 @@ void LLFloaterEditDayCycle::reset()
 	}
 }
 
+void LLFloaterEditDayCycle::saveRegionDayCycle()
+{
+	LLEnvManagerNew& env_mgr = LLEnvManagerNew::instance();
+	LLWLDayCycle& cur_dayp = LLWLParamManager::instance().mDay; // the day cycle being edited
+
+	// Get current day cycle and the sky preset it references.
+	LLSD day_cycle = cur_dayp.asLLSD();
+	LLSD sky_map;
+	cur_dayp.getSkyMap(sky_map);
+
+	// Apply it to the region.
+	LLEnvironmentSettings new_region_settings;
+	new_region_settings.saveParams(day_cycle, sky_map, env_mgr.getRegionSettings().getWaterParams(), 0.0f);
+
+	if (!LLEnvManagerNew::instance().sendRegionSettings(new_region_settings))
+	{
+		llwarns << "Error applying region environment settings" << llendl;
+		return;
+	}
+
+	setApplyProgress(true);
+}
+
+void LLFloaterEditDayCycle::setApplyProgress(bool started)
+{
+	LLLoadingIndicator* indicator = getChild<LLLoadingIndicator>("progress_indicator");
+
+	indicator->setVisible(started);
+
+	if (started)
+	{
+		indicator->start();
+	}
+	else
+	{
+		indicator->stop();
+	}
+}
+
+bool LLFloaterEditDayCycle::getApplyProgress() const
+{
+	return getChild<LLLoadingIndicator>("progress_indicator")->getVisible();
+}
+
 void LLFloaterEditDayCycle::onDeleteKey()
 {
 	if (mSliderToKey.size() == 0)
@@ -533,6 +588,62 @@ void LLFloaterEditDayCycle::onDeleteKey()
 	applyTrack();
 }
 
+void LLFloaterEditDayCycle::onRegionSettingsChange()
+{
+	LL_DEBUGS("Windlight") << "Region settings changed" << LL_ENDL;
+
+	if (getApplyProgress()) // our region settings have being applied
+	{
+		setApplyProgress(false);
+
+		// Change preference if requested.
+		if (mMakeDefaultCheckBox->getValue())
+		{
+			LL_DEBUGS("Windlight") << "Changed environment preference to region settings" << llendl;
+			LLEnvManagerNew::instance().setUseRegionSettings(true);
+		}
+
+		closeFloater();
+	}
+}
+
+void LLFloaterEditDayCycle::onRegionChange()
+{
+	LL_DEBUGS("Windlight") << "Region changed" << LL_ENDL;
+
+	// If we're editing the region day cycle
+	if (getSelectedDayCycle().scope == LLEnvKey::SCOPE_REGION)
+	{
+		reset(); // undoes all unsaved changes
+	}
+}
+
+void LLFloaterEditDayCycle::onRegionSettingsApplied(bool success)
+{
+	LL_DEBUGS("Windlight") << "Region settings applied: " << success << LL_ENDL;
+
+	if (!success)
+	{
+		// stop progress indicator
+		setApplyProgress(false);
+	}
+}
+
+void LLFloaterEditDayCycle::onRegionInfoUpdate()
+{
+	LL_DEBUGS("Windlight") << "Region info updated" << LL_ENDL;
+	bool can_edit = true;
+
+	// If we've selected the region day cycle for editing.
+	if (getSelectedDayCycle().scope == LLEnvKey::SCOPE_REGION)
+	{
+		// check whether we have the access
+		can_edit = canEditRegionSettings();
+	}
+
+	enableEditing(can_edit);
+}
+
 void LLFloaterEditDayCycle::onDayCycleNameEdited()
 {
 	// Disable saving a day cycle having empty name.
@@ -544,6 +655,7 @@ void LLFloaterEditDayCycle::onDayCycleSelected()
 {
 	LLSD day_data;
 	LLWLParamKey dc_key = getSelectedDayCycle();
+	bool can_edit = true;
 
 	if (dc_key.scope == LLEnvKey::SCOPE_LOCAL)
 	{
@@ -562,6 +674,8 @@ void LLFloaterEditDayCycle::onDayCycleSelected()
 			llassert(day_data.size() > 0);
 			return;
 		}
+
+		can_edit = canEditRegionSettings();
 	}
 
 	F32 slider_time = mTimeSlider->getCurSliderValue() / sHoursPerDay;
@@ -569,7 +683,7 @@ void LLFloaterEditDayCycle::onDayCycleSelected()
 	LLWLParamManager::instance().applyDayCycleParams(day_data, dc_key.scope, slider_time);
 	loadTrack();
 
-	enableEditing(true);
+	enableEditing(can_edit);
 }
 
 void LLFloaterEditDayCycle::onBtnSave()
@@ -579,7 +693,8 @@ void LLFloaterEditDayCycle::onBtnSave()
 
 	if (selected_day.scope == LLEnvKey::SCOPE_REGION)
 	{
-		llwarns << "Saving to a local day cycle" << llendl;
+		saveRegionDayCycle();
+		return;
 	}
 
 	std::string name = selected_day.name;
@@ -660,3 +775,14 @@ std::string LLFloaterEditDayCycle::getRegionName()
 {
 	return gAgent.getRegion() ? gAgent.getRegion()->getName() : LLTrans::getString("Unknown");
 }
+
+// static
+bool LLFloaterEditDayCycle::canEditRegionSettings()
+{
+	LLViewerRegion* region = gAgent.getRegion();
+	BOOL owner_or_god = gAgent.isGodlike() || (region && region->getOwner() == gAgent.getID());
+	BOOL owner_or_god_or_manager = owner_or_god || (region && region->isEstateManager());
+
+	LL_DEBUGS("Windlight") << "Can edit region settings: " << owner_or_god_or_manager << LL_ENDL;
+	return owner_or_god_or_manager;
+}
diff --git a/indra/newview/llfloatereditdaycycle.h b/indra/newview/llfloatereditdaycycle.h
index 742608712b..0a2ba32dfe 100644
--- a/indra/newview/llfloatereditdaycycle.h
+++ b/indra/newview/llfloatereditdaycycle.h
@@ -78,6 +78,10 @@ private:
 	void dumpTrack();
 	void enableEditing(bool enable);
 	void reset();
+	void saveRegionDayCycle();
+
+	void setApplyProgress(bool started);
+	bool getApplyProgress() const;
 
 	void onTimeSliderMoved();	/// time slider moved
 	void onKeyTimeMoved();		/// a key frame moved
@@ -86,6 +90,11 @@ private:
 	void onAddKey();			/// new key added on slider
 	void onDeleteKey();			/// a key frame deleted
 
+	void onRegionSettingsChange();
+	void onRegionChange();
+	void onRegionSettingsApplied(bool success);
+	void onRegionInfoUpdate();
+
 	void onDayCycleNameEdited();
 	void onDayCycleSelected();
 	void onBtnSave();
@@ -95,6 +104,7 @@ private:
 	void onSaveConfirmed();
 
 	static std::string getRegionName();
+	static bool canEditRegionSettings();
 
 	/// convenience class for holding keyframes mapped to sliders
 	struct SliderKey
diff --git a/indra/newview/llfloaterregioninfo.cpp b/indra/newview/llfloaterregioninfo.cpp
index 2778671e24..39ed2534b2 100644
--- a/indra/newview/llfloaterregioninfo.cpp
+++ b/indra/newview/llfloaterregioninfo.cpp
@@ -3538,13 +3538,9 @@ bool LLPanelEnvironmentInfo::getSelectedDayCycleParams(LLSD& day_cycle, LLSD& sk
 
 		// Create sky map from the day cycle.
 		{
-			std::map<LLWLParamKey, LLWLParamSet> refs;
 			LLWLDayCycle tmp_day;
-
 			tmp_day.loadDayCycle(day_cycle, dc.scope);
-			tmp_day.getSkyRefs(refs);
-
-			sky_map = LLWLParamManager::createSkyMap(refs);
+			tmp_day.getSkyMap(sky_map);
 		}
 	}
 
diff --git a/indra/newview/llregioninfomodel.cpp b/indra/newview/llregioninfomodel.cpp
new file mode 100644
index 0000000000..a74fb81355
--- /dev/null
+++ b/indra/newview/llregioninfomodel.cpp
@@ -0,0 +1,106 @@
+/** 
+ * @file llregioninfomodel.cpp
+ * @brief Region info model
+ *
+ * $LicenseInfo:firstyear=2011&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2011, 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$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llregioninfomodel.h"
+
+// libs
+#include "message.h"
+
+// viewers
+
+void LLRegionInfoModel::reset()
+{
+	mSimAccess			= 0;
+	mAgentLimit			= 0;
+
+	mRegionFlags		= 0;
+	mEstateID			= 0;
+	mParentEstateID		= 0;
+
+	mPricePerMeter		= 0;
+	mRedirectGridX		= 0;
+	mRedirectGridY		= 0;
+
+	mBillableFactor		= 0.0f;
+	mObjectBonusFactor	= 0.0f;
+	mWaterHeight		= 0.0f;
+	mTerrainRaiseLimit	= 0.0f;
+	mTerrainLowerLimit	= 0.0f;
+	mSunHour			= 0.0f;
+
+	mUseEstateSun		= false;
+
+	mSimType.clear();
+	mSimName.clear();
+}
+
+LLRegionInfoModel::LLRegionInfoModel()
+{
+	reset();
+}
+
+boost::signals2::connection LLRegionInfoModel::setUpdateCallback(const update_signal_t::slot_type& cb)
+{
+	return mUpdateSignal.connect(cb);
+}
+
+void LLRegionInfoModel::update(LLMessageSystem* msg)
+{
+	reset();
+
+	msg->getStringFast(_PREHASH_RegionInfo, _PREHASH_SimName, mSimName);
+	msg->getU32Fast(_PREHASH_RegionInfo, _PREHASH_EstateID, mEstateID);
+	msg->getU32Fast(_PREHASH_RegionInfo, _PREHASH_ParentEstateID, mParentEstateID);
+	msg->getU32Fast(_PREHASH_RegionInfo, _PREHASH_RegionFlags, mRegionFlags);
+	msg->getU8Fast(_PREHASH_RegionInfo, _PREHASH_SimAccess, mSimAccess);
+	msg->getU8Fast(_PREHASH_RegionInfo, _PREHASH_MaxAgents, mAgentLimit);
+	msg->getF32Fast(_PREHASH_RegionInfo, _PREHASH_ObjectBonusFactor, mObjectBonusFactor);
+	msg->getF32Fast(_PREHASH_RegionInfo, _PREHASH_BillableFactor, mBillableFactor);
+	msg->getF32Fast(_PREHASH_RegionInfo, _PREHASH_WaterHeight, mWaterHeight);
+	msg->getF32Fast(_PREHASH_RegionInfo, _PREHASH_TerrainRaiseLimit, mTerrainRaiseLimit);
+	msg->getF32Fast(_PREHASH_RegionInfo, _PREHASH_TerrainLowerLimit, mTerrainLowerLimit);
+	msg->getS32Fast(_PREHASH_RegionInfo, _PREHASH_PricePerMeter, mPricePerMeter);
+	msg->getS32Fast(_PREHASH_RegionInfo, _PREHASH_RedirectGridX, mRedirectGridX);
+	msg->getS32Fast(_PREHASH_RegionInfo, _PREHASH_RedirectGridY, mRedirectGridY);
+
+	msg->getBOOL(_PREHASH_RegionInfo, _PREHASH_UseEstateSun, mUseEstateSun);
+
+	// actually the "last set" sun hour, not the current sun hour. JC
+	msg->getF32(_PREHASH_RegionInfo, _PREHASH_SunHour, mSunHour);
+
+	// the only reasonable way to decide if we actually have any data is to
+	// check to see if any of these fields have nonzero sizes
+	if (msg->getSize(_PREHASH_RegionInfo2, _PREHASH_ProductSKU) > 0 ||
+		msg->getSize(_PREHASH_RegionInfo2, "ProductName") > 0)
+	{
+		msg->getString(_PREHASH_RegionInfo2, "ProductName", mSimType);
+	}
+
+	// Let interested parties know that region info has been updated.
+	mUpdateSignal();
+}
diff --git a/indra/newview/llregioninfomodel.h b/indra/newview/llregioninfomodel.h
new file mode 100644
index 0000000000..87e1b35ff8
--- /dev/null
+++ b/indra/newview/llregioninfomodel.h
@@ -0,0 +1,82 @@
+/** 
+ * @file llregioninfomodel.h
+ * @brief Region info model
+ *
+ * $LicenseInfo:firstyear=2011&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2011, 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$
+ */
+
+#ifndef LL_LLREGIONINFOMODEL_H
+#define LL_LLREGIONINFOMODEL_H
+
+class LLMessageSystem;
+
+#include "llsingleton.h"
+
+/**
+ * Contains region info, notifies interested parties of its changes.
+ */
+class LLRegionInfoModel : public LLSingleton<LLRegionInfoModel>
+{
+	LOG_CLASS(LLRegionInfoModel);
+
+public:
+	typedef boost::signals2::signal<void()> update_signal_t;
+	boost::signals2::connection setUpdateCallback(const update_signal_t::slot_type& cb);
+
+	// *TODO: Add getters and make the data private.
+	U8			mSimAccess;
+	U8			mAgentLimit;
+
+	U32			mRegionFlags;
+	U32			mEstateID;
+	U32			mParentEstateID;
+
+	S32			mPricePerMeter;
+	S32			mRedirectGridX;
+	S32			mRedirectGridY;
+
+	F32			mBillableFactor;
+	F32			mObjectBonusFactor;
+	F32			mWaterHeight;
+	F32			mTerrainRaiseLimit;
+	F32			mTerrainLowerLimit;
+	F32			mSunHour;
+
+	BOOL		mUseEstateSun;
+
+	std::string	mSimName;
+	std::string	mSimType;
+
+protected:
+	friend class LLSingleton<LLRegionInfoModel>;
+	friend class LLViewerRegion;
+
+	LLRegionInfoModel();
+	void update(LLMessageSystem* msg);
+
+private:
+	void reset();
+
+	update_signal_t mUpdateSignal;
+};
+
+#endif // LL_LLREGIONINFOMODEL_H
diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp
index 6f4dff5819..7762b8755d 100644
--- a/indra/newview/llviewerregion.cpp
+++ b/indra/newview/llviewerregion.cpp
@@ -54,6 +54,7 @@
 #include "llfloaterreporter.h"
 #include "llfloaterregioninfo.h"
 #include "llhttpnode.h"
+#include "llregioninfomodel.h"
 #include "llsdutil.h"
 #include "llstartup.h"
 #include "lltrans.h"
@@ -642,9 +643,11 @@ std::string LLViewerRegion::accessToShortString(U8 sim_access)
 void LLViewerRegion::processRegionInfo(LLMessageSystem* msg, void**)
 {
 	// send it to 'observers'
+	// *TODO: switch the floaters to using LLRegionInfoModel
 	LLFloaterGodTools::processRegionInfo(msg);
 	LLFloaterRegionInfo::processRegionInfo(msg);
 	LLFloaterReporter::processRegionInfo(msg);
+	LLRegionInfoModel::instance().update(msg);
 }
 
 void LLViewerRegion::setCacheID(const LLUUID& id)
diff --git a/indra/newview/llwldaycycle.cpp b/indra/newview/llwldaycycle.cpp
index 8dca9c5ecb..ca5b65f0da 100644
--- a/indra/newview/llwldaycycle.cpp
+++ b/indra/newview/llwldaycycle.cpp
@@ -158,15 +158,15 @@ LLSD LLWLDayCycle::asLLSD()
 	return day_data;
 }
 
-bool LLWLDayCycle::getSkyRefs(std::map<LLWLParamKey, LLWLParamSet>& refs)
+bool LLWLDayCycle::getSkyRefs(std::map<LLWLParamKey, LLWLParamSet>& refs) const
 {
 	bool result = true;
 	LLWLParamManager& wl_mgr = LLWLParamManager::instance();
 
 	refs.clear();
-	for (std::map<F32, LLWLParamKey>::iterator iter = mTimeMap.begin(); iter != mTimeMap.end(); ++iter)
+	for (std::map<F32, LLWLParamKey>::const_iterator iter = mTimeMap.begin(); iter != mTimeMap.end(); ++iter)
 	{
-		LLWLParamKey& key = iter->second;
+		const LLWLParamKey& key = iter->second;
 		if (!wl_mgr.getParamSet(key, refs[key]))
 		{
 			llwarns << "Cannot find sky [" << key.name << "] referenced by a day cycle" << llendl;
@@ -177,6 +177,19 @@ bool LLWLDayCycle::getSkyRefs(std::map<LLWLParamKey, LLWLParamSet>& refs)
 	return result;
 }
 
+bool LLWLDayCycle::getSkyMap(LLSD& sky_map) const
+{
+	std::map<LLWLParamKey, LLWLParamSet> refs;
+
+	if (!getSkyRefs(refs))
+	{
+		return false;
+	}
+
+	sky_map = LLWLParamManager::createSkyMap(refs);
+	return true;
+}
+
 void LLWLDayCycle::clearKeyframes()
 {
 	lldebugs << "Clearing key frames" << llendl;
diff --git a/indra/newview/llwldaycycle.h b/indra/newview/llwldaycycle.h
index e5bc82bb6c..9dbfad294a 100644
--- a/indra/newview/llwldaycycle.h
+++ b/indra/newview/llwldaycycle.h
@@ -78,7 +78,10 @@ public:
 	LLSD asLLSD();
 
 	// get skies referenced by this day cycle
-	bool getSkyRefs(std::map<LLWLParamKey, LLWLParamSet>& refs);
+	bool getSkyRefs(std::map<LLWLParamKey, LLWLParamSet>& refs) const;
+
+	// get referenced skies as LLSD
+	bool getSkyMap(LLSD& sky_map) const;
 
 	/// clear keyframes
 	void clearKeyframes();
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 79dac60486..6cfa67f766 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
@@ -455,6 +455,14 @@
      top_delta="-1"
      value="6:00 AM"
      width="75"/> 
+        <loading_indicator
+         height="23"
+         layout="topleft"
+         left="25"
+         name="progress_indicator"
+         top="417"
+         visible="false"
+         width="23" />
         <check_box
          follows="top|left"
          height="10"
@@ -471,6 +479,7 @@
          layout="topleft"
          left_pad="0"
          name="save"
+         top="417"
          width="70"/>
         <button
          follows="bottom|right"
@@ -479,5 +488,6 @@
          layout="topleft"
          left_pad="15"
          name="cancel"
+         top_delta="0"
          width="70"/>
  </floater>
-- 
cgit v1.2.3