winamp/Src/external_dependencies/openmpt-trunk/mptrack/View_tre.h
2024-09-24 14:54:57 +02:00

334 lines
12 KiB
C++

/*
* 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