diff options
author | Jef <jef@targetspot.com> | 2024-09-24 08:54:57 -0400 |
---|---|---|
committer | Jef <jef@targetspot.com> | 2024-09-24 08:54:57 -0400 |
commit | 20d28e80a5c861a9d5f449ea911ab75b4f37ad0d (patch) | |
tree | 12f17f78986871dd2cfb0a56e5e93b545c1ae0d0 /Src/external_dependencies/openmpt-trunk/mptrack/TempoSwingDialog.cpp | |
parent | 537bcbc86291b32fc04ae4133ce4d7cac8ebe9a7 (diff) | |
download | winamp-20d28e80a5c861a9d5f449ea911ab75b4f37ad0d.tar.gz |
Initial community commit
Diffstat (limited to 'Src/external_dependencies/openmpt-trunk/mptrack/TempoSwingDialog.cpp')
-rw-r--r-- | Src/external_dependencies/openmpt-trunk/mptrack/TempoSwingDialog.cpp | 429 |
1 files changed, 429 insertions, 0 deletions
diff --git a/Src/external_dependencies/openmpt-trunk/mptrack/TempoSwingDialog.cpp b/Src/external_dependencies/openmpt-trunk/mptrack/TempoSwingDialog.cpp new file mode 100644 index 00000000..b9373a59 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/mptrack/TempoSwingDialog.cpp @@ -0,0 +1,429 @@ +/* + * TempoSwingDialog.cpp + * -------------------- + * Purpose: Implementation of the tempo swing configuration dialog. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#include "stdafx.h" +#include "TempoSwingDialog.h" +#include "Mainfrm.h" + + +OPENMPT_NAMESPACE_BEGIN + +void CTempoSwingDlg::RowCtls::SetValue(TempoSwing::value_type v) +{ + int32 val = Util::muldivr(static_cast<int32>(v) - TempoSwing::Unity, CTempoSwingDlg::SliderUnity, TempoSwing::Unity); + valueSlider.SetPos(val); +} + + +TempoSwing::value_type CTempoSwingDlg::RowCtls::GetValue() const +{ + return Util::muldivr(valueSlider.GetPos(), TempoSwing::Unity, SliderUnity) + TempoSwing::Unity; +} + + +BEGIN_MESSAGE_MAP(CTempoSwingDlg, CDialog) + //{{AFX_MSG_MAP(CTempoSwingDlg) + ON_WM_VSCROLL() + ON_COMMAND(IDC_BUTTON1, &CTempoSwingDlg::OnReset) + ON_COMMAND(IDC_BUTTON2, &CTempoSwingDlg::OnUseGlobal) + ON_COMMAND(IDC_CHECK1, &CTempoSwingDlg::OnToggleGroup) + ON_EN_CHANGE(IDC_EDIT1, &CTempoSwingDlg::OnGroupChanged) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + +int CTempoSwingDlg::m_groupSize = 1; + +CTempoSwingDlg::CTempoSwingDlg(CWnd *parent, const TempoSwing ¤tTempoSwing, CSoundFile &sndFile, PATTERNINDEX pattern) + : CDialog(IDD_TEMPO_SWING, parent) + , m_container(*this) + , m_scrollPos(0) + , m_tempoSwing(currentTempoSwing) + , m_origTempoSwing(pattern == PATTERNINDEX_INVALID ? sndFile.m_tempoSwing : sndFile.Patterns[pattern].GetTempoSwing()) + , m_sndFile(sndFile) + , m_pattern(pattern) +{ + m_groupSize = std::min(m_groupSize, static_cast<int>(m_tempoSwing.size())); +} + + +void CTempoSwingDlg::DoDataExchange(CDataExchange* pDX) +{ + CDialog::DoDataExchange(pDX); + DDX_Control(pDX, IDC_CHECK1, m_checkGroup); + DDX_Control(pDX, IDC_SCROLLBAR1, m_scrollBar); + DDX_Control(pDX, IDC_CONTAINER, m_container); +} + + +BOOL CTempoSwingDlg::OnInitDialog() +{ + struct Measurements + { + enum + { + edRowLabelWidth = 64, // Label "Row 999:" + edSliderWidth = 220, // Setting slider + edSliderHeight = 20, // Setting slider + edValueLabelWidth = 64, // Label "100%" + edPaddingX = 8, // Spacing between elements + edPaddingY = 4, // Spacing between elements + edPaddingTop = 64, // Spacing from top of dialog + edRowHeight = edSliderHeight + edPaddingY, // Height of one set of controls + edFooterHeight = 32, // Buttons + edScrollbarWidth = 16, // Width of optional scrollbar + }; + + const int rowLabelWidth; + const int sliderWidth; + const int sliderHeight; + const int valueLabelWidth; + const int paddingX; + const int paddingY; + const int paddingTop; + const int rowHeight; + const int footerHeight; + const int scrollbarWidth; + + Measurements(HWND hWnd) + : rowLabelWidth(Util::ScalePixels(edRowLabelWidth, hWnd)) + , sliderWidth(Util::ScalePixels(edSliderWidth, hWnd)) + , sliderHeight(Util::ScalePixels(edSliderHeight, hWnd)) + , valueLabelWidth(Util::ScalePixels(edValueLabelWidth, hWnd)) + , paddingX(Util::ScalePixels(edPaddingX, hWnd)) + , paddingY(Util::ScalePixels(edPaddingY, hWnd)) + , paddingTop(Util::ScalePixels(edPaddingTop, hWnd)) + , rowHeight(Util::ScalePixels(edRowHeight, hWnd)) + , footerHeight(Util::ScalePixels(edFooterHeight, hWnd)) + , scrollbarWidth(Util::ScalePixels(edScrollbarWidth, hWnd)) + { } + }; + + CDialog::OnInitDialog(); + Measurements m(m_hWnd); + CRect windowRect, rect; + GetWindowRect(windowRect); + GetClientRect(rect); + windowRect.bottom = windowRect.top + windowRect.Height() - rect.Height(); + + CRect mainWindowRect; + CMainFrame::GetMainFrame()->GetClientRect(mainWindowRect); + + const int realHeight = static_cast<int>(m_tempoSwing.size()) * m.rowHeight; + const int displayHeight = std::min(realHeight, static_cast<int>(mainWindowRect.bottom - windowRect.Height() - m.paddingTop - m.footerHeight)); + + CRect containerRect; + m_container.GetClientRect(containerRect); + containerRect.bottom = displayHeight; + m_container.SetWindowPos(nullptr, 0, m.paddingTop, rect.right - m.scrollbarWidth, containerRect.bottom, SWP_NOZORDER); + m_container.ModifyStyleEx(0, WS_EX_CONTROLPARENT, 0); + + // Need scrollbar? + if(realHeight > displayHeight) + { + SCROLLINFO info; + info.cbSize = sizeof(info); + info.fMask = SIF_ALL; + info.nMin = 0; + info.nMax = realHeight; + info.nPage = displayHeight; + info.nTrackPos = info.nPos = 0; + m_scrollBar.SetScrollInfo(&info, FALSE); + + CRect scrollRect; + m_scrollBar.GetClientRect(scrollRect); + m_scrollBar.SetWindowPos(nullptr, containerRect.right, m.paddingTop, scrollRect.Width(), displayHeight, SWP_NOZORDER); + } else + { + m_scrollBar.ShowWindow(SW_HIDE); + } + + rect.DeflateRect(m.paddingX, 0/* m.paddingTop*/, m.paddingX + m.scrollbarWidth, 0); + + GetDlgItem(IDC_BUTTON2)->ShowWindow((m_pattern != PATTERNINDEX_INVALID) ? SW_SHOW : SW_HIDE); + + m_controls.resize(m_tempoSwing.size()); + for(size_t i = 0; i < m_controls.size(); i++) + { + m_controls[i] = std::make_unique<RowCtls>(); + auto &r = m_controls[i]; + // Row label + r->rowLabel.Create(MPT_CFORMAT("Row {}:")(i + 1), WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE, CRect(rect.left, rect.top, rect.right, rect.top + m.rowHeight), &m_container); + r->rowLabel.SetFont(GetFont()); + + // Value label + r->valueLabel.Create(_T("100%"), WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE, CRect(rect.right - m.valueLabelWidth, rect.top, rect.right, rect.top + m.sliderHeight), &m_container); + r->valueLabel.SetFont(GetFont()); + + // Value slider + r->valueSlider.Create(WS_CHILD | WS_VISIBLE | WS_TABSTOP | TBS_TOOLTIPS | TBS_AUTOTICKS, CRect(rect.left + m.rowLabelWidth, rect.top, rect.right - m.valueLabelWidth, rect.top + m.sliderHeight), &m_container, 0xFFFF); + r->valueSlider.SetFont(GetFont()); + r->valueSlider.SetRange(-SliderResolution / 2, SliderResolution / 2); + r->valueSlider.SetTicFreq(SliderResolution / 8); + r->valueSlider.SetPageSize(SliderResolution / 8); + r->valueSlider.SetPos(1); // Work around https://bugs.winehq.org/show_bug.cgi?id=41909 + r->SetValue(m_tempoSwing[i]); + rect.MoveToY(rect.top + m.rowHeight); + } + + ((CSpinButtonCtrl *)GetDlgItem(IDC_SPIN1))->SetRange32(1, static_cast<int>(m_tempoSwing.size())); + SetDlgItemInt(IDC_EDIT1, m_groupSize); + OnToggleGroup(); + + m_container.OnHScroll(0, 0, reinterpret_cast<CScrollBar *>(&(m_controls[0]->valueSlider))); + rect.MoveToY(m.paddingTop + containerRect.bottom + m.paddingY); + { + // Buttons at dialog bottom + CRect buttonRect; + for(auto i : { IDOK, IDCANCEL, IDC_BUTTON2 }) + { + auto wnd = GetDlgItem(i); + wnd->GetWindowRect(buttonRect); + wnd->SetWindowPos(nullptr, buttonRect.left - windowRect.left - GetSystemMetrics(SM_CXEDGE), rect.top, 0, 0, SWP_NOSIZE | SWP_NOOWNERZORDER); + } + } + + windowRect.bottom += displayHeight + m.paddingTop + m.footerHeight; + SetWindowPos(nullptr, 0, 0, windowRect.Width(), windowRect.Height(), SWP_NOMOVE | SWP_NOOWNERZORDER); + EnableToolTips(); + + return TRUE; +} + + +void CTempoSwingDlg::OnOK() +{ + CDialog::OnOK(); + // If this is the default setup, just clear the vector. + if(m_pattern == PATTERNINDEX_INVALID) + { + if(static_cast<size_t>(std::count(m_tempoSwing.begin(), m_tempoSwing.end(), static_cast<TempoSwing::value_type>(TempoSwing::Unity))) == m_tempoSwing.size()) + { + m_tempoSwing.clear(); + } + } else + { + if(m_tempoSwing == m_sndFile.m_tempoSwing) + { + m_tempoSwing.clear(); + } + } + OnClose(); +} + + +void CTempoSwingDlg::OnCancel() +{ + CDialog::OnCancel(); + OnClose(); +} + + +void CTempoSwingDlg::OnClose() +{ + // Restore original swing properties after preview + if(m_pattern == PATTERNINDEX_INVALID) + { + m_sndFile.m_tempoSwing = m_origTempoSwing; + } else + { + m_sndFile.Patterns[m_pattern].SetTempoSwing(m_origTempoSwing); + } +} + + +void CTempoSwingDlg::OnReset() +{ + for(size_t i = 0; i < m_controls.size(); i++) + { + m_controls[i]->valueSlider.SetPos(0); + } + m_container.OnHScroll(0, 0, reinterpret_cast<CScrollBar *>(&(m_controls[0]->valueSlider))); +} + + +void CTempoSwingDlg::OnUseGlobal() +{ + if(m_sndFile.m_tempoSwing.empty()) + { + OnReset(); + return; + } + for(size_t i = 0; i < m_tempoSwing.size(); i++) + { + m_controls[i]->SetValue(m_sndFile.m_tempoSwing[i % m_sndFile.m_tempoSwing.size()]); + } + m_container.OnHScroll(0, 0, reinterpret_cast<CScrollBar *>(&(m_controls[0]->valueSlider))); +} + + +void CTempoSwingDlg::OnToggleGroup() +{ + const BOOL checked = m_checkGroup.GetCheck() != BST_UNCHECKED; + GetDlgItem(IDC_EDIT1)->EnableWindow(checked); + GetDlgItem(IDC_SPIN1)->EnableWindow(checked); +} + + +void CTempoSwingDlg::OnGroupChanged() +{ + int val = GetDlgItemInt(IDC_EDIT1); + if(val > 0) m_groupSize = std::min(val, static_cast<int>(m_tempoSwing.size())); +} + + +void CTempoSwingDlg::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar *pScrollBar) +{ + if(pScrollBar == &m_scrollBar) + { + // Get the minimum and maximum scrollbar positions. + int minpos; + int maxpos; + pScrollBar->GetScrollRange(&minpos, &maxpos); + + SCROLLINFO sbInfo; + pScrollBar->GetScrollInfo(&sbInfo); + + // Get the current position of scroll box. + int curpos = pScrollBar->GetScrollPos(); + + // Determine the new position of scroll box. + switch(nSBCode) + { + case SB_LEFT: // Scroll to far left. + curpos = minpos; + break; + + case SB_RIGHT: // Scroll to far right. + curpos = maxpos; + break; + + case SB_ENDSCROLL: // End scroll. + m_container.Invalidate(); + break; + + case SB_LINELEFT: // Scroll left. + if(curpos > minpos) + curpos--; + break; + + case SB_LINERIGHT: // Scroll right. + if(curpos < maxpos) + curpos++; + break; + + case SB_PAGELEFT: // Scroll one page left. + if(curpos > minpos) + { + curpos = std::max(minpos, curpos - static_cast<int>(sbInfo.nPage)); + } + break; + + case SB_PAGERIGHT: // Scroll one page right. + if(curpos < maxpos) + { + curpos = std::min(maxpos, curpos + static_cast<int>(sbInfo.nPage)); + } + break; + + case SB_THUMBPOSITION: // Scroll to absolute position. nPos is the position + curpos = nPos; // of the scroll box at the end of the drag operation. + break; + + case SB_THUMBTRACK: // Drag scroll box to specified position. nPos is the + curpos = nPos; // position that the scroll box has been dragged to. + break; + } + + // Set the new position of the thumb (scroll box). + pScrollBar->SetScrollPos(curpos); + + m_container.ScrollWindowEx(0, m_scrollPos - curpos, nullptr, nullptr, nullptr, nullptr, SW_SCROLLCHILDREN | SW_INVALIDATE | SW_ERASE); + m_scrollPos = curpos; + } + + CDialog::OnVScroll(nSBCode, nPos, pScrollBar); +} + + +// Scrollable container for the sliders +BEGIN_MESSAGE_MAP(CTempoSwingDlg::SliderContainer, CDialog) + //{{AFX_MSG_MAP(CTempoSwingDlg::SliderContainer) + ON_WM_HSCROLL() + ON_NOTIFY_EX(TTN_NEEDTEXT, 0, &CTempoSwingDlg::SliderContainer::OnToolTipNotify) + //}}AFX_MSG_MAP +END_MESSAGE_MAP() + + +void CTempoSwingDlg::SliderContainer::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar *pScrollBar) +{ + if(m_parent.m_checkGroup.GetCheck() != BST_UNCHECKED) + { + // Edit groups + size_t editedGroup = 0; + int editedValue = reinterpret_cast<CSliderCtrl *>(pScrollBar)->GetPos(); + for(size_t i = 0; i < m_parent.m_controls.size(); i++) + { + if(m_parent.m_controls[i]->valueSlider.m_hWnd == pScrollBar->m_hWnd) + { + editedGroup = (i / m_parent.m_groupSize) % 2u; + break; + } + } + for(size_t i = 0; i < m_parent.m_controls.size(); i++) + { + if((i / m_parent.m_groupSize) % 2u == editedGroup) + { + m_parent.m_controls[i]->valueSlider.SetPos(editedValue); + } + } + } + + for(size_t i = 0; i < m_parent.m_controls.size(); i++) + { + m_parent.m_tempoSwing[i] = m_parent.m_controls[i]->GetValue(); + } + m_parent.m_tempoSwing.Normalize(); + // Apply preview + if(m_parent.m_pattern == PATTERNINDEX_INVALID) + { + m_parent.m_sndFile.m_tempoSwing = m_parent.m_tempoSwing; + } else + { + m_parent.m_sndFile.Patterns[m_parent.m_pattern].SetTempoSwing(m_parent.m_tempoSwing); + } + + for(size_t i = 0; i < m_parent.m_tempoSwing.size(); i++) + { + TCHAR s[32]; + wsprintf(s, _T("%i%%"), Util::muldivr(m_parent.m_tempoSwing[i], 100, TempoSwing::Unity)); + m_parent.m_controls[i]->valueLabel.SetWindowText(s); + } + + CStatic::OnHScroll(nSBCode, nPos, pScrollBar); +} + + +BOOL CTempoSwingDlg::SliderContainer::OnToolTipNotify(UINT, NMHDR *pNMHDR, LRESULT *) +{ + TOOLTIPTEXT *pTTT = (TOOLTIPTEXT*)pNMHDR; + for(size_t i = 0; i < m_parent.m_controls.size(); i++) + { + if((HWND)pNMHDR->idFrom == m_parent.m_controls[i]->valueSlider.m_hWnd) + { + int32 val = Util::muldivr(m_parent.m_tempoSwing[i], 100, TempoSwing::Unity) - 100; + wsprintf(pTTT->szText, _T("%s%d"), val > 0 ? _T("+") : _T(""), val); + return TRUE; + } + } + return FALSE; +} + + +OPENMPT_NAMESPACE_END |