winamp/Src/external_dependencies/openmpt-trunk/mptrack/View_gen.cpp

1817 lines
52 KiB
C++

/*
* view_gen.cpp
* ------------
* Purpose: General tab, lower panel.
* Notes : (currently none)
* Authors: Olivier Lapicque
* 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 "InputHandler.h"
#include "Childfrm.h"
#include "Moddoc.h"
#include "Globals.h"
#include "Ctrl_gen.h"
#include "View_gen.h"
#include "../soundlib/plugins/PlugInterface.h"
#include "EffectVis.h"
#include "MoveFXSlotDialog.h"
#include "ChannelManagerDlg.h"
#include "SelectPluginDialog.h"
#include "../soundlib/mod_specifications.h"
#include "../common/mptStringBuffer.h"
#include "AbstractVstEditor.h"
// This is used for retrieving the correct background colour for the
// frames on the general tab when using WinXP Luna or Vista/Win7 Aero.
#include <uxtheme.h>
OPENMPT_NAMESPACE_BEGIN
IMPLEMENT_SERIAL(CViewGlobals, CFormView, 0)
BEGIN_MESSAGE_MAP(CViewGlobals, CFormView)
//{{AFX_MSG_MAP(CViewGlobals)
ON_WM_SIZE()
ON_WM_HSCROLL()
ON_WM_VSCROLL()
ON_WM_DESTROY()
ON_MESSAGE(WM_MOD_MDIACTIVATE, &CViewGlobals::OnMDIDeactivate)
ON_MESSAGE(WM_MOD_MDIDEACTIVATE, &CViewGlobals::OnMDIDeactivate)
ON_COMMAND(IDC_CHECK1, &CViewGlobals::OnMute1)
ON_COMMAND(IDC_CHECK3, &CViewGlobals::OnMute2)
ON_COMMAND(IDC_CHECK5, &CViewGlobals::OnMute3)
ON_COMMAND(IDC_CHECK7, &CViewGlobals::OnMute4)
ON_COMMAND(IDC_CHECK2, &CViewGlobals::OnSurround1)
ON_COMMAND(IDC_CHECK4, &CViewGlobals::OnSurround2)
ON_COMMAND(IDC_CHECK6, &CViewGlobals::OnSurround3)
ON_COMMAND(IDC_CHECK8, &CViewGlobals::OnSurround4)
ON_COMMAND(IDC_BUTTON9, &CViewGlobals::OnEditColor1)
ON_COMMAND(IDC_BUTTON10, &CViewGlobals::OnEditColor2)
ON_COMMAND(IDC_BUTTON11, &CViewGlobals::OnEditColor3)
ON_COMMAND(IDC_BUTTON12, &CViewGlobals::OnEditColor4)
ON_EN_UPDATE(IDC_EDIT1, &CViewGlobals::OnEditVol1)
ON_EN_UPDATE(IDC_EDIT3, &CViewGlobals::OnEditVol2)
ON_EN_UPDATE(IDC_EDIT5, &CViewGlobals::OnEditVol3)
ON_EN_UPDATE(IDC_EDIT7, &CViewGlobals::OnEditVol4)
ON_EN_UPDATE(IDC_EDIT2, &CViewGlobals::OnEditPan1)
ON_EN_UPDATE(IDC_EDIT4, &CViewGlobals::OnEditPan2)
ON_EN_UPDATE(IDC_EDIT6, &CViewGlobals::OnEditPan3)
ON_EN_UPDATE(IDC_EDIT8, &CViewGlobals::OnEditPan4)
ON_EN_UPDATE(IDC_EDIT9, &CViewGlobals::OnEditName1)
ON_EN_UPDATE(IDC_EDIT10, &CViewGlobals::OnEditName2)
ON_EN_UPDATE(IDC_EDIT11, &CViewGlobals::OnEditName3)
ON_EN_UPDATE(IDC_EDIT12, &CViewGlobals::OnEditName4)
ON_CBN_SELCHANGE(IDC_COMBO1, &CViewGlobals::OnFx1Changed)
ON_CBN_SELCHANGE(IDC_COMBO2, &CViewGlobals::OnFx2Changed)
ON_CBN_SELCHANGE(IDC_COMBO3, &CViewGlobals::OnFx3Changed)
ON_CBN_SELCHANGE(IDC_COMBO4, &CViewGlobals::OnFx4Changed)
// Plugins
ON_COMMAND(IDC_CHECK9, &CViewGlobals::OnMixModeChanged)
ON_COMMAND(IDC_CHECK10, &CViewGlobals::OnBypassChanged)
ON_COMMAND(IDC_CHECK11, &CViewGlobals::OnDryMixChanged)
ON_COMMAND(IDC_BUTTON1, &CViewGlobals::OnSelectPlugin)
ON_COMMAND(IDC_DELPLUGIN, &CViewGlobals::OnRemovePlugin)
ON_COMMAND(IDC_BUTTON2, &CViewGlobals::OnEditPlugin)
ON_COMMAND(IDC_BUTTON4, &CViewGlobals::OnNextPlugin)
ON_COMMAND(IDC_BUTTON5, &CViewGlobals::OnPrevPlugin)
ON_COMMAND(IDC_MOVEFXSLOT, &CViewGlobals::OnMovePlugToSlot)
ON_COMMAND(IDC_INSERTFXSLOT,&CViewGlobals::OnInsertSlot)
ON_COMMAND(IDC_CLONEPLUG, &CViewGlobals::OnClonePlug)
ON_COMMAND(IDC_BUTTON6, &CViewGlobals::OnLoadParam)
ON_COMMAND(IDC_BUTTON8, &CViewGlobals::OnSaveParam)
ON_EN_UPDATE(IDC_EDIT13, &CViewGlobals::OnPluginNameChanged)
ON_EN_UPDATE(IDC_EDIT14, &CViewGlobals::OnSetParameter)
ON_EN_SETFOCUS(IDC_EDIT14, &CViewGlobals::OnFocusParam)
ON_EN_KILLFOCUS(IDC_EDIT14, &CViewGlobals::OnParamChanged)
ON_CBN_SELCHANGE(IDC_COMBO5, &CViewGlobals::OnPluginChanged)
ON_CBN_SELCHANGE(IDC_COMBO6, &CViewGlobals::OnParamChanged)
ON_CBN_SETFOCUS(IDC_COMBO6, &CViewGlobals::OnFillParamCombo)
ON_CBN_SELCHANGE(IDC_COMBO7, &CViewGlobals::OnOutputRoutingChanged)
ON_CBN_SELCHANGE(IDC_COMBO8, &CViewGlobals::OnProgramChanged)
ON_CBN_SETFOCUS(IDC_COMBO8, &CViewGlobals::OnFillProgramCombo)
ON_COMMAND(IDC_CHECK12, &CViewGlobals::OnWetDryExpandChanged)
ON_CBN_SELCHANGE(IDC_COMBO9, &CViewGlobals::OnSpecialMixProcessingChanged)
ON_NOTIFY(TCN_SELCHANGE, IDC_TABCTRL1, &CViewGlobals::OnTabSelchange)
ON_MESSAGE(WM_MOD_UNLOCKCONTROLS, &CViewGlobals::OnUnlockControls)
ON_MESSAGE(WM_MOD_VIEWMSG, &CViewGlobals::OnModViewMsg)
ON_MESSAGE(WM_MOD_MIDIMSG, &CViewGlobals::OnMidiMsg)
ON_MESSAGE(WM_MOD_PLUGPARAMAUTOMATE, &CViewGlobals::OnParamAutomated)
ON_MESSAGE(WM_MOD_PLUGINDRYWETRATIOCHANGED, &CViewGlobals::OnDryWetRatioChangedFromPlayer)
ON_COMMAND(ID_EDIT_UNDO, &CViewGlobals::OnEditUndo)
ON_COMMAND(ID_EDIT_REDO, &CViewGlobals::OnEditRedo)
ON_UPDATE_COMMAND_UI(ID_EDIT_UNDO, &CViewGlobals::OnUpdateUndo)
ON_UPDATE_COMMAND_UI(ID_EDIT_REDO, &CViewGlobals::OnUpdateRedo)
ON_NOTIFY_EX_RANGE(TTN_NEEDTEXT, 0, 0xFFFF, &CViewGlobals::OnToolTipText)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
void CViewGlobals::DoDataExchange(CDataExchange* pDX)
{
CFormView::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CViewGlobals)
DDX_Control(pDX, IDC_TABCTRL1, m_TabCtrl);
DDX_Control(pDX, IDC_COMBO1, m_CbnEffects[0]);
DDX_Control(pDX, IDC_COMBO2, m_CbnEffects[1]);
DDX_Control(pDX, IDC_COMBO3, m_CbnEffects[2]);
DDX_Control(pDX, IDC_COMBO4, m_CbnEffects[3]);
DDX_Control(pDX, IDC_COMBO5, m_CbnPlugin);
DDX_Control(pDX, IDC_COMBO6, m_CbnParam);
DDX_Control(pDX, IDC_COMBO7, m_CbnOutput);
DDX_Control(pDX, IDC_COMBO8, m_CbnPreset);
DDX_Control(pDX, IDC_COMBO9, m_CbnSpecialMixProcessing);
DDX_Control(pDX, IDC_SPIN10, m_SpinMixGain);
DDX_Control(pDX, IDC_SLIDER1, m_sbVolume[0]);
DDX_Control(pDX, IDC_SLIDER2, m_sbPan[0]);
DDX_Control(pDX, IDC_SLIDER3, m_sbVolume[1]);
DDX_Control(pDX, IDC_SLIDER4, m_sbPan[1]);
DDX_Control(pDX, IDC_SLIDER5, m_sbVolume[2]);
DDX_Control(pDX, IDC_SLIDER6, m_sbPan[2]);
DDX_Control(pDX, IDC_SLIDER7, m_sbVolume[3]);
DDX_Control(pDX, IDC_SLIDER8, m_sbPan[3]);
DDX_Control(pDX, IDC_SLIDER9, m_sbValue);
DDX_Control(pDX, IDC_SLIDER10, m_sbDryRatio);
DDX_Control(pDX, IDC_SPIN1, m_spinVolume[0]);
DDX_Control(pDX, IDC_SPIN2, m_spinPan[0]);
DDX_Control(pDX, IDC_SPIN3, m_spinVolume[1]);
DDX_Control(pDX, IDC_SPIN4, m_spinPan[1]);
DDX_Control(pDX, IDC_SPIN5, m_spinVolume[2]);
DDX_Control(pDX, IDC_SPIN6, m_spinPan[2]);
DDX_Control(pDX, IDC_SPIN7, m_spinVolume[3]);
DDX_Control(pDX, IDC_SPIN8, m_spinPan[3]);
DDX_Control(pDX, IDC_BUTTON1, m_BtnSelect);
DDX_Control(pDX, IDC_BUTTON2, m_BtnEdit);
//}}AFX_DATA_MAP
}
void CViewGlobals::OnInitialUpdate()
{
CChildFrame *pFrame = (CChildFrame *)GetParentFrame();
int nMapMode = MM_TEXT;
SIZE sizeTotal, sizePage, sizeLine;
m_nActiveTab = CHANNELINDEX(-1);
m_nCurrentPlugin = 0;
m_nCurrentParam = 0;
CFormView::OnInitialUpdate();
EnableToolTips();
if (pFrame)
{
GENERALVIEWSTATE &generalState = pFrame->GetGeneralViewState();
if (generalState.initialized)
{
m_TabCtrl.SetCurSel(generalState.nTab);
m_nActiveTab = generalState.nTab;
m_nCurrentPlugin = generalState.nPlugin;
m_nCurrentParam = generalState.nParam;
}
}
GetDeviceScrollSizes(nMapMode, sizeTotal, sizePage, sizeLine);
m_rcClient.SetRect(0, 0, sizeTotal.cx, sizeTotal.cy);
RecalcLayout();
// Initializing scroll ranges
for(int ichn = 0; ichn < CHANNELS_IN_TAB; ichn++)
{
// Color select
m_channelColor[ichn].SubclassDlgItem(IDC_BUTTON9 + ichn, this);
// Volume Slider
m_sbVolume[ichn].SetRange(0, 64);
m_sbVolume[ichn].SetTicFreq(8);
// Pan Slider
m_sbPan[ichn].SetRange(0, 64);
m_sbPan[ichn].SetTicFreq(8);
// Volume Spin
m_spinVolume[ichn].SetRange(0, 64);
// Pan Spin
m_spinPan[ichn].SetRange(0, 256);
}
m_sbValue.SetPos(0);
m_sbValue.SetRange(0, 100);
m_sbValue.SetPos(0);
m_sbValue.SetRange(0, 100);
m_CbnSpecialMixProcessing.AddString(_T("Default"));
m_CbnSpecialMixProcessing.AddString(_T("Wet Subtract"));
m_CbnSpecialMixProcessing.AddString(_T("Dry Subtract"));
m_CbnSpecialMixProcessing.AddString(_T("Mix Subtract"));
m_CbnSpecialMixProcessing.AddString(_T("Middle Subtract"));
m_CbnSpecialMixProcessing.AddString(_T("LR Balance"));
m_SpinMixGain.SetRange(0, 80);
m_SpinMixGain.SetPos(10);
SetDlgItemText(IDC_EDIT16, _T("Gain: x1.0"));
UpdateView(UpdateHint().ModType());
OnParamChanged();
m_nLockCount = 0;
// Use tab background color rather than regular dialog background color (required for Aero/etc. where they are not the same color)
EnableThemeDialogTexture(m_hWnd, ETDT_ENABLETAB);
}
void CViewGlobals::OnDestroy()
{
CChildFrame *pFrame = (CChildFrame *)GetParentFrame();
if (pFrame)
{
GENERALVIEWSTATE &generalState = pFrame->GetGeneralViewState();
generalState.initialized = true;
generalState.nTab = m_nActiveTab;
generalState.nPlugin = m_nCurrentPlugin;
generalState.nParam = m_nCurrentParam;
}
CFormView::OnDestroy();
}
LRESULT CViewGlobals::OnMDIDeactivate(WPARAM, LPARAM)
{
// Create new undo point if we switch to / from other window
m_lastEdit = CHANNELINDEX_INVALID;
return 0;
}
LRESULT CViewGlobals::OnMidiMsg(WPARAM midiData_, LPARAM)
{
uint32 midiData = static_cast<uint32>(midiData_);
// Handle MIDI messages assigned to shortcuts
CInputHandler *ih = CMainFrame::GetInputHandler();
ih->HandleMIDIMessage(kCtxViewGeneral, midiData) != kcNull
|| ih->HandleMIDIMessage(kCtxAllContexts, midiData) != kcNull;
return 1;
}
void CViewGlobals::RecalcLayout()
{
if (m_TabCtrl.m_hWnd != NULL)
{
CRect rect;
GetClientRect(&rect);
if (rect.right < m_rcClient.right) rect.right = m_rcClient.right;
if (rect.bottom < m_rcClient.bottom) rect.bottom = m_rcClient.bottom;
m_TabCtrl.SetWindowPos(&CWnd::wndBottom, 0,0, rect.right, rect.bottom, SWP_NOMOVE);
}
}
int CViewGlobals::GetDlgItemIntEx(UINT nID)
{
CString s;
GetDlgItemText(nID, s);
if(s.GetLength() < 1 || s[0] < _T('0') || s[0] > _T('9')) return -1;
return _ttoi(s);
}
void CViewGlobals::OnSize(UINT nType, int cx, int cy)
{
CFormView::OnSize(nType, cx, cy);
if (((nType == SIZE_RESTORED) || (nType == SIZE_MAXIMIZED)) && (cx > 0) && (cy > 0) && (m_hWnd))
{
RecalcLayout();
}
}
void CViewGlobals::OnUpdate(CView *pView, LPARAM lHint, CObject *pHint)
{
if (pView != this) UpdateView(UpdateHint::FromLPARAM(lHint), pHint);
}
void CViewGlobals::UpdateView(UpdateHint hint, CObject *pObject)
{
const CModDoc *pModDoc = GetDocument();
int nTabCount, nTabIndex;
if (!pModDoc || pObject == this ) return;
const CSoundFile &sndFile = pModDoc->GetSoundFile();
const GeneralHint genHint = hint.ToType<GeneralHint>();
const PluginHint plugHint = hint.ToType<PluginHint>();
if (!genHint.GetType()[HINT_MODTYPE | HINT_MODCHANNELS]
&& !plugHint.GetType()[HINT_MIXPLUGINS | HINT_PLUGINNAMES | HINT_PLUGINPARAM])
{
return;
}
FlagSet<HintType> hintType = hint.GetType();
const bool updateAll = hintType[HINT_MODTYPE];
const auto updateChannel = genHint.GetChannel();
const int updateTab = (updateChannel < sndFile.GetNumChannels()) ? (updateChannel / CHANNELS_IN_TAB) : m_nActiveTab;
if(genHint.GetType()[HINT_MODCHANNELS] && updateTab != m_nActiveTab)
return;
CString s;
nTabCount = (sndFile.m_nChannels + (CHANNELS_IN_TAB - 1)) / CHANNELS_IN_TAB;
if (nTabCount != m_TabCtrl.GetItemCount())
{
UINT nOldSel = m_TabCtrl.GetCurSel();
if (!m_TabCtrl.GetItemCount()) nOldSel = m_nActiveTab;
m_TabCtrl.SetRedraw(FALSE);
m_TabCtrl.DeleteAllItems();
for (int iItem=0; iItem<nTabCount; iItem++)
{
const int lastItem = std::min(iItem * CHANNELS_IN_TAB + CHANNELS_IN_TAB, static_cast<int>(MAX_BASECHANNELS));
s = MPT_CFORMAT("{} - {}")(iItem * CHANNELS_IN_TAB + 1, lastItem);
TC_ITEM tci;
tci.mask = TCIF_TEXT | TCIF_PARAM;
tci.pszText = const_cast<TCHAR *>(s.GetString());
tci.lParam = iItem * CHANNELS_IN_TAB;
m_TabCtrl.InsertItem(iItem, &tci);
}
if (nOldSel >= (UINT)nTabCount) nOldSel = 0;
m_TabCtrl.SetRedraw(TRUE);
m_TabCtrl.SetCurSel(nOldSel);
InvalidateRect(NULL, FALSE);
}
nTabIndex = m_TabCtrl.GetCurSel();
if ((nTabIndex < 0) || (nTabIndex >= nTabCount)) return; // ???
if (m_nActiveTab != nTabIndex || genHint.GetType()[HINT_MODTYPE | HINT_MODCHANNELS])
{
LockControls();
m_nActiveTab = static_cast<CHANNELINDEX>(nTabIndex);
for (CHANNELINDEX ichn = 0; ichn < CHANNELS_IN_TAB; ichn++)
{
const CHANNELINDEX nChn = m_nActiveTab * CHANNELS_IN_TAB + ichn;
const BOOL bEnable = (nChn < sndFile.GetNumChannels()) ? TRUE : FALSE;
if(nChn < MAX_BASECHANNELS)
{
const auto &chnSettings = sndFile.ChnSettings[nChn];
// Text
if(bEnable)
s = MPT_CFORMAT("Channel {}")(nChn + 1);
else
s = _T("");
SetDlgItemText(IDC_TEXT1 + ichn, s);
// Channel color
m_channelColor[ichn].SetColor(chnSettings.color);
m_channelColor[ichn].EnableWindow(bEnable);
// Mute
CheckDlgButton(IDC_CHECK1 + ichn * 2, chnSettings.dwFlags[CHN_MUTE] ? TRUE : FALSE);
// Surround
CheckDlgButton(IDC_CHECK2 + ichn * 2, chnSettings.dwFlags[CHN_SURROUND] ? TRUE : FALSE);
// Volume
int vol = chnSettings.nVolume;
m_sbVolume[ichn].SetPos(vol);
m_sbVolume[ichn].Invalidate(FALSE);
SetDlgItemInt(IDC_EDIT1+ichn*2, vol);
// Pan
int pan = chnSettings.nPan;
m_sbPan[ichn].SetPos(pan/4);
m_sbPan[ichn].Invalidate(FALSE);
SetDlgItemInt(IDC_EDIT2+ichn*2, pan);
// Channel name
s = mpt::ToCString(sndFile.GetCharsetInternal(), chnSettings.szName);
SetDlgItemText(IDC_EDIT9 + ichn, s);
((CEdit*)(GetDlgItem(IDC_EDIT9 + ichn)))->LimitText(MAX_CHANNELNAME - 1);
} else
{
SetDlgItemText(IDC_TEXT1 + ichn, _T(""));
SetDlgItemText(IDC_EDIT9 + ichn, _T(""));
m_channelColor[ichn].EnableWindow(FALSE);
}
// Enable/Disable controls for this channel
BOOL bIT = ((bEnable) && (sndFile.m_nType & (MOD_TYPE_IT|MOD_TYPE_MPT)));
GetDlgItem(IDC_CHECK1 + ichn * 2)->EnableWindow(bEnable);
GetDlgItem(IDC_CHECK2 + ichn * 2)->EnableWindow(bIT);
m_sbVolume[ichn].EnableWindow(bIT);
m_spinVolume[ichn].EnableWindow(bIT);
m_sbPan[ichn].EnableWindow(bEnable && !(sndFile.GetType() & (MOD_TYPE_XM|MOD_TYPE_MOD)));
m_spinPan[ichn].EnableWindow(bEnable && !(sndFile.GetType() & (MOD_TYPE_XM|MOD_TYPE_MOD)));
GetDlgItem(IDC_EDIT1 + ichn * 2)->EnableWindow(bIT); // channel vol
GetDlgItem(IDC_EDIT2 + ichn * 2)->EnableWindow(bEnable && !(sndFile.GetType() & (MOD_TYPE_XM|MOD_TYPE_MOD))); // channel pan
GetDlgItem(IDC_EDIT9 + ichn)->EnableWindow(((bEnable) && (sndFile.m_nType & (MOD_TYPE_XM|MOD_TYPE_IT|MOD_TYPE_MPT)))); // channel name
m_CbnEffects[ichn].EnableWindow(bEnable & (sndFile.GetModSpecifications().supportsPlugins ? TRUE : FALSE));
}
UnlockControls();
}
// Update plugin names
if(genHint.GetType()[HINT_MODTYPE | HINT_MODCHANNELS] || plugHint.GetType()[HINT_PLUGINNAMES])
{
PopulateChannelPlugins(plugHint.GetPlugin() ? plugHint.GetPlugin() - 1 : PLUGINDEX_INVALID);
SetDlgItemText(IDC_EDIT13, mpt::ToCString(sndFile.m_MixPlugins[m_nCurrentPlugin].GetName()));
}
// Update plugin info
const SNDMIXPLUGIN &plugin = sndFile.m_MixPlugins[m_nCurrentPlugin];
const bool updatePlug = (plugHint.GetPlugin() == 0 || plugHint.GetPlugin() == m_nCurrentPlugin + 1);
const bool updateWholePluginView = updateAll || (plugHint.GetType()[HINT_MIXPLUGINS] && updatePlug);
if(updateWholePluginView)
{
const PLUGINDEX plugIndex = (plugHint.GetPlugin() != 0) ? plugHint.GetPlugin() - 1 : PLUGINDEX_INVALID;
m_CbnPlugin.SetRedraw(FALSE);
if(plugIndex == PLUGINDEX_INVALID)
m_CbnPlugin.ResetContent();
AddPluginNamesToCombobox(m_CbnPlugin, sndFile.m_MixPlugins, true, plugIndex);
m_CbnPlugin.SetRedraw(TRUE);
m_CbnPlugin.SetCurSel(m_nCurrentPlugin);
if (m_nCurrentPlugin >= MAX_MIXPLUGINS) m_nCurrentPlugin = 0;
SetDlgItemText(IDC_EDIT13, mpt::ToCString(plugin.GetName()));
CheckDlgButton(IDC_CHECK9, plugin.IsMasterEffect() ? BST_CHECKED : BST_UNCHECKED);
CheckDlgButton(IDC_CHECK10, plugin.IsBypassed() ? BST_CHECKED : BST_UNCHECKED);
CheckDlgButton(IDC_CHECK11, plugin.IsWetMix() ? BST_CHECKED : BST_UNCHECKED);
IMixPlugin *pPlugin = plugin.pMixPlugin;
m_BtnEdit.EnableWindow((pPlugin != nullptr && (pPlugin->HasEditor() || pPlugin->GetNumParameters())) ? TRUE : FALSE);
GetDlgItem(IDC_MOVEFXSLOT)->EnableWindow((pPlugin) ? TRUE : FALSE);
GetDlgItem(IDC_INSERTFXSLOT)->EnableWindow((pPlugin) ? TRUE : FALSE);
GetDlgItem(IDC_CLONEPLUG)->EnableWindow((pPlugin) ? TRUE : FALSE);
UpdateDryWetDisplay();
if(pPlugin && pPlugin->IsInstrument())
{
m_CbnSpecialMixProcessing.EnableWindow(FALSE);
GetDlgItem(IDC_CHECK12)->EnableWindow(FALSE);
} else
{
m_CbnSpecialMixProcessing.EnableWindow(TRUE);
GetDlgItem(IDC_CHECK12)->EnableWindow(TRUE);
m_CbnSpecialMixProcessing.SetCurSel(plugin.GetMixMode());
CheckDlgButton(IDC_CHECK12, plugin.IsExpandedMix() ? BST_CHECKED : BST_UNCHECKED);
}
int gain = plugin.GetGain();
if(gain == 0) gain = 10;
float value = 0.1f * (float)gain;
s.Format(_T("Gain: x%1.1f"), value);
SetDlgItemText(IDC_EDIT16, s);
m_SpinMixGain.SetPos(gain);
if (pPlugin)
{
const PlugParamIndex nParams = pPlugin->GetNumParameters();
m_CbnParam.SetRedraw(FALSE);
m_CbnParam.ResetContent();
if (m_nCurrentParam >= nParams) m_nCurrentParam = 0;
if(nParams)
{
m_CbnParam.SetItemData(m_CbnParam.AddString(pPlugin->GetFormattedParamName(m_nCurrentParam)), m_nCurrentParam);
}
m_CbnParam.SetCurSel(0);
m_CbnParam.SetRedraw(TRUE);
OnParamChanged();
// Input / Output type
int in = pPlugin->GetNumInputChannels(), out = pPlugin->GetNumOutputChannels();
if (in < 1) s = _T("No input");
else if (in == 1) s = _T("Mono-In");
else s = _T("Stereo-In");
s += _T(", ");
if (out < 1) s += _T("No output");
else if (out == 1) s += _T("Mono-Out");
else s += _T("Stereo-Out");
// For now, only display the "current" preset.
// This prevents the program from hanging when switching between plugin slots or
// switching to the general tab and the first plugin in the list has a lot of presets.
// Some plugins like Synth1 have so many presets that this *does* indeed make a difference,
// even on fairly modern CPUs. The rest of the presets are just added when the combo box
// gets the focus, i.e. just when they're needed.
int32 currentProg = pPlugin->GetCurrentProgram();
FillPluginProgramBox(currentProg, currentProg);
m_CbnPreset.SetCurSel(0);
m_sbValue.EnableWindow(TRUE);
m_sbDryRatio.EnableWindow(TRUE);
GetDlgItem(IDC_EDIT14)->EnableWindow(TRUE);
} else
{
s.Empty();
if (m_CbnParam.GetCount() > 0) m_CbnParam.ResetContent();
m_nCurrentParam = 0;
m_CbnPreset.SetRedraw(FALSE);
m_CbnPreset.ResetContent();
m_CbnPreset.SetItemData(m_CbnPreset.AddString(_T("none")), 0);
m_CbnPreset.SetRedraw(TRUE);
m_CbnPreset.SetCurSel(0);
m_sbValue.EnableWindow(FALSE);
m_sbDryRatio.EnableWindow(FALSE);
GetDlgItem(IDC_EDIT14)->EnableWindow(FALSE);
}
SetDlgItemText(IDC_TEXT6, s);
}
if(updateWholePluginView || plugHint.GetPlugin() > m_nCurrentPlugin)
{
int insertAt = 1;
m_CbnOutput.SetRedraw(FALSE);
if(updateWholePluginView)
{
m_CbnOutput.ResetContent();
m_CbnOutput.SetItemData(m_CbnOutput.AddString(_T("Default")), 0);
for(PLUGINDEX i = m_nCurrentPlugin + 1; i < MAX_MIXPLUGINS; i++)
{
if(!sndFile.m_MixPlugins[i].IsValidPlugin())
{
m_CbnOutput.SetItemData(m_CbnOutput.AddString(_T("New Plugin...")), 1);
insertAt = 2;
break;
}
}
} else
{
const DWORD_PTR changedPlugin = 0x80 + (plugHint.GetPlugin() - 1);
const int items = m_CbnOutput.GetCount();
for(insertAt = 1; insertAt < items; insertAt++)
{
DWORD_PTR thisPlugin = m_CbnOutput.GetItemData(insertAt);
if(thisPlugin == changedPlugin)
m_CbnOutput.DeleteString(insertAt);
if(thisPlugin >= changedPlugin)
break;
}
}
int outputSel = plugin.IsOutputToMaster() ? 0 : -1;
for(PLUGINDEX iOut = m_nCurrentPlugin + 1; iOut < MAX_MIXPLUGINS; iOut++)
{
if(!updateWholePluginView && (iOut + 1) != plugHint.GetPlugin())
continue;
const SNDMIXPLUGIN &outPlug = sndFile.m_MixPlugins[iOut];
if(outPlug.IsValidPlugin())
{
const auto name = outPlug.GetName(), libName = outPlug.GetLibraryName();
s.Format(_T("FX%d: "), iOut + 1);
s += mpt::ToCString(name.empty() ? libName : name);
if(!name.empty() && libName != name)
{
s += _T(" (");
s += mpt::ToCString(libName);
s += _T(")");
}
insertAt = m_CbnOutput.InsertString(insertAt, s);
m_CbnOutput.SetItemData(insertAt, 0x80 + iOut);
if(!plugin.IsOutputToMaster() && (plugin.GetOutputPlugin() == iOut))
{
outputSel = insertAt;
}
insertAt++;
}
}
m_CbnOutput.SetRedraw(TRUE);
if(outputSel >= 0)
m_CbnOutput.SetCurSel(outputSel);
}
if(plugHint.GetType()[HINT_PLUGINPARAM] && updatePlug)
{
OnParamChanged();
}
m_CbnPlugin.Invalidate(FALSE);
m_CbnParam.Invalidate(FALSE);
m_CbnPreset.Invalidate(FALSE);
m_CbnSpecialMixProcessing.Invalidate(FALSE);
m_CbnOutput.Invalidate(FALSE);
}
void CViewGlobals::PopulateChannelPlugins(PLUGINDEX plugin)
{
// Channel effect lists
const CSoundFile &sndFile = GetDocument()->GetSoundFile();
CString s;
for(CHANNELINDEX ichn = 0; ichn < CHANNELS_IN_TAB; ichn++)
{
const CHANNELINDEX nChn = m_nActiveTab * CHANNELS_IN_TAB + ichn;
auto &comboBox = m_CbnEffects[ichn];
if(nChn < MAX_BASECHANNELS)
{
comboBox.SetRedraw(FALSE);
int insertAt = 1;
if(plugin == PLUGINDEX_INVALID)
{
comboBox.ResetContent();
comboBox.SetItemData(comboBox.AddString(_T("No plugin")), 0);
} else
{
const int items = comboBox.GetCount();
for(insertAt = 1; insertAt < items; insertAt++)
{
auto thisPlugin = static_cast<PLUGINDEX>(comboBox.GetItemData(insertAt));
if(thisPlugin == (plugin + 1))
comboBox.DeleteString(insertAt);
if(thisPlugin >= (plugin + 1))
break;
}
}
int fxsel = 0;
for(PLUGINDEX ifx = 0; ifx < MAX_MIXPLUGINS; ifx++)
{
if(plugin != PLUGINDEX_INVALID && ifx != plugin)
continue;
if(sndFile.m_MixPlugins[ifx].IsValidPlugin()
|| (sndFile.m_MixPlugins[ifx].GetName() != U_(""))
|| (sndFile.ChnSettings[nChn].nMixPlugin == ifx + 1))
{
s = MPT_CFORMAT("FX{}: ")(ifx + 1);
s += mpt::ToCString(sndFile.m_MixPlugins[ifx].GetName());
insertAt = comboBox.InsertString(insertAt, s);
comboBox.SetItemData(insertAt, ifx + 1);
if(sndFile.ChnSettings[nChn].nMixPlugin == ifx + 1)
fxsel = insertAt;
insertAt++;
}
}
comboBox.SetRedraw(TRUE);
if(plugin == PLUGINDEX_INVALID || fxsel > 0)
comboBox.SetCurSel(fxsel);
comboBox.Invalidate(FALSE);
}
}
}
IMixPlugin *CViewGlobals::GetCurrentPlugin() const
{
if(GetDocument() == nullptr || m_nCurrentPlugin >= MAX_MIXPLUGINS)
{
return nullptr;
}
return GetDocument()->GetSoundFile().m_MixPlugins[m_nCurrentPlugin].pMixPlugin;
}
void CViewGlobals::OnTabSelchange(NMHDR*, LRESULT* pResult)
{
UpdateView(GeneralHint().Channels());
if (pResult) *pResult = 0;
}
void CViewGlobals::OnUpdateUndo(CCmdUI *pCmdUI)
{
CModDoc *pModDoc = GetDocument();
if((pCmdUI) && (pModDoc))
{
pCmdUI->Enable(pModDoc->GetPatternUndo().CanUndoChannelSettings());
pCmdUI->SetText(CMainFrame::GetInputHandler()->GetKeyTextFromCommand(kcEditUndo, _T("Undo ") + pModDoc->GetPatternUndo().GetUndoName()));
}
}
void CViewGlobals::OnUpdateRedo(CCmdUI *pCmdUI)
{
CModDoc *pModDoc = GetDocument();
if((pCmdUI) && (pModDoc))
{
pCmdUI->Enable(pModDoc->GetPatternUndo().CanRedoChannelSettings());
pCmdUI->SetText(CMainFrame::GetInputHandler()->GetKeyTextFromCommand(kcEditRedo, _T("Redo ") + pModDoc->GetPatternUndo().GetRedoName()));
}
}
void CViewGlobals::OnEditUndo()
{
UndoRedo(true);
}
void CViewGlobals::OnEditRedo()
{
UndoRedo(false);
}
void CViewGlobals::UndoRedo(bool undo)
{
CModDoc *pModDoc = GetDocument();
if(!pModDoc)
return;
if(undo && pModDoc->GetPatternUndo().CanUndoChannelSettings())
pModDoc->GetPatternUndo().Undo();
else if(!undo && pModDoc->GetPatternUndo().CanRedoChannelSettings())
pModDoc->GetPatternUndo().Redo();
}
void CViewGlobals::PrepareUndo(CHANNELINDEX chnMod4)
{
if(m_lastEdit != chnMod4)
{
// Backup old channel settings through pattern undo.
m_lastEdit = chnMod4;
const CHANNELINDEX chn = static_cast<CHANNELINDEX>(m_nActiveTab * CHANNELS_IN_TAB) + chnMod4;
GetDocument()->GetPatternUndo().PrepareChannelUndo(chn, 1, "Channel Settings");
}
}
void CViewGlobals::OnEditColor(const CHANNELINDEX chnMod4)
{
auto *modDoc = GetDocument();
auto &sndFile = modDoc->GetSoundFile();
const CHANNELINDEX chn = static_cast<CHANNELINDEX>(m_nActiveTab * CHANNELS_IN_TAB) + chnMod4;
if(auto color = m_channelColor[chnMod4].PickColor(sndFile, chn); color.has_value())
{
PrepareUndo(chnMod4);
sndFile.ChnSettings[chn].color = *color;
if(modDoc->SupportsChannelColors())
modDoc->SetModified();
modDoc->UpdateAllViews(nullptr, GeneralHint(chn).Channels());
}
}
void CViewGlobals::OnEditColor1() { OnEditColor(0); }
void CViewGlobals::OnEditColor2() { OnEditColor(1); }
void CViewGlobals::OnEditColor3() { OnEditColor(2); }
void CViewGlobals::OnEditColor4() { OnEditColor(3); }
void CViewGlobals::OnMute(const CHANNELINDEX chnMod4, const UINT itemID)
{
CModDoc *pModDoc = GetDocument();
if (pModDoc)
{
const bool b = (IsDlgButtonChecked(itemID) != FALSE);
const CHANNELINDEX nChn = (CHANNELINDEX)(m_nActiveTab * CHANNELS_IN_TAB) + chnMod4;
pModDoc->MuteChannel(nChn, b);
pModDoc->UpdateAllViews(this, GeneralHint(nChn).Channels());
}
}
void CViewGlobals::OnMute1() {OnMute(0, IDC_CHECK1);}
void CViewGlobals::OnMute2() {OnMute(1, IDC_CHECK3);}
void CViewGlobals::OnMute3() {OnMute(2, IDC_CHECK5);}
void CViewGlobals::OnMute4() {OnMute(3, IDC_CHECK7);}
void CViewGlobals::OnSurround(const CHANNELINDEX chnMod4, const UINT itemID)
{
CModDoc *pModDoc = GetDocument();
if (pModDoc)
{
PrepareUndo(chnMod4);
const bool b = (IsDlgButtonChecked(itemID) != FALSE);
const CHANNELINDEX nChn = (CHANNELINDEX)(m_nActiveTab * CHANNELS_IN_TAB) + chnMod4;
pModDoc->SurroundChannel(nChn, b);
pModDoc->UpdateAllViews(nullptr, GeneralHint(nChn).Channels());
}
}
void CViewGlobals::OnSurround1() {OnSurround(0, IDC_CHECK2);}
void CViewGlobals::OnSurround2() {OnSurround(1, IDC_CHECK4);}
void CViewGlobals::OnSurround3() {OnSurround(2, IDC_CHECK6);}
void CViewGlobals::OnSurround4() {OnSurround(3, IDC_CHECK8);}
void CViewGlobals::OnEditVol(const CHANNELINDEX chnMod4, const UINT itemID)
{
CModDoc *pModDoc = GetDocument();
const CHANNELINDEX nChn = (CHANNELINDEX)(m_nActiveTab * CHANNELS_IN_TAB) + chnMod4;
const int vol = GetDlgItemIntEx(itemID);
if ((pModDoc) && (vol >= 0) && (vol <= 64) && (!m_nLockCount))
{
PrepareUndo(chnMod4);
if (pModDoc->SetChannelGlobalVolume(nChn, static_cast<uint16>(vol)))
{
m_sbVolume[chnMod4].SetPos(vol);
pModDoc->UpdateAllViews(this, GeneralHint(nChn).Channels());
}
}
}
void CViewGlobals::OnEditVol1() {OnEditVol(0, IDC_EDIT1);}
void CViewGlobals::OnEditVol2() {OnEditVol(1, IDC_EDIT3);}
void CViewGlobals::OnEditVol3() {OnEditVol(2, IDC_EDIT5);}
void CViewGlobals::OnEditVol4() {OnEditVol(3, IDC_EDIT7);}
void CViewGlobals::OnEditPan(const CHANNELINDEX chnMod4, const UINT itemID)
{
CModDoc *pModDoc = GetDocument();
const CHANNELINDEX nChn = (CHANNELINDEX)(m_nActiveTab * CHANNELS_IN_TAB) + chnMod4;
const int pan = GetDlgItemIntEx(itemID);
if ((pModDoc) && (pan >= 0) && (pan <= 256) && (!m_nLockCount))
{
PrepareUndo(chnMod4);
if (pModDoc->SetChannelDefaultPan(nChn, static_cast<uint16>(pan)))
{
m_sbPan[chnMod4].SetPos(pan / 4);
pModDoc->UpdateAllViews(this, GeneralHint(nChn).Channels());
// Surround is forced off when changing pan, so uncheck the checkbox.
CheckDlgButton(IDC_CHECK2 + chnMod4 * 2, BST_UNCHECKED);
}
}
}
void CViewGlobals::OnEditPan1() {OnEditPan(0, IDC_EDIT2);}
void CViewGlobals::OnEditPan2() {OnEditPan(1, IDC_EDIT4);}
void CViewGlobals::OnEditPan3() {OnEditPan(2, IDC_EDIT6);}
void CViewGlobals::OnEditPan4() {OnEditPan(3, IDC_EDIT8);}
void CViewGlobals::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
CModDoc *pModDoc;
CHANNELINDEX nChn;
CFormView::OnHScroll(nSBCode, nPos, pScrollBar);
pModDoc = GetDocument();
nChn = (CHANNELINDEX)(m_nActiveTab * CHANNELS_IN_TAB);
if ((pModDoc) && (!IsLocked()) && (nChn < MAX_BASECHANNELS))
{
BOOL bUpdate = FALSE;
short int pos;
LockControls();
const CHANNELINDEX nLoopLimit = std::min(static_cast<CHANNELINDEX>(CHANNELS_IN_TAB), static_cast<CHANNELINDEX>(pModDoc->GetSoundFile().GetNumChannels() - nChn));
for (CHANNELINDEX iCh = 0; iCh < nLoopLimit; iCh++)
{
if(pScrollBar == (CScrollBar *) &m_sbVolume[iCh])
{
// Volume sliders
pos = (short int)m_sbVolume[iCh].GetPos();
if ((pos >= 0) && (pos <= 64))
{
PrepareUndo(iCh);
if (pModDoc->SetChannelGlobalVolume(nChn + iCh, pos))
{
SetDlgItemInt(IDC_EDIT1 + iCh * 2, pos);
bUpdate = TRUE;
}
}
} else if(pScrollBar == (CScrollBar *) &m_sbPan[iCh])
{
// Pan sliders
pos = (short int)m_sbPan[iCh].GetPos();
if(pos >= 0 && pos <= 64 && (static_cast<uint16>(pos) != pModDoc->GetSoundFile().ChnSettings[nChn+iCh].nPan / 4u))
{
PrepareUndo(iCh);
if (pModDoc->SetChannelDefaultPan(nChn + iCh, pos * 4))
{
SetDlgItemInt(IDC_EDIT2 + iCh * 2, pos * 4);
CheckDlgButton(IDC_CHECK2 + iCh * 2, BST_UNCHECKED);
bUpdate = TRUE;
}
}
}
}
if ((pScrollBar) && (pScrollBar->m_hWnd == m_sbDryRatio.m_hWnd))
{
int n = 100 - m_sbDryRatio.GetPos();
if ((n >= 0) && (n <= 100) && (m_nCurrentPlugin < MAX_MIXPLUGINS))
{
SNDMIXPLUGIN &plugin = pModDoc->GetSoundFile().m_MixPlugins[m_nCurrentPlugin];
if(plugin.pMixPlugin)
{
plugin.fDryRatio = static_cast<float>(n) / 100.0f;
SetPluginModified();
}
UpdateDryWetDisplay();
}
}
if (bUpdate) pModDoc->UpdateAllViews(this, GeneralHint(nChn).Channels());
UnlockControls();
if ((pScrollBar) && (pScrollBar->m_hWnd == m_sbValue.m_hWnd))
{
int n = (short int)m_sbValue.GetPos();
if ((n >= 0) && (n <= 100) && (m_nCurrentPlugin < MAX_MIXPLUGINS))
{
IMixPlugin *pPlugin = GetCurrentPlugin();
if(pPlugin != nullptr)
{
const PlugParamIndex nParams = pPlugin->GetNumParameters();
if(m_nCurrentParam < nParams)
{
if (nSBCode == SB_THUMBPOSITION || nSBCode == SB_THUMBTRACK || nSBCode == SB_ENDSCROLL)
{
pPlugin->SetScaledUIParam(m_nCurrentParam, 0.01f * n);
OnParamChanged();
SetPluginModified();
}
}
}
}
}
}
}
void CViewGlobals::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
CModDoc *pModDoc = GetDocument();
if((m_nCurrentPlugin >= MAX_MIXPLUGINS) || (!pModDoc)) return;
CSoundFile &sndFile = pModDoc->GetSoundFile();
TCHAR s[32];
if(nSBCode != SB_ENDSCROLL && pScrollBar && pScrollBar == (CScrollBar*)&m_SpinMixGain)
{
SNDMIXPLUGIN &plugin = sndFile.m_MixPlugins[m_nCurrentPlugin];
if(plugin.pMixPlugin)
{
uint8 gain = (uint8)nPos;
if(gain == 0) gain = 1;
plugin.SetGain(gain);
float fValue = 0.1f * (float)gain;
_stprintf(s, _T("Gain: x%1.1f"), fValue);
CEdit *gainEdit = (CEdit *)GetDlgItem(IDC_EDIT16);
gainEdit->SetWindowText(s);
SetPluginModified();
}
}
CFormView::OnVScroll(nSBCode, nPos, pScrollBar);
}
void CViewGlobals::OnEditName(const CHANNELINDEX chnMod4, const UINT itemID)
{
CModDoc *pModDoc = GetDocument();
if ((pModDoc) && (!m_nLockCount))
{
CSoundFile &sndFile = pModDoc->GetSoundFile();
const CHANNELINDEX nChn = m_nActiveTab * CHANNELS_IN_TAB + chnMod4;
CString tmp;
GetDlgItemText(itemID, tmp);
const std::string s = mpt::ToCharset(sndFile.GetCharsetInternal(), tmp);
if ((sndFile.GetType() & (MOD_TYPE_XM|MOD_TYPE_IT|MOD_TYPE_MPT)) && (nChn < sndFile.GetNumChannels()) && (s != sndFile.ChnSettings[nChn].szName))
{
PrepareUndo(chnMod4);
sndFile.ChnSettings[nChn].szName = s;
pModDoc->SetModified();
pModDoc->UpdateAllViews(this, GeneralHint(nChn).Channels());
}
}
}
void CViewGlobals::OnEditName1() {OnEditName(0, IDC_EDIT9);}
void CViewGlobals::OnEditName2() {OnEditName(1, IDC_EDIT10);}
void CViewGlobals::OnEditName3() {OnEditName(2, IDC_EDIT11);}
void CViewGlobals::OnEditName4() {OnEditName(3, IDC_EDIT12);}
void CViewGlobals::OnFxChanged(const CHANNELINDEX chnMod4)
{
CModDoc *pModDoc = GetDocument();
if (pModDoc)
{
CSoundFile &sndFile = pModDoc->GetSoundFile();
CHANNELINDEX nChn = m_nActiveTab * CHANNELS_IN_TAB + chnMod4;
int nfx = static_cast<int>(m_CbnEffects[chnMod4].GetItemData(m_CbnEffects[chnMod4].GetCurSel()));
if ((nfx >= 0) && (nfx <= MAX_MIXPLUGINS) && (nChn < sndFile.GetNumChannels())
&& (sndFile.ChnSettings[nChn].nMixPlugin != (UINT)nfx))
{
PrepareUndo(chnMod4);
sndFile.ChnSettings[nChn].nMixPlugin = (PLUGINDEX)nfx;
if(sndFile.GetModSpecifications().supportsPlugins)
pModDoc->SetModified();
pModDoc->UpdateAllViews(this, GeneralHint(nChn).Channels());
}
}
}
void CViewGlobals::OnFx1Changed() {OnFxChanged(0);}
void CViewGlobals::OnFx2Changed() {OnFxChanged(1);}
void CViewGlobals::OnFx3Changed() {OnFxChanged(2);}
void CViewGlobals::OnFx4Changed() {OnFxChanged(3);}
void CViewGlobals::OnPluginNameChanged()
{
CModDoc *pModDoc = GetDocument();
if ((pModDoc) && (m_nCurrentPlugin < MAX_MIXPLUGINS))
{
CSoundFile &sndFile = pModDoc->GetSoundFile();
SNDMIXPLUGIN &plugin = sndFile.m_MixPlugins[m_nCurrentPlugin];
CString s;
GetDlgItemText(IDC_EDIT13, s);
if (s != mpt::ToCString(plugin.GetName()))
{
plugin.Info.szName = mpt::ToCharset(mpt::Charset::Locale, s);
if(sndFile.GetModSpecifications().supportsPlugins)
pModDoc->SetModified();
pModDoc->UpdateAllViews(this, PluginHint(m_nCurrentPlugin + 1).Info().Names(), this);
IMixPlugin *pPlugin = plugin.pMixPlugin;
if(pPlugin != nullptr && pPlugin->GetEditor() != nullptr)
{
pPlugin->GetEditor()->SetTitle();
}
// Update channel plugin assignments
PopulateChannelPlugins(m_nCurrentPlugin);
m_CbnPlugin.SetRedraw(FALSE);
AddPluginNamesToCombobox(m_CbnPlugin, sndFile.m_MixPlugins, true, m_nCurrentPlugin);
m_CbnPlugin.SetCurSel(m_nCurrentPlugin);
m_CbnPlugin.Invalidate(FALSE);
m_CbnPlugin.SetRedraw(TRUE);
}
}
}
void CViewGlobals::OnPrevPlugin()
{
CModDoc *pModDoc = GetDocument();
if ((m_nCurrentPlugin > 0) && (pModDoc))
{
m_nCurrentPlugin--;
UpdateView(PluginHint(m_nCurrentPlugin + 1).Info());
}
}
void CViewGlobals::OnNextPlugin()
{
CModDoc *pModDoc = GetDocument();
if ((m_nCurrentPlugin < MAX_MIXPLUGINS-1) && (pModDoc))
{
m_nCurrentPlugin++;
UpdateView(PluginHint(m_nCurrentPlugin + 1).Info());
}
}
void CViewGlobals::OnPluginChanged()
{
CModDoc *pModDoc = GetDocument();
int nPlugin = m_CbnPlugin.GetCurSel();
if ((pModDoc) && (nPlugin >= 0) && (nPlugin < MAX_MIXPLUGINS))
{
m_nCurrentPlugin = (PLUGINDEX)nPlugin;
UpdateView(PluginHint(m_nCurrentPlugin + 1).Info());
}
m_CbnPreset.SetCurSel(0);
}
void CViewGlobals::OnSelectPlugin()
{
#ifndef NO_PLUGINS
CModDoc *pModDoc = GetDocument();
if ((pModDoc) && (m_nCurrentPlugin < MAX_MIXPLUGINS))
{
CSelectPluginDlg dlg(pModDoc, m_nCurrentPlugin, this);
if (dlg.DoModal() == IDOK)
{
if(pModDoc->GetSoundFile().GetModSpecifications().supportsPlugins)
pModDoc->SetModified();
}
OnPluginChanged();
OnParamChanged();
}
#endif // NO_PLUGINS
}
void CViewGlobals::OnRemovePlugin()
{
#ifndef NO_PLUGINS
CModDoc *pModDoc = GetDocument();
if(pModDoc && m_nCurrentPlugin < MAX_MIXPLUGINS && Reporting::Confirm(MPT_UFORMAT("Remove plugin FX{}: {}?")(m_nCurrentPlugin + 1, pModDoc->GetSoundFile().m_MixPlugins[m_nCurrentPlugin].GetName()), false, true) == cnfYes)
{
if(pModDoc->RemovePlugin(m_nCurrentPlugin))
{
OnPluginChanged();
OnParamChanged();
}
}
#endif // NO_PLUGINS
}
LRESULT CViewGlobals::OnParamAutomated(WPARAM plugin, LPARAM param)
{
if(plugin == m_nCurrentPlugin && static_cast<PlugParamIndex>(param) == m_nCurrentParam)
{
OnParamChanged();
}
return 0;
}
LRESULT CViewGlobals::OnDryWetRatioChangedFromPlayer(WPARAM plugin, LPARAM)
{
if(plugin == m_nCurrentPlugin)
{
UpdateDryWetDisplay();
}
return 0;
}
void CViewGlobals::OnParamChanged()
{
PlugParamIndex cursel = static_cast<PlugParamIndex>(m_CbnParam.GetItemData(m_CbnParam.GetCurSel()));
IMixPlugin *pPlugin = GetCurrentPlugin();
if(pPlugin != nullptr && cursel != static_cast<PlugParamIndex>(CB_ERR))
{
const PlugParamIndex nParams = pPlugin->GetNumParameters();
if(cursel < nParams) m_nCurrentParam = cursel;
if(m_nCurrentParam < nParams)
{
const auto value = pPlugin->GetScaledUIParam(m_nCurrentParam);
int intValue = mpt::saturate_round<int>(value * 100.0f);
LockControls();
if(GetFocus() != GetDlgItem(IDC_EDIT14))
{
CString s = pPlugin->GetFormattedParamValue(m_nCurrentParam).Trim();
if(s.IsEmpty())
{
s.Format(_T("%f"), value);
}
SetDlgItemText(IDC_EDIT14, s);
}
m_sbValue.SetPos(intValue);
UnlockControls();
return;
}
}
SetDlgItemText(IDC_EDIT14, _T(""));
m_sbValue.SetPos(0);
}
// When focussing the parameter value, show its real value to edit
void CViewGlobals::OnFocusParam()
{
IMixPlugin *pPlugin = GetCurrentPlugin();
if(pPlugin != nullptr)
{
const PlugParamIndex nParams = pPlugin->GetNumParameters();
if(m_nCurrentParam < nParams)
{
TCHAR s[32];
float fValue = pPlugin->GetScaledUIParam(m_nCurrentParam);
_stprintf(s, _T("%f"), fValue);
LockControls();
SetDlgItemText(IDC_EDIT14, s);
UnlockControls();
}
}
}
void CViewGlobals::SetPluginModified()
{
CModDoc *pModDoc = GetDocument();
if(pModDoc->GetSoundFile().GetModSpecifications().supportsPlugins)
pModDoc->SetModified();
pModDoc->UpdateAllViews(this, PluginHint(m_nCurrentPlugin + 1).Info());
}
void CViewGlobals::OnProgramChanged()
{
int32 curProg = static_cast<int32>(m_CbnPreset.GetItemData(m_CbnPreset.GetCurSel()));
IMixPlugin *pPlugin = GetCurrentPlugin();
if(pPlugin != nullptr)
{
const int32 numProgs = pPlugin->GetNumPrograms();
if(curProg <= numProgs)
{
pPlugin->SetCurrentProgram(curProg);
// Update parameter display
OnParamChanged();
SetPluginModified();
}
}
}
void CViewGlobals::OnLoadParam()
{
IMixPlugin *pPlugin = GetCurrentPlugin();
if(pPlugin != nullptr && pPlugin->LoadProgram())
{
int32 currentProg = pPlugin->GetCurrentProgram();
FillPluginProgramBox(currentProg, currentProg);
m_CbnPreset.SetCurSel(0);
SetPluginModified();
}
}
void CViewGlobals::OnSaveParam()
{
IMixPlugin *pPlugin = GetCurrentPlugin();
if(pPlugin != nullptr)
{
pPlugin->SaveProgram();
}
}
void CViewGlobals::OnSetParameter()
{
if(m_nCurrentPlugin >= MAX_MIXPLUGINS || IsLocked()) return;
IMixPlugin *pPlugin = GetCurrentPlugin();
if(pPlugin != nullptr)
{
const PlugParamIndex nParams = pPlugin->GetNumParameters();
TCHAR s[32];
GetDlgItemText(IDC_EDIT14, s, mpt::saturate_cast<int>(std::size(s)));
if ((m_nCurrentParam < nParams) && (s[0]))
{
float fValue = (float)_tstof(s);
pPlugin->SetScaledUIParam(m_nCurrentParam, fValue);
OnParamChanged();
SetPluginModified();
}
}
}
void CViewGlobals::UpdateDryWetDisplay()
{
SNDMIXPLUGIN &plugin = GetDocument()->GetSoundFile().m_MixPlugins[m_nCurrentPlugin];
float wetRatio = 1.0f - plugin.fDryRatio, dryRatio = plugin.fDryRatio;
m_sbDryRatio.SetPos(mpt::saturate_round<int>(wetRatio * 100));
if(plugin.IsExpandedMix())
{
wetRatio = 2.0f * wetRatio - 1.0f;
dryRatio = -wetRatio;
}
int wetInt = mpt::saturate_round<int>(wetRatio * 100), dryInt = mpt::saturate_round<int>(dryRatio * 100);
SetDlgItemText(IDC_STATIC8, MPT_TFORMAT("{}% wet, {}% dry")(wetInt, dryInt).c_str());
}
void CViewGlobals::OnMixModeChanged()
{
CModDoc *pModDoc = GetDocument();
if ((m_nCurrentPlugin >= MAX_MIXPLUGINS) || (!pModDoc)) return;
pModDoc->GetSoundFile().m_MixPlugins[m_nCurrentPlugin].SetMasterEffect(IsDlgButtonChecked(IDC_CHECK9) != BST_UNCHECKED);
SetPluginModified();
}
void CViewGlobals::OnBypassChanged()
{
CModDoc *pModDoc = GetDocument();
if ((m_nCurrentPlugin >= MAX_MIXPLUGINS) || (!pModDoc)) return;
pModDoc->GetSoundFile().m_MixPlugins[m_nCurrentPlugin].SetBypass(IsDlgButtonChecked(IDC_CHECK10) != BST_UNCHECKED);
SetPluginModified();
}
void CViewGlobals::OnWetDryExpandChanged()
{
CModDoc *pModDoc = GetDocument();
if ((m_nCurrentPlugin >= MAX_MIXPLUGINS) || (!pModDoc)) return;
pModDoc->GetSoundFile().m_MixPlugins[m_nCurrentPlugin].SetExpandedMix(IsDlgButtonChecked(IDC_CHECK12) != BST_UNCHECKED);
UpdateDryWetDisplay();
SetPluginModified();
}
void CViewGlobals::OnSpecialMixProcessingChanged()
{
CModDoc *pModDoc = GetDocument();
if ((m_nCurrentPlugin >= MAX_MIXPLUGINS) || (!pModDoc)) return;
pModDoc->GetSoundFile().m_MixPlugins[m_nCurrentPlugin].SetMixMode((uint8)m_CbnSpecialMixProcessing.GetCurSel());
SetPluginModified();
}
void CViewGlobals::OnDryMixChanged()
{
CModDoc *pModDoc = GetDocument();
if ((m_nCurrentPlugin >= MAX_MIXPLUGINS) || (!pModDoc)) return;
pModDoc->GetSoundFile().m_MixPlugins[m_nCurrentPlugin].SetWetMix(IsDlgButtonChecked(IDC_CHECK11) != BST_UNCHECKED);
SetPluginModified();
}
void CViewGlobals::OnEditPlugin()
{
CModDoc *pModDoc = GetDocument();
if ((m_nCurrentPlugin >= MAX_MIXPLUGINS) || (!pModDoc)) return;
pModDoc->TogglePluginEditor(m_nCurrentPlugin, CMainFrame::GetInputHandler()->ShiftPressed());
return;
}
void CViewGlobals::OnOutputRoutingChanged()
{
CModDoc *pModDoc = GetDocument();
int nroute;
if ((m_nCurrentPlugin >= MAX_MIXPLUGINS) || (!pModDoc)) return;
CSoundFile &sndFile = pModDoc->GetSoundFile();
SNDMIXPLUGIN &plugin = sndFile.m_MixPlugins[m_nCurrentPlugin];
nroute = static_cast<int>(m_CbnOutput.GetItemData(m_CbnOutput.GetCurSel()));
if(nroute == 1)
{
// Add new plugin
for(PLUGINDEX i = m_nCurrentPlugin + 1; i < MAX_MIXPLUGINS; i++)
{
if(!sndFile.m_MixPlugins[i].IsValidPlugin())
{
CSelectPluginDlg dlg(pModDoc, i, this);
if(dlg.DoModal() != IDOK)
return;
plugin.SetOutputPlugin(i);
SetPluginModified();
nroute = 0x80 + i;
m_nCurrentPlugin = i;
m_CbnPlugin.SetCurSel(i);
OnPluginChanged();
break;
}
}
if(nroute == 1)
{
Reporting::Error("All following plugin slots are used.", "Unable to add new plugin");
return;
}
}
if(!nroute)
plugin.SetOutputToMaster();
else
plugin.SetOutputPlugin(static_cast<PLUGINDEX>(nroute - 0x80));
SetPluginModified();
}
LRESULT CViewGlobals::OnModViewMsg(WPARAM wParam, LPARAM /*lParam*/)
{
switch(wParam)
{
case VIEWMSG_SETFOCUS:
case VIEWMSG_SETACTIVE:
GetParentFrame()->SetActiveView(this);
SetFocus();
return 0;
default:
return 0;
}
}
void CViewGlobals::OnMovePlugToSlot()
{
if(GetCurrentPlugin() == nullptr)
{
return;
}
// If any plugin routes its output to the current plugin, we shouldn't try to move it before that plugin...
PLUGINDEX defaultIndex = 0;
CSoundFile &sndFile = GetDocument()->GetSoundFile();
for(PLUGINDEX i = 0; i < m_nCurrentPlugin; i++)
{
if(sndFile.m_MixPlugins[i].GetOutputPlugin() == m_nCurrentPlugin)
{
defaultIndex = i + 1;
}
}
std::vector<PLUGINDEX> emptySlots;
BuildEmptySlotList(emptySlots);
CMoveFXSlotDialog dlg(this, m_nCurrentPlugin, emptySlots, defaultIndex, false, !sndFile.m_MixPlugins[m_nCurrentPlugin].IsOutputToMaster());
if(dlg.DoModal() == IDOK)
{
size_t toIndex = dlg.GetSlotIndex();
do
{
const SNDMIXPLUGIN &curPlugin = sndFile.m_MixPlugins[m_nCurrentPlugin];
SNDMIXPLUGIN &newPlugin = sndFile.m_MixPlugins[emptySlots[toIndex]];
const PLUGINDEX nextPlugin = curPlugin.GetOutputPlugin();
MovePlug(m_nCurrentPlugin, emptySlots[toIndex]);
if(nextPlugin == PLUGINDEX_INVALID || toIndex == emptySlots.size() - 1)
{
break;
}
m_nCurrentPlugin = nextPlugin;
if(dlg.DoMoveChain())
{
toIndex++;
newPlugin.SetOutputPlugin(emptySlots[toIndex]);
}
} while(dlg.DoMoveChain());
m_CbnPlugin.SetCurSel(dlg.GetSlot());
OnPluginChanged();
GetDocument()->UpdateAllViews(nullptr, PluginHint().Names().Info());
}
}
// Functor for adjusting plug indexes in modcommands. Adjusts all instrument column values in
// range [m_nInstrMin, m_nInstrMax] by m_nDiff.
struct PlugIndexModifier
{
PlugIndexModifier(PLUGINDEX nMin, PLUGINDEX nMax, int nDiff) :
m_nDiff(nDiff), m_nInstrMin(nMin), m_nInstrMax(nMax) {}
void operator()(ModCommand& m)
{
if (m.IsInstrPlug() && m.instr >= m_nInstrMin && m.instr <= m_nInstrMax)
m.instr = (ModCommand::INSTR)((int)m.instr + m_nDiff);
}
int m_nDiff;
ModCommand::INSTR m_nInstrMin;
ModCommand::INSTR m_nInstrMax;
};
bool CViewGlobals::MovePlug(PLUGINDEX src, PLUGINDEX dest, bool bAdjustPat)
{
if (src == dest)
return false;
CModDoc *pModDoc = GetDocument();
CSoundFile &sndFile = pModDoc->GetSoundFile();
BeginWaitCursor();
CriticalSection cs;
// Move plug data
sndFile.m_MixPlugins[dest] = std::move(sndFile.m_MixPlugins[src]);
sndFile.m_MixPlugins[src] = SNDMIXPLUGIN();
// Prevent plug from pointing backwards.
if(!sndFile.m_MixPlugins[dest].IsOutputToMaster())
{
PLUGINDEX nOutput = sndFile.m_MixPlugins[dest].GetOutputPlugin();
if (nOutput <= dest && nOutput != PLUGINDEX_INVALID)
{
sndFile.m_MixPlugins[dest].SetOutputToMaster();
}
}
// Update current plug
IMixPlugin *pPlugin = sndFile.m_MixPlugins[dest].pMixPlugin;
if(pPlugin != nullptr)
{
pPlugin->SetSlot(dest);
if(pPlugin->GetEditor() != nullptr)
{
pPlugin->GetEditor()->SetTitle();
}
}
// Update all other plugs' outputs
for (PLUGINDEX nPlug = 0; nPlug < src; nPlug++)
{
if(!sndFile.m_MixPlugins[nPlug].IsOutputToMaster())
{
if(sndFile.m_MixPlugins[nPlug].GetOutputPlugin() == src)
{
sndFile.m_MixPlugins[nPlug].SetOutputPlugin(dest);
}
}
}
// Update channels
for (CHANNELINDEX nChn = 0; nChn < sndFile.GetNumChannels(); nChn++)
{
if (sndFile.ChnSettings[nChn].nMixPlugin == src + 1u)
{
sndFile.ChnSettings[nChn].nMixPlugin = dest + 1u;
}
}
// Update instruments
for (INSTRUMENTINDEX nIns = 1; nIns <= sndFile.GetNumInstruments(); nIns++)
{
if (sndFile.Instruments[nIns] && (sndFile.Instruments[nIns]->nMixPlug == src + 1))
{
sndFile.Instruments[nIns]->nMixPlug = dest + 1u;
}
}
// Update MODCOMMANDs so that they won't be referring to old indexes (e.g. with NOTE_PC).
if (bAdjustPat && sndFile.GetModSpecifications().HasNote(NOTE_PC))
sndFile.Patterns.ForEachModCommand(PlugIndexModifier(src + 1, src + 1, int(dest) - int(src)));
cs.Leave();
SetPluginModified();
EndWaitCursor();
return true;
}
void CViewGlobals::BuildEmptySlotList(std::vector<PLUGINDEX> &emptySlots)
{
const CSoundFile &sndFile = GetDocument()->GetSoundFile();
emptySlots.clear();
for(PLUGINDEX nSlot = 0; nSlot < MAX_MIXPLUGINS; nSlot++)
{
if(sndFile.m_MixPlugins[nSlot].pMixPlugin == nullptr)
{
emptySlots.push_back(nSlot);
}
}
return;
}
void CViewGlobals::OnInsertSlot()
{
CString prompt;
CSoundFile &sndFile = GetDocument()->GetSoundFile();
prompt.Format(_T("Insert empty slot before slot FX%d?"), m_nCurrentPlugin + 1);
// If last plugin slot is occupied, move it so that the plugin is not lost.
// This could certainly be improved...
bool moveLastPlug = false;
if(sndFile.m_MixPlugins[MAX_MIXPLUGINS - 1].pMixPlugin)
{
if(sndFile.m_MixPlugins[MAX_MIXPLUGINS - 2].pMixPlugin == nullptr)
{
moveLastPlug = true;
} else
{
prompt += _T("\nWarning: plugin data in last slot will be lost.");
}
}
if(Reporting::Confirm(prompt) == cnfYes)
{
// Delete last plug...
if(sndFile.m_MixPlugins[MAX_MIXPLUGINS - 1].pMixPlugin)
{
if(moveLastPlug)
{
MovePlug(MAX_MIXPLUGINS - 1, MAX_MIXPLUGINS - 2, true);
} else
{
sndFile.m_MixPlugins[MAX_MIXPLUGINS - 1].Destroy();
MemsetZero(sndFile.m_MixPlugins[MAX_MIXPLUGINS - 1].Info);
}
}
// Update MODCOMMANDs so that they won't be referring to old indexes (e.g. with NOTE_PC).
if(sndFile.GetModSpecifications().HasNote(NOTE_PC))
sndFile.Patterns.ForEachModCommand(PlugIndexModifier(m_nCurrentPlugin + 1, MAX_MIXPLUGINS - 1, 1));
for(PLUGINDEX nSlot = MAX_MIXPLUGINS - 1; nSlot > m_nCurrentPlugin; nSlot--)
{
if(sndFile.m_MixPlugins[nSlot-1].pMixPlugin)
{
MovePlug(nSlot - 1, nSlot, NoPatternAdjust);
}
}
m_CbnPlugin.SetCurSel(m_nCurrentPlugin + 1);
OnPluginChanged();
GetDocument()->UpdateAllViews(nullptr, PluginHint().Names().Info());
SetPluginModified();
}
}
void CViewGlobals::OnClonePlug()
{
if(GetCurrentPlugin() == nullptr)
{
return;
}
CSoundFile &sndFile = GetDocument()->GetSoundFile();
std::vector<PLUGINDEX> emptySlots;
BuildEmptySlotList(emptySlots);
CMoveFXSlotDialog dlg(this, m_nCurrentPlugin, emptySlots, 0, true, !sndFile.m_MixPlugins[m_nCurrentPlugin].IsOutputToMaster());
if(dlg.DoModal() == IDOK)
{
size_t toIndex = dlg.GetSlotIndex();
do
{
const SNDMIXPLUGIN &curPlugin = sndFile.m_MixPlugins[m_nCurrentPlugin];
SNDMIXPLUGIN &newPlugin = sndFile.m_MixPlugins[emptySlots[toIndex]];
GetDocument()->ClonePlugin(newPlugin, curPlugin);
IMixPlugin *mixPlug = newPlugin.pMixPlugin;
if(mixPlug != nullptr && mixPlug->IsInstrument() && GetDocument()->HasInstrumentForPlugin(emptySlots[toIndex]) == INSTRUMENTINDEX_INVALID)
{
GetDocument()->InsertInstrumentForPlugin(emptySlots[toIndex]);
}
if(curPlugin.IsOutputToMaster() || toIndex == emptySlots.size() - 1)
{
break;
}
m_nCurrentPlugin = curPlugin.GetOutputPlugin();
if(dlg.DoMoveChain())
{
toIndex++;
newPlugin.SetOutputPlugin(emptySlots[toIndex]);
}
} while(dlg.DoMoveChain());
m_CbnPlugin.SetCurSel(dlg.GetSlot());
OnPluginChanged();
PopulateChannelPlugins();
GetDocument()->UpdateAllViews(this, PluginHint().Names(), this);
SetPluginModified();
}
}
// The plugin param box is only filled when it gets the focus (done here).
void CViewGlobals::OnFillParamCombo()
{
// no need to fill it again.
if(m_CbnParam.GetCount() > 1)
return;
IMixPlugin *pPlugin = GetCurrentPlugin();
if(pPlugin == nullptr) return;
const PlugParamIndex nParams = pPlugin->GetNumParameters();
m_CbnParam.SetRedraw(FALSE);
m_CbnParam.ResetContent();
AddPluginParameternamesToCombobox(m_CbnParam, *pPlugin);
if (m_nCurrentParam >= nParams) m_nCurrentParam = 0;
m_CbnParam.SetCurSel(m_nCurrentParam);
m_CbnParam.SetRedraw(TRUE);
m_CbnParam.Invalidate(FALSE);
}
// The preset box is only filled when it gets the focus (done here).
void CViewGlobals::OnFillProgramCombo()
{
// no need to fill it again.
if(m_CbnPreset.GetCount() > 1)
return;
IMixPlugin *pPlugin = GetCurrentPlugin();
if(pPlugin == nullptr) return;
FillPluginProgramBox(0, pPlugin->GetNumPrograms() - 1);
m_CbnPreset.SetCurSel(pPlugin->GetCurrentProgram());
}
void CViewGlobals::FillPluginProgramBox(int32 firstProg, int32 lastProg)
{
IMixPlugin *pPlugin = GetCurrentPlugin();
m_CbnPreset.SetRedraw(FALSE);
m_CbnPreset.ResetContent();
pPlugin->CacheProgramNames(firstProg, lastProg + 1);
for (int32 i = firstProg; i <= lastProg; i++)
{
m_CbnPreset.SetItemData(m_CbnPreset.AddString(pPlugin->GetFormattedProgramName(i)), i);
}
m_CbnPreset.SetRedraw(TRUE);
m_CbnPreset.Invalidate(FALSE);
}
BOOL CViewGlobals::OnToolTipText(UINT, NMHDR *pNMHDR, LRESULT *pResult)
{
auto pTTT = reinterpret_cast<TOOLTIPTEXT *>(pNMHDR);
UINT_PTR id = pNMHDR->idFrom;
if(pTTT->uFlags & TTF_IDISHWND)
{
// idFrom is actually the HWND of the tool
id = static_cast<UINT_PTR>(::GetDlgCtrlID(reinterpret_cast<HWND>(id)));
}
mpt::tstring text;
const auto &chnSettings = GetDocument()->GetSoundFile().ChnSettings;
switch(id)
{
case IDC_EDIT1:
case IDC_EDIT3:
case IDC_EDIT5:
case IDC_EDIT7:
text = CModDoc::LinearToDecibels(chnSettings[m_nActiveTab * CHANNELS_IN_TAB + (id - IDC_EDIT1) / 2].nVolume, 64.0);
break;
case IDC_SLIDER1:
case IDC_SLIDER3:
case IDC_SLIDER5:
case IDC_SLIDER7:
text = CModDoc::LinearToDecibels(chnSettings[m_nActiveTab * CHANNELS_IN_TAB + (id - IDC_SLIDER1) / 2].nVolume, 64.0);
break;
case IDC_EDIT2:
case IDC_EDIT4:
case IDC_EDIT6:
case IDC_EDIT8:
text = CModDoc::PanningToString(chnSettings[m_nActiveTab * CHANNELS_IN_TAB + (id - IDC_EDIT2) / 2].nPan, 128);
break;
case IDC_SLIDER2:
case IDC_SLIDER4:
case IDC_SLIDER6:
case IDC_SLIDER8:
text = CModDoc::PanningToString(chnSettings[m_nActiveTab * CHANNELS_IN_TAB + (id - IDC_SLIDER2) / 2].nPan, 128);
break;
case IDC_EDIT16:
{
const auto gain = GetDocument()->GetSoundFile().m_MixPlugins[m_nCurrentPlugin].GetGain();
text = CModDoc::LinearToDecibels(gain ? gain : 10, 10.0);
}
break;
case IDC_BUTTON5:
text = _T("Previous Plugin");
break;
case IDC_BUTTON4:
text = _T("Next Plugin");
break;
default:
return FALSE;
}
mpt::String::WriteWinBuf(pTTT->szText) = text;
*pResult = 0;
// bring the tooltip window above other popup windows
::SetWindowPos(pNMHDR->hwndFrom, HWND_TOP, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE | SWP_NOOWNERZORDER);
return TRUE; // message was handled
}
OPENMPT_NAMESPACE_END