290 lines
10 KiB
C
290 lines
10 KiB
C
|
/*
|
||
|
* 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<ORDERINDEX>(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<LONG> &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
|