// NeL - MMORPG Framework // Copyright (C) 2010 Winch Gate Property Limited // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as // published by the Free Software Foundation, either version 3 of the // License, or (at your option) any later version. // // This program 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 Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . #if !defined(AFX_EDITABLE_RANGE_H__0B1EFF2B_FA0E_4AC8_88B8_416605043BF9__INCLUDED_) #define AFX_EDITABLE_RANGE_H__0B1EFF2B_FA0E_4AC8_88B8_416605043BF9__INCLUDED_ #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 // editable_range.h : header file // #include #include "edit_attrib_dlg.h" #include "range_manager.h" #include "range_selector.h" #include "ps_wrapper.h" #include "bound_checker.h" #include "particle_workspace.h" ///////////////////////////////////////////////////////////////////////////// // CEditableRange dialog class CEditableRange : public CEditAttribDlg { public: /** * Construct the dialog by giving it an Id. It will be used to save the user preference. * Each dialog of this type must have its own id in the app. */ CEditableRange(const std::string &id, CParticleWorkspace::CNode *node); // standard constructor // Dialog Data //{{AFX_DATA(CEditableRange) enum { IDD = IDD_EDITABLE_RANGE }; CSliderCtrl m_SliderCtrl; CEdit m_ValueCtrl; CButton m_UpdateValue; CButton m_SelectRange; CString m_MinRange; CString m_MaxRange; CString m_Value; int m_SliderPos; //}}AFX_DATA // Overrides // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CEditableRange) protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support //}}AFX_VIRTUAL // init the dialog at the given position public: virtual void init(uint32 x, uint32 y, CWnd *pParent); BOOL EnableWindow( BOOL bEnable = TRUE ); public: /// for derivers : this must querries the value to the desired manager and set the dialog values virtual void updateRange(void) = 0; /** for derivers : value update from a linked object (reader). See examples in subclasses */ virtual void updateValueFromReader(void) = 0; // empty the values and the slider void emptyDialog(void); // validate the lower an upper bound of a range from their string representation virtual bool editableRangeValueValidator(const CString &lo, const CString &up) = 0; // update the dialog display void update(); // Implementation protected: /** for derivers : value update : this is call with a float ranging from 0.f to 1.f (from the slider) * And it must convert it to the desired value, changing the value of this dialog */ virtual void updateValueFromSlider(double sliderValue) = 0; /// the text has changed, and the user has pressed update virtual void updateValueFromText(void) = 0; // the range tune button was pressed. It muist show a dialog that allows the user to choose the range he wants virtual void selectRange(void) = 0; // the unique id of this dialog std::string _Id; CParticleWorkspace::CNode *_Node; // Node that owns the value // Generated message map functions //{{AFX_MSG(CEditableRange) virtual BOOL OnInitDialog(); afx_msg void OnReleasedcaptureSlider(NMHDR* pNMHDR, LRESULT* pResult); afx_msg void OnSelectRange(); afx_msg void OnSetfocusValue(); afx_msg void OnUpdateValue(); afx_msg void OnKillfocusValue(); afx_msg void OnChangeValue(); afx_msg void OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar); //}}AFX_MSG DECLARE_MESSAGE_MAP() }; /** here, we define a template for editable ranges EditableRanges, that help to instanciate it * Derivers have just to specialize 2 static methods : value2CString and string2value */ template class CEditableRangeT : public CEditableRange, public CBoundChecker { public: /// ctor, it gives the range for the values CEditableRangeT(const std::string &id, CParticleWorkspace::CNode *node, T defaultMin, T defaultMax) : CEditableRange(id, node), _Range(defaultMin, defaultMax), _Wrapper(NULL) { } /** set an interface of a wrapper to read / write values in the particle system * NB : The 'OwnerNode' field of the wrapper if set to the value given when to 'node' when this object was constructed */ void setWrapper(IPSWrapper *wrapper) { _Wrapper = wrapper; _Wrapper->OwnerNode = _Node; } public: // SPECIALIZE THAT. write value into the given CString static void value2CString(T value, CString &dest); // SPECIALIZE THAT. convert a CString into a value, return NULL if ok, or a pointer to an error message static const TCHAR *string2value(const CString &value, T &result); void updateRange(void) { // retrieve our range _Range = CRangeManager::GetRange(_Id, _Range.first, _Range.second); value2CString(_Range.first, m_MinRange); value2CString(_Range.second, m_MaxRange); /* if (_Wrapper) { setValue(_Wrapper->get()); }*/ UpdateData(FALSE); } void updateValueFromReader(void) { if (_Wrapper) { setValue(_Wrapper->get()); } } protected: void updateValueFromText(void) { T value; const TCHAR *message = string2value(m_Value, value); if (!message) { const TCHAR *mess = validateUpperBound(value), *mess2 = validateLowerBound(value); if (mess || mess2) { MessageBox(mess ? mess : mess2, _T("error")); return; } _Wrapper->setAndUpdateModifiedFlag(value); setValue(value); return; } MessageBox(message, _T("error")); } void selectRange(void) { CString lowerBound, upperBound; value2CString(_Range.first, lowerBound); value2CString(_Range.second, upperBound); CRangeSelector rs(lowerBound, upperBound, this); if (rs.DoModal() == IDOK) { string2value(rs.getLowerBound(), _Range.first); string2value(rs.getUpperBound(), _Range.second); CRangeManager::SetRange(_Id, _Range.first, _Range.second); updateRange(); } } void updateValueFromSlider(double sliderValue) { nlassert(_Wrapper); T value = _Range.first + (T) ((_Range.second - _Range.first) * sliderValue); value2CString(value, m_Value); if (_Wrapper) { _Wrapper->setAndUpdateModifiedFlag(value); } UpdateData(FALSE); } /// set a new value that will affect both slider and value display void setValue(T value) { value2CString(value, m_Value); // _Wrapper->set(value); if (value < _Range.first) { m_SliderPos = (uint) (m_SliderCtrl.GetRangeMin()); } else if (value > _Range.second) { m_SliderPos = (uint) (m_SliderCtrl.GetRangeMax()); } else { if (_Range.second != _Range.first) { m_SliderPos = (uint) ((double) (value - _Range.first) / (_Range.second - _Range.first) * (m_SliderCtrl.GetRangeMax() - m_SliderCtrl.GetRangeMin()) + m_SliderCtrl.GetRangeMin()); } else { m_SliderPos = m_SliderCtrl.GetRangeMin(); } } UpdateData(FALSE); } virtual bool editableRangeValueValidator(const CString &lo, const CString &up) { T upT, loT; const TCHAR *message = string2value(lo, loT); if (message) { ::MessageBox(NULL, message, _T("Range selection error"), MB_OK); return false; } const TCHAR *mess = validateUpperBound(loT), *mess2 = validateLowerBound(loT); if (mess || mess2) { MessageBox(mess ? mess : mess2, _T("error")); return false; } message = string2value(up, upT); if (message) { ::MessageBox(NULL, message, _T("Range selection error"), MB_OK); return false; } mess = validateUpperBound(upT); mess2 = validateLowerBound(upT); if (mess || mess2) { MessageBox(mess ? mess : mess2, _T("error")); return false; } if (upT <= loT) { ::MessageBox(NULL, _T("upper bound must be strictly greater than lower bound"), _T("Range selection error"), MB_OK); return false; } return true; } // min max values std::pair _Range; // wrapper to the particle system IPSWrapper *_Wrapper; }; /////////////////////////////////////////////////////// // IMPLEMENTATION OF TEMPLATE METHOD SPECIALIZATIONS // /////////////////////////////////////////////////////// //////////////////////////////////////////////////////// // float specialization. // //////////////////////////////////////////////////////// CEditableRangeT::CEditableRangeT(const std::string &id, CParticleWorkspace::CNode *node, float defaultMin, float defaultMax ) : CEditableRange(id, node), _Range(defaultMin, defaultMax), _Wrapper(NULL) { } inline void CEditableRangeT::value2CString(float value, CString &dest) { dest.Format(_T("%g"), (double) value); } inline const TCHAR *CEditableRangeT::string2value(const CString &value, float &result) { if (NLMISC::fromString(NLMISC::tStrToUtf8(value), result)) { return NULL; } else { return _T("invalid value"); } } //////////////////////////////////////////////////////// // uint32 specialization. // //////////////////////////////////////////////////////// CEditableRangeT::CEditableRangeT(const std::string &id, CParticleWorkspace::CNode *node, uint32 defaultMin , uint32 defaultMax ) : CEditableRange(id, node), _Range(defaultMin, defaultMax), _Wrapper(NULL) { } inline void CEditableRangeT::value2CString(uint32 value, CString &dest) { dest.Format(_T("%d"), value); } inline const TCHAR *CEditableRangeT::string2value(const CString &value, uint32 &result) { sint32 tmp; if (NLMISC::fromString(NLMISC::tStrToUtf8(value), tmp)) { if (value.Find(_T("-")) > -1) { return _T("negative values not allowed"); } else { result = tmp; return NULL; } } else { return _T("invalid value"); } } //////////////////////////////////////////////////////// // sint32 specialization. // //////////////////////////////////////////////////////// CEditableRangeT::CEditableRangeT(const std::string &id, CParticleWorkspace::CNode *node, sint32 defaultMin , sint32 defaultMax) : CEditableRange(id, node), _Range(defaultMin, defaultMax), _Wrapper(NULL) { } inline void CEditableRangeT::value2CString(sint32 value, CString &dest) { dest.Format(_T("%d"), value); } inline const TCHAR *CEditableRangeT::string2value(const CString &value, sint32 &result) { sint32 tmp; if (NLMISC::fromString(NLMISC::tStrToUtf8(value), tmp)) { result = tmp; return NULL; } else { return _T("invalid value"); } } // some typedefs typedef CEditableRangeT CEditableRangeFloat; typedef CEditableRangeT CEditableRangeUInt; typedef CEditableRangeT CEditableRangeInt; //{{AFX_INSERT_LOCATION}} // Microsoft Visual C++ will insert additional declarations immediately before the previous line. #endif // !defined(AFX_EDITABLE_RANGE_H__0B1EFF2B_FA0E_4AC8_88B8_416605043BF9__INCLUDED_)