/* * 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 #include OPENMPT_NAMESPACE_BEGIN class CModDoc; class CModTree; class CSoundFile; class CDLSBank; struct ModTreeDocInfo { // Tree state variables std::vector> tiOrders; std::vector 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 samplesPlaying; std::bitset 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 m_tiDLS; std::map m_docInfo; std::unique_ptr 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 m_modExtensions; // cached in order to avoid querying too often when changing browsed folder std::vector 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