/* * Ctrl_pat.h * ---------- * Purpose: Pattern tab, upper panel. * 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 "Globals.h" #include "PatternCursor.h" OPENMPT_NAMESPACE_BEGIN class COrderList; class CCtrlPatterns; struct OrdSelection { ORDERINDEX firstOrd = 0, lastOrd = 0; ORDERINDEX GetSelCount() const { return lastOrd - firstOrd + 1; } }; class COrderList: public CWnd { friend class CCtrlPatterns; protected: HFONT m_hFont = nullptr; int m_cxFont = 0, m_cyFont = 0; //m_nXScroll : The order at the beginning of shown orderlist //m_nScrollPos: The same as order //m_nScrollPos2nd: 2nd selection point if multiple orders are selected // (not neccessarily the higher order - GetCurSel() is taking care of that.) ORDERINDEX m_nXScroll = 0, m_nScrollPos = 0, m_nScrollPos2nd = ORDERINDEX_INVALID, m_nDropPos, m_nMouseDownPos, m_playPos = ORDERINDEX_INVALID; ORDERINDEX m_nDragOrder; //To tell how many orders('orderboxes') to show at least //on both sides of current order(when updating orderslist position). int m_nOrderlistMargins; CModDoc &m_modDoc; CCtrlPatterns &m_pParent; bool m_bScrolling = false, m_bDragging = false; public: COrderList(CCtrlPatterns &parent, CModDoc &document); public: BOOL Init(const CRect&, HFONT hFont); void UpdateView(UpdateHint hint, CObject *pObj = nullptr); void InvalidateSelection(); PATTERNINDEX GetCurrentPattern() const; // make the current selection the secondary selection (used for keyboard orderlist navigation) inline void SetCurSelTo2ndSel(bool isSelectionKeyPressed) { if(isSelectionKeyPressed && m_nScrollPos2nd == ORDERINDEX_INVALID) m_nScrollPos2nd = m_nScrollPos; else if(!isSelectionKeyPressed && m_nScrollPos2nd != ORDERINDEX_INVALID) m_nScrollPos2nd = ORDERINDEX_INVALID; }; void SetSelection(ORDERINDEX firstOrd, ORDERINDEX lastOrd = ORDERINDEX_INVALID); // Why VC wants to inline this huge function is beyond my understanding... MPT_NOINLINE bool SetCurSel(ORDERINDEX sel, bool setPlayPos = true, bool shiftClick = false, bool ignoreCurSel = false); void UpdateScrollInfo(); void UpdateInfoText(); int GetFontWidth(); void QueuePattern(CPoint pt); // Check if this module is currently playing bool IsPlaying() const; ORDERINDEX GetOrderFromPoint(const CPoint &pt) const; CRect GetRectFromOrder(ORDERINDEX ord) const; // Get the currently selected pattern(s). // Set ignoreSelection to true if only the first selected point is important. OrdSelection GetCurSel(bool ignoreSelection = false) const; // Sets target margin value and returns the effective margin value. ORDERINDEX SetMargins(int); // Returns the effective margin value. ORDERINDEX GetMargins() { return GetMargins(GetMarginsMax()); } // Returns the effective margin value. ORDERINDEX GetMargins(const ORDERINDEX maxMargins) const { return std::min(maxMargins, static_cast(m_nOrderlistMargins)); } // Returns maximum margin value given current window width. ORDERINDEX GetMarginsMax() { return GetMarginsMax(GetLength()); } // Returns maximum margin value when shown sequence has nLength orders. // For example: If length is 4 orders -> maxMargins = 4/2 - 1 = 1; // if maximum is 5 -> maxMargins = (int)5/2 = 2 ORDERINDEX GetMarginsMax(const ORDERINDEX length) const { return (length > 0 && length % 2 == 0) ? length / 2 - 1 : length / 2; } // Returns the number of sequence items visible in the list. ORDERINDEX GetLength(); // Return true if given order is in margins given that first shown order // is 'startOrder'. Begin part of the whole sequence // is not interpreted to be in margins regardless of the margin value. bool IsOrderInMargins(int order, int startOrder); // Ensure that a given order index is visible in the orderlist view. void EnsureVisible(ORDERINDEX order); // Set given sqeuence and update orderlist display. void SelectSequence(const SEQUENCEINDEX nSeq); // Helper function for entering pattern number void EnterPatternNum(int enterNum); void OnCopy(bool onlyOrders); // Update play state and order lock ranges after inserting order items. void InsertUpdatePlaystate(ORDERINDEX first, ORDERINDEX last); // Update play state and order lock ranges after deleting order items. void DeleteUpdatePlaystate(ORDERINDEX first, ORDERINDEX last); //{{AFX_VIRTUAL(COrderList) BOOL PreTranslateMessage(MSG *pMsg) override; INT_PTR OnToolHitTest(CPoint point, TOOLINFO* pTI) const override; HRESULT get_accName(VARIANT varChild, BSTR *pszName) override; //}}AFX_VIRTUAL protected: ModSequence& Order(); const ModSequence& Order() const; void SetScrollPos(int pos); int GetScrollPos(bool getTrackPos = false); // Resizes the order list if the specified order is past the order list length bool EnsureEditable(ORDERINDEX ord); //{{AFX_MSG(COrderList) afx_msg void OnPaint(); afx_msg BOOL OnEraseBkgnd(CDC *) { return TRUE; } afx_msg void OnSetFocus(CWnd *); afx_msg void OnKillFocus(CWnd *); afx_msg void OnLButtonDown(UINT, CPoint); afx_msg void OnLButtonDblClk(UINT, CPoint); afx_msg void OnRButtonDown(UINT, CPoint); afx_msg void OnLButtonUp(UINT, CPoint); afx_msg void OnMButtonDown(UINT, CPoint); afx_msg void OnMouseMove(UINT, CPoint); afx_msg void OnHScroll(UINT nSBCode, UINT nPos, CScrollBar*); afx_msg void OnSize(UINT nType, int cx, int cy); afx_msg void OnSwitchToView(); afx_msg void OnInsertOrder(); afx_msg void OnInsertSeparatorPattern(); afx_msg void OnDeleteOrder(); afx_msg void OnRenderOrder(); afx_msg void OnPatternProperties(); afx_msg void OnPlayerPlay(); afx_msg void OnPlayerPause(); afx_msg void OnPlayerPlayFromStart(); afx_msg void OnPatternPlayFromStart(); afx_msg void OnCreateNewPattern(); afx_msg void OnDuplicatePattern(); afx_msg void OnMergePatterns(); afx_msg void OnPatternCopy(); afx_msg void OnPatternPaste(); afx_msg void OnSetRestartPos(); afx_msg void OnEditCopy() { OnCopy(false); } afx_msg void OnEditCopyOrders() { OnCopy(true); } afx_msg void OnEditCut(); afx_msg LRESULT OnDragonDropping(WPARAM bDoDrop, LPARAM lParam); afx_msg LRESULT OnHelpHitTest(WPARAM, LPARAM lParam); afx_msg void OnSelectSequence(UINT nid); afx_msg LRESULT OnCustomKeyMsg(WPARAM, LPARAM); afx_msg void OnLockPlayback(); afx_msg void OnUnlockPlayback(); afx_msg BOOL OnToolTipText(UINT, NMHDR *pNMHDR, LRESULT *pResult); //}}AFX_MSG DECLARE_MESSAGE_MAP() }; // CPatEdit: Edit control that switches back to the pattern view if Tab key is pressed. class CPatEdit: public CEdit { protected: CCtrlPatterns *m_pParent = nullptr; public: CPatEdit() = default; void SetParent(CCtrlPatterns *parent) { m_pParent = parent; } BOOL PreTranslateMessage(MSG *pMsg) override; }; class CCtrlPatterns: public CModControlDlg { friend class COrderList; protected: COrderList m_OrderList; CButton m_BtnPrev, m_BtnNext; CComboBox m_CbnInstrument; CPatEdit m_EditSpacing, m_EditPatName, m_EditSequence; CSpinButtonCtrl m_SpinInstrument, m_SpinSpacing, m_SpinSequence; CModControlBar m_ToolBar; INSTRUMENTINDEX m_nInstrument = 0; PatternCursor::Columns m_nDetailLevel = PatternCursor::lastColumn; // Visible Columns bool m_bRecord = false, m_bVUMeters = false, m_bPluginNames = false; public: CCtrlPatterns(CModControlView &parent, CModDoc &document); Setting &GetSplitPosRef() override { return TrackerSettings::Instance().glPatternWindowHeight; } public: const ModSequence &Order() const; ModSequence &Order(); void SetCurrentPattern(PATTERNINDEX nPat); BOOL SetCurrentInstrument(UINT nIns); BOOL GetFollowSong() { return IsDlgButtonChecked(IDC_PATTERN_FOLLOWSONG); } BOOL GetLoopPattern() {return IsDlgButtonChecked(IDC_PATTERN_LOOP);} COrderList &GetOrderList() { return m_OrderList; } //{{AFX_VIRTUAL(CCtrlPatterns) BOOL OnInitDialog() override; void DoDataExchange(CDataExchange* pDX) override; // DDX/DDV support void RecalcLayout() override; void UpdateView(UpdateHint hint = UpdateHint(), CObject *pObj = nullptr) override; CRuntimeClass *GetAssociatedViewClass() override; LRESULT OnModCtrlMsg(WPARAM wParam, LPARAM lParam) override; void OnActivatePage(LPARAM) override; void OnDeactivatePage() override; BOOL GetToolTipText(UINT, LPTSTR) override; //}}AFX_VIRTUAL protected: //{{AFX_MSG(CCtrlPatterns) afx_msg void OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar); afx_msg void OnSequenceNext(); afx_msg void OnSequencePrev(); afx_msg void OnChannelManager(); afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags); afx_msg void OnPlayerPause(); afx_msg void OnPatternNew(); afx_msg void OnPatternDuplicate(); afx_msg void OnPatternMerge(); afx_msg void OnPatternStop(); afx_msg void OnPatternPlay(); afx_msg void OnPatternPlayNoLoop(); afx_msg void OnPatternPlayRow(); afx_msg void OnPatternPlayFromStart(); afx_msg void OnPatternRecord(); afx_msg void OnPatternVUMeters(); afx_msg void OnPatternViewPlugNames(); afx_msg void OnPatternProperties(); afx_msg void OnPatternExpand(); afx_msg void OnPatternShrink(); afx_msg void OnPatternAmplify(); afx_msg void OnPatternCopy(); afx_msg void OnPatternPaste(); afx_msg void OnFollowSong(); afx_msg void OnChangeLoopStatus(); afx_msg void OnSwitchToView(); afx_msg void OnInstrumentChanged(); afx_msg void OnPrevInstrument(); afx_msg void OnNextInstrument(); afx_msg void OnSpacingChanged(); afx_msg void OnPatternNameChanged(); afx_msg void OnSequenceNameChanged(); afx_msg void OnChordEditor(); afx_msg void OnDetailLo(); afx_msg void OnDetailMed(); afx_msg void OnDetailHi(); afx_msg void OnEditUndo(); afx_msg void OnUpdateRecord(CCmdUI *pCmdUI); afx_msg void TogglePluginEditor(); afx_msg void OnToggleOverflowPaste(); afx_msg void OnSequenceNumChanged(); afx_msg LRESULT OnCustomKeyMsg(WPARAM, LPARAM); //}}AFX_MSG DECLARE_MESSAGE_MAP() private: bool HasValidPlug(INSTRUMENTINDEX instr) const; public: afx_msg BOOL OnMouseWheel(UINT nFlags, short zDelta, CPoint pt); afx_msg void OnXButtonUp(UINT nFlags, UINT nButton, CPoint point); afx_msg BOOL OnToolTip(UINT id, NMHDR *pTTTStruct, LRESULT *pResult); }; OPENMPT_NAMESPACE_END