123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873 |
- /*
- * EffectVis.cpp
- * -------------
- * Purpose: Implementation of parameter visualisation dialog.
- * Notes : (currenlty none)
- * Authors: OpenMPT Devs
- * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
- */
- #include "stdafx.h"
- #include "Mptrack.h"
- #include "Mainfrm.h"
- #include "Childfrm.h"
- #include "Moddoc.h"
- #include "Globals.h"
- #include "View_pat.h"
- #include "EffectVis.h"
- OPENMPT_NAMESPACE_BEGIN
- CEffectVis::EditAction CEffectVis::m_nAction = CEffectVis::kAction_OverwriteFX;
- IMPLEMENT_DYNAMIC(CEffectVis, CDialog)
- CEffectVis::CEffectVis(CViewPattern *pViewPattern, ROWINDEX startRow, ROWINDEX endRow, CHANNELINDEX nchn, CModDoc &modDoc, PATTERNINDEX pat)
- : effectInfo(modDoc.GetSoundFile())
- , m_ModDoc(modDoc)
- , m_SndFile(modDoc.GetSoundFile())
- , m_pViewPattern(pViewPattern)
- {
- m_nFillEffect = effectInfo.GetIndexFromEffect(CMD_SMOOTHMIDI, 0);
- m_templatePCNote.Set(NOTE_PCS, 1, 0, 0);
- UpdateSelection(startRow, endRow, nchn, pat);
- }
- BEGIN_MESSAGE_MAP(CEffectVis, CDialog)
- ON_WM_ERASEBKGND()
- ON_WM_PAINT()
- ON_WM_SIZE()
- ON_WM_LBUTTONDOWN()
- ON_WM_LBUTTONUP()
- ON_WM_MOUSEMOVE()
- ON_WM_RBUTTONDOWN()
- ON_WM_RBUTTONUP()
- ON_CBN_SELCHANGE(IDC_VISACTION, &CEffectVis::OnActionChanged)
- ON_CBN_SELCHANGE(IDC_VISEFFECTLIST, &CEffectVis::OnEffectChanged)
- END_MESSAGE_MAP()
- void CEffectVis::DoDataExchange(CDataExchange* pDX)
- {
- CDialog::DoDataExchange(pDX);
- DDX_Control(pDX, IDC_VISSTATUS, m_edVisStatus);
- DDX_Control(pDX, IDC_VISEFFECTLIST, m_cmbEffectList);
- DDX_Control(pDX, IDC_VISACTION, m_cmbActionList);
- }
- void CEffectVis::OnActionChanged()
- {
- m_nAction = static_cast<EditAction>(m_cmbActionList.GetItemData(m_cmbActionList.GetCurSel()));
- if (m_nAction == kAction_FillPC
- || m_nAction == kAction_OverwritePC
- || m_nAction == kAction_Preserve)
- m_cmbEffectList.EnableWindow(FALSE);
- else
- m_cmbEffectList.EnableWindow(TRUE);
- }
- void CEffectVis::OnEffectChanged()
- {
- m_nFillEffect = static_cast<UINT>(m_cmbEffectList.GetItemData(m_cmbEffectList.GetCurSel()));
- }
- void CEffectVis::OnPaint()
- {
- CPaintDC dc(this); // device context for painting
- ShowVis(&dc);
- }
- uint16 CEffectVis::GetParam(ROWINDEX row) const
- {
- uint16 paramValue = 0;
- if(m_SndFile.Patterns.IsValidPat(m_nPattern))
- {
- const ModCommand &m = *m_SndFile.Patterns[m_nPattern].GetpModCommand(row, m_nChan);
- if (m.IsPcNote())
- {
- paramValue = m.GetValueEffectCol();
- } else
- {
- paramValue = m.param;
- }
- }
- return paramValue;
- }
- // Sets a row's param value based on the vertical cursor position.
- // Sets either plain pattern effect parameter or PC note parameter
- // as appropriate, depending on contents of row.
- void CEffectVis::SetParamFromY(ROWINDEX row, int y)
- {
- if(!m_SndFile.Patterns.IsValidPat(m_nPattern))
- return;
- ModCommand &m = *m_SndFile.Patterns[m_nPattern].GetpModCommand(row, m_nChan);
- if (IsPcNote(row))
- {
- uint16 param = ScreenYToPCParam(y);
- m.SetValueEffectCol(param);
- } else
- {
- ModCommand::PARAM param = ScreenYToFXParam(y);
- // Cap the parameter value as appropriate, based on effect type (e.g. Zxx gets capped to [0x00,0x7F])
- effectInfo.GetEffectFromIndex(effectInfo.GetIndexFromEffect(m.command, param), param);
- m.param = param;
- }
- }
- EffectCommand CEffectVis::GetCommand(ROWINDEX row) const
- {
- if(m_SndFile.Patterns.IsValidPat(m_nPattern))
- return static_cast<EffectCommand>(m_SndFile.Patterns[m_nPattern].GetpModCommand(row, m_nChan)->command);
- else
- return CMD_NONE;
- }
- void CEffectVis::SetCommand(ROWINDEX row, EffectCommand command)
- {
- if(m_SndFile.Patterns.IsValidPat(m_nPattern))
- {
- ModCommand &m = *m_SndFile.Patterns[m_nPattern].GetpModCommand(row, m_nChan);
- if(m.IsPcNote())
- {
- // Clear PC note
- m.note = 0;
- m.instr = 0;
- m.volcmd = VOLCMD_NONE;
- m.vol = 0;
- }
- m.command = command;
- }
- }
- int CEffectVis::RowToScreenX(ROWINDEX row) const
- {
- if ((row >= m_startRow) || (row <= m_endRow))
- return mpt::saturate_round<int>(m_rcDraw.left + m_innerBorder + (row - m_startRow) * m_pixelsPerRow);
- return -1;
- }
- int CEffectVis::RowToScreenY(ROWINDEX row) const
- {
- int screenY = -1;
- if(m_SndFile.Patterns.IsValidPat(m_nPattern))
- {
- const ModCommand &m = *m_SndFile.Patterns[m_nPattern].GetpModCommand(row, m_nChan);
- if (m.IsPcNote())
- {
- uint16 paramValue = m.GetValueEffectCol();
- screenY = PCParamToScreenY(paramValue);
- } else
- {
- uint16 paramValue = m.param;
- screenY = FXParamToScreenY(paramValue);
- }
- }
- return screenY;
- }
- int CEffectVis::FXParamToScreenY(uint16 param) const
- {
- if(param >= 0x00 && param <= 0xFF)
- return mpt::saturate_round<int>(m_rcDraw.bottom - param * m_pixelsPerFXParam);
- return -1;
- }
- int CEffectVis::PCParamToScreenY(uint16 param) const
- {
- if(param >= 0x00 && param <= ModCommand::maxColumnValue)
- return mpt::saturate_round<int>(m_rcDraw.bottom - param*m_pixelsPerPCParam);
- return -1;
- }
- ModCommand::PARAM CEffectVis::ScreenYToFXParam(int y) const
- {
- if(y <= FXParamToScreenY(0xFF))
- return 0xFF;
- if(y >= FXParamToScreenY(0x00))
- return 0x00;
- return mpt::saturate_round<ModCommand::PARAM>((m_rcDraw.bottom - y) / m_pixelsPerFXParam);
- }
- uint16 CEffectVis::ScreenYToPCParam(int y) const
- {
- if(y <= PCParamToScreenY(ModCommand::maxColumnValue))
- return ModCommand::maxColumnValue;
- if(y >= PCParamToScreenY(0x00))
- return 0x00;
- return mpt::saturate_round<uint16>((m_rcDraw.bottom - y) / m_pixelsPerPCParam);
- }
- ROWINDEX CEffectVis::ScreenXToRow(int x) const
- {
- if(x <= RowToScreenX(m_startRow))
- return m_startRow;
- if(x >= RowToScreenX(m_endRow))
- return m_endRow;
- return mpt::saturate_round<ROWINDEX>(m_startRow + (x - m_innerBorder) / m_pixelsPerRow);
- }
- void CEffectVis::DrawGrid()
- {
- // Lots of room for optimisation here.
- // Draw vertical grid lines
- ROWINDEX nBeat = m_SndFile.m_nDefaultRowsPerBeat, nMeasure = m_SndFile.m_nDefaultRowsPerMeasure;
- if(m_SndFile.Patterns[m_nPattern].GetOverrideSignature())
- {
- nBeat = m_SndFile.Patterns[m_nPattern].GetRowsPerBeat();
- nMeasure = m_SndFile.Patterns[m_nPattern].GetRowsPerMeasure();
- }
- m_dcGrid.FillSolidRect(&m_rcDraw, 0);
- auto oldPen = m_dcGrid.SelectStockObject(DC_PEN);
- for(ROWINDEX row = m_startRow; row <= m_endRow; row++)
- {
- if(row % nMeasure == 0)
- m_dcGrid.SetDCPenColor(RGB(0xFF, 0xFF, 0xFF));
- else if(row % nBeat == 0)
- m_dcGrid.SetDCPenColor(RGB(0x99, 0x99, 0x99));
- else
- m_dcGrid.SetDCPenColor(RGB(0x55, 0x55, 0x55));
- int x1 = RowToScreenX(row);
- m_dcGrid.MoveTo(x1, m_rcDraw.top);
- m_dcGrid.LineTo(x1, m_rcDraw.bottom);
- }
- // Draw horizontal grid lines
- constexpr UINT numHorizontalLines = 4;
- for(UINT i = 0; i < numHorizontalLines; i++)
- {
- COLORREF c = 0;
- switch(i % 4)
- {
- case 0: c = RGB(0x00, 0x00, 0x00); break;
- case 1: c = RGB(0x40, 0x40, 0x40); break;
- case 2: c = RGB(0x80, 0x80, 0x80); break;
- case 3: c = RGB(0xCC, 0xCC, 0xCC); break;
- }
- m_dcGrid.SetDCPenColor(c);
- int y1 = m_rcDraw.bottom / numHorizontalLines * i;
- m_dcGrid.MoveTo(m_rcDraw.left + m_innerBorder, y1);
- m_dcGrid.LineTo(m_rcDraw.right - m_innerBorder, y1);
- }
- m_dcGrid.SelectObject(oldPen);
- }
- void CEffectVis::SetPlayCursor(PATTERNINDEX nPat, ROWINDEX nRow)
- {
- if(nPat == m_nPattern && nRow == m_nOldPlayPos)
- return;
- if(m_nOldPlayPos >= m_startRow && m_nOldPlayPos <= m_endRow)
- {
- // erase current playpos
- int x1 = RowToScreenX(m_nOldPlayPos);
- m_dcPlayPos.SelectStockObject(BLACK_PEN);
- m_dcPlayPos.MoveTo(x1,m_rcDraw.top);
- m_dcPlayPos.LineTo(x1,m_rcDraw.bottom);
- }
- if((nRow < m_startRow) || (nRow > m_endRow) || (nPat != m_nPattern))
- return;
- int x1 = RowToScreenX(nRow);
- m_dcPlayPos.SelectStockObject(DC_PEN);
- m_dcPlayPos.SetDCPenColor(TrackerSettings::Instance().rgbCustomColors[MODCOLOR_SAMPLE]);
- m_dcPlayPos.MoveTo(x1,m_rcDraw.top);
- m_dcPlayPos.LineTo(x1,m_rcDraw.bottom);
- m_nOldPlayPos = nRow;
- InvalidateRect(NULL, FALSE);
- }
- void CEffectVis::ShowVis(CDC *pDC)
- {
- if (m_forceRedraw)
- {
- m_forceRedraw = false;
- // if we already have a memory dc, destroy it (this occurs for a re-size)
- if (m_dcGrid.m_hDC)
- {
- m_dcGrid.SelectObject(m_pbOldGrid);
- m_dcGrid.DeleteDC();
- m_dcNodes.SelectObject(m_pbOldNodes);
- m_dcNodes.DeleteDC();
- m_dcPlayPos.SelectObject(m_pbOldPlayPos);
- m_dcPlayPos.DeleteDC();
- m_bPlayPos.DeleteObject();
- m_bGrid.DeleteObject();
- m_bNodes.DeleteObject();
- }
- // create a memory based dc for drawing the grid
- m_dcGrid.CreateCompatibleDC(pDC);
- m_bGrid.CreateCompatibleBitmap(pDC, m_rcDraw.Width(), m_rcDraw.Height());
- m_pbOldGrid = *m_dcGrid.SelectObject(&m_bGrid);
- // create a memory based dc for drawing the nodes
- m_dcNodes.CreateCompatibleDC(pDC);
- m_bNodes.CreateCompatibleBitmap(pDC, m_rcDraw.Width(), m_rcDraw.Height());
- m_pbOldNodes = *m_dcNodes.SelectObject(&m_bNodes);
- // create a memory based dc for drawing the nodes
- m_dcPlayPos.CreateCompatibleDC(pDC);
- m_bPlayPos.CreateCompatibleBitmap(pDC, m_rcDraw.Width(), m_rcDraw.Height());
- m_pbOldPlayPos = *m_dcPlayPos.SelectObject(&m_bPlayPos);
- SetPlayCursor(m_nPattern, m_nOldPlayPos);
- DrawGrid();
- DrawNodes();
- }
- // display the new image, combining the nodes with the grid
- ShowVisImage(pDC);
- }
- void CEffectVis::ShowVisImage(CDC *pDC)
- {
- // to avoid flicker, establish a memory dc, draw to it
- // and then BitBlt it to the destination "pDC"
- CDC memDC;
- memDC.CreateCompatibleDC(pDC);
- if (!memDC)
- return;
- CBitmap memBitmap;
- memBitmap.CreateCompatibleBitmap(pDC, m_rcDraw.Width(), m_rcDraw.Height());
- CBitmap *oldBitmap = memDC.SelectObject(&memBitmap);
- // make sure we have the bitmaps
- if (!m_dcGrid.m_hDC)
- return;
- if (!m_dcNodes.m_hDC)
- return;
- if (!m_dcPlayPos.m_hDC)
- return;
- if (memDC.m_hDC != nullptr)
- {
- // draw the grid
- memDC.BitBlt(0, 0, m_rcDraw.Width(), m_rcDraw.Height(), &m_dcGrid, 0, 0, SRCCOPY);
- // merge the nodes image with the grid
- memDC.TransparentBlt(0, 0, m_rcDraw.Width(), m_rcDraw.Height(), &m_dcNodes, 0, 0, m_rcDraw.Width(), m_rcDraw.Height(), 0x00000000);
- // further merge the playpos
- memDC.TransparentBlt(0, 0, m_rcDraw.Width(), m_rcDraw.Height(), &m_dcPlayPos, 0, 0, m_rcDraw.Width(), m_rcDraw.Height(), 0x00000000);
- // copy the resulting bitmap to the destination
- pDC->BitBlt(0, 0, m_rcDraw.Width(), m_rcDraw.Height(), &memDC, 0, 0, SRCCOPY);
- }
- memDC.SelectObject(oldBitmap);
- }
- void CEffectVis::DrawNodes()
- {
- if(m_rcDraw.IsRectEmpty())
- return;
- //Draw
- const int lineWidth = Util::ScalePixels(1, m_hWnd);
- const int nodeSizeHalf = m_nodeSizeHalf;
- const int nodeSizeHalf2 = nodeSizeHalf - lineWidth + 1;
- const int nodeSize = 2 * nodeSizeHalf + 1;
- //erase
- if ((ROWINDEX)m_nRowToErase < m_startRow || m_nParamToErase < 0)
- {
- m_dcNodes.FillSolidRect(&m_rcDraw, 0);
- } else
- {
- int x = RowToScreenX(m_nRowToErase);
- CRect r(x - nodeSizeHalf, m_rcDraw.top, x + nodeSizeHalf + 1, m_rcDraw.bottom);
- m_dcNodes.FillSolidRect(&r, 0);
- }
- for (ROWINDEX row = m_startRow; row <= m_endRow; row++)
- {
- COLORREF col = IsPcNote(row) ? RGB(0xFF, 0xFF, 0x00) : RGB(0xD0, 0xFF, 0xFF);
- int x = RowToScreenX(row);
- int y = RowToScreenY(row);
- m_dcNodes.FillSolidRect(x - nodeSizeHalf, y - nodeSizeHalf, nodeSize, lineWidth, col); // Top
- m_dcNodes.FillSolidRect(x + nodeSizeHalf2, y - nodeSizeHalf, lineWidth, nodeSize, col); // Right
- m_dcNodes.FillSolidRect(x - nodeSizeHalf, y + nodeSizeHalf2, nodeSize, lineWidth, col); // Bottom
- m_dcNodes.FillSolidRect(x - nodeSizeHalf, y - nodeSizeHalf, lineWidth, nodeSize, col); // Left
- }
- }
- void CEffectVis::InvalidateRow(int row)
- {
- if (((UINT)row < m_startRow) || ((UINT)row > m_endRow)) return;
- //It seems this optimisation doesn't work properly yet. Disable in Update()
- int x = RowToScreenX(row);
- invalidated.bottom = m_rcDraw.bottom;
- invalidated.top = m_rcDraw.top;
- invalidated.left = x - m_nodeSizeHalf;
- invalidated.right = x + m_nodeSizeHalf + 1;
- InvalidateRect(&invalidated, FALSE);
- }
- void CEffectVis::OpenEditor(CWnd *parent)
- {
- Create(IDD_EFFECTVISUALIZER, parent);
- m_forceRedraw = true;
- if(TrackerSettings::Instance().effectVisWidth > 0 && TrackerSettings::Instance().effectVisHeight > 0)
- {
- WINDOWPLACEMENT wnd;
- wnd.length = sizeof(wnd);
- GetWindowPlacement(&wnd);
- wnd.showCmd = SW_SHOWNOACTIVATE;
- CRect rect = wnd.rcNormalPosition;
- if(TrackerSettings::Instance().effectVisX > int32_min && TrackerSettings::Instance().effectVisY > int32_min)
- {
- CRect mainRect;
- CMainFrame::GetMainFrame()->GetWindowRect(mainRect);
- rect.left = mainRect.left + MulDiv(TrackerSettings::Instance().effectVisX, Util::GetDPIx(m_hWnd), 96);
- rect.top = mainRect.top + MulDiv(TrackerSettings::Instance().effectVisY, Util::GetDPIx(m_hWnd), 96);
- }
- rect.right = rect.left + MulDiv(TrackerSettings::Instance().effectVisWidth, Util::GetDPIx(m_hWnd), 96);
- rect.bottom = rect.top + MulDiv(TrackerSettings::Instance().effectVisHeight, Util::GetDPIx(m_hWnd), 96);
- wnd.rcNormalPosition = rect;
- SetWindowPlacement(&wnd);
- }
- ShowWindow(SW_SHOW);
- }
- void CEffectVis::OnClose()
- {
- DoClose();
- }
- void CEffectVis::OnOK()
- {
- OnClose();
- }
- void CEffectVis::OnCancel()
- {
- OnClose();
- }
- void CEffectVis::DoClose()
- {
- WINDOWPLACEMENT wnd;
- wnd.length = sizeof(wnd);
- GetWindowPlacement(&wnd);
- CRect mainRect;
- CMainFrame::GetMainFrame()->GetWindowRect(mainRect);
- CRect rect = wnd.rcNormalPosition;
- rect.MoveToXY(rect.left - mainRect.left, rect.top - mainRect.top);
- TrackerSettings::Instance().effectVisWidth = MulDiv(rect.Width(), 96, Util::GetDPIx(m_hWnd));
- TrackerSettings::Instance().effectVisHeight = MulDiv(rect.Height(), 96, Util::GetDPIy(m_hWnd));
- TrackerSettings::Instance().effectVisX = MulDiv(rect.left, 96, Util::GetDPIx(m_hWnd));
- TrackerSettings::Instance().effectVisY = MulDiv(rect.top, 96, Util::GetDPIy(m_hWnd));
- m_dcGrid.SelectObject(m_pbOldGrid);
- m_dcGrid.DeleteDC();
- m_dcNodes.SelectObject(m_pbOldNodes);
- m_dcNodes.DeleteDC();
- m_dcPlayPos.SelectObject(m_pbOldPlayPos);
- m_dcPlayPos.DeleteDC();
- m_bGrid.DeleteObject();
- m_bNodes.DeleteObject();
- m_bPlayPos.DeleteObject();
- DestroyWindow();
- }
- void CEffectVis::PostNcDestroy()
- {
- m_pViewPattern->m_pEffectVis = nullptr;
- }
- void CEffectVis::OnSize(UINT nType, int cx, int cy)
- {
- MPT_UNREFERENCED_PARAMETER(nType);
- MPT_UNREFERENCED_PARAMETER(cx);
- MPT_UNREFERENCED_PARAMETER(cy);
- GetClientRect(&m_rcFullWin);
- m_rcDraw.SetRect(m_rcFullWin.left, m_rcFullWin.top, m_rcFullWin.right, m_rcFullWin.bottom - m_marginBottom);
- const int actionListWidth = Util::ScalePixels(170, m_hWnd);
- const int commandListWidth = Util::ScalePixels(160, m_hWnd);
- if (IsWindow(m_edVisStatus.m_hWnd))
- m_edVisStatus.SetWindowPos(this, m_rcFullWin.left, m_rcDraw.bottom, m_rcFullWin.right-commandListWidth-actionListWidth, m_rcFullWin.bottom-m_rcDraw.bottom, SWP_NOACTIVATE|SWP_NOCOPYBITS|SWP_SHOWWINDOW|SWP_NOZORDER);
- if (IsWindow(m_cmbActionList))
- m_cmbActionList.SetWindowPos(this, m_rcFullWin.right-commandListWidth-actionListWidth, m_rcDraw.bottom, actionListWidth, m_rcFullWin.bottom-m_rcDraw.bottom, SWP_NOACTIVATE|SWP_NOCOPYBITS|SWP_SHOWWINDOW|SWP_NOZORDER);
- if (IsWindow(m_cmbEffectList))
- m_cmbEffectList.SetWindowPos(this, m_rcFullWin.right-commandListWidth, m_rcDraw.bottom, commandListWidth, m_rcFullWin.bottom-m_rcDraw.bottom, SWP_NOACTIVATE|SWP_NOCOPYBITS|SWP_SHOWWINDOW|SWP_NOZORDER);
- if(m_nRows)
- m_pixelsPerRow = (float)(m_rcDraw.Width() - m_innerBorder * 2) / (float)m_nRows;
- else
- m_pixelsPerRow = 1;
- m_pixelsPerFXParam = (float)(m_rcDraw.Height())/(float)0xFF;
- m_pixelsPerPCParam = (float)(m_rcDraw.Height())/(float)ModCommand::maxColumnValue;
- m_forceRedraw = true;
- InvalidateRect(NULL, FALSE); //redraw everything
- }
- void CEffectVis::Update()
- {
- DrawNodes();
- if (::IsWindow(m_hWnd))
- {
- OnPaint();
- if (m_nRowToErase<0)
- InvalidateRect(NULL, FALSE); // redraw everything
- else
- {
- InvalidateRow(m_nRowToErase);
- m_nParamToErase=-1;
- m_nRowToErase=-1;
- }
- }
- }
- void CEffectVis::UpdateSelection(ROWINDEX startRow, ROWINDEX endRow, CHANNELINDEX nchn, PATTERNINDEX pat)
- {
- m_startRow = startRow;
- m_endRow = endRow;
- m_nRows = endRow - startRow;
- m_nChan = nchn;
- m_nPattern = pat;
- //Check pattern, start row and channel exist
- if(!m_SndFile.Patterns.IsValidPat(m_nPattern) || !m_SndFile.Patterns[m_nPattern].IsValidRow(m_startRow) || m_nChan >= m_SndFile.GetNumChannels())
- {
- DoClose();
- return;
- }
- //Check end exists
- if(!m_SndFile.Patterns[m_nPattern].IsValidRow(m_endRow))
- {
- m_endRow = m_SndFile.Patterns[m_nPattern].GetNumRows() - 1;
- }
- if(m_nRows)
- m_pixelsPerRow = (float)(m_rcDraw.Width() - m_innerBorder * 2) / (float)m_nRows;
- else
- m_pixelsPerRow = 1;
- m_pixelsPerFXParam = (float)(m_rcDraw.Height())/(float)0xFF;
- m_pixelsPerPCParam = (float)(m_rcDraw.Height())/(float)ModCommand::maxColumnValue;
- m_forceRedraw = true;
- Update();
- }
- void CEffectVis::OnRButtonDown(UINT nFlags, CPoint point)
- {
- if (!(m_dwStatus & FXVSTATUS_LDRAGGING))
- {
- SetFocus();
- SetCapture();
- m_nDragItem = ScreenXToRow(point.x);
- m_dwStatus |= FXVSTATUS_RDRAGGING;
- m_ModDoc.GetPatternUndo().PrepareUndo(static_cast<PATTERNINDEX>(m_nPattern), m_nChan, m_nDragItem, 1, 1, "Parameter Editor entry");
- OnMouseMove(nFlags, point);
- }
- CDialog::OnRButtonDown(nFlags, point);
- }
- void CEffectVis::OnRButtonUp(UINT nFlags, CPoint point)
- {
- ReleaseCapture();
- m_dwStatus = 0x00;
- m_nDragItem = -1;
- CDialog::OnRButtonUp(nFlags, point);
- }
- void CEffectVis::OnMouseMove(UINT nFlags, CPoint point)
- {
- CDialog::OnMouseMove(nFlags, point);
- ROWINDEX row = ScreenXToRow(point.x);
- if ((m_dwStatus & FXVSTATUS_RDRAGGING) && (m_nDragItem>=0) )
- {
- m_nRowToErase = m_nDragItem;
- m_nParamToErase = GetParam(m_nDragItem);
- MakeChange(m_nDragItem, point.y);
- } else if ((m_dwStatus & FXVSTATUS_LDRAGGING))
- {
- // Interpolate if we detect that rows have been skipped but the left mouse button was not released.
- // This ensures we produce a smooth curve even when we are not notified of mouse movements at a high frequency (e.g. if CPU usage is high)
- const int steps = std::abs((int)row - (int)m_nLastDrawnRow);
- if (m_nLastDrawnRow != ROWINDEX_INVALID && m_nLastDrawnRow > m_startRow && steps > 1)
- {
- int direction = ((int)(row - m_nLastDrawnRow) > 0) ? 1 : -1;
- float factor = (float)(point.y - m_nLastDrawnY)/(float)steps + 0.5f;
- int currentRow;
- for (int i=1; i<=steps; i++)
- {
- currentRow = m_nLastDrawnRow+(direction*i);
- int interpolatedY = mpt::saturate_round<int>(m_nLastDrawnY + ((float)i * factor));
- MakeChange(currentRow, interpolatedY);
- }
- //Don't use single value update
- m_nRowToErase = -1;
- m_nParamToErase = -1;
- } else
- {
- m_nRowToErase = -1;
- m_nParamToErase = -1;
- MakeChange(row, point.y);
- }
- // Remember last modified point in case we need to interpolate
- m_nLastDrawnRow = row;
- m_nLastDrawnY = point.y;
- }
- //update status bar
- CString status;
- CString effectName;
- uint16 paramValue;
- if (IsPcNote(row))
- {
- paramValue = ScreenYToPCParam(point.y);
- effectName.Format(_T("%s"), _T("Param Control")); // TODO - show smooth & plug+param
- } else
- {
- paramValue = ScreenYToFXParam(point.y);
- effectInfo.GetEffectInfo(effectInfo.GetIndexFromEffect(GetCommand(row), ModCommand::PARAM(GetParam(row))), &effectName, true);
- }
- status.Format(_T("Pat: %d\tChn: %d\tRow: %d\tVal: %02X (%03d) [%s]"),
- m_nPattern, m_nChan+1, static_cast<signed int>(row), paramValue, paramValue, effectName.GetString());
- m_edVisStatus.SetWindowText(status);
- }
- void CEffectVis::OnLButtonDown(UINT nFlags, CPoint point)
- {
- if (!(m_dwStatus & FXVSTATUS_RDRAGGING))
- {
- SetFocus();
- SetCapture();
- m_nDragItem = ScreenXToRow(point.x);
- m_dwStatus |= FXVSTATUS_LDRAGGING;
- m_ModDoc.GetPatternUndo().PrepareUndo(static_cast<PATTERNINDEX>(m_nPattern), m_nChan, m_startRow, 1, m_endRow - m_startRow + 1, "Parameter Editor entry");
- OnMouseMove(nFlags, point);
- }
- CDialog::OnLButtonDown(nFlags, point);
- }
- void CEffectVis::OnLButtonUp(UINT nFlags, CPoint point)
- {
- ReleaseCapture();
- m_dwStatus = 0x00;
- CDialog::OnLButtonUp(nFlags, point);
- m_nLastDrawnRow = ROWINDEX_INVALID;
- }
- BOOL CEffectVis::OnInitDialog()
- {
- CDialog::OnInitDialog();
- int dpi = Util::GetDPIx(m_hWnd);
- m_nodeSizeHalf = MulDiv(3, dpi, 96);
- m_marginBottom = MulDiv(20, dpi, 96);
- m_innerBorder = MulDiv(4, dpi, 96);
- // If first selected row is a PC event (or some other row but there aren't any other effects), default to PC note overwrite mode
- // and use it as a template for new PC notes that will be created via the visualiser.
- bool isPCevent = IsPcNote(m_startRow);
- if(!isPCevent)
- {
- for(ROWINDEX row = m_startRow; row <= m_endRow; row++)
- {
- if(IsPcNote(row))
- {
- isPCevent = true;
- } else if(GetCommand(row) != CMD_NONE)
- {
- isPCevent = false;
- break;
- }
- }
- }
- if(m_ModDoc.GetModType() == MOD_TYPE_MPT && isPCevent)
- {
- m_nAction = kAction_OverwritePC;
- if(m_SndFile.Patterns.IsValidPat(m_nPattern))
- {
- ModCommand &m = *m_SndFile.Patterns[m_nPattern].GetpModCommand(m_startRow, m_nChan);
- m_templatePCNote.Set(m.note, m.instr, m.GetValueVolCol(), 0);
- }
- m_cmbEffectList.EnableWindow(FALSE);
- } else
- {
- // Otherwise, default to FX overwrite and
- // use effect of first selected row as default effect type
- m_nAction = kAction_OverwriteFX;
- m_nFillEffect = effectInfo.GetIndexFromEffect(GetCommand(m_startRow), ModCommand::PARAM(GetParam(m_startRow)));
- if (m_nFillEffect < 0 || m_nFillEffect >= MAX_EFFECTS)
- m_nFillEffect = effectInfo.GetIndexFromEffect(CMD_SMOOTHMIDI, 0);
- }
- CString s;
- UINT numfx = effectInfo.GetNumEffects();
- m_cmbEffectList.ResetContent();
- int k;
- for (UINT i=0; i<numfx; i++)
- {
- if (effectInfo.GetEffectInfo(i, &s, true))
- {
- k =m_cmbEffectList.AddString(s);
- m_cmbEffectList.SetItemData(k, i);
- if ((int)i == m_nFillEffect)
- m_cmbEffectList.SetCurSel(k);
- }
- }
- m_cmbActionList.ResetContent();
- m_cmbActionList.SetItemData(m_cmbActionList.AddString(_T("Overwrite with effect:")), kAction_OverwriteFX);
- m_cmbActionList.SetItemData(m_cmbActionList.AddString(_T("Overwrite effect next to note:")), kAction_OverwriteFXWithNote);
- m_cmbActionList.SetItemData(m_cmbActionList.AddString(_T("Fill blanks with effect:")), kAction_FillFX);
- if (m_ModDoc.GetModType() == MOD_TYPE_MPT)
- {
- m_cmbActionList.SetItemData(m_cmbActionList.AddString(_T("Overwrite with PC note")), kAction_OverwritePC);
- m_cmbActionList.SetItemData(m_cmbActionList.AddString(_T("Fill blanks with PC note")), kAction_FillPC);
- }
- m_cmbActionList.SetItemData(m_cmbActionList.AddString(_T("Never change effect type")), kAction_Preserve);
- m_cmbActionList.SetCurSel(m_nAction);
- return true;
- }
- void CEffectVis::MakeChange(ROWINDEX row, int y)
- {
- if(!m_SndFile.Patterns.IsValidPat(m_nPattern))
- return;
- ModCommand &m = *m_SndFile.Patterns[m_nPattern].GetpModCommand(row, m_nChan);
- switch (m_nAction)
- {
- case kAction_FillFX:
- // Only set command if there isn't a command already at this row and it's not a PC note
- if (GetCommand(row) == CMD_NONE && !IsPcNote(row))
- {
- SetCommand(row, effectInfo.GetEffectFromIndex(m_nFillEffect));
- }
- // Always set param
- SetParamFromY(row, y);
- break;
- case kAction_OverwriteFXWithNote:
- if(!m_SndFile.Patterns.IsValidPat(m_nPattern))
- break;
- if(!m_SndFile.Patterns[m_nPattern].GetpModCommand(row, m_nChan)->IsNote())
- break;
- [[fallthrough]];
- case kAction_OverwriteFX:
- // Always set command and param. Blows away any PC notes.
- SetCommand(row, effectInfo.GetEffectFromIndex(m_nFillEffect));
- SetParamFromY(row, y);
- break;
- case kAction_FillPC:
- // Fill only empty slots with PC notes - leave other slots alone.
- if (m.IsEmpty())
- {
- SetPcNote(row);
- }
- // Always set param
- SetParamFromY(row, y);
- break;
- case kAction_OverwritePC:
- // Always convert to PC Note and set param value
- SetPcNote(row);
- SetParamFromY(row, y);
- break;
- case kAction_Preserve:
- if (GetCommand(row) != CMD_NONE || IsPcNote(row))
- {
- // Only set param if we have an effect type or if this is a PC note.
- // Never change the effect type.
- SetParamFromY(row, y);
- }
- break;
- }
- m_ModDoc.SetModified();
- m_ModDoc.UpdateAllViews(nullptr, PatternHint(m_nPattern).Data());
- }
- void CEffectVis::SetPcNote(ROWINDEX row)
- {
- if(!m_SndFile.Patterns.IsValidPat(m_nPattern))
- return;
- ModCommand &m = *m_SndFile.Patterns[m_nPattern].GetpModCommand(row, m_nChan);
- m.Set(m_templatePCNote.note, m_templatePCNote.instr, m_templatePCNote.GetValueVolCol(), 0);
- }
- bool CEffectVis::IsPcNote(ROWINDEX row) const
- {
- if(m_SndFile.Patterns.IsValidPat(m_nPattern))
- return m_SndFile.Patterns[m_nPattern].GetpModCommand(row, m_nChan)->IsPcNote();
- else
- return false;
- }
- OPENMPT_NAMESPACE_END
|