diff options
Diffstat (limited to 'Src/external_dependencies/openmpt-trunk/mptrack/ChannelManagerDlg.cpp')
-rw-r--r-- | Src/external_dependencies/openmpt-trunk/mptrack/ChannelManagerDlg.cpp | 1087 |
1 files changed, 1087 insertions, 0 deletions
diff --git a/Src/external_dependencies/openmpt-trunk/mptrack/ChannelManagerDlg.cpp b/Src/external_dependencies/openmpt-trunk/mptrack/ChannelManagerDlg.cpp new file mode 100644 index 00000000..a96265f0 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/mptrack/ChannelManagerDlg.cpp @@ -0,0 +1,1087 @@ +/* + * ChannelManagerDlg.cpp + * --------------------- + * Purpose: Dialog class for moving, removing, managing channels + * 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 "Moddoc.h" +#include "Mainfrm.h" +#include "ChannelManagerDlg.h" +#include "../common/mptStringBuffer.h" + +#include <functional> + +OPENMPT_NAMESPACE_BEGIN + +#define CM_NB_COLS 8 +#define CM_BT_HEIGHT 22 + +/////////////////////////////////////////////////////////// +// CChannelManagerDlg + +BEGIN_MESSAGE_MAP(CChannelManagerDlg, CDialog) + ON_WM_PAINT() + ON_WM_MOUSEMOVE() + ON_WM_LBUTTONUP() + ON_WM_LBUTTONDOWN() + ON_WM_RBUTTONUP() + ON_WM_RBUTTONDOWN() + ON_WM_MBUTTONDOWN() + ON_WM_CLOSE() + + ON_COMMAND(IDC_BUTTON1, &CChannelManagerDlg::OnApply) + ON_COMMAND(IDC_BUTTON2, &CChannelManagerDlg::OnClose) + ON_COMMAND(IDC_BUTTON3, &CChannelManagerDlg::OnSelectAll) + ON_COMMAND(IDC_BUTTON4, &CChannelManagerDlg::OnInvert) + ON_COMMAND(IDC_BUTTON5, &CChannelManagerDlg::OnAction1) + ON_COMMAND(IDC_BUTTON6, &CChannelManagerDlg::OnAction2) + ON_COMMAND(IDC_BUTTON7, &CChannelManagerDlg::OnStore) + ON_COMMAND(IDC_BUTTON8, &CChannelManagerDlg::OnRestore) + ON_NOTIFY(TCN_SELCHANGE, IDC_TAB1, &CChannelManagerDlg::OnTabSelchange) + + ON_WM_LBUTTONDBLCLK() + ON_WM_RBUTTONDBLCLK() +END_MESSAGE_MAP() + +CChannelManagerDlg * CChannelManagerDlg::sharedInstance_ = nullptr; + +CChannelManagerDlg * CChannelManagerDlg::sharedInstanceCreate() +{ + try + { + if(sharedInstance_ == nullptr) + sharedInstance_ = new CChannelManagerDlg(); + } catch(mpt::out_of_memory e) + { + mpt::delete_out_of_memory(e); + } + return sharedInstance_; +} + +void CChannelManagerDlg::SetDocument(CModDoc *modDoc) +{ + if(modDoc != m_ModDoc) + { + m_ModDoc = modDoc; + ResetState(true, true, true, true, false); + if(m_show) + { + if(m_ModDoc) + { + ResizeWindow(); + ShowWindow(SW_SHOWNOACTIVATE); // In case the window was hidden because no module was loaded + InvalidateRect(m_drawableArea, FALSE); + } else + { + ShowWindow(SW_HIDE); + } + } + } +} + +bool CChannelManagerDlg::IsDisplayed() const +{ + return m_show; +} + +void CChannelManagerDlg::Update(UpdateHint hint, CObject* pHint) +{ + if(!m_hWnd || !m_show) + return; + if(!hint.ToType<GeneralHint>().GetType()[HINT_MODCHANNELS | HINT_MODGENERAL | HINT_MODTYPE | HINT_MPTOPTIONS]) + return; + ResizeWindow(); + InvalidateRect(nullptr, FALSE); + if(hint.ToType<GeneralHint>().GetType()[HINT_MODCHANNELS] && m_quickChannelProperties.m_hWnd && pHint != &m_quickChannelProperties) + m_quickChannelProperties.UpdateDisplay(); +} + +void CChannelManagerDlg::Show() +{ + if(!m_hWnd) + { + Create(IDD_CHANNELMANAGER, nullptr); + } + ResizeWindow(); + ShowWindow(SW_SHOW); + m_show = true; +} + +void CChannelManagerDlg::Hide() +{ + if(m_hWnd != nullptr && m_show) + { + ResetState(true, true, true, true, true); + ShowWindow(SW_HIDE); + m_show = false; + } +} + + +CChannelManagerDlg::CChannelManagerDlg() + : m_buttonHeight(CM_BT_HEIGHT) +{ + for(CHANNELINDEX chn = 0; chn < MAX_BASECHANNELS; chn++) + { + pattern[chn] = chn; + memory[0][chn] = 0; + memory[1][chn] = 0; + memory[2][chn] = 0; + memory[3][chn] = chn; + } +} + +CChannelManagerDlg::~CChannelManagerDlg() +{ + if(this == sharedInstance_) + sharedInstance_ = nullptr; + if(m_bkgnd) + DeleteBitmap(m_bkgnd); + DestroyWindow(); +} + +BOOL CChannelManagerDlg::OnInitDialog() +{ + CDialog::OnInitDialog(); + + HWND menu = ::GetDlgItem(m_hWnd, IDC_TAB1); + + TCITEM tie; + tie.mask = TCIF_TEXT | TCIF_IMAGE; + tie.iImage = -1; + tie.pszText = const_cast<LPTSTR>(_T("Solo/Mute")); + TabCtrl_InsertItem(menu, kSoloMute, &tie); + tie.pszText = const_cast<LPTSTR>(_T("Record select")); + TabCtrl_InsertItem(menu, kRecordSelect, &tie); + tie.pszText = const_cast<LPTSTR>(_T("Plugins")); + TabCtrl_InsertItem(menu, kPluginState, &tie); + tie.pszText = const_cast<LPTSTR>(_T("Reorder/Remove")); + TabCtrl_InsertItem(menu, kReorderRemove, &tie); + m_currentTab = kSoloMute; + + m_buttonHeight = MulDiv(CM_BT_HEIGHT, Util::GetDPIy(m_hWnd), 96); + ::ShowWindow(::GetDlgItem(m_hWnd, IDC_BUTTON1), SW_HIDE); + + return TRUE; +} + +void CChannelManagerDlg::OnApply() +{ + if(!m_ModDoc) return; + + CHANNELINDEX numChannels, newMemory[4][MAX_BASECHANNELS]; + std::vector<CHANNELINDEX> newChnOrder; + newChnOrder.reserve(m_ModDoc->GetNumChannels()); + + // Count new number of channels, copy pattern pointers & manager internal store memory + numChannels = 0; + for(CHANNELINDEX chn = 0; chn < m_ModDoc->GetNumChannels(); chn++) + { + if(!removed[pattern[chn]]) + { + newMemory[0][numChannels] = memory[0][numChannels]; + newMemory[1][numChannels] = memory[1][numChannels]; + newMemory[2][numChannels] = memory[2][numChannels]; + newChnOrder.push_back(pattern[chn]); + numChannels++; + } + } + + BeginWaitCursor(); + + //Creating new order-vector for ReArrangeChannels. + CriticalSection cs; + if(m_ModDoc->ReArrangeChannels(newChnOrder) != numChannels) + { + cs.Leave(); + EndWaitCursor(); + return; + } + + // Update manager internal store memory + for(CHANNELINDEX chn = 0; chn < numChannels; chn++) + { + CHANNELINDEX newChn = newChnOrder[chn]; + if(chn != newChn) + { + memory[0][chn] = newMemory[0][newChn]; + memory[1][chn] = newMemory[1][newChn]; + memory[2][chn] = newMemory[2][newChn]; + } + memory[3][chn] = chn; + } + + cs.Leave(); + EndWaitCursor(); + + ResetState(true, true, true, true, true); + + // Update document & windows + m_ModDoc->SetModified(); + m_ModDoc->UpdateAllViews(nullptr, GeneralHint().Channels().ModType(), this); //refresh channel headers + + // Redraw channel manager window + ResizeWindow(); + InvalidateRect(nullptr, FALSE); +} + +void CChannelManagerDlg::OnClose() +{ + if(m_bkgnd) DeleteBitmap(m_bkgnd); + ResetState(true, true, true, true, true); + m_bkgnd = nullptr; + m_show = false; + + CDialog::OnCancel(); +} + +void CChannelManagerDlg::OnSelectAll() +{ + select.set(); + InvalidateRect(m_drawableArea, FALSE); +} + +void CChannelManagerDlg::OnInvert() +{ + select.flip(); + InvalidateRect(m_drawableArea, FALSE); +} + +void CChannelManagerDlg::OnAction1() +{ + if(m_ModDoc) + { + int nbOk = 0, nbSelect = 0; + + switch(m_currentTab) + { + case kSoloMute: + for(CHANNELINDEX chn = 0; chn < m_ModDoc->GetNumChannels(); chn++) + { + CHANNELINDEX sourceChn = pattern[chn]; + if(!removed[sourceChn]) + { + if(select[sourceChn]) + nbSelect++; + if(select[sourceChn] && m_ModDoc->IsChannelSolo(sourceChn)) + nbOk++; + } + } + for(CHANNELINDEX chn = 0; chn < m_ModDoc->GetNumChannels(); chn++) + { + CHANNELINDEX sourceChn = pattern[chn]; + if(select[sourceChn] && !removed[sourceChn]) + { + if(m_ModDoc->IsChannelMuted(sourceChn)) + m_ModDoc->MuteChannel(sourceChn, false); + if(nbSelect == nbOk) + m_ModDoc->SoloChannel(sourceChn, !m_ModDoc->IsChannelSolo(sourceChn)); + else + m_ModDoc->SoloChannel(sourceChn, true); + } + else if(!m_ModDoc->IsChannelSolo(sourceChn)) + m_ModDoc->MuteChannel(sourceChn, true); + } + break; + case kRecordSelect: + for(CHANNELINDEX chn = 0; chn < m_ModDoc->GetNumChannels(); chn++) + { + CHANNELINDEX sourceChn = pattern[chn]; + if(!removed[sourceChn]) + { + if(select[sourceChn]) + nbSelect++; + if(select[sourceChn] && m_ModDoc->GetChannelRecordGroup(sourceChn) == RecordGroup::Group1) + nbOk++; + } + } + for(CHANNELINDEX chn = 0; chn < m_ModDoc->GetNumChannels(); chn++) + { + CHANNELINDEX sourceChn = pattern[chn]; + if(!removed[sourceChn] && select[sourceChn]) + { + if(select[sourceChn] && nbSelect != nbOk && m_ModDoc->GetChannelRecordGroup(sourceChn) != RecordGroup::Group1) + m_ModDoc->SetChannelRecordGroup(sourceChn, RecordGroup::Group1); + else if(nbSelect == nbOk) + m_ModDoc->SetChannelRecordGroup(sourceChn, RecordGroup::NoGroup); + } + } + break; + case kPluginState: + for(CHANNELINDEX chn = 0; chn < m_ModDoc->GetNumChannels(); chn++) + { + CHANNELINDEX sourceChn = pattern[chn]; + if(select[sourceChn] && !removed[sourceChn]) + m_ModDoc->NoFxChannel(sourceChn, false); + } + break; + case kReorderRemove: + for(CHANNELINDEX chn = 0; chn < m_ModDoc->GetNumChannels(); chn++) + { + CHANNELINDEX sourceChn = pattern[chn]; + if(select[sourceChn]) + removed[sourceChn] = !removed[sourceChn]; + } + break; + default: + break; + } + + + ResetState(); + + m_ModDoc->UpdateAllViews(nullptr, GeneralHint().Channels(), this); + InvalidateRect(m_drawableArea, FALSE); + } +} + +void CChannelManagerDlg::OnAction2() +{ + if(m_ModDoc) + { + + int nbOk = 0, nbSelect = 0; + + switch(m_currentTab) + { + case kSoloMute: + for(CHANNELINDEX chn = 0; chn < m_ModDoc->GetNumChannels(); chn++) + { + CHANNELINDEX sourceChn = pattern[chn]; + if(!removed[sourceChn]) + { + if(select[sourceChn]) + nbSelect++; + if(select[sourceChn] && m_ModDoc->IsChannelMuted(sourceChn)) + nbOk++; + } + } + for(CHANNELINDEX chn = 0; chn < m_ModDoc->GetNumChannels(); chn++) + { + CHANNELINDEX sourceChn = pattern[chn]; + if(select[sourceChn] && !removed[sourceChn]) + { + if(m_ModDoc->IsChannelSolo(sourceChn)) + m_ModDoc->SoloChannel(sourceChn, false); + if(nbSelect == nbOk) + m_ModDoc->MuteChannel(sourceChn, !m_ModDoc->IsChannelMuted(sourceChn)); + else + m_ModDoc->MuteChannel(sourceChn, true); + } + } + break; + case kRecordSelect: + for(CHANNELINDEX chn = 0; chn < m_ModDoc->GetNumChannels(); chn++) + { + CHANNELINDEX sourceChn = pattern[chn]; + if(!removed[sourceChn]) + { + if(select[sourceChn]) + nbSelect++; + if(select[sourceChn] && m_ModDoc->GetChannelRecordGroup(sourceChn) == RecordGroup::Group2) + nbOk++; + } + } + for(CHANNELINDEX chn = 0; chn < m_ModDoc->GetNumChannels(); chn++) + { + CHANNELINDEX sourceChn = pattern[chn]; + if(!removed[sourceChn] && select[sourceChn]) + { + if(select[sourceChn] && nbSelect != nbOk && m_ModDoc->GetChannelRecordGroup(sourceChn) != RecordGroup::Group2) + m_ModDoc->SetChannelRecordGroup(sourceChn, RecordGroup::Group2); + else if(nbSelect == nbOk) + m_ModDoc->SetChannelRecordGroup(sourceChn, RecordGroup::NoGroup); + } + } + break; + case kPluginState: + for(CHANNELINDEX chn = 0; chn < m_ModDoc->GetNumChannels(); chn++) + { + CHANNELINDEX sourceChn = pattern[chn]; + if(select[sourceChn] && !removed[sourceChn]) + m_ModDoc->NoFxChannel(sourceChn, true); + } + break; + case kReorderRemove: + ResetState(false, false, false, false, true); + break; + default: + break; + } + + if(m_currentTab != 3) ResetState(); + + m_ModDoc->UpdateAllViews(nullptr, GeneralHint().Channels(), this); + InvalidateRect(m_drawableArea, FALSE); + } +} + +void CChannelManagerDlg::OnStore(void) +{ + if(!m_show || m_ModDoc == nullptr) + { + return; + } + + switch(m_currentTab) + { + case kSoloMute: + for(CHANNELINDEX chn = 0; chn < m_ModDoc->GetNumChannels(); chn++) + { + CHANNELINDEX sourceChn = pattern[chn]; + memory[0][sourceChn] = 0; + if(m_ModDoc->IsChannelMuted(sourceChn)) memory[0][chn] |= 1; + if(m_ModDoc->IsChannelSolo(sourceChn)) memory[0][chn] |= 2; + } + break; + case kRecordSelect: + for(CHANNELINDEX chn = 0; chn < m_ModDoc->GetNumChannels(); chn++) + memory[1][chn] = static_cast<uint8>(m_ModDoc->GetChannelRecordGroup(pattern[chn])); + break; + case kPluginState: + for(CHANNELINDEX chn = 0; chn < m_ModDoc->GetNumChannels(); chn++) + memory[2][chn] = m_ModDoc->IsChannelNoFx(pattern[chn]); + break; + case kReorderRemove: + for(CHANNELINDEX chn = 0; chn < m_ModDoc->GetNumChannels(); chn++) + memory[3][chn] = pattern[chn]; + break; + default: + break; + } +} + +void CChannelManagerDlg::OnRestore(void) +{ + if(!m_show || m_ModDoc == nullptr) + { + return; + } + + switch(m_currentTab) + { + case kSoloMute: + for(CHANNELINDEX chn = 0; chn < m_ModDoc->GetNumChannels(); chn++) + { + CHANNELINDEX sourceChn = pattern[chn]; + m_ModDoc->MuteChannel(sourceChn, (memory[0][chn] & 1) != 0); + m_ModDoc->SoloChannel(sourceChn, (memory[0][chn] & 2) != 0); + } + break; + case kRecordSelect: + m_ModDoc->ReinitRecordState(true); + for(CHANNELINDEX chn = 0; chn < m_ModDoc->GetNumChannels(); chn++) + { + m_ModDoc->SetChannelRecordGroup(chn, static_cast<RecordGroup>(memory[1][chn])); + } + break; + case kPluginState: + for(CHANNELINDEX chn = 0; chn < m_ModDoc->GetNumChannels(); chn++) + m_ModDoc->NoFxChannel(pattern[chn], memory[2][chn] != 0); + break; + case kReorderRemove: + for(CHANNELINDEX chn = 0; chn < m_ModDoc->GetNumChannels(); chn++) + pattern[chn] = memory[3][chn]; + ResetState(false, false, false, false, true); + break; + default: + break; + } + + if(m_currentTab != 3) ResetState(); + + m_ModDoc->UpdateAllViews(nullptr, GeneralHint().Channels(), this); + InvalidateRect(m_drawableArea, FALSE); +} + +void CChannelManagerDlg::OnTabSelchange(NMHDR* /*header*/, LRESULT* /*pResult*/) +{ + if(!m_show) return; + + m_currentTab = static_cast<Tab>(TabCtrl_GetCurFocus(::GetDlgItem(m_hWnd, IDC_TAB1))); + + switch(m_currentTab) + { + case kSoloMute: + SetDlgItemText(IDC_BUTTON5, _T("Solo")); + SetDlgItemText(IDC_BUTTON6, _T("Mute")); + ::ShowWindow(::GetDlgItem(m_hWnd, IDC_BUTTON5),SW_SHOW); + ::ShowWindow(::GetDlgItem(m_hWnd, IDC_BUTTON6),SW_SHOW); + ::ShowWindow(::GetDlgItem(m_hWnd, IDC_BUTTON1),SW_HIDE); + break; + case kRecordSelect: + SetDlgItemText(IDC_BUTTON5, _T("Instrument 1")); + SetDlgItemText(IDC_BUTTON6, _T("Instrument 2")); + ::ShowWindow(::GetDlgItem(m_hWnd, IDC_BUTTON5),SW_SHOW); + ::ShowWindow(::GetDlgItem(m_hWnd, IDC_BUTTON6),SW_SHOW); + ::ShowWindow(::GetDlgItem(m_hWnd, IDC_BUTTON1),SW_HIDE); + break; + case kPluginState: + SetDlgItemText(IDC_BUTTON5, _T("Enable FX")); + SetDlgItemText(IDC_BUTTON6, _T("Disable FX")); + ::ShowWindow(::GetDlgItem(m_hWnd, IDC_BUTTON5),SW_SHOW); + ::ShowWindow(::GetDlgItem(m_hWnd, IDC_BUTTON6),SW_SHOW); + ::ShowWindow(::GetDlgItem(m_hWnd, IDC_BUTTON1),SW_HIDE); + break; + case kReorderRemove: + SetDlgItemText(IDC_BUTTON5, _T("Remove")); + SetDlgItemText(IDC_BUTTON6, _T("Cancel All")); + ::ShowWindow(::GetDlgItem(m_hWnd, IDC_BUTTON5),SW_SHOW); + ::ShowWindow(::GetDlgItem(m_hWnd, IDC_BUTTON6),SW_SHOW); + ::ShowWindow(::GetDlgItem(m_hWnd, IDC_BUTTON1),SW_SHOW); + break; + default: + break; + } + + InvalidateRect(m_drawableArea, FALSE); +} + + +void CChannelManagerDlg::ResizeWindow() +{ + if(!m_hWnd || !m_ModDoc) return; + + const int dpiX = Util::GetDPIx(m_hWnd); + const int dpiY = Util::GetDPIy(m_hWnd); + + m_buttonHeight = MulDiv(CM_BT_HEIGHT, dpiY, 96); + + CHANNELINDEX channels = m_ModDoc->GetNumChannels(); + int lines = channels / CM_NB_COLS + (channels % CM_NB_COLS ? 1 : 0); + + CRect window; + GetWindowRect(window); + + CRect client; + GetClientRect(client); + m_drawableArea = client; + m_drawableArea.DeflateRect(MulDiv(10, dpiX, 96), MulDiv(38, dpiY, 96), MulDiv(8, dpiX, 96), MulDiv(30, dpiY, 96)); + + int chnSizeY = m_drawableArea.Height() / lines; + + if(chnSizeY != m_buttonHeight) + { + SetWindowPos(nullptr, 0, 0, window.Width(), window.Height() + (m_buttonHeight - chnSizeY) * lines, SWP_NOMOVE | SWP_NOZORDER | SWP_NOREDRAW); + + GetClientRect(client); + + // Move butttons to bottom of the window + for(auto id : { IDC_BUTTON1, IDC_BUTTON2, IDC_BUTTON3, IDC_BUTTON4, IDC_BUTTON5, IDC_BUTTON6 }) + { + CWnd *button = GetDlgItem(id); + if(button != nullptr) + { + CRect btn; + button->GetClientRect(btn); + button->MapWindowPoints(this, btn); + button->SetWindowPos(nullptr, btn.left, client.Height() - btn.Height() - MulDiv(3, dpiY, 96), 0, 0, SWP_NOSIZE | SWP_NOZORDER); + } + } + + if(m_bkgnd) + { + DeleteObject(m_bkgnd); + m_bkgnd = nullptr; + } + + m_drawableArea = client; + m_drawableArea.DeflateRect(MulDiv(10, dpiX, 96), MulDiv(38, dpiY, 96), MulDiv(8, dpiX, 96), MulDiv(30, dpiY, 96)); + InvalidateRect(nullptr, FALSE); + } +} + + +void CChannelManagerDlg::OnPaint() +{ + if(!m_hWnd || !m_show || m_ModDoc == nullptr) + { + CDialog::OnPaint(); + ShowWindow(SW_HIDE); + return; + } + if(IsIconic()) + { + CDialog::OnPaint(); + return; + } + + const int dpiX = Util::GetDPIx(m_hWnd); + const int dpiY = Util::GetDPIy(m_hWnd); + const CHANNELINDEX channels = m_ModDoc->GetNumChannels(); + + PAINTSTRUCT pDC; + ::BeginPaint(m_hWnd, &pDC); + const CRect &rcPaint = pDC.rcPaint; + + const int chnSizeX = m_drawableArea.Width() / CM_NB_COLS; + const int chnSizeY = m_buttonHeight; + + if(m_currentTab == 3 && m_moveRect && m_bkgnd) + { + // Only draw channels to be moved around + HDC bdc = ::CreateCompatibleDC(pDC.hdc); + ::SelectObject(bdc, m_bkgnd); + ::BitBlt(pDC.hdc, rcPaint.left, rcPaint.top, rcPaint.Width(), rcPaint.Height(), bdc, rcPaint.left, rcPaint.top, SRCCOPY); + + BLENDFUNCTION ftn; + ftn.BlendOp = AC_SRC_OVER; + ftn.BlendFlags = 0; + ftn.SourceConstantAlpha = 192; + ftn.AlphaFormat = 0; + + for(CHANNELINDEX chn = 0; chn < channels; chn++) + { + CHANNELINDEX sourceChn = pattern[chn]; + if(select[sourceChn]) + { + CRect btn = move[sourceChn]; + btn.DeflateRect(3, 3, 0, 0); + + AlphaBlend(pDC.hdc, btn.left + m_moveX - m_downX, btn.top + m_moveY - m_downY, btn.Width(), btn.Height(), bdc, + btn.left, btn.top, btn.Width(), btn.Height(), ftn); + } + } + ::SelectObject(bdc, (HBITMAP)NULL); + ::DeleteDC(bdc); + + ::EndPaint(m_hWnd, &pDC); + return; + } + + CRect client; + GetClientRect(&client); + + HDC dc = ::CreateCompatibleDC(pDC.hdc); + if(!m_bkgnd) + m_bkgnd = ::CreateCompatibleBitmap(pDC.hdc, client.Width(), client.Height()); + HGDIOBJ oldBmp = ::SelectObject(dc, m_bkgnd); + HGDIOBJ oldFont = ::SelectObject(dc, CMainFrame::GetGUIFont()); + + const auto dcBrush = GetStockBrush(DC_BRUSH); + + client.SetRect(client.left + MulDiv(2, dpiX, 96), client.top + MulDiv(32, dpiY, 96), client.right - MulDiv(2, dpiX, 96), client.bottom - MulDiv(24, dpiY, 96)); + // Draw background + { + const auto bgIntersected = client & pDC.rcPaint; // In case of partial redraws, FillRect may still draw into areas that are not part of the redraw area and thus make some buttons disappear + ::FillRect(dc, &pDC.rcPaint, GetSysColorBrush(COLOR_BTNFACE)); + ::FillRect(dc, &bgIntersected, GetSysColorBrush(COLOR_HIGHLIGHT)); + ::SetDCBrushColor(dc, RGB(20, 20, 20)); + ::FrameRect(dc, &client, dcBrush); + } + + client.SetRect(client.left + 8,client.top + 6,client.right - 6,client.bottom - 6); + + const COLORREF highlight = GetSysColor(COLOR_HIGHLIGHT), red = RGB(192, 96, 96), green = RGB(96, 192, 96), redBright = RGB(218, 163, 163), greenBright = RGB(163, 218, 163); + const COLORREF brushColors[] = { highlight, green, red }; + const COLORREF brushColorsBright[] = { highlight, greenBright, redBright }; + const auto buttonFaceColor = GetSysColor(COLOR_BTNFACE), windowColor = GetSysColor(COLOR_WINDOW); + + uint32 col = 0, row = 0; + const CSoundFile &sndFile = m_ModDoc->GetSoundFile(); + CString s; + for(CHANNELINDEX chn = 0; chn < channels; chn++, col++) + { + if(col >= CM_NB_COLS) + { + col = 0; + row++; + } + + const CHANNELINDEX sourceChn = pattern[chn]; + const auto &chnSettings = sndFile.ChnSettings[sourceChn]; + + if(!chnSettings.szName.empty()) + s = MPT_CFORMAT("{}: {}")(sourceChn + 1, mpt::ToCString(sndFile.GetCharsetInternal(), sndFile.ChnSettings[sourceChn].szName)); + else + s = MPT_CFORMAT("Channel {}")(sourceChn + 1); + + const int borderX = MulDiv(3, dpiX, 96), borderY = MulDiv(3, dpiY, 96); + CRect btn; + btn.left = client.left + col * chnSizeX + borderX; + btn.right = btn.left + chnSizeX - borderX; + btn.top = client.top + row * chnSizeY + borderY; + btn.bottom = btn.top + chnSizeY - borderY; + + if(!CRect{}.IntersectRect(&pDC.rcPaint, &btn)) + continue; + + // Button + const bool activate = select[sourceChn]; + const bool enable = !removed[sourceChn]; + auto btnAdjusted = btn; // Without border + ::DrawEdge(dc, btnAdjusted, enable ? EDGE_RAISED : EDGE_SUNKEN, BF_RECT | BF_MIDDLE | BF_ADJUST); + if(activate) + ::FillRect(dc, btnAdjusted, GetSysColorBrush(COLOR_WINDOW)); + + if(chnSettings.color != ModChannelSettings::INVALID_COLOR) + { + // Channel color + const auto startColor = chnSettings.color; + const auto endColor = activate ? windowColor : buttonFaceColor; + const auto width = btnAdjusted.Width() / 2; + auto rect = btnAdjusted; + rect.right = rect.left + 1; + for(int i = 0; i < width; i++) + { + auto blend = static_cast<double>(i) / width, blendInv = 1.0 - blend; + auto blendColor = RGB(mpt::saturate_round<uint8>(GetRValue(startColor) * blendInv + GetRValue(endColor) * blend), + mpt::saturate_round<uint8>(GetGValue(startColor) * blendInv + GetGValue(endColor) * blend), + mpt::saturate_round<uint8>(GetBValue(startColor) * blendInv + GetBValue(endColor) * blend)); + ::SetDCBrushColor(dc, blendColor); + ::FillRect(dc, &rect, dcBrush); + rect.left++; + rect.right++; + } + } + + // Text + { + auto rect = btnAdjusted; + rect.left += Util::ScalePixels(9, m_hWnd); + rect.right -= Util::ScalePixels(3, m_hWnd); + + ::SetBkMode(dc, TRANSPARENT); + ::SetTextColor(dc, GetSysColor(enable || activate ? COLOR_BTNTEXT : COLOR_GRAYTEXT)); + ::DrawText(dc, s, -1, &rect, DT_RIGHT | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX); + } + + // Draw red/green markers + { + const int margin = Util::ScalePixels(1, m_hWnd); + auto rect = btnAdjusted; + rect.DeflateRect(margin, margin); + rect.right = rect.left + Util::ScalePixels(7, m_hWnd); + const auto &brushes = activate ? brushColorsBright : brushColors; + const auto redBrush = brushes[2], greenBrush = brushes[1]; + COLORREF color = 0; + switch(m_currentTab) + { + case kSoloMute: + color = chnSettings.dwFlags[CHN_MUTE] ? redBrush : greenBrush; + break; + case kRecordSelect: + color = brushColors[static_cast<size_t>(m_ModDoc->GetChannelRecordGroup(sourceChn)) % std::size(brushColors)]; + break; + case kPluginState: + color = chnSettings.dwFlags[CHN_NOFX] ? redBrush : greenBrush; + break; + case kReorderRemove: + color = removed[sourceChn] ? redBrush : greenBrush; + break; + } + ::SetDCBrushColor(dc, color); + ::FillRect(dc, rect, dcBrush); + // Draw border around marker + ::SetDCBrushColor(dc, RGB(20, 20, 20)); + ::FrameRect(dc, rect, dcBrush); + } + } + + ::BitBlt(pDC.hdc, rcPaint.left, rcPaint.top, rcPaint.Width(), rcPaint.Height(), dc, rcPaint.left, rcPaint.top, SRCCOPY); + ::SelectObject(dc, oldFont); + ::SelectObject(dc, oldBmp); + ::DeleteDC(dc); + + ::EndPaint(m_hWnd, &pDC); +} + + +bool CChannelManagerDlg::ButtonHit(CPoint point, CHANNELINDEX *id, CRect *invalidate) const +{ + const CRect &client = m_drawableArea; + + if(PtInRect(client, point) && m_ModDoc != nullptr) + { + UINT nColns = CM_NB_COLS; + + int x = point.x - client.left; + int y = point.y - client.top; + + int dx = client.Width() / (int)nColns; + int dy = m_buttonHeight; + + x = x / dx; + y = y / dy; + CHANNELINDEX n = static_cast<CHANNELINDEX>(y * nColns + x); + if(n < m_ModDoc->GetNumChannels()) + { + if(id) *id = n; + if(invalidate) + { + invalidate->left = client.left + x * dx; + invalidate->right = invalidate->left + dx; + invalidate->top = client.top + y * dy; + invalidate->bottom = invalidate->top + dy; + } + return true; + } + } + return false; +} + + +void CChannelManagerDlg::ResetState(bool bSelection, bool bMove, bool bButton, bool bInternal, bool bOrder) +{ + for(CHANNELINDEX chn = 0; chn < MAX_BASECHANNELS; chn++) + { + if(bSelection) + select[pattern[chn]] = false; + if(bButton) + state[pattern[chn]] = false; + if(bOrder) + { + pattern[chn] = chn; + removed[chn] = false; + } + } + if(bMove || bInternal) + { + m_leftButton = false; + m_rightButton = false; + } + if(bMove) m_moveRect = false; +} + + +void CChannelManagerDlg::OnMouseMove(UINT nFlags,CPoint point) +{ + if(!m_hWnd || m_show == false) return; + + if(!m_leftButton && !m_rightButton) + { + m_moveX = point.x; + m_moveY = point.y; + return; + } + MouseEvent(nFlags, point, m_moveRect ? CM_BT_NONE : (m_leftButton ? CM_BT_LEFT : CM_BT_RIGHT)); +} + +void CChannelManagerDlg::OnLButtonUp(UINT /*nFlags*/,CPoint point) +{ + ReleaseCapture(); + if(!m_hWnd || m_show == false) return; + + if(m_moveRect && m_ModDoc) + { + CHANNELINDEX dropChn = 0; + CRect dropRect; + if(ButtonHit(point, &dropChn, &dropRect)) + { + // Rearrange channels + const auto IsSelected = std::bind(&decltype(select)::test, &select, std::placeholders::_1); + + const auto numChannels = m_ModDoc->GetNumChannels(); + if(point.x > dropRect.left + dropRect.Width() / 2 && dropChn < numChannels) + dropChn++; + + std::vector<CHANNELINDEX> newOrder{ pattern.begin(), pattern.begin() + numChannels }; + // How many selected channels are there before the drop target? + // cppcheck false-positive + // cppcheck-suppress danglingTemporaryLifetime + const CHANNELINDEX selectedBeforeDropChn = static_cast<CHANNELINDEX>(std::count_if(pattern.begin(), pattern.begin() + dropChn, IsSelected)); + dropChn -= selectedBeforeDropChn; + // Remove all selected channels from the order + newOrder.erase(std::remove_if(newOrder.begin(), newOrder.end(), IsSelected), newOrder.end()); + const CHANNELINDEX numSelected = static_cast<CHANNELINDEX>(numChannels - newOrder.size()); + // Then insert them at the drop position + newOrder.insert(newOrder.begin() + dropChn, numSelected, PATTERNINDEX_INVALID); + std::copy_if(pattern.begin(), pattern.begin() + numChannels, newOrder.begin() + dropChn, IsSelected); + + std::copy(newOrder.begin(), newOrder.begin() + numChannels, pattern.begin()); + select.reset(); + } else + { + ResetState(true, false, false, false, false); + } + + m_moveRect = false; + InvalidateRect(m_drawableArea, FALSE); + if(m_ModDoc) m_ModDoc->UpdateAllViews(nullptr, GeneralHint().Channels(), this); + } + + m_leftButton = false; + + for(CHANNELINDEX chn : pattern) + state[chn] = false; +} + +void CChannelManagerDlg::OnLButtonDown(UINT nFlags,CPoint point) +{ + if(!m_hWnd || m_show == false) return; + SetCapture(); + + if(!ButtonHit(point, nullptr, nullptr)) ResetState(true, false, false, false); + + m_leftButton = true; + m_buttonAction = kUndetermined; + MouseEvent(nFlags,point,CM_BT_LEFT); + m_downX = point.x; + m_downY = point.y; +} + +void CChannelManagerDlg::OnRButtonUp(UINT /*nFlags*/,CPoint /*point*/) +{ + ReleaseCapture(); + if(!m_hWnd || m_show == false) return; + + ResetState(false, false, true, false); + + m_rightButton = false; +} + +void CChannelManagerDlg::OnRButtonDown(UINT nFlags,CPoint point) +{ + if(!m_hWnd || m_show == false) return; + SetCapture(); + + m_rightButton = true; + m_buttonAction = kUndetermined; + if(m_moveRect) + { + ResetState(true, true, false, false, false); + InvalidateRect(m_drawableArea, FALSE); + } else + { + MouseEvent(nFlags, point, CM_BT_RIGHT); + m_downX = point.x; + m_downY = point.y; + } +} + +void CChannelManagerDlg::OnMButtonDown(UINT /*nFlags*/, CPoint point) +{ + CHANNELINDEX chn; + CRect rect; + if(m_ModDoc != nullptr && (m_ModDoc->GetModType() & (MOD_TYPE_XM | MOD_TYPE_IT | MOD_TYPE_MPT)) && ButtonHit(point, &chn, &rect)) + { + ClientToScreen(&point); + m_quickChannelProperties.Show(m_ModDoc, pattern[chn], point); + } +} + +void CChannelManagerDlg::MouseEvent(UINT nFlags,CPoint point, MouseButton button) +{ + CHANNELINDEX n; + CRect client, invalidate; + bool hit = ButtonHit(point, &n, &invalidate); + if(hit) n = pattern[n]; + + m_moveX = point.x; + m_moveY = point.y; + + if(!m_ModDoc) return; + + if(hit && !state[n] && button != CM_BT_NONE) + { + if(nFlags & MK_CONTROL) + { + if(button == CM_BT_LEFT) + { + if(!select[n] && !removed[n]) move[n] = invalidate; + select[n] = true; + } + else if(button == CM_BT_RIGHT) select[n] = false; + } + else if(!removed[n] || m_currentTab == 3) + { + switch(m_currentTab) + { + case kSoloMute: + if(button == CM_BT_LEFT) + { + if(m_buttonAction == kUndetermined) + m_buttonAction = (!m_ModDoc->IsChannelSolo(n) || m_ModDoc->IsChannelMuted(n)) ? kAction1 : kAction2; + if(m_buttonAction == kAction1) + { + m_ModDoc->MuteChannel(n, false); + m_ModDoc->SoloChannel(n, true); + for(CHANNELINDEX chn = 0; chn < m_ModDoc->GetNumChannels(); chn++) + { + if(chn != n) + m_ModDoc->MuteChannel(chn, true); + } + invalidate = client = m_drawableArea; + } + else m_ModDoc->SoloChannel(n, false); + } else + { + if(m_ModDoc->IsChannelSolo(n)) m_ModDoc->SoloChannel(n, false); + if(m_buttonAction == kUndetermined) + m_buttonAction = m_ModDoc->IsChannelMuted(n) ? kAction1 : kAction2; + m_ModDoc->MuteChannel(n, m_buttonAction == kAction2); + } + m_ModDoc->SetModified(); + m_ModDoc->UpdateAllViews(nullptr, GeneralHint(n).Channels(), this); + break; + case kRecordSelect: + { + auto rec = m_ModDoc->GetChannelRecordGroup(n); + if(m_buttonAction == kUndetermined) + m_buttonAction = (rec == RecordGroup::NoGroup || rec != (button == CM_BT_LEFT ? RecordGroup::Group1 : RecordGroup::Group2)) ? kAction1 : kAction2; + + if(m_buttonAction == kAction1 && button == CM_BT_LEFT) + m_ModDoc->SetChannelRecordGroup(n, RecordGroup::Group1); + else if(m_buttonAction == kAction1 && button == CM_BT_RIGHT) + m_ModDoc->SetChannelRecordGroup(n, RecordGroup::Group2); + else + m_ModDoc->SetChannelRecordGroup(n, RecordGroup::NoGroup); + m_ModDoc->UpdateAllViews(nullptr, GeneralHint(n).Channels(), this); + break; + } + case kPluginState: + if(button == CM_BT_LEFT) m_ModDoc->NoFxChannel(n, false); + else m_ModDoc->NoFxChannel(n, true); + m_ModDoc->SetModified(); + m_ModDoc->UpdateAllViews(nullptr, GeneralHint(n).Channels(), this); + break; + case kReorderRemove: + if(button == CM_BT_LEFT) + { + move[n] = invalidate; + select[n] = true; + } + if(button == CM_BT_RIGHT) + { + if(m_buttonAction == kUndetermined) + m_buttonAction = removed[n] ? kAction1 : kAction2; + select[n] = false; + removed[n] = (m_buttonAction == kAction2); + } + + if(select[n] || button == 0) + { + m_moveRect = true; + } + break; + } + } + + state[n] = false; + InvalidateRect(invalidate, FALSE); + } else + { + InvalidateRect(m_drawableArea, FALSE); + } +} + + +void CChannelManagerDlg::OnLButtonDblClk(UINT nFlags, CPoint point) +{ + OnLButtonDown(nFlags, point); + CDialog::OnLButtonDblClk(nFlags, point); +} + +void CChannelManagerDlg::OnRButtonDblClk(UINT nFlags, CPoint point) +{ + OnRButtonDown(nFlags, point); + CDialog::OnRButtonDblClk(nFlags, point); +} + + +OPENMPT_NAMESPACE_END |