388 lines
9.3 KiB
C
388 lines
9.3 KiB
C
|
/*
|
||
|
* TuningDialog.h
|
||
|
* --------------
|
||
|
* Purpose: Alternative sample tuning configuration dialog.
|
||
|
* Notes : (currently none)
|
||
|
* Authors: OpenMPT Devs
|
||
|
* The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
|
||
|
*/
|
||
|
|
||
|
|
||
|
#pragma once
|
||
|
|
||
|
#include "openmpt/all/BuildSettings.hpp"
|
||
|
|
||
|
#include "tuningRatioMapWnd.h"
|
||
|
#include "tuningcollection.h"
|
||
|
#include <vector>
|
||
|
#include <string>
|
||
|
#include "resource.h"
|
||
|
#include "CDecimalSupport.h"
|
||
|
|
||
|
OPENMPT_NAMESPACE_BEGIN
|
||
|
|
||
|
|
||
|
// Tunings exist even outside of CSoundFile objects. We thus cannot use the
|
||
|
// GetCharsetInternal() encoding consistently. For now, just always treat
|
||
|
// tuning strings as Charset::Locale. As of OpenMPT 1.27, this distinction does
|
||
|
// not yet matter, because GetCharsetInteral() is always mpt::Charset::Locale if
|
||
|
// MODPLUG_TRACKER anyway.
|
||
|
extern const mpt::Charset TuningCharsetFallback;
|
||
|
|
||
|
template<class T1, class T2>
|
||
|
class CBijectiveMap
|
||
|
{
|
||
|
public:
|
||
|
CBijectiveMap(const T1& a, const T2& b)
|
||
|
: m_NotFoundT1(a),
|
||
|
m_NotFoundT2(b)
|
||
|
{}
|
||
|
|
||
|
void AddPair(const T1& a, const T2& b)
|
||
|
{
|
||
|
m_T1.push_back(a);
|
||
|
m_T2.push_back(b);
|
||
|
}
|
||
|
|
||
|
void ClearMapping()
|
||
|
{
|
||
|
m_T1.clear();
|
||
|
m_T2.clear();
|
||
|
}
|
||
|
|
||
|
size_t Size() const
|
||
|
{
|
||
|
ASSERT(m_T1.size() == m_T2.size());
|
||
|
return m_T1.size();
|
||
|
}
|
||
|
|
||
|
void RemoveValue_1(const T1& a)
|
||
|
{
|
||
|
auto iter = find(m_T1.begin(), m_T1.end(), a);
|
||
|
if(iter != m_T1.end())
|
||
|
{
|
||
|
m_T2.erase(m_T2.begin() + (iter-m_T1.begin()));
|
||
|
m_T1.erase(iter);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void RemoveValue_2(const T2& b)
|
||
|
{
|
||
|
auto iter = find(m_T2.begin(), m_T2.end(), b);
|
||
|
if(iter != m_T2.end())
|
||
|
{
|
||
|
m_T1.erase(m_T1.begin() + (iter-m_T2.begin()));
|
||
|
m_T2.erase(iter);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
T2 GetMapping_12(const T1& a) const
|
||
|
{
|
||
|
auto iter = find(m_T1.begin(), m_T1.end(), a);
|
||
|
if(iter != m_T1.end())
|
||
|
{
|
||
|
return m_T2[iter-m_T1.begin()];
|
||
|
}
|
||
|
else
|
||
|
return m_NotFoundT2;
|
||
|
}
|
||
|
|
||
|
T1 GetMapping_21(const T2& b) const
|
||
|
{
|
||
|
auto iter = find(m_T2.begin(), m_T2.end(), b);
|
||
|
if(iter != m_T2.end())
|
||
|
{
|
||
|
return m_T1[iter-m_T2.begin()];
|
||
|
}
|
||
|
else
|
||
|
return m_NotFoundT1;
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
//Elements are collected to two arrays so that elements with the
|
||
|
//same index are mapped to each other.
|
||
|
std::vector<T1> m_T1;
|
||
|
std::vector<T2> m_T2;
|
||
|
|
||
|
T1 m_NotFoundT1;
|
||
|
T2 m_NotFoundT2;
|
||
|
};
|
||
|
|
||
|
class CTuningDialog;
|
||
|
|
||
|
class CTuningTreeCtrl : public CTreeCtrl
|
||
|
{
|
||
|
private:
|
||
|
CTuningDialog& m_rParentDialog;
|
||
|
bool m_Dragging;
|
||
|
|
||
|
public:
|
||
|
CTuningTreeCtrl(CTuningDialog* parent)
|
||
|
: m_rParentDialog(*parent)
|
||
|
, m_Dragging(false)
|
||
|
{}
|
||
|
//Note: Parent address may be given in its initializer list.
|
||
|
|
||
|
void SetDragging(bool state = true) {m_Dragging = state;}
|
||
|
bool IsDragging() {return m_Dragging;}
|
||
|
|
||
|
afx_msg void OnMouseMove(UINT nFlags, CPoint point);
|
||
|
afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
|
||
|
DECLARE_MESSAGE_MAP()
|
||
|
};
|
||
|
|
||
|
class CTuningTreeItem
|
||
|
{
|
||
|
private:
|
||
|
CTuning* m_pTuning;
|
||
|
CTuningCollection* m_pTuningCollection;
|
||
|
|
||
|
public:
|
||
|
CTuningTreeItem() : m_pTuning(NULL),
|
||
|
m_pTuningCollection(NULL)
|
||
|
{}
|
||
|
|
||
|
CTuningTreeItem(CTuning* pT) :
|
||
|
m_pTuning(pT),
|
||
|
m_pTuningCollection(NULL)
|
||
|
{}
|
||
|
|
||
|
CTuningTreeItem(CTuningCollection* pTC) :
|
||
|
m_pTuning(NULL),
|
||
|
m_pTuningCollection(pTC)
|
||
|
{}
|
||
|
|
||
|
bool operator==(const CTuningTreeItem& ti) const
|
||
|
{
|
||
|
if(m_pTuning == ti.m_pTuning &&
|
||
|
m_pTuningCollection == ti.m_pTuningCollection)
|
||
|
return true;
|
||
|
else
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
void Reset() {m_pTuning = NULL; m_pTuningCollection = NULL;}
|
||
|
|
||
|
|
||
|
void Set(CTuning* pT)
|
||
|
{
|
||
|
m_pTuning = pT;
|
||
|
m_pTuningCollection = NULL;
|
||
|
}
|
||
|
|
||
|
void Set(CTuningCollection* pTC)
|
||
|
{
|
||
|
m_pTuning = NULL;
|
||
|
m_pTuningCollection = pTC;
|
||
|
}
|
||
|
|
||
|
operator bool () const
|
||
|
{
|
||
|
return m_pTuning || m_pTuningCollection;
|
||
|
}
|
||
|
bool operator ! () const
|
||
|
{
|
||
|
return !operator bool();
|
||
|
}
|
||
|
|
||
|
CTuningCollection* GetTC() {return m_pTuningCollection;}
|
||
|
|
||
|
CTuning* GetT() {return m_pTuning;}
|
||
|
};
|
||
|
|
||
|
// CTuningDialog dialog
|
||
|
|
||
|
class CTuningDialog : public CDialog
|
||
|
{
|
||
|
friend class CTuningTreeCtrl;
|
||
|
|
||
|
enum EnSclImport
|
||
|
{
|
||
|
enSclImportOk,
|
||
|
enSclImportFailTooLargeNumDenomIntegers,
|
||
|
enSclImportFailZeroDenominator,
|
||
|
enSclImportFailNegativeRatio,
|
||
|
enSclImportFailUnableToOpenFile,
|
||
|
enSclImportLineCountMismatch,
|
||
|
enSclImportTuningCreationFailure,
|
||
|
enSclImportAddTuningFailure,
|
||
|
enSclImportFailTooManyNotes
|
||
|
};
|
||
|
|
||
|
public:
|
||
|
using TUNINGVECTOR = std::vector<CTuningCollection*>;
|
||
|
|
||
|
public:
|
||
|
CTuningDialog(CWnd* pParent, INSTRUMENTINDEX inst, CSoundFile &csf);
|
||
|
virtual ~CTuningDialog();
|
||
|
|
||
|
BOOL OnInitDialog();
|
||
|
|
||
|
void UpdateRatioMapEdits(const Tuning::NOTEINDEXTYPE&);
|
||
|
|
||
|
bool GetModifiedStatus(const CTuningCollection* const pTc) const;
|
||
|
|
||
|
// Dialog Data
|
||
|
enum { IDD = IDD_TUNING };
|
||
|
|
||
|
protected:
|
||
|
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
|
||
|
|
||
|
private:
|
||
|
|
||
|
bool CanEdit(CTuningCollection * pTC) const;
|
||
|
bool CanEdit(CTuning * pT, CTuningCollection * pTC) const;
|
||
|
|
||
|
void UpdateView(const int UpdateMask = 0);
|
||
|
void UpdateTuningType();
|
||
|
|
||
|
HTREEITEM AddTreeItem(CTuningCollection* pTC, HTREEITEM parent, HTREEITEM insertAfter);
|
||
|
HTREEITEM AddTreeItem(CTuning* pT, HTREEITEM parent, HTREEITEM insertAfter);
|
||
|
|
||
|
void DeleteTreeItem(CTuning* pT);
|
||
|
void DeleteTreeItem(CTuningCollection* pTC);
|
||
|
|
||
|
// Check if item can be dropped here. If yes, the target collection is returned, otherwise nullptr.
|
||
|
CTuningCollection *CanDrop(HTREEITEM dragDestItem);
|
||
|
void OnEndDrag(HTREEITEM dragDestItem);
|
||
|
|
||
|
//Returns pointer to the tuning collection where tuning given as argument
|
||
|
//belongs to.
|
||
|
CTuningCollection* GetpTuningCollection(const CTuning* const) const;
|
||
|
|
||
|
//Returns the address of corresponding tuningcollection; if it points
|
||
|
//to tuning-entry, returning the owning tuningcollection
|
||
|
CTuningCollection* GetpTuningCollection(HTREEITEM ti) const;
|
||
|
|
||
|
//Checks whether tuning collection can be deleted.
|
||
|
bool IsDeletable(const CTuningCollection* const pTC) const;
|
||
|
|
||
|
// Scl-file import.
|
||
|
EnSclImport ImportScl(const mpt::PathString &filename, const mpt::ustring &name, std::unique_ptr<CTuning> & result);
|
||
|
EnSclImport ImportScl(std::istream& iStrm, const mpt::ustring &name, std::unique_ptr<CTuning> & result);
|
||
|
|
||
|
|
||
|
private:
|
||
|
|
||
|
CSoundFile & m_sndFile;
|
||
|
|
||
|
CTuningRatioMapWnd m_RatioMapWnd;
|
||
|
TUNINGVECTOR m_TuningCollections;
|
||
|
std::vector<CTuningCollection*> m_DeletableTuningCollections;
|
||
|
|
||
|
std::map<const CTuningCollection*, CString> m_TuningCollectionsNames;
|
||
|
std::map<const CTuningCollection*, mpt::PathString> m_TuningCollectionsFilenames;
|
||
|
|
||
|
CTuning* m_pActiveTuning;
|
||
|
CTuningCollection* m_pActiveTuningCollection;
|
||
|
|
||
|
CComboBox m_CombobTuningType;
|
||
|
|
||
|
//Tuning Edits-->
|
||
|
CEdit m_EditSteps;
|
||
|
CNumberEdit m_EditRatioPeriod;
|
||
|
CNumberEdit m_EditRatio;
|
||
|
CEdit m_EditNotename;
|
||
|
CEdit m_EditMiscActions;
|
||
|
CEdit m_EditFineTuneSteps;
|
||
|
CEdit m_EditName;
|
||
|
//<--Tuning Edits
|
||
|
|
||
|
CButton m_ButtonSet;
|
||
|
CButton m_ButtonNew;
|
||
|
CButton m_ButtonExport;
|
||
|
CButton m_ButtonImport;
|
||
|
CButton m_ButtonRemove;
|
||
|
|
||
|
CTuningTreeCtrl m_TreeCtrlTuning;
|
||
|
|
||
|
private:
|
||
|
using TUNINGTREEITEM = CTuningTreeItem;
|
||
|
using TREETUNING_MAP = CBijectiveMap<HTREEITEM, TUNINGTREEITEM>;
|
||
|
TREETUNING_MAP m_TreeItemTuningItemMap;
|
||
|
|
||
|
TUNINGTREEITEM m_DragItem;
|
||
|
TUNINGTREEITEM m_CommandItemSrc;
|
||
|
TUNINGTREEITEM m_CommandItemDest;
|
||
|
//Commanditem is used when receiving context menu-commands,
|
||
|
//m_CommandItemDest is used when the command really need only
|
||
|
//one argument.
|
||
|
|
||
|
using MODIFIED_MAP = std::map<const CTuningCollection* const, bool>;
|
||
|
MODIFIED_MAP m_ModifiedTCs;
|
||
|
//If tuning collection seems to have been modified, its address
|
||
|
//is added to this map.
|
||
|
|
||
|
enum
|
||
|
{
|
||
|
TT_TUNINGCOLLECTION = 1,
|
||
|
TT_TUNING
|
||
|
};
|
||
|
|
||
|
static CString GetSclImportFailureMsg(EnSclImport);
|
||
|
static constexpr size_t s_nSclImportMaxNoteCount = 256;
|
||
|
|
||
|
//To indicate whether to apply changes made to
|
||
|
//those edit boxes(they are modified by certain activities
|
||
|
//in case which the modifications should not be applied to
|
||
|
//tuning data.
|
||
|
bool m_NoteEditApply;
|
||
|
bool m_RatioEditApply;
|
||
|
|
||
|
enum
|
||
|
{
|
||
|
UM_TUNINGDATA = 1, //UM <-> Update Mask
|
||
|
UM_TUNINGCOLLECTION = 2,
|
||
|
};
|
||
|
|
||
|
static const TUNINGTREEITEM s_notFoundItemTuning;
|
||
|
static const HTREEITEM s_notFoundItemTree;
|
||
|
|
||
|
bool AddTuning(CTuningCollection*, CTuning* pT);
|
||
|
bool AddTuning(CTuningCollection*, Tuning::Type type);
|
||
|
|
||
|
//Flag to prevent multiple exit error-messages.
|
||
|
bool m_DoErrorExit;
|
||
|
|
||
|
void DoErrorExit();
|
||
|
|
||
|
virtual void OnOK();
|
||
|
|
||
|
//Treectrl context menu functions.
|
||
|
public:
|
||
|
afx_msg void OnRemoveTuning();
|
||
|
afx_msg void OnAddTuningGeneral();
|
||
|
afx_msg void OnAddTuningGroupGeometric();
|
||
|
afx_msg void OnAddTuningGeometric();
|
||
|
afx_msg void OnCopyTuning();
|
||
|
afx_msg void OnRemoveTuningCollection();
|
||
|
|
||
|
//Event-functions
|
||
|
public:
|
||
|
afx_msg void OnEnChangeEditSteps();
|
||
|
afx_msg void OnEnChangeEditRatioperiod();
|
||
|
afx_msg void OnEnChangeEditNotename();
|
||
|
afx_msg void OnBnClickedButtonSetvalues();
|
||
|
afx_msg void OnEnChangeEditRatiovalue();
|
||
|
afx_msg void OnBnClickedButtonNew();
|
||
|
afx_msg void OnBnClickedButtonExport();
|
||
|
afx_msg void OnBnClickedButtonImport();
|
||
|
afx_msg void OnBnClickedButtonRemove();
|
||
|
afx_msg void OnEnChangeEditFinetunesteps();
|
||
|
afx_msg void OnEnKillfocusEditFinetunesteps();
|
||
|
afx_msg void OnEnKillfocusEditName();
|
||
|
afx_msg void OnEnKillfocusEditSteps();
|
||
|
afx_msg void OnEnKillfocusEditRatioperiod();
|
||
|
afx_msg void OnEnKillfocusEditRatiovalue();
|
||
|
afx_msg void OnEnKillfocusEditNotename();
|
||
|
|
||
|
//Treeview events
|
||
|
afx_msg void OnTvnSelchangedTreeTuning(NMHDR *pNMHDR, LRESULT *pResult);
|
||
|
afx_msg void OnTvnDeleteitemTreeTuning(NMHDR *pNMHDR, LRESULT *pResult);
|
||
|
afx_msg void OnNMRclickTreeTuning(NMHDR *pNMHDR, LRESULT *pResult);
|
||
|
afx_msg void OnTvnBegindragTreeTuning(NMHDR *pNMHDR, LRESULT *pResult);
|
||
|
|
||
|
DECLARE_MESSAGE_MAP()
|
||
|
};
|
||
|
|
||
|
OPENMPT_NAMESPACE_END
|