Initial community commit
This commit is contained in:
@@ -0,0 +1,334 @@
|
||||
/*
|
||||
* view_tre.h
|
||||
* ----------
|
||||
* Purpose: Tree view for managing open songs, sound files, file browser, ...
|
||||
* Notes : (currently none)
|
||||
* Authors: Olivier Lapicque
|
||||
* 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 <vector>
|
||||
#include <bitset>
|
||||
|
||||
OPENMPT_NAMESPACE_BEGIN
|
||||
|
||||
class CModDoc;
|
||||
class CModTree;
|
||||
class CSoundFile;
|
||||
class CDLSBank;
|
||||
|
||||
struct ModTreeDocInfo
|
||||
{
|
||||
// Tree state variables
|
||||
std::vector<std::vector<HTREEITEM>> tiOrders;
|
||||
std::vector<HTREEITEM> tiSequences, tiPatterns;
|
||||
CModDoc &modDoc;
|
||||
HTREEITEM hSong = nullptr, hPatterns = nullptr, hSamples = nullptr, hInstruments = nullptr, hComments = nullptr, hOrders = nullptr, hEffects = nullptr;
|
||||
|
||||
// Module information
|
||||
ORDERINDEX ordSel = ORDERINDEX_INVALID;
|
||||
SEQUENCEINDEX seqSel = SEQUENCEINDEX_INVALID;
|
||||
|
||||
std::bitset<MAX_SAMPLES> samplesPlaying;
|
||||
std::bitset<MAX_INSTRUMENTS> instrumentsPlaying;
|
||||
|
||||
ModTreeDocInfo(CModDoc &modDoc);
|
||||
};
|
||||
|
||||
|
||||
class CModTreeDropTarget: public COleDropTarget
|
||||
{
|
||||
protected:
|
||||
CModTree *m_pModTree;
|
||||
|
||||
public:
|
||||
CModTreeDropTarget() { m_pModTree = nullptr; }
|
||||
BOOL Register(CModTree *pWnd);
|
||||
|
||||
public:
|
||||
DROPEFFECT OnDragEnter(CWnd *pWnd, COleDataObject* pDataObject, DWORD dwKeyState, CPoint point) override;
|
||||
DROPEFFECT OnDragOver(CWnd *pWnd, COleDataObject* pDataObject, DWORD dwKeyState, CPoint point) override;
|
||||
BOOL OnDrop(CWnd *pWnd, COleDataObject* pDataObject, DROPEFFECT dropEffect, CPoint point) override;
|
||||
};
|
||||
|
||||
|
||||
class CModTree: public CTreeCtrl
|
||||
{
|
||||
protected:
|
||||
enum TreeStatus
|
||||
{
|
||||
TREESTATUS_RDRAG = 0x01,
|
||||
TREESTATUS_LDRAG = 0x02,
|
||||
TREESTATUS_SINGLEEXPAND = 0x04,
|
||||
TREESTATUS_DRAGGING = (TREESTATUS_RDRAG | TREESTATUS_LDRAG)
|
||||
};
|
||||
|
||||
enum ModItemType : uint8
|
||||
{
|
||||
MODITEM_NULL = 0,
|
||||
|
||||
MODITEM_BEGIN_SONGITEMS,
|
||||
MODITEM_ORDER = MODITEM_BEGIN_SONGITEMS,
|
||||
MODITEM_PATTERN,
|
||||
MODITEM_SAMPLE,
|
||||
MODITEM_INSTRUMENT,
|
||||
MODITEM_COMMENTS,
|
||||
MODITEM_EFFECT,
|
||||
MODITEM_SEQUENCE,
|
||||
MODITEM_HDR_SONG,
|
||||
MODITEM_HDR_ORDERS,
|
||||
MODITEM_HDR_PATTERNS,
|
||||
MODITEM_HDR_SAMPLES,
|
||||
MODITEM_HDR_INSTRUMENTS,
|
||||
MODITEM_HDR_EFFECTS,
|
||||
MODITEM_END_SONGITEMS = MODITEM_HDR_EFFECTS,
|
||||
|
||||
MODITEM_HDR_INSTRUMENTLIB,
|
||||
MODITEM_HDR_MIDILIB,
|
||||
MODITEM_HDR_MIDIGROUP,
|
||||
MODITEM_MIDIINSTRUMENT,
|
||||
MODITEM_MIDIPERCUSSION,
|
||||
MODITEM_INSLIB_FOLDER,
|
||||
MODITEM_INSLIB_SAMPLE,
|
||||
MODITEM_INSLIB_INSTRUMENT,
|
||||
MODITEM_INSLIB_SONG,
|
||||
MODITEM_DLSBANK_FOLDER,
|
||||
MODITEM_DLSBANK_INSTRUMENT,
|
||||
};
|
||||
|
||||
// Bit mask magic
|
||||
enum
|
||||
{
|
||||
MIDILIB_SHIFT = 16,
|
||||
MIDILIB_MASK = (1 << MIDILIB_SHIFT) - 1,
|
||||
|
||||
// Must be consistent with CCtrlPatterns::OnActivatePage
|
||||
SEQU_SHIFT = 16,
|
||||
SEQU_MASK = (1 << SEQU_SHIFT) - 1,
|
||||
SEQU_INDICATOR = 0x80000000,
|
||||
|
||||
// Soundbank instrument identification (must be consistent with CViewInstrument::OnDragonDrop / CViewSample::OnDragonDrop)
|
||||
DLS_TYPEPERC = 0x80000000,
|
||||
DLS_INSTRMASK = 0x0000FFFF,
|
||||
DLS_REGIONMASK = 0x7FFF0000, // Drum region
|
||||
DLS_REGIONSHIFT = 16,
|
||||
|
||||
DLS_DRUM_FOLDER_LPARAM = 0x12345678,
|
||||
};
|
||||
static_assert((ORDERINDEX_INVALID & SEQU_MASK) == ORDERINDEX_INVALID, "ORDERINDEX doesn't fit in GetItemData() parameter");
|
||||
static_assert((ORDERINDEX_MAX & SEQU_MASK) == ORDERINDEX_MAX, "ORDERINDEX doesn't fit in GetItemData() parameter");
|
||||
static_assert((((SEQUENCEINDEX_INVALID << SEQU_SHIFT) & ~SEQU_INDICATOR) >> SEQU_SHIFT) == SEQUENCEINDEX_INVALID, "SEQUENCEINDEX doesn't fit in GetItemData() parameter");
|
||||
|
||||
struct ModItem
|
||||
{
|
||||
uint32 val1;
|
||||
uint16 val2;
|
||||
ModItemType type;
|
||||
|
||||
ModItem(ModItemType t = MODITEM_NULL, uint32 v1 = 0, uint16 v2 = 0) : val1(v1), val2(v2), type(t) { }
|
||||
bool IsSongItem() const noexcept { return type >= MODITEM_BEGIN_SONGITEMS && type <= MODITEM_END_SONGITEMS; }
|
||||
bool operator==(const ModItem &other) const noexcept { return val1 == other.val1 && val2 == other.val2 && type == other.type; }
|
||||
bool operator!=(const ModItem &other) const noexcept { return !(*this == other); }
|
||||
};
|
||||
|
||||
struct DlsItem : public ModItem
|
||||
{
|
||||
explicit DlsItem(uint16 instr) : ModItem(MODITEM_DLSBANK_INSTRUMENT, instr, 0) { }
|
||||
DlsItem(uint16 instr, uint16 region) : ModItem(MODITEM_DLSBANK_INSTRUMENT, (instr & DLS_INSTRMASK) | ((region << DLS_REGIONSHIFT) & DLS_REGIONMASK) | DLS_TYPEPERC, 0) { }
|
||||
|
||||
uint32 GetRegion() const noexcept { return (val1 & DLS_REGIONMASK) >> DLS_REGIONSHIFT; }
|
||||
uint32 GetInstr() const noexcept { return (val1 & DLS_INSTRMASK); }
|
||||
bool IsPercussion() const noexcept { return ((val1 & DLS_TYPEPERC) == DLS_TYPEPERC); }
|
||||
bool IsMelodic() const noexcept { return !IsPercussion(); }
|
||||
|
||||
static ModItem FromLPARAM(uint32 lparam) { return ModItem{MODITEM_DLSBANK_INSTRUMENT, lparam, 0}; }
|
||||
static LPARAM ToLPARAM(uint16 instr, uint16 region, bool isPerc) { return (instr & DLS_INSTRMASK) | ((region << DLS_REGIONSHIFT) & DLS_REGIONMASK) | (isPerc ? DLS_TYPEPERC : 0); }
|
||||
};
|
||||
|
||||
static CSoundFile *m_SongFile; // For browsing samples and instruments inside modules on disk
|
||||
CModTreeDropTarget m_DropTarget;
|
||||
CModTree *m_pDataTree = nullptr; // Pointer to instrument browser (lower part of tree view) - if it's a nullptr, this object is the instrument browser itself.
|
||||
HWND m_hDropWnd = nullptr;
|
||||
mpt::mutex m_WatchDirMutex;
|
||||
HANDLE m_hSwitchWatchDir = nullptr;
|
||||
mpt::PathString m_WatchDir;
|
||||
HANDLE m_hWatchDirKillThread = nullptr;
|
||||
std::thread m_WatchDirThread;
|
||||
ModItem m_itemDrag;
|
||||
DWORD m_dwStatus = 0;
|
||||
CModDoc *m_selectedDoc = nullptr, *m_dragDoc = nullptr;
|
||||
HTREEITEM m_hItemDrag = nullptr, m_hItemDrop = nullptr;
|
||||
HTREEITEM m_hInsLib = nullptr, m_hMidiLib = nullptr;
|
||||
HTREEITEM m_tiMidi[128];
|
||||
HTREEITEM m_tiPerc[128];
|
||||
std::vector<HTREEITEM> m_tiDLS;
|
||||
std::map<const CModDoc *, ModTreeDocInfo> m_docInfo;
|
||||
|
||||
std::unique_ptr<CDLSBank> m_cachedBank;
|
||||
mpt::PathString m_cachedBankName;
|
||||
|
||||
CString m_compareStrL, m_compareStrR; // Cache for ModTreeInsLibCompareNamesProc to avoid constant re-allocations
|
||||
#if (_WIN32_WINNT >= _WIN32_WINNT_WIN7)
|
||||
DWORD m_stringCompareFlags = NORM_IGNORECASE | NORM_IGNOREWIDTH | SORT_DIGITSASNUMBERS;
|
||||
#else
|
||||
DWORD m_stringCompareFlags = NORM_IGNORECASE | NORM_IGNOREWIDTH;
|
||||
#endif
|
||||
|
||||
// Instrument library
|
||||
mpt::PathString m_InstrLibPath; // Current path to be explored
|
||||
mpt::PathString m_InstrLibHighlightPath; // Folder to highlight in browser after a refresh
|
||||
mpt::PathString m_SongFileName; // Name of open module, without path (== m_InstrLibPath).
|
||||
mpt::PathString m_previousPath; // The folder from which we came from when navigating one folder up
|
||||
std::vector<const char*> m_modExtensions; // cached in order to avoid querying too often when changing browsed folder
|
||||
std::vector<mpt::PathString> m_MediaFoundationExtensions; // cached in order to avoid querying too often when changing browsed folder
|
||||
bool m_showAllFiles = false;
|
||||
|
||||
bool m_doLabelEdit = false;
|
||||
|
||||
public:
|
||||
CModTree(CModTree *pDataTree);
|
||||
~CModTree();
|
||||
|
||||
// Attributes
|
||||
public:
|
||||
void Init();
|
||||
bool InsLibSetFullPath(const mpt::PathString &libPath, const mpt::PathString &songFolder);
|
||||
mpt::PathString InsLibGetFullPath(HTREEITEM hItem) const;
|
||||
bool SetSoundFile(FileReader &file);
|
||||
void RefreshMidiLibrary();
|
||||
void RefreshDlsBanks();
|
||||
void RefreshInstrumentLibrary();
|
||||
void EmptyInstrumentLibrary();
|
||||
void FillInstrumentLibrary(const TCHAR *selectedItem = nullptr);
|
||||
void MonitorInstrumentLibrary();
|
||||
ModItem GetModItem(HTREEITEM hItem);
|
||||
BOOL SetMidiInstrument(UINT nIns, const mpt::PathString &fileName);
|
||||
BOOL SetMidiPercussion(UINT nPerc, const mpt::PathString &fileName);
|
||||
bool ExecuteItem(HTREEITEM hItem);
|
||||
void DeleteTreeItem(HTREEITEM hItem);
|
||||
static void PlayDLSItem(const CDLSBank &dlsBank, const DlsItem &item, ModCommand::NOTE note);
|
||||
BOOL PlayItem(HTREEITEM hItem, ModCommand::NOTE nParam, int volume = -1);
|
||||
BOOL OpenTreeItem(HTREEITEM hItem);
|
||||
BOOL OpenMidiInstrument(DWORD dwItem);
|
||||
void SetFullInstrumentLibraryPath(mpt::PathString path);
|
||||
void InstrumentLibraryChDir(mpt::PathString dir, bool isSong);
|
||||
bool GetDropInfo(DRAGONDROP &dropInfo, mpt::PathString &fullPath);
|
||||
void OnOptionsChanged();
|
||||
void AddDocument(CModDoc &modDoc);
|
||||
void RemoveDocument(const CModDoc &modDoc);
|
||||
void UpdateView(ModTreeDocInfo &info, UpdateHint hint);
|
||||
void OnUpdate(CModDoc *pModDoc, UpdateHint hint, CObject *pHint);
|
||||
bool CanDrop(HTREEITEM hItem, bool bDoDrop);
|
||||
void UpdatePlayPos(CModDoc &modDoc, Notification *pNotify);
|
||||
bool IsItemExpanded(HTREEITEM hItem);
|
||||
void DeleteChildren(HTREEITEM hItem);
|
||||
HTREEITEM GetNthChildItem(HTREEITEM hItem, int index) const;
|
||||
HTREEITEM GetParentRootItem(HTREEITEM hItem) const;
|
||||
|
||||
bool IsSampleBrowser() const { return m_pDataTree == nullptr; }
|
||||
CModTree *GetSampleBrowser() { return IsSampleBrowser() ? this : m_pDataTree; }
|
||||
|
||||
// Overrides
|
||||
// ClassWizard generated virtual function overrides
|
||||
//{{AFX_VIRTUAL(CModTree)
|
||||
public:
|
||||
BOOL PreTranslateMessage(MSG* pMsg) override;
|
||||
//}}AFX_VIRTUAL
|
||||
|
||||
// Drag & Drop operations
|
||||
public:
|
||||
DROPEFFECT OnDragEnter(COleDataObject* pDataObject, DWORD dwKeyState, CPoint point);
|
||||
DROPEFFECT OnDragOver(COleDataObject* pDataObject, DWORD dwKeyState, CPoint point);
|
||||
BOOL OnDrop(COleDataObject* pDataObject, DROPEFFECT dropEffect, CPoint point);
|
||||
|
||||
protected:
|
||||
int ModTreeInsLibCompareNamesGetItem(HTREEITEM item, CString &resultStr);
|
||||
static int CALLBACK ModTreeInsLibCompareNamesProc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort);
|
||||
static int CALLBACK ModTreeInsLibCompareProc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort);
|
||||
static int CALLBACK ModTreeDrumCompareProc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort);
|
||||
HTREEITEM InsertInsLibItem(const TCHAR *name, int image, const TCHAR *selectIfMatch);
|
||||
ModTreeDocInfo *GetDocumentInfoFromItem(HTREEITEM hItem);
|
||||
CModDoc *GetDocumentFromItem(HTREEITEM hItem) { ModTreeDocInfo *info = GetDocumentInfoFromItem(hItem); return info ? &info->modDoc : nullptr; }
|
||||
ModTreeDocInfo *GetDocumentInfoFromModDoc(CModDoc &modDoc);
|
||||
|
||||
size_t GetDLSBankIndexFromItem(HTREEITEM hItem) const;
|
||||
CDLSBank *GetDLSBankFromItem(HTREEITEM hItem) const;
|
||||
|
||||
void InsertOrDupItem(bool insert);
|
||||
void OnItemRightClick(HTREEITEM hItem, CPoint pt);
|
||||
|
||||
static bool HasEffectPlugins(const CSoundFile &sndFile);
|
||||
static bool AllPluginsBypassed(const CSoundFile &sndFile, bool onlyEffects);
|
||||
static void BypassAllPlugins(CSoundFile &sndFile, bool bypass, bool onlyEffects);
|
||||
|
||||
// Generated message map functions
|
||||
protected:
|
||||
//{{AFX_MSG(CModTree)
|
||||
afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
|
||||
afx_msg void OnRButtonUp(UINT nFlags, CPoint point);
|
||||
afx_msg void OnXButtonUp(UINT nFlags, UINT nButton, CPoint point);
|
||||
afx_msg void OnMouseMove(UINT nFlags, CPoint point);
|
||||
afx_msg void OnBeginDrag(HTREEITEM, bool bLeft, LRESULT *pResult);
|
||||
afx_msg void OnBeginLDrag(LPNMHDR, LRESULT *pResult);
|
||||
afx_msg void OnBeginRDrag(LPNMHDR, LRESULT *pResult);
|
||||
afx_msg void OnEndDrag(DWORD dwMask);
|
||||
afx_msg void OnItemDblClk(LPNMHDR phdr, LRESULT *pResult);
|
||||
afx_msg void OnItemReturn(LPNMHDR, LRESULT *pResult);
|
||||
afx_msg void OnItemLeftClick(LPNMHDR pNMHDR, LRESULT *pResult);
|
||||
afx_msg void OnItemRightClick(LPNMHDR, LRESULT *pResult);
|
||||
afx_msg void OnItemExpanded(LPNMHDR pnmhdr, LRESULT *pResult);
|
||||
afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);
|
||||
afx_msg void OnRefreshTree();
|
||||
afx_msg void OnExecuteItem();
|
||||
afx_msg void OnPlayTreeItem();
|
||||
afx_msg void OnDeleteTreeItem();
|
||||
afx_msg void OnOpenTreeItem();
|
||||
afx_msg void OnMuteTreeItem();
|
||||
afx_msg void OnMuteOnlyEffects();
|
||||
afx_msg void OnSoloTreeItem();
|
||||
afx_msg void OnUnmuteAllTreeItem();
|
||||
afx_msg void OnDuplicateTreeItem() { InsertOrDupItem(false); }
|
||||
afx_msg void OnInsertTreeItem() { InsertOrDupItem(true); }
|
||||
afx_msg void OnSwitchToTreeItem(); // hack for sequence items to avoid double-click action
|
||||
afx_msg void OnCloseItem();
|
||||
afx_msg void OnRenameItem();
|
||||
afx_msg void OnBeginLabelEdit(NMHDR *nmhdr, LRESULT *result);
|
||||
afx_msg void OnEndLabelEdit(NMHDR *nmhdr, LRESULT *result);
|
||||
afx_msg void OnDropFiles(HDROP hDropInfo);
|
||||
|
||||
afx_msg void OnSetItemPath();
|
||||
afx_msg void OnSaveItem();
|
||||
afx_msg void OnSaveAll();
|
||||
afx_msg void OnReloadItem();
|
||||
afx_msg void OnReloadAll();
|
||||
afx_msg void OnFindMissing();
|
||||
|
||||
afx_msg void OnAddDlsBank();
|
||||
afx_msg void OnImportMidiLib();
|
||||
afx_msg void OnExportMidiLib();
|
||||
afx_msg void OnSoundBankProperties();
|
||||
afx_msg void OnRefreshInstrLib();
|
||||
afx_msg void OnShowDirectories();
|
||||
afx_msg void OnShowAllFiles();
|
||||
afx_msg void OnShowSoundFiles();
|
||||
|
||||
afx_msg void OnGotoInstrumentDir();
|
||||
afx_msg void OnGotoSampleDir();
|
||||
|
||||
afx_msg LRESULT OnCustomKeyMsg(WPARAM, LPARAM);
|
||||
LRESULT OnMidiMsg(WPARAM midiData, LPARAM);
|
||||
//}}AFX_MSG
|
||||
DECLARE_MESSAGE_MAP()
|
||||
public:
|
||||
afx_msg void OnKillFocus(CWnd *pNewWnd);
|
||||
afx_msg void OnSetFocus(CWnd *pOldWnd);
|
||||
};
|
||||
|
||||
|
||||
OPENMPT_NAMESPACE_END
|
||||
Reference in New Issue
Block a user