1
0

View_gen.cpp 52 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816
  1. /*
  2. * view_gen.cpp
  3. * ------------
  4. * Purpose: General tab, lower panel.
  5. * Notes : (currently none)
  6. * Authors: Olivier Lapicque
  7. * OpenMPT Devs
  8. * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
  9. */
  10. #include "stdafx.h"
  11. #include "Mptrack.h"
  12. #include "Mainfrm.h"
  13. #include "InputHandler.h"
  14. #include "Childfrm.h"
  15. #include "Moddoc.h"
  16. #include "Globals.h"
  17. #include "Ctrl_gen.h"
  18. #include "View_gen.h"
  19. #include "../soundlib/plugins/PlugInterface.h"
  20. #include "EffectVis.h"
  21. #include "MoveFXSlotDialog.h"
  22. #include "ChannelManagerDlg.h"
  23. #include "SelectPluginDialog.h"
  24. #include "../soundlib/mod_specifications.h"
  25. #include "../common/mptStringBuffer.h"
  26. #include "AbstractVstEditor.h"
  27. // This is used for retrieving the correct background colour for the
  28. // frames on the general tab when using WinXP Luna or Vista/Win7 Aero.
  29. #include <uxtheme.h>
  30. OPENMPT_NAMESPACE_BEGIN
  31. IMPLEMENT_SERIAL(CViewGlobals, CFormView, 0)
  32. BEGIN_MESSAGE_MAP(CViewGlobals, CFormView)
  33. //{{AFX_MSG_MAP(CViewGlobals)
  34. ON_WM_SIZE()
  35. ON_WM_HSCROLL()
  36. ON_WM_VSCROLL()
  37. ON_WM_DESTROY()
  38. ON_MESSAGE(WM_MOD_MDIACTIVATE, &CViewGlobals::OnMDIDeactivate)
  39. ON_MESSAGE(WM_MOD_MDIDEACTIVATE, &CViewGlobals::OnMDIDeactivate)
  40. ON_COMMAND(IDC_CHECK1, &CViewGlobals::OnMute1)
  41. ON_COMMAND(IDC_CHECK3, &CViewGlobals::OnMute2)
  42. ON_COMMAND(IDC_CHECK5, &CViewGlobals::OnMute3)
  43. ON_COMMAND(IDC_CHECK7, &CViewGlobals::OnMute4)
  44. ON_COMMAND(IDC_CHECK2, &CViewGlobals::OnSurround1)
  45. ON_COMMAND(IDC_CHECK4, &CViewGlobals::OnSurround2)
  46. ON_COMMAND(IDC_CHECK6, &CViewGlobals::OnSurround3)
  47. ON_COMMAND(IDC_CHECK8, &CViewGlobals::OnSurround4)
  48. ON_COMMAND(IDC_BUTTON9, &CViewGlobals::OnEditColor1)
  49. ON_COMMAND(IDC_BUTTON10, &CViewGlobals::OnEditColor2)
  50. ON_COMMAND(IDC_BUTTON11, &CViewGlobals::OnEditColor3)
  51. ON_COMMAND(IDC_BUTTON12, &CViewGlobals::OnEditColor4)
  52. ON_EN_UPDATE(IDC_EDIT1, &CViewGlobals::OnEditVol1)
  53. ON_EN_UPDATE(IDC_EDIT3, &CViewGlobals::OnEditVol2)
  54. ON_EN_UPDATE(IDC_EDIT5, &CViewGlobals::OnEditVol3)
  55. ON_EN_UPDATE(IDC_EDIT7, &CViewGlobals::OnEditVol4)
  56. ON_EN_UPDATE(IDC_EDIT2, &CViewGlobals::OnEditPan1)
  57. ON_EN_UPDATE(IDC_EDIT4, &CViewGlobals::OnEditPan2)
  58. ON_EN_UPDATE(IDC_EDIT6, &CViewGlobals::OnEditPan3)
  59. ON_EN_UPDATE(IDC_EDIT8, &CViewGlobals::OnEditPan4)
  60. ON_EN_UPDATE(IDC_EDIT9, &CViewGlobals::OnEditName1)
  61. ON_EN_UPDATE(IDC_EDIT10, &CViewGlobals::OnEditName2)
  62. ON_EN_UPDATE(IDC_EDIT11, &CViewGlobals::OnEditName3)
  63. ON_EN_UPDATE(IDC_EDIT12, &CViewGlobals::OnEditName4)
  64. ON_CBN_SELCHANGE(IDC_COMBO1, &CViewGlobals::OnFx1Changed)
  65. ON_CBN_SELCHANGE(IDC_COMBO2, &CViewGlobals::OnFx2Changed)
  66. ON_CBN_SELCHANGE(IDC_COMBO3, &CViewGlobals::OnFx3Changed)
  67. ON_CBN_SELCHANGE(IDC_COMBO4, &CViewGlobals::OnFx4Changed)
  68. // Plugins
  69. ON_COMMAND(IDC_CHECK9, &CViewGlobals::OnMixModeChanged)
  70. ON_COMMAND(IDC_CHECK10, &CViewGlobals::OnBypassChanged)
  71. ON_COMMAND(IDC_CHECK11, &CViewGlobals::OnDryMixChanged)
  72. ON_COMMAND(IDC_BUTTON1, &CViewGlobals::OnSelectPlugin)
  73. ON_COMMAND(IDC_DELPLUGIN, &CViewGlobals::OnRemovePlugin)
  74. ON_COMMAND(IDC_BUTTON2, &CViewGlobals::OnEditPlugin)
  75. ON_COMMAND(IDC_BUTTON4, &CViewGlobals::OnNextPlugin)
  76. ON_COMMAND(IDC_BUTTON5, &CViewGlobals::OnPrevPlugin)
  77. ON_COMMAND(IDC_MOVEFXSLOT, &CViewGlobals::OnMovePlugToSlot)
  78. ON_COMMAND(IDC_INSERTFXSLOT,&CViewGlobals::OnInsertSlot)
  79. ON_COMMAND(IDC_CLONEPLUG, &CViewGlobals::OnClonePlug)
  80. ON_COMMAND(IDC_BUTTON6, &CViewGlobals::OnLoadParam)
  81. ON_COMMAND(IDC_BUTTON8, &CViewGlobals::OnSaveParam)
  82. ON_EN_UPDATE(IDC_EDIT13, &CViewGlobals::OnPluginNameChanged)
  83. ON_EN_UPDATE(IDC_EDIT14, &CViewGlobals::OnSetParameter)
  84. ON_EN_SETFOCUS(IDC_EDIT14, &CViewGlobals::OnFocusParam)
  85. ON_EN_KILLFOCUS(IDC_EDIT14, &CViewGlobals::OnParamChanged)
  86. ON_CBN_SELCHANGE(IDC_COMBO5, &CViewGlobals::OnPluginChanged)
  87. ON_CBN_SELCHANGE(IDC_COMBO6, &CViewGlobals::OnParamChanged)
  88. ON_CBN_SETFOCUS(IDC_COMBO6, &CViewGlobals::OnFillParamCombo)
  89. ON_CBN_SELCHANGE(IDC_COMBO7, &CViewGlobals::OnOutputRoutingChanged)
  90. ON_CBN_SELCHANGE(IDC_COMBO8, &CViewGlobals::OnProgramChanged)
  91. ON_CBN_SETFOCUS(IDC_COMBO8, &CViewGlobals::OnFillProgramCombo)
  92. ON_COMMAND(IDC_CHECK12, &CViewGlobals::OnWetDryExpandChanged)
  93. ON_CBN_SELCHANGE(IDC_COMBO9, &CViewGlobals::OnSpecialMixProcessingChanged)
  94. ON_NOTIFY(TCN_SELCHANGE, IDC_TABCTRL1, &CViewGlobals::OnTabSelchange)
  95. ON_MESSAGE(WM_MOD_UNLOCKCONTROLS, &CViewGlobals::OnUnlockControls)
  96. ON_MESSAGE(WM_MOD_VIEWMSG, &CViewGlobals::OnModViewMsg)
  97. ON_MESSAGE(WM_MOD_MIDIMSG, &CViewGlobals::OnMidiMsg)
  98. ON_MESSAGE(WM_MOD_PLUGPARAMAUTOMATE, &CViewGlobals::OnParamAutomated)
  99. ON_MESSAGE(WM_MOD_PLUGINDRYWETRATIOCHANGED, &CViewGlobals::OnDryWetRatioChangedFromPlayer)
  100. ON_COMMAND(ID_EDIT_UNDO, &CViewGlobals::OnEditUndo)
  101. ON_COMMAND(ID_EDIT_REDO, &CViewGlobals::OnEditRedo)
  102. ON_UPDATE_COMMAND_UI(ID_EDIT_UNDO, &CViewGlobals::OnUpdateUndo)
  103. ON_UPDATE_COMMAND_UI(ID_EDIT_REDO, &CViewGlobals::OnUpdateRedo)
  104. ON_NOTIFY_EX_RANGE(TTN_NEEDTEXT, 0, 0xFFFF, &CViewGlobals::OnToolTipText)
  105. //}}AFX_MSG_MAP
  106. END_MESSAGE_MAP()
  107. void CViewGlobals::DoDataExchange(CDataExchange* pDX)
  108. {
  109. CFormView::DoDataExchange(pDX);
  110. //{{AFX_DATA_MAP(CViewGlobals)
  111. DDX_Control(pDX, IDC_TABCTRL1, m_TabCtrl);
  112. DDX_Control(pDX, IDC_COMBO1, m_CbnEffects[0]);
  113. DDX_Control(pDX, IDC_COMBO2, m_CbnEffects[1]);
  114. DDX_Control(pDX, IDC_COMBO3, m_CbnEffects[2]);
  115. DDX_Control(pDX, IDC_COMBO4, m_CbnEffects[3]);
  116. DDX_Control(pDX, IDC_COMBO5, m_CbnPlugin);
  117. DDX_Control(pDX, IDC_COMBO6, m_CbnParam);
  118. DDX_Control(pDX, IDC_COMBO7, m_CbnOutput);
  119. DDX_Control(pDX, IDC_COMBO8, m_CbnPreset);
  120. DDX_Control(pDX, IDC_COMBO9, m_CbnSpecialMixProcessing);
  121. DDX_Control(pDX, IDC_SPIN10, m_SpinMixGain);
  122. DDX_Control(pDX, IDC_SLIDER1, m_sbVolume[0]);
  123. DDX_Control(pDX, IDC_SLIDER2, m_sbPan[0]);
  124. DDX_Control(pDX, IDC_SLIDER3, m_sbVolume[1]);
  125. DDX_Control(pDX, IDC_SLIDER4, m_sbPan[1]);
  126. DDX_Control(pDX, IDC_SLIDER5, m_sbVolume[2]);
  127. DDX_Control(pDX, IDC_SLIDER6, m_sbPan[2]);
  128. DDX_Control(pDX, IDC_SLIDER7, m_sbVolume[3]);
  129. DDX_Control(pDX, IDC_SLIDER8, m_sbPan[3]);
  130. DDX_Control(pDX, IDC_SLIDER9, m_sbValue);
  131. DDX_Control(pDX, IDC_SLIDER10, m_sbDryRatio);
  132. DDX_Control(pDX, IDC_SPIN1, m_spinVolume[0]);
  133. DDX_Control(pDX, IDC_SPIN2, m_spinPan[0]);
  134. DDX_Control(pDX, IDC_SPIN3, m_spinVolume[1]);
  135. DDX_Control(pDX, IDC_SPIN4, m_spinPan[1]);
  136. DDX_Control(pDX, IDC_SPIN5, m_spinVolume[2]);
  137. DDX_Control(pDX, IDC_SPIN6, m_spinPan[2]);
  138. DDX_Control(pDX, IDC_SPIN7, m_spinVolume[3]);
  139. DDX_Control(pDX, IDC_SPIN8, m_spinPan[3]);
  140. DDX_Control(pDX, IDC_BUTTON1, m_BtnSelect);
  141. DDX_Control(pDX, IDC_BUTTON2, m_BtnEdit);
  142. //}}AFX_DATA_MAP
  143. }
  144. void CViewGlobals::OnInitialUpdate()
  145. {
  146. CChildFrame *pFrame = (CChildFrame *)GetParentFrame();
  147. int nMapMode = MM_TEXT;
  148. SIZE sizeTotal, sizePage, sizeLine;
  149. m_nActiveTab = CHANNELINDEX(-1);
  150. m_nCurrentPlugin = 0;
  151. m_nCurrentParam = 0;
  152. CFormView::OnInitialUpdate();
  153. EnableToolTips();
  154. if (pFrame)
  155. {
  156. GENERALVIEWSTATE &generalState = pFrame->GetGeneralViewState();
  157. if (generalState.initialized)
  158. {
  159. m_TabCtrl.SetCurSel(generalState.nTab);
  160. m_nActiveTab = generalState.nTab;
  161. m_nCurrentPlugin = generalState.nPlugin;
  162. m_nCurrentParam = generalState.nParam;
  163. }
  164. }
  165. GetDeviceScrollSizes(nMapMode, sizeTotal, sizePage, sizeLine);
  166. m_rcClient.SetRect(0, 0, sizeTotal.cx, sizeTotal.cy);
  167. RecalcLayout();
  168. // Initializing scroll ranges
  169. for(int ichn = 0; ichn < CHANNELS_IN_TAB; ichn++)
  170. {
  171. // Color select
  172. m_channelColor[ichn].SubclassDlgItem(IDC_BUTTON9 + ichn, this);
  173. // Volume Slider
  174. m_sbVolume[ichn].SetRange(0, 64);
  175. m_sbVolume[ichn].SetTicFreq(8);
  176. // Pan Slider
  177. m_sbPan[ichn].SetRange(0, 64);
  178. m_sbPan[ichn].SetTicFreq(8);
  179. // Volume Spin
  180. m_spinVolume[ichn].SetRange(0, 64);
  181. // Pan Spin
  182. m_spinPan[ichn].SetRange(0, 256);
  183. }
  184. m_sbValue.SetPos(0);
  185. m_sbValue.SetRange(0, 100);
  186. m_sbValue.SetPos(0);
  187. m_sbValue.SetRange(0, 100);
  188. m_CbnSpecialMixProcessing.AddString(_T("Default"));
  189. m_CbnSpecialMixProcessing.AddString(_T("Wet Subtract"));
  190. m_CbnSpecialMixProcessing.AddString(_T("Dry Subtract"));
  191. m_CbnSpecialMixProcessing.AddString(_T("Mix Subtract"));
  192. m_CbnSpecialMixProcessing.AddString(_T("Middle Subtract"));
  193. m_CbnSpecialMixProcessing.AddString(_T("LR Balance"));
  194. m_SpinMixGain.SetRange(0, 80);
  195. m_SpinMixGain.SetPos(10);
  196. SetDlgItemText(IDC_EDIT16, _T("Gain: x1.0"));
  197. UpdateView(UpdateHint().ModType());
  198. OnParamChanged();
  199. m_nLockCount = 0;
  200. // Use tab background color rather than regular dialog background color (required for Aero/etc. where they are not the same color)
  201. EnableThemeDialogTexture(m_hWnd, ETDT_ENABLETAB);
  202. }
  203. void CViewGlobals::OnDestroy()
  204. {
  205. CChildFrame *pFrame = (CChildFrame *)GetParentFrame();
  206. if (pFrame)
  207. {
  208. GENERALVIEWSTATE &generalState = pFrame->GetGeneralViewState();
  209. generalState.initialized = true;
  210. generalState.nTab = m_nActiveTab;
  211. generalState.nPlugin = m_nCurrentPlugin;
  212. generalState.nParam = m_nCurrentParam;
  213. }
  214. CFormView::OnDestroy();
  215. }
  216. LRESULT CViewGlobals::OnMDIDeactivate(WPARAM, LPARAM)
  217. {
  218. // Create new undo point if we switch to / from other window
  219. m_lastEdit = CHANNELINDEX_INVALID;
  220. return 0;
  221. }
  222. LRESULT CViewGlobals::OnMidiMsg(WPARAM midiData_, LPARAM)
  223. {
  224. uint32 midiData = static_cast<uint32>(midiData_);
  225. // Handle MIDI messages assigned to shortcuts
  226. CInputHandler *ih = CMainFrame::GetInputHandler();
  227. ih->HandleMIDIMessage(kCtxViewGeneral, midiData) != kcNull
  228. || ih->HandleMIDIMessage(kCtxAllContexts, midiData) != kcNull;
  229. return 1;
  230. }
  231. void CViewGlobals::RecalcLayout()
  232. {
  233. if (m_TabCtrl.m_hWnd != NULL)
  234. {
  235. CRect rect;
  236. GetClientRect(&rect);
  237. if (rect.right < m_rcClient.right) rect.right = m_rcClient.right;
  238. if (rect.bottom < m_rcClient.bottom) rect.bottom = m_rcClient.bottom;
  239. m_TabCtrl.SetWindowPos(&CWnd::wndBottom, 0,0, rect.right, rect.bottom, SWP_NOMOVE);
  240. }
  241. }
  242. int CViewGlobals::GetDlgItemIntEx(UINT nID)
  243. {
  244. CString s;
  245. GetDlgItemText(nID, s);
  246. if(s.GetLength() < 1 || s[0] < _T('0') || s[0] > _T('9')) return -1;
  247. return _ttoi(s);
  248. }
  249. void CViewGlobals::OnSize(UINT nType, int cx, int cy)
  250. {
  251. CFormView::OnSize(nType, cx, cy);
  252. if (((nType == SIZE_RESTORED) || (nType == SIZE_MAXIMIZED)) && (cx > 0) && (cy > 0) && (m_hWnd))
  253. {
  254. RecalcLayout();
  255. }
  256. }
  257. void CViewGlobals::OnUpdate(CView *pView, LPARAM lHint, CObject *pHint)
  258. {
  259. if (pView != this) UpdateView(UpdateHint::FromLPARAM(lHint), pHint);
  260. }
  261. void CViewGlobals::UpdateView(UpdateHint hint, CObject *pObject)
  262. {
  263. const CModDoc *pModDoc = GetDocument();
  264. int nTabCount, nTabIndex;
  265. if (!pModDoc || pObject == this ) return;
  266. const CSoundFile &sndFile = pModDoc->GetSoundFile();
  267. const GeneralHint genHint = hint.ToType<GeneralHint>();
  268. const PluginHint plugHint = hint.ToType<PluginHint>();
  269. if (!genHint.GetType()[HINT_MODTYPE | HINT_MODCHANNELS]
  270. && !plugHint.GetType()[HINT_MIXPLUGINS | HINT_PLUGINNAMES | HINT_PLUGINPARAM])
  271. {
  272. return;
  273. }
  274. FlagSet<HintType> hintType = hint.GetType();
  275. const bool updateAll = hintType[HINT_MODTYPE];
  276. const auto updateChannel = genHint.GetChannel();
  277. const int updateTab = (updateChannel < sndFile.GetNumChannels()) ? (updateChannel / CHANNELS_IN_TAB) : m_nActiveTab;
  278. if(genHint.GetType()[HINT_MODCHANNELS] && updateTab != m_nActiveTab)
  279. return;
  280. CString s;
  281. nTabCount = (sndFile.m_nChannels + (CHANNELS_IN_TAB - 1)) / CHANNELS_IN_TAB;
  282. if (nTabCount != m_TabCtrl.GetItemCount())
  283. {
  284. UINT nOldSel = m_TabCtrl.GetCurSel();
  285. if (!m_TabCtrl.GetItemCount()) nOldSel = m_nActiveTab;
  286. m_TabCtrl.SetRedraw(FALSE);
  287. m_TabCtrl.DeleteAllItems();
  288. for (int iItem=0; iItem<nTabCount; iItem++)
  289. {
  290. const int lastItem = std::min(iItem * CHANNELS_IN_TAB + CHANNELS_IN_TAB, static_cast<int>(MAX_BASECHANNELS));
  291. s = MPT_CFORMAT("{} - {}")(iItem * CHANNELS_IN_TAB + 1, lastItem);
  292. TC_ITEM tci;
  293. tci.mask = TCIF_TEXT | TCIF_PARAM;
  294. tci.pszText = const_cast<TCHAR *>(s.GetString());
  295. tci.lParam = iItem * CHANNELS_IN_TAB;
  296. m_TabCtrl.InsertItem(iItem, &tci);
  297. }
  298. if (nOldSel >= (UINT)nTabCount) nOldSel = 0;
  299. m_TabCtrl.SetRedraw(TRUE);
  300. m_TabCtrl.SetCurSel(nOldSel);
  301. InvalidateRect(NULL, FALSE);
  302. }
  303. nTabIndex = m_TabCtrl.GetCurSel();
  304. if ((nTabIndex < 0) || (nTabIndex >= nTabCount)) return; // ???
  305. if (m_nActiveTab != nTabIndex || genHint.GetType()[HINT_MODTYPE | HINT_MODCHANNELS])
  306. {
  307. LockControls();
  308. m_nActiveTab = static_cast<CHANNELINDEX>(nTabIndex);
  309. for (CHANNELINDEX ichn = 0; ichn < CHANNELS_IN_TAB; ichn++)
  310. {
  311. const CHANNELINDEX nChn = m_nActiveTab * CHANNELS_IN_TAB + ichn;
  312. const BOOL bEnable = (nChn < sndFile.GetNumChannels()) ? TRUE : FALSE;
  313. if(nChn < MAX_BASECHANNELS)
  314. {
  315. const auto &chnSettings = sndFile.ChnSettings[nChn];
  316. // Text
  317. if(bEnable)
  318. s = MPT_CFORMAT("Channel {}")(nChn + 1);
  319. else
  320. s = _T("");
  321. SetDlgItemText(IDC_TEXT1 + ichn, s);
  322. // Channel color
  323. m_channelColor[ichn].SetColor(chnSettings.color);
  324. m_channelColor[ichn].EnableWindow(bEnable);
  325. // Mute
  326. CheckDlgButton(IDC_CHECK1 + ichn * 2, chnSettings.dwFlags[CHN_MUTE] ? TRUE : FALSE);
  327. // Surround
  328. CheckDlgButton(IDC_CHECK2 + ichn * 2, chnSettings.dwFlags[CHN_SURROUND] ? TRUE : FALSE);
  329. // Volume
  330. int vol = chnSettings.nVolume;
  331. m_sbVolume[ichn].SetPos(vol);
  332. m_sbVolume[ichn].Invalidate(FALSE);
  333. SetDlgItemInt(IDC_EDIT1+ichn*2, vol);
  334. // Pan
  335. int pan = chnSettings.nPan;
  336. m_sbPan[ichn].SetPos(pan/4);
  337. m_sbPan[ichn].Invalidate(FALSE);
  338. SetDlgItemInt(IDC_EDIT2+ichn*2, pan);
  339. // Channel name
  340. s = mpt::ToCString(sndFile.GetCharsetInternal(), chnSettings.szName);
  341. SetDlgItemText(IDC_EDIT9 + ichn, s);
  342. ((CEdit*)(GetDlgItem(IDC_EDIT9 + ichn)))->LimitText(MAX_CHANNELNAME - 1);
  343. } else
  344. {
  345. SetDlgItemText(IDC_TEXT1 + ichn, _T(""));
  346. SetDlgItemText(IDC_EDIT9 + ichn, _T(""));
  347. m_channelColor[ichn].EnableWindow(FALSE);
  348. }
  349. // Enable/Disable controls for this channel
  350. BOOL bIT = ((bEnable) && (sndFile.m_nType & (MOD_TYPE_IT|MOD_TYPE_MPT)));
  351. GetDlgItem(IDC_CHECK1 + ichn * 2)->EnableWindow(bEnable);
  352. GetDlgItem(IDC_CHECK2 + ichn * 2)->EnableWindow(bIT);
  353. m_sbVolume[ichn].EnableWindow(bIT);
  354. m_spinVolume[ichn].EnableWindow(bIT);
  355. m_sbPan[ichn].EnableWindow(bEnable && !(sndFile.GetType() & (MOD_TYPE_XM|MOD_TYPE_MOD)));
  356. m_spinPan[ichn].EnableWindow(bEnable && !(sndFile.GetType() & (MOD_TYPE_XM|MOD_TYPE_MOD)));
  357. GetDlgItem(IDC_EDIT1 + ichn * 2)->EnableWindow(bIT); // channel vol
  358. GetDlgItem(IDC_EDIT2 + ichn * 2)->EnableWindow(bEnable && !(sndFile.GetType() & (MOD_TYPE_XM|MOD_TYPE_MOD))); // channel pan
  359. GetDlgItem(IDC_EDIT9 + ichn)->EnableWindow(((bEnable) && (sndFile.m_nType & (MOD_TYPE_XM|MOD_TYPE_IT|MOD_TYPE_MPT)))); // channel name
  360. m_CbnEffects[ichn].EnableWindow(bEnable & (sndFile.GetModSpecifications().supportsPlugins ? TRUE : FALSE));
  361. }
  362. UnlockControls();
  363. }
  364. // Update plugin names
  365. if(genHint.GetType()[HINT_MODTYPE | HINT_MODCHANNELS] || plugHint.GetType()[HINT_PLUGINNAMES])
  366. {
  367. PopulateChannelPlugins(plugHint.GetPlugin() ? plugHint.GetPlugin() - 1 : PLUGINDEX_INVALID);
  368. SetDlgItemText(IDC_EDIT13, mpt::ToCString(sndFile.m_MixPlugins[m_nCurrentPlugin].GetName()));
  369. }
  370. // Update plugin info
  371. const SNDMIXPLUGIN &plugin = sndFile.m_MixPlugins[m_nCurrentPlugin];
  372. const bool updatePlug = (plugHint.GetPlugin() == 0 || plugHint.GetPlugin() == m_nCurrentPlugin + 1);
  373. const bool updateWholePluginView = updateAll || (plugHint.GetType()[HINT_MIXPLUGINS] && updatePlug);
  374. if(updateWholePluginView)
  375. {
  376. const PLUGINDEX plugIndex = (plugHint.GetPlugin() != 0) ? plugHint.GetPlugin() - 1 : PLUGINDEX_INVALID;
  377. m_CbnPlugin.SetRedraw(FALSE);
  378. if(plugIndex == PLUGINDEX_INVALID)
  379. m_CbnPlugin.ResetContent();
  380. AddPluginNamesToCombobox(m_CbnPlugin, sndFile.m_MixPlugins, true, plugIndex);
  381. m_CbnPlugin.SetRedraw(TRUE);
  382. m_CbnPlugin.SetCurSel(m_nCurrentPlugin);
  383. if (m_nCurrentPlugin >= MAX_MIXPLUGINS) m_nCurrentPlugin = 0;
  384. SetDlgItemText(IDC_EDIT13, mpt::ToCString(plugin.GetName()));
  385. CheckDlgButton(IDC_CHECK9, plugin.IsMasterEffect() ? BST_CHECKED : BST_UNCHECKED);
  386. CheckDlgButton(IDC_CHECK10, plugin.IsBypassed() ? BST_CHECKED : BST_UNCHECKED);
  387. CheckDlgButton(IDC_CHECK11, plugin.IsWetMix() ? BST_CHECKED : BST_UNCHECKED);
  388. IMixPlugin *pPlugin = plugin.pMixPlugin;
  389. m_BtnEdit.EnableWindow((pPlugin != nullptr && (pPlugin->HasEditor() || pPlugin->GetNumParameters())) ? TRUE : FALSE);
  390. GetDlgItem(IDC_MOVEFXSLOT)->EnableWindow((pPlugin) ? TRUE : FALSE);
  391. GetDlgItem(IDC_INSERTFXSLOT)->EnableWindow((pPlugin) ? TRUE : FALSE);
  392. GetDlgItem(IDC_CLONEPLUG)->EnableWindow((pPlugin) ? TRUE : FALSE);
  393. UpdateDryWetDisplay();
  394. if(pPlugin && pPlugin->IsInstrument())
  395. {
  396. m_CbnSpecialMixProcessing.EnableWindow(FALSE);
  397. GetDlgItem(IDC_CHECK12)->EnableWindow(FALSE);
  398. } else
  399. {
  400. m_CbnSpecialMixProcessing.EnableWindow(TRUE);
  401. GetDlgItem(IDC_CHECK12)->EnableWindow(TRUE);
  402. m_CbnSpecialMixProcessing.SetCurSel(plugin.GetMixMode());
  403. CheckDlgButton(IDC_CHECK12, plugin.IsExpandedMix() ? BST_CHECKED : BST_UNCHECKED);
  404. }
  405. int gain = plugin.GetGain();
  406. if(gain == 0) gain = 10;
  407. float value = 0.1f * (float)gain;
  408. s.Format(_T("Gain: x%1.1f"), value);
  409. SetDlgItemText(IDC_EDIT16, s);
  410. m_SpinMixGain.SetPos(gain);
  411. if (pPlugin)
  412. {
  413. const PlugParamIndex nParams = pPlugin->GetNumParameters();
  414. m_CbnParam.SetRedraw(FALSE);
  415. m_CbnParam.ResetContent();
  416. if (m_nCurrentParam >= nParams) m_nCurrentParam = 0;
  417. if(nParams)
  418. {
  419. m_CbnParam.SetItemData(m_CbnParam.AddString(pPlugin->GetFormattedParamName(m_nCurrentParam)), m_nCurrentParam);
  420. }
  421. m_CbnParam.SetCurSel(0);
  422. m_CbnParam.SetRedraw(TRUE);
  423. OnParamChanged();
  424. // Input / Output type
  425. int in = pPlugin->GetNumInputChannels(), out = pPlugin->GetNumOutputChannels();
  426. if (in < 1) s = _T("No input");
  427. else if (in == 1) s = _T("Mono-In");
  428. else s = _T("Stereo-In");
  429. s += _T(", ");
  430. if (out < 1) s += _T("No output");
  431. else if (out == 1) s += _T("Mono-Out");
  432. else s += _T("Stereo-Out");
  433. // For now, only display the "current" preset.
  434. // This prevents the program from hanging when switching between plugin slots or
  435. // switching to the general tab and the first plugin in the list has a lot of presets.
  436. // Some plugins like Synth1 have so many presets that this *does* indeed make a difference,
  437. // even on fairly modern CPUs. The rest of the presets are just added when the combo box
  438. // gets the focus, i.e. just when they're needed.
  439. int32 currentProg = pPlugin->GetCurrentProgram();
  440. FillPluginProgramBox(currentProg, currentProg);
  441. m_CbnPreset.SetCurSel(0);
  442. m_sbValue.EnableWindow(TRUE);
  443. m_sbDryRatio.EnableWindow(TRUE);
  444. GetDlgItem(IDC_EDIT14)->EnableWindow(TRUE);
  445. } else
  446. {
  447. s.Empty();
  448. if (m_CbnParam.GetCount() > 0) m_CbnParam.ResetContent();
  449. m_nCurrentParam = 0;
  450. m_CbnPreset.SetRedraw(FALSE);
  451. m_CbnPreset.ResetContent();
  452. m_CbnPreset.SetItemData(m_CbnPreset.AddString(_T("none")), 0);
  453. m_CbnPreset.SetRedraw(TRUE);
  454. m_CbnPreset.SetCurSel(0);
  455. m_sbValue.EnableWindow(FALSE);
  456. m_sbDryRatio.EnableWindow(FALSE);
  457. GetDlgItem(IDC_EDIT14)->EnableWindow(FALSE);
  458. }
  459. SetDlgItemText(IDC_TEXT6, s);
  460. }
  461. if(updateWholePluginView || plugHint.GetPlugin() > m_nCurrentPlugin)
  462. {
  463. int insertAt = 1;
  464. m_CbnOutput.SetRedraw(FALSE);
  465. if(updateWholePluginView)
  466. {
  467. m_CbnOutput.ResetContent();
  468. m_CbnOutput.SetItemData(m_CbnOutput.AddString(_T("Default")), 0);
  469. for(PLUGINDEX i = m_nCurrentPlugin + 1; i < MAX_MIXPLUGINS; i++)
  470. {
  471. if(!sndFile.m_MixPlugins[i].IsValidPlugin())
  472. {
  473. m_CbnOutput.SetItemData(m_CbnOutput.AddString(_T("New Plugin...")), 1);
  474. insertAt = 2;
  475. break;
  476. }
  477. }
  478. } else
  479. {
  480. const DWORD_PTR changedPlugin = 0x80 + (plugHint.GetPlugin() - 1);
  481. const int items = m_CbnOutput.GetCount();
  482. for(insertAt = 1; insertAt < items; insertAt++)
  483. {
  484. DWORD_PTR thisPlugin = m_CbnOutput.GetItemData(insertAt);
  485. if(thisPlugin == changedPlugin)
  486. m_CbnOutput.DeleteString(insertAt);
  487. if(thisPlugin >= changedPlugin)
  488. break;
  489. }
  490. }
  491. int outputSel = plugin.IsOutputToMaster() ? 0 : -1;
  492. for(PLUGINDEX iOut = m_nCurrentPlugin + 1; iOut < MAX_MIXPLUGINS; iOut++)
  493. {
  494. if(!updateWholePluginView && (iOut + 1) != plugHint.GetPlugin())
  495. continue;
  496. const SNDMIXPLUGIN &outPlug = sndFile.m_MixPlugins[iOut];
  497. if(outPlug.IsValidPlugin())
  498. {
  499. const auto name = outPlug.GetName(), libName = outPlug.GetLibraryName();
  500. s.Format(_T("FX%d: "), iOut + 1);
  501. s += mpt::ToCString(name.empty() ? libName : name);
  502. if(!name.empty() && libName != name)
  503. {
  504. s += _T(" (");
  505. s += mpt::ToCString(libName);
  506. s += _T(")");
  507. }
  508. insertAt = m_CbnOutput.InsertString(insertAt, s);
  509. m_CbnOutput.SetItemData(insertAt, 0x80 + iOut);
  510. if(!plugin.IsOutputToMaster() && (plugin.GetOutputPlugin() == iOut))
  511. {
  512. outputSel = insertAt;
  513. }
  514. insertAt++;
  515. }
  516. }
  517. m_CbnOutput.SetRedraw(TRUE);
  518. if(outputSel >= 0)
  519. m_CbnOutput.SetCurSel(outputSel);
  520. }
  521. if(plugHint.GetType()[HINT_PLUGINPARAM] && updatePlug)
  522. {
  523. OnParamChanged();
  524. }
  525. m_CbnPlugin.Invalidate(FALSE);
  526. m_CbnParam.Invalidate(FALSE);
  527. m_CbnPreset.Invalidate(FALSE);
  528. m_CbnSpecialMixProcessing.Invalidate(FALSE);
  529. m_CbnOutput.Invalidate(FALSE);
  530. }
  531. void CViewGlobals::PopulateChannelPlugins(PLUGINDEX plugin)
  532. {
  533. // Channel effect lists
  534. const CSoundFile &sndFile = GetDocument()->GetSoundFile();
  535. CString s;
  536. for(CHANNELINDEX ichn = 0; ichn < CHANNELS_IN_TAB; ichn++)
  537. {
  538. const CHANNELINDEX nChn = m_nActiveTab * CHANNELS_IN_TAB + ichn;
  539. auto &comboBox = m_CbnEffects[ichn];
  540. if(nChn < MAX_BASECHANNELS)
  541. {
  542. comboBox.SetRedraw(FALSE);
  543. int insertAt = 1;
  544. if(plugin == PLUGINDEX_INVALID)
  545. {
  546. comboBox.ResetContent();
  547. comboBox.SetItemData(comboBox.AddString(_T("No plugin")), 0);
  548. } else
  549. {
  550. const int items = comboBox.GetCount();
  551. for(insertAt = 1; insertAt < items; insertAt++)
  552. {
  553. auto thisPlugin = static_cast<PLUGINDEX>(comboBox.GetItemData(insertAt));
  554. if(thisPlugin == (plugin + 1))
  555. comboBox.DeleteString(insertAt);
  556. if(thisPlugin >= (plugin + 1))
  557. break;
  558. }
  559. }
  560. int fxsel = 0;
  561. for(PLUGINDEX ifx = 0; ifx < MAX_MIXPLUGINS; ifx++)
  562. {
  563. if(plugin != PLUGINDEX_INVALID && ifx != plugin)
  564. continue;
  565. if(sndFile.m_MixPlugins[ifx].IsValidPlugin()
  566. || (sndFile.m_MixPlugins[ifx].GetName() != U_(""))
  567. || (sndFile.ChnSettings[nChn].nMixPlugin == ifx + 1))
  568. {
  569. s = MPT_CFORMAT("FX{}: ")(ifx + 1);
  570. s += mpt::ToCString(sndFile.m_MixPlugins[ifx].GetName());
  571. insertAt = comboBox.InsertString(insertAt, s);
  572. comboBox.SetItemData(insertAt, ifx + 1);
  573. if(sndFile.ChnSettings[nChn].nMixPlugin == ifx + 1)
  574. fxsel = insertAt;
  575. insertAt++;
  576. }
  577. }
  578. comboBox.SetRedraw(TRUE);
  579. if(plugin == PLUGINDEX_INVALID || fxsel > 0)
  580. comboBox.SetCurSel(fxsel);
  581. comboBox.Invalidate(FALSE);
  582. }
  583. }
  584. }
  585. IMixPlugin *CViewGlobals::GetCurrentPlugin() const
  586. {
  587. if(GetDocument() == nullptr || m_nCurrentPlugin >= MAX_MIXPLUGINS)
  588. {
  589. return nullptr;
  590. }
  591. return GetDocument()->GetSoundFile().m_MixPlugins[m_nCurrentPlugin].pMixPlugin;
  592. }
  593. void CViewGlobals::OnTabSelchange(NMHDR*, LRESULT* pResult)
  594. {
  595. UpdateView(GeneralHint().Channels());
  596. if (pResult) *pResult = 0;
  597. }
  598. void CViewGlobals::OnUpdateUndo(CCmdUI *pCmdUI)
  599. {
  600. CModDoc *pModDoc = GetDocument();
  601. if((pCmdUI) && (pModDoc))
  602. {
  603. pCmdUI->Enable(pModDoc->GetPatternUndo().CanUndoChannelSettings());
  604. pCmdUI->SetText(CMainFrame::GetInputHandler()->GetKeyTextFromCommand(kcEditUndo, _T("Undo ") + pModDoc->GetPatternUndo().GetUndoName()));
  605. }
  606. }
  607. void CViewGlobals::OnUpdateRedo(CCmdUI *pCmdUI)
  608. {
  609. CModDoc *pModDoc = GetDocument();
  610. if((pCmdUI) && (pModDoc))
  611. {
  612. pCmdUI->Enable(pModDoc->GetPatternUndo().CanRedoChannelSettings());
  613. pCmdUI->SetText(CMainFrame::GetInputHandler()->GetKeyTextFromCommand(kcEditRedo, _T("Redo ") + pModDoc->GetPatternUndo().GetRedoName()));
  614. }
  615. }
  616. void CViewGlobals::OnEditUndo()
  617. {
  618. UndoRedo(true);
  619. }
  620. void CViewGlobals::OnEditRedo()
  621. {
  622. UndoRedo(false);
  623. }
  624. void CViewGlobals::UndoRedo(bool undo)
  625. {
  626. CModDoc *pModDoc = GetDocument();
  627. if(!pModDoc)
  628. return;
  629. if(undo && pModDoc->GetPatternUndo().CanUndoChannelSettings())
  630. pModDoc->GetPatternUndo().Undo();
  631. else if(!undo && pModDoc->GetPatternUndo().CanRedoChannelSettings())
  632. pModDoc->GetPatternUndo().Redo();
  633. }
  634. void CViewGlobals::PrepareUndo(CHANNELINDEX chnMod4)
  635. {
  636. if(m_lastEdit != chnMod4)
  637. {
  638. // Backup old channel settings through pattern undo.
  639. m_lastEdit = chnMod4;
  640. const CHANNELINDEX chn = static_cast<CHANNELINDEX>(m_nActiveTab * CHANNELS_IN_TAB) + chnMod4;
  641. GetDocument()->GetPatternUndo().PrepareChannelUndo(chn, 1, "Channel Settings");
  642. }
  643. }
  644. void CViewGlobals::OnEditColor(const CHANNELINDEX chnMod4)
  645. {
  646. auto *modDoc = GetDocument();
  647. auto &sndFile = modDoc->GetSoundFile();
  648. const CHANNELINDEX chn = static_cast<CHANNELINDEX>(m_nActiveTab * CHANNELS_IN_TAB) + chnMod4;
  649. if(auto color = m_channelColor[chnMod4].PickColor(sndFile, chn); color.has_value())
  650. {
  651. PrepareUndo(chnMod4);
  652. sndFile.ChnSettings[chn].color = *color;
  653. if(modDoc->SupportsChannelColors())
  654. modDoc->SetModified();
  655. modDoc->UpdateAllViews(nullptr, GeneralHint(chn).Channels());
  656. }
  657. }
  658. void CViewGlobals::OnEditColor1() { OnEditColor(0); }
  659. void CViewGlobals::OnEditColor2() { OnEditColor(1); }
  660. void CViewGlobals::OnEditColor3() { OnEditColor(2); }
  661. void CViewGlobals::OnEditColor4() { OnEditColor(3); }
  662. void CViewGlobals::OnMute(const CHANNELINDEX chnMod4, const UINT itemID)
  663. {
  664. CModDoc *pModDoc = GetDocument();
  665. if (pModDoc)
  666. {
  667. const bool b = (IsDlgButtonChecked(itemID) != FALSE);
  668. const CHANNELINDEX nChn = (CHANNELINDEX)(m_nActiveTab * CHANNELS_IN_TAB) + chnMod4;
  669. pModDoc->MuteChannel(nChn, b);
  670. pModDoc->UpdateAllViews(this, GeneralHint(nChn).Channels());
  671. }
  672. }
  673. void CViewGlobals::OnMute1() {OnMute(0, IDC_CHECK1);}
  674. void CViewGlobals::OnMute2() {OnMute(1, IDC_CHECK3);}
  675. void CViewGlobals::OnMute3() {OnMute(2, IDC_CHECK5);}
  676. void CViewGlobals::OnMute4() {OnMute(3, IDC_CHECK7);}
  677. void CViewGlobals::OnSurround(const CHANNELINDEX chnMod4, const UINT itemID)
  678. {
  679. CModDoc *pModDoc = GetDocument();
  680. if (pModDoc)
  681. {
  682. PrepareUndo(chnMod4);
  683. const bool b = (IsDlgButtonChecked(itemID) != FALSE);
  684. const CHANNELINDEX nChn = (CHANNELINDEX)(m_nActiveTab * CHANNELS_IN_TAB) + chnMod4;
  685. pModDoc->SurroundChannel(nChn, b);
  686. pModDoc->UpdateAllViews(nullptr, GeneralHint(nChn).Channels());
  687. }
  688. }
  689. void CViewGlobals::OnSurround1() {OnSurround(0, IDC_CHECK2);}
  690. void CViewGlobals::OnSurround2() {OnSurround(1, IDC_CHECK4);}
  691. void CViewGlobals::OnSurround3() {OnSurround(2, IDC_CHECK6);}
  692. void CViewGlobals::OnSurround4() {OnSurround(3, IDC_CHECK8);}
  693. void CViewGlobals::OnEditVol(const CHANNELINDEX chnMod4, const UINT itemID)
  694. {
  695. CModDoc *pModDoc = GetDocument();
  696. const CHANNELINDEX nChn = (CHANNELINDEX)(m_nActiveTab * CHANNELS_IN_TAB) + chnMod4;
  697. const int vol = GetDlgItemIntEx(itemID);
  698. if ((pModDoc) && (vol >= 0) && (vol <= 64) && (!m_nLockCount))
  699. {
  700. PrepareUndo(chnMod4);
  701. if (pModDoc->SetChannelGlobalVolume(nChn, static_cast<uint16>(vol)))
  702. {
  703. m_sbVolume[chnMod4].SetPos(vol);
  704. pModDoc->UpdateAllViews(this, GeneralHint(nChn).Channels());
  705. }
  706. }
  707. }
  708. void CViewGlobals::OnEditVol1() {OnEditVol(0, IDC_EDIT1);}
  709. void CViewGlobals::OnEditVol2() {OnEditVol(1, IDC_EDIT3);}
  710. void CViewGlobals::OnEditVol3() {OnEditVol(2, IDC_EDIT5);}
  711. void CViewGlobals::OnEditVol4() {OnEditVol(3, IDC_EDIT7);}
  712. void CViewGlobals::OnEditPan(const CHANNELINDEX chnMod4, const UINT itemID)
  713. {
  714. CModDoc *pModDoc = GetDocument();
  715. const CHANNELINDEX nChn = (CHANNELINDEX)(m_nActiveTab * CHANNELS_IN_TAB) + chnMod4;
  716. const int pan = GetDlgItemIntEx(itemID);
  717. if ((pModDoc) && (pan >= 0) && (pan <= 256) && (!m_nLockCount))
  718. {
  719. PrepareUndo(chnMod4);
  720. if (pModDoc->SetChannelDefaultPan(nChn, static_cast<uint16>(pan)))
  721. {
  722. m_sbPan[chnMod4].SetPos(pan / 4);
  723. pModDoc->UpdateAllViews(this, GeneralHint(nChn).Channels());
  724. // Surround is forced off when changing pan, so uncheck the checkbox.
  725. CheckDlgButton(IDC_CHECK2 + chnMod4 * 2, BST_UNCHECKED);
  726. }
  727. }
  728. }
  729. void CViewGlobals::OnEditPan1() {OnEditPan(0, IDC_EDIT2);}
  730. void CViewGlobals::OnEditPan2() {OnEditPan(1, IDC_EDIT4);}
  731. void CViewGlobals::OnEditPan3() {OnEditPan(2, IDC_EDIT6);}
  732. void CViewGlobals::OnEditPan4() {OnEditPan(3, IDC_EDIT8);}
  733. void CViewGlobals::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
  734. {
  735. CModDoc *pModDoc;
  736. CHANNELINDEX nChn;
  737. CFormView::OnHScroll(nSBCode, nPos, pScrollBar);
  738. pModDoc = GetDocument();
  739. nChn = (CHANNELINDEX)(m_nActiveTab * CHANNELS_IN_TAB);
  740. if ((pModDoc) && (!IsLocked()) && (nChn < MAX_BASECHANNELS))
  741. {
  742. BOOL bUpdate = FALSE;
  743. short int pos;
  744. LockControls();
  745. const CHANNELINDEX nLoopLimit = std::min(static_cast<CHANNELINDEX>(CHANNELS_IN_TAB), static_cast<CHANNELINDEX>(pModDoc->GetSoundFile().GetNumChannels() - nChn));
  746. for (CHANNELINDEX iCh = 0; iCh < nLoopLimit; iCh++)
  747. {
  748. if(pScrollBar == (CScrollBar *) &m_sbVolume[iCh])
  749. {
  750. // Volume sliders
  751. pos = (short int)m_sbVolume[iCh].GetPos();
  752. if ((pos >= 0) && (pos <= 64))
  753. {
  754. PrepareUndo(iCh);
  755. if (pModDoc->SetChannelGlobalVolume(nChn + iCh, pos))
  756. {
  757. SetDlgItemInt(IDC_EDIT1 + iCh * 2, pos);
  758. bUpdate = TRUE;
  759. }
  760. }
  761. } else if(pScrollBar == (CScrollBar *) &m_sbPan[iCh])
  762. {
  763. // Pan sliders
  764. pos = (short int)m_sbPan[iCh].GetPos();
  765. if(pos >= 0 && pos <= 64 && (static_cast<uint16>(pos) != pModDoc->GetSoundFile().ChnSettings[nChn+iCh].nPan / 4u))
  766. {
  767. PrepareUndo(iCh);
  768. if (pModDoc->SetChannelDefaultPan(nChn + iCh, pos * 4))
  769. {
  770. SetDlgItemInt(IDC_EDIT2 + iCh * 2, pos * 4);
  771. CheckDlgButton(IDC_CHECK2 + iCh * 2, BST_UNCHECKED);
  772. bUpdate = TRUE;
  773. }
  774. }
  775. }
  776. }
  777. if ((pScrollBar) && (pScrollBar->m_hWnd == m_sbDryRatio.m_hWnd))
  778. {
  779. int n = 100 - m_sbDryRatio.GetPos();
  780. if ((n >= 0) && (n <= 100) && (m_nCurrentPlugin < MAX_MIXPLUGINS))
  781. {
  782. SNDMIXPLUGIN &plugin = pModDoc->GetSoundFile().m_MixPlugins[m_nCurrentPlugin];
  783. if(plugin.pMixPlugin)
  784. {
  785. plugin.fDryRatio = static_cast<float>(n) / 100.0f;
  786. SetPluginModified();
  787. }
  788. UpdateDryWetDisplay();
  789. }
  790. }
  791. if (bUpdate) pModDoc->UpdateAllViews(this, GeneralHint(nChn).Channels());
  792. UnlockControls();
  793. if ((pScrollBar) && (pScrollBar->m_hWnd == m_sbValue.m_hWnd))
  794. {
  795. int n = (short int)m_sbValue.GetPos();
  796. if ((n >= 0) && (n <= 100) && (m_nCurrentPlugin < MAX_MIXPLUGINS))
  797. {
  798. IMixPlugin *pPlugin = GetCurrentPlugin();
  799. if(pPlugin != nullptr)
  800. {
  801. const PlugParamIndex nParams = pPlugin->GetNumParameters();
  802. if(m_nCurrentParam < nParams)
  803. {
  804. if (nSBCode == SB_THUMBPOSITION || nSBCode == SB_THUMBTRACK || nSBCode == SB_ENDSCROLL)
  805. {
  806. pPlugin->SetScaledUIParam(m_nCurrentParam, 0.01f * n);
  807. OnParamChanged();
  808. SetPluginModified();
  809. }
  810. }
  811. }
  812. }
  813. }
  814. }
  815. }
  816. void CViewGlobals::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
  817. {
  818. CModDoc *pModDoc = GetDocument();
  819. if((m_nCurrentPlugin >= MAX_MIXPLUGINS) || (!pModDoc)) return;
  820. CSoundFile &sndFile = pModDoc->GetSoundFile();
  821. TCHAR s[32];
  822. if(nSBCode != SB_ENDSCROLL && pScrollBar && pScrollBar == (CScrollBar*)&m_SpinMixGain)
  823. {
  824. SNDMIXPLUGIN &plugin = sndFile.m_MixPlugins[m_nCurrentPlugin];
  825. if(plugin.pMixPlugin)
  826. {
  827. uint8 gain = (uint8)nPos;
  828. if(gain == 0) gain = 1;
  829. plugin.SetGain(gain);
  830. float fValue = 0.1f * (float)gain;
  831. _stprintf(s, _T("Gain: x%1.1f"), fValue);
  832. CEdit *gainEdit = (CEdit *)GetDlgItem(IDC_EDIT16);
  833. gainEdit->SetWindowText(s);
  834. SetPluginModified();
  835. }
  836. }
  837. CFormView::OnVScroll(nSBCode, nPos, pScrollBar);
  838. }
  839. void CViewGlobals::OnEditName(const CHANNELINDEX chnMod4, const UINT itemID)
  840. {
  841. CModDoc *pModDoc = GetDocument();
  842. if ((pModDoc) && (!m_nLockCount))
  843. {
  844. CSoundFile &sndFile = pModDoc->GetSoundFile();
  845. const CHANNELINDEX nChn = m_nActiveTab * CHANNELS_IN_TAB + chnMod4;
  846. CString tmp;
  847. GetDlgItemText(itemID, tmp);
  848. const std::string s = mpt::ToCharset(sndFile.GetCharsetInternal(), tmp);
  849. if ((sndFile.GetType() & (MOD_TYPE_XM|MOD_TYPE_IT|MOD_TYPE_MPT)) && (nChn < sndFile.GetNumChannels()) && (s != sndFile.ChnSettings[nChn].szName))
  850. {
  851. PrepareUndo(chnMod4);
  852. sndFile.ChnSettings[nChn].szName = s;
  853. pModDoc->SetModified();
  854. pModDoc->UpdateAllViews(this, GeneralHint(nChn).Channels());
  855. }
  856. }
  857. }
  858. void CViewGlobals::OnEditName1() {OnEditName(0, IDC_EDIT9);}
  859. void CViewGlobals::OnEditName2() {OnEditName(1, IDC_EDIT10);}
  860. void CViewGlobals::OnEditName3() {OnEditName(2, IDC_EDIT11);}
  861. void CViewGlobals::OnEditName4() {OnEditName(3, IDC_EDIT12);}
  862. void CViewGlobals::OnFxChanged(const CHANNELINDEX chnMod4)
  863. {
  864. CModDoc *pModDoc = GetDocument();
  865. if (pModDoc)
  866. {
  867. CSoundFile &sndFile = pModDoc->GetSoundFile();
  868. CHANNELINDEX nChn = m_nActiveTab * CHANNELS_IN_TAB + chnMod4;
  869. int nfx = static_cast<int>(m_CbnEffects[chnMod4].GetItemData(m_CbnEffects[chnMod4].GetCurSel()));
  870. if ((nfx >= 0) && (nfx <= MAX_MIXPLUGINS) && (nChn < sndFile.GetNumChannels())
  871. && (sndFile.ChnSettings[nChn].nMixPlugin != (UINT)nfx))
  872. {
  873. PrepareUndo(chnMod4);
  874. sndFile.ChnSettings[nChn].nMixPlugin = (PLUGINDEX)nfx;
  875. if(sndFile.GetModSpecifications().supportsPlugins)
  876. pModDoc->SetModified();
  877. pModDoc->UpdateAllViews(this, GeneralHint(nChn).Channels());
  878. }
  879. }
  880. }
  881. void CViewGlobals::OnFx1Changed() {OnFxChanged(0);}
  882. void CViewGlobals::OnFx2Changed() {OnFxChanged(1);}
  883. void CViewGlobals::OnFx3Changed() {OnFxChanged(2);}
  884. void CViewGlobals::OnFx4Changed() {OnFxChanged(3);}
  885. void CViewGlobals::OnPluginNameChanged()
  886. {
  887. CModDoc *pModDoc = GetDocument();
  888. if ((pModDoc) && (m_nCurrentPlugin < MAX_MIXPLUGINS))
  889. {
  890. CSoundFile &sndFile = pModDoc->GetSoundFile();
  891. SNDMIXPLUGIN &plugin = sndFile.m_MixPlugins[m_nCurrentPlugin];
  892. CString s;
  893. GetDlgItemText(IDC_EDIT13, s);
  894. if (s != mpt::ToCString(plugin.GetName()))
  895. {
  896. plugin.Info.szName = mpt::ToCharset(mpt::Charset::Locale, s);
  897. if(sndFile.GetModSpecifications().supportsPlugins)
  898. pModDoc->SetModified();
  899. pModDoc->UpdateAllViews(this, PluginHint(m_nCurrentPlugin + 1).Info().Names(), this);
  900. IMixPlugin *pPlugin = plugin.pMixPlugin;
  901. if(pPlugin != nullptr && pPlugin->GetEditor() != nullptr)
  902. {
  903. pPlugin->GetEditor()->SetTitle();
  904. }
  905. // Update channel plugin assignments
  906. PopulateChannelPlugins(m_nCurrentPlugin);
  907. m_CbnPlugin.SetRedraw(FALSE);
  908. AddPluginNamesToCombobox(m_CbnPlugin, sndFile.m_MixPlugins, true, m_nCurrentPlugin);
  909. m_CbnPlugin.SetCurSel(m_nCurrentPlugin);
  910. m_CbnPlugin.Invalidate(FALSE);
  911. m_CbnPlugin.SetRedraw(TRUE);
  912. }
  913. }
  914. }
  915. void CViewGlobals::OnPrevPlugin()
  916. {
  917. CModDoc *pModDoc = GetDocument();
  918. if ((m_nCurrentPlugin > 0) && (pModDoc))
  919. {
  920. m_nCurrentPlugin--;
  921. UpdateView(PluginHint(m_nCurrentPlugin + 1).Info());
  922. }
  923. }
  924. void CViewGlobals::OnNextPlugin()
  925. {
  926. CModDoc *pModDoc = GetDocument();
  927. if ((m_nCurrentPlugin < MAX_MIXPLUGINS-1) && (pModDoc))
  928. {
  929. m_nCurrentPlugin++;
  930. UpdateView(PluginHint(m_nCurrentPlugin + 1).Info());
  931. }
  932. }
  933. void CViewGlobals::OnPluginChanged()
  934. {
  935. CModDoc *pModDoc = GetDocument();
  936. int nPlugin = m_CbnPlugin.GetCurSel();
  937. if ((pModDoc) && (nPlugin >= 0) && (nPlugin < MAX_MIXPLUGINS))
  938. {
  939. m_nCurrentPlugin = (PLUGINDEX)nPlugin;
  940. UpdateView(PluginHint(m_nCurrentPlugin + 1).Info());
  941. }
  942. m_CbnPreset.SetCurSel(0);
  943. }
  944. void CViewGlobals::OnSelectPlugin()
  945. {
  946. #ifndef NO_PLUGINS
  947. CModDoc *pModDoc = GetDocument();
  948. if ((pModDoc) && (m_nCurrentPlugin < MAX_MIXPLUGINS))
  949. {
  950. CSelectPluginDlg dlg(pModDoc, m_nCurrentPlugin, this);
  951. if (dlg.DoModal() == IDOK)
  952. {
  953. if(pModDoc->GetSoundFile().GetModSpecifications().supportsPlugins)
  954. pModDoc->SetModified();
  955. }
  956. OnPluginChanged();
  957. OnParamChanged();
  958. }
  959. #endif // NO_PLUGINS
  960. }
  961. void CViewGlobals::OnRemovePlugin()
  962. {
  963. #ifndef NO_PLUGINS
  964. CModDoc *pModDoc = GetDocument();
  965. 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)
  966. {
  967. if(pModDoc->RemovePlugin(m_nCurrentPlugin))
  968. {
  969. OnPluginChanged();
  970. OnParamChanged();
  971. }
  972. }
  973. #endif // NO_PLUGINS
  974. }
  975. LRESULT CViewGlobals::OnParamAutomated(WPARAM plugin, LPARAM param)
  976. {
  977. if(plugin == m_nCurrentPlugin && static_cast<PlugParamIndex>(param) == m_nCurrentParam)
  978. {
  979. OnParamChanged();
  980. }
  981. return 0;
  982. }
  983. LRESULT CViewGlobals::OnDryWetRatioChangedFromPlayer(WPARAM plugin, LPARAM)
  984. {
  985. if(plugin == m_nCurrentPlugin)
  986. {
  987. UpdateDryWetDisplay();
  988. }
  989. return 0;
  990. }
  991. void CViewGlobals::OnParamChanged()
  992. {
  993. PlugParamIndex cursel = static_cast<PlugParamIndex>(m_CbnParam.GetItemData(m_CbnParam.GetCurSel()));
  994. IMixPlugin *pPlugin = GetCurrentPlugin();
  995. if(pPlugin != nullptr && cursel != static_cast<PlugParamIndex>(CB_ERR))
  996. {
  997. const PlugParamIndex nParams = pPlugin->GetNumParameters();
  998. if(cursel < nParams) m_nCurrentParam = cursel;
  999. if(m_nCurrentParam < nParams)
  1000. {
  1001. const auto value = pPlugin->GetScaledUIParam(m_nCurrentParam);
  1002. int intValue = mpt::saturate_round<int>(value * 100.0f);
  1003. LockControls();
  1004. if(GetFocus() != GetDlgItem(IDC_EDIT14))
  1005. {
  1006. CString s = pPlugin->GetFormattedParamValue(m_nCurrentParam).Trim();
  1007. if(s.IsEmpty())
  1008. {
  1009. s.Format(_T("%f"), value);
  1010. }
  1011. SetDlgItemText(IDC_EDIT14, s);
  1012. }
  1013. m_sbValue.SetPos(intValue);
  1014. UnlockControls();
  1015. return;
  1016. }
  1017. }
  1018. SetDlgItemText(IDC_EDIT14, _T(""));
  1019. m_sbValue.SetPos(0);
  1020. }
  1021. // When focussing the parameter value, show its real value to edit
  1022. void CViewGlobals::OnFocusParam()
  1023. {
  1024. IMixPlugin *pPlugin = GetCurrentPlugin();
  1025. if(pPlugin != nullptr)
  1026. {
  1027. const PlugParamIndex nParams = pPlugin->GetNumParameters();
  1028. if(m_nCurrentParam < nParams)
  1029. {
  1030. TCHAR s[32];
  1031. float fValue = pPlugin->GetScaledUIParam(m_nCurrentParam);
  1032. _stprintf(s, _T("%f"), fValue);
  1033. LockControls();
  1034. SetDlgItemText(IDC_EDIT14, s);
  1035. UnlockControls();
  1036. }
  1037. }
  1038. }
  1039. void CViewGlobals::SetPluginModified()
  1040. {
  1041. CModDoc *pModDoc = GetDocument();
  1042. if(pModDoc->GetSoundFile().GetModSpecifications().supportsPlugins)
  1043. pModDoc->SetModified();
  1044. pModDoc->UpdateAllViews(this, PluginHint(m_nCurrentPlugin + 1).Info());
  1045. }
  1046. void CViewGlobals::OnProgramChanged()
  1047. {
  1048. int32 curProg = static_cast<int32>(m_CbnPreset.GetItemData(m_CbnPreset.GetCurSel()));
  1049. IMixPlugin *pPlugin = GetCurrentPlugin();
  1050. if(pPlugin != nullptr)
  1051. {
  1052. const int32 numProgs = pPlugin->GetNumPrograms();
  1053. if(curProg <= numProgs)
  1054. {
  1055. pPlugin->SetCurrentProgram(curProg);
  1056. // Update parameter display
  1057. OnParamChanged();
  1058. SetPluginModified();
  1059. }
  1060. }
  1061. }
  1062. void CViewGlobals::OnLoadParam()
  1063. {
  1064. IMixPlugin *pPlugin = GetCurrentPlugin();
  1065. if(pPlugin != nullptr && pPlugin->LoadProgram())
  1066. {
  1067. int32 currentProg = pPlugin->GetCurrentProgram();
  1068. FillPluginProgramBox(currentProg, currentProg);
  1069. m_CbnPreset.SetCurSel(0);
  1070. SetPluginModified();
  1071. }
  1072. }
  1073. void CViewGlobals::OnSaveParam()
  1074. {
  1075. IMixPlugin *pPlugin = GetCurrentPlugin();
  1076. if(pPlugin != nullptr)
  1077. {
  1078. pPlugin->SaveProgram();
  1079. }
  1080. }
  1081. void CViewGlobals::OnSetParameter()
  1082. {
  1083. if(m_nCurrentPlugin >= MAX_MIXPLUGINS || IsLocked()) return;
  1084. IMixPlugin *pPlugin = GetCurrentPlugin();
  1085. if(pPlugin != nullptr)
  1086. {
  1087. const PlugParamIndex nParams = pPlugin->GetNumParameters();
  1088. TCHAR s[32];
  1089. GetDlgItemText(IDC_EDIT14, s, mpt::saturate_cast<int>(std::size(s)));
  1090. if ((m_nCurrentParam < nParams) && (s[0]))
  1091. {
  1092. float fValue = (float)_tstof(s);
  1093. pPlugin->SetScaledUIParam(m_nCurrentParam, fValue);
  1094. OnParamChanged();
  1095. SetPluginModified();
  1096. }
  1097. }
  1098. }
  1099. void CViewGlobals::UpdateDryWetDisplay()
  1100. {
  1101. SNDMIXPLUGIN &plugin = GetDocument()->GetSoundFile().m_MixPlugins[m_nCurrentPlugin];
  1102. float wetRatio = 1.0f - plugin.fDryRatio, dryRatio = plugin.fDryRatio;
  1103. m_sbDryRatio.SetPos(mpt::saturate_round<int>(wetRatio * 100));
  1104. if(plugin.IsExpandedMix())
  1105. {
  1106. wetRatio = 2.0f * wetRatio - 1.0f;
  1107. dryRatio = -wetRatio;
  1108. }
  1109. int wetInt = mpt::saturate_round<int>(wetRatio * 100), dryInt = mpt::saturate_round<int>(dryRatio * 100);
  1110. SetDlgItemText(IDC_STATIC8, MPT_TFORMAT("{}% wet, {}% dry")(wetInt, dryInt).c_str());
  1111. }
  1112. void CViewGlobals::OnMixModeChanged()
  1113. {
  1114. CModDoc *pModDoc = GetDocument();
  1115. if ((m_nCurrentPlugin >= MAX_MIXPLUGINS) || (!pModDoc)) return;
  1116. pModDoc->GetSoundFile().m_MixPlugins[m_nCurrentPlugin].SetMasterEffect(IsDlgButtonChecked(IDC_CHECK9) != BST_UNCHECKED);
  1117. SetPluginModified();
  1118. }
  1119. void CViewGlobals::OnBypassChanged()
  1120. {
  1121. CModDoc *pModDoc = GetDocument();
  1122. if ((m_nCurrentPlugin >= MAX_MIXPLUGINS) || (!pModDoc)) return;
  1123. pModDoc->GetSoundFile().m_MixPlugins[m_nCurrentPlugin].SetBypass(IsDlgButtonChecked(IDC_CHECK10) != BST_UNCHECKED);
  1124. SetPluginModified();
  1125. }
  1126. void CViewGlobals::OnWetDryExpandChanged()
  1127. {
  1128. CModDoc *pModDoc = GetDocument();
  1129. if ((m_nCurrentPlugin >= MAX_MIXPLUGINS) || (!pModDoc)) return;
  1130. pModDoc->GetSoundFile().m_MixPlugins[m_nCurrentPlugin].SetExpandedMix(IsDlgButtonChecked(IDC_CHECK12) != BST_UNCHECKED);
  1131. UpdateDryWetDisplay();
  1132. SetPluginModified();
  1133. }
  1134. void CViewGlobals::OnSpecialMixProcessingChanged()
  1135. {
  1136. CModDoc *pModDoc = GetDocument();
  1137. if ((m_nCurrentPlugin >= MAX_MIXPLUGINS) || (!pModDoc)) return;
  1138. pModDoc->GetSoundFile().m_MixPlugins[m_nCurrentPlugin].SetMixMode((uint8)m_CbnSpecialMixProcessing.GetCurSel());
  1139. SetPluginModified();
  1140. }
  1141. void CViewGlobals::OnDryMixChanged()
  1142. {
  1143. CModDoc *pModDoc = GetDocument();
  1144. if ((m_nCurrentPlugin >= MAX_MIXPLUGINS) || (!pModDoc)) return;
  1145. pModDoc->GetSoundFile().m_MixPlugins[m_nCurrentPlugin].SetWetMix(IsDlgButtonChecked(IDC_CHECK11) != BST_UNCHECKED);
  1146. SetPluginModified();
  1147. }
  1148. void CViewGlobals::OnEditPlugin()
  1149. {
  1150. CModDoc *pModDoc = GetDocument();
  1151. if ((m_nCurrentPlugin >= MAX_MIXPLUGINS) || (!pModDoc)) return;
  1152. pModDoc->TogglePluginEditor(m_nCurrentPlugin, CMainFrame::GetInputHandler()->ShiftPressed());
  1153. return;
  1154. }
  1155. void CViewGlobals::OnOutputRoutingChanged()
  1156. {
  1157. CModDoc *pModDoc = GetDocument();
  1158. int nroute;
  1159. if ((m_nCurrentPlugin >= MAX_MIXPLUGINS) || (!pModDoc)) return;
  1160. CSoundFile &sndFile = pModDoc->GetSoundFile();
  1161. SNDMIXPLUGIN &plugin = sndFile.m_MixPlugins[m_nCurrentPlugin];
  1162. nroute = static_cast<int>(m_CbnOutput.GetItemData(m_CbnOutput.GetCurSel()));
  1163. if(nroute == 1)
  1164. {
  1165. // Add new plugin
  1166. for(PLUGINDEX i = m_nCurrentPlugin + 1; i < MAX_MIXPLUGINS; i++)
  1167. {
  1168. if(!sndFile.m_MixPlugins[i].IsValidPlugin())
  1169. {
  1170. CSelectPluginDlg dlg(pModDoc, i, this);
  1171. if(dlg.DoModal() != IDOK)
  1172. return;
  1173. plugin.SetOutputPlugin(i);
  1174. SetPluginModified();
  1175. nroute = 0x80 + i;
  1176. m_nCurrentPlugin = i;
  1177. m_CbnPlugin.SetCurSel(i);
  1178. OnPluginChanged();
  1179. break;
  1180. }
  1181. }
  1182. if(nroute == 1)
  1183. {
  1184. Reporting::Error("All following plugin slots are used.", "Unable to add new plugin");
  1185. return;
  1186. }
  1187. }
  1188. if(!nroute)
  1189. plugin.SetOutputToMaster();
  1190. else
  1191. plugin.SetOutputPlugin(static_cast<PLUGINDEX>(nroute - 0x80));
  1192. SetPluginModified();
  1193. }
  1194. LRESULT CViewGlobals::OnModViewMsg(WPARAM wParam, LPARAM /*lParam*/)
  1195. {
  1196. switch(wParam)
  1197. {
  1198. case VIEWMSG_SETFOCUS:
  1199. case VIEWMSG_SETACTIVE:
  1200. GetParentFrame()->SetActiveView(this);
  1201. SetFocus();
  1202. return 0;
  1203. default:
  1204. return 0;
  1205. }
  1206. }
  1207. void CViewGlobals::OnMovePlugToSlot()
  1208. {
  1209. if(GetCurrentPlugin() == nullptr)
  1210. {
  1211. return;
  1212. }
  1213. // If any plugin routes its output to the current plugin, we shouldn't try to move it before that plugin...
  1214. PLUGINDEX defaultIndex = 0;
  1215. CSoundFile &sndFile = GetDocument()->GetSoundFile();
  1216. for(PLUGINDEX i = 0; i < m_nCurrentPlugin; i++)
  1217. {
  1218. if(sndFile.m_MixPlugins[i].GetOutputPlugin() == m_nCurrentPlugin)
  1219. {
  1220. defaultIndex = i + 1;
  1221. }
  1222. }
  1223. std::vector<PLUGINDEX> emptySlots;
  1224. BuildEmptySlotList(emptySlots);
  1225. CMoveFXSlotDialog dlg(this, m_nCurrentPlugin, emptySlots, defaultIndex, false, !sndFile.m_MixPlugins[m_nCurrentPlugin].IsOutputToMaster());
  1226. if(dlg.DoModal() == IDOK)
  1227. {
  1228. size_t toIndex = dlg.GetSlotIndex();
  1229. do
  1230. {
  1231. const SNDMIXPLUGIN &curPlugin = sndFile.m_MixPlugins[m_nCurrentPlugin];
  1232. SNDMIXPLUGIN &newPlugin = sndFile.m_MixPlugins[emptySlots[toIndex]];
  1233. const PLUGINDEX nextPlugin = curPlugin.GetOutputPlugin();
  1234. MovePlug(m_nCurrentPlugin, emptySlots[toIndex]);
  1235. if(nextPlugin == PLUGINDEX_INVALID || toIndex == emptySlots.size() - 1)
  1236. {
  1237. break;
  1238. }
  1239. m_nCurrentPlugin = nextPlugin;
  1240. if(dlg.DoMoveChain())
  1241. {
  1242. toIndex++;
  1243. newPlugin.SetOutputPlugin(emptySlots[toIndex]);
  1244. }
  1245. } while(dlg.DoMoveChain());
  1246. m_CbnPlugin.SetCurSel(dlg.GetSlot());
  1247. OnPluginChanged();
  1248. GetDocument()->UpdateAllViews(nullptr, PluginHint().Names().Info());
  1249. }
  1250. }
  1251. // Functor for adjusting plug indexes in modcommands. Adjusts all instrument column values in
  1252. // range [m_nInstrMin, m_nInstrMax] by m_nDiff.
  1253. struct PlugIndexModifier
  1254. {
  1255. PlugIndexModifier(PLUGINDEX nMin, PLUGINDEX nMax, int nDiff) :
  1256. m_nDiff(nDiff), m_nInstrMin(nMin), m_nInstrMax(nMax) {}
  1257. void operator()(ModCommand& m)
  1258. {
  1259. if (m.IsInstrPlug() && m.instr >= m_nInstrMin && m.instr <= m_nInstrMax)
  1260. m.instr = (ModCommand::INSTR)((int)m.instr + m_nDiff);
  1261. }
  1262. int m_nDiff;
  1263. ModCommand::INSTR m_nInstrMin;
  1264. ModCommand::INSTR m_nInstrMax;
  1265. };
  1266. bool CViewGlobals::MovePlug(PLUGINDEX src, PLUGINDEX dest, bool bAdjustPat)
  1267. {
  1268. if (src == dest)
  1269. return false;
  1270. CModDoc *pModDoc = GetDocument();
  1271. CSoundFile &sndFile = pModDoc->GetSoundFile();
  1272. BeginWaitCursor();
  1273. CriticalSection cs;
  1274. // Move plug data
  1275. sndFile.m_MixPlugins[dest] = std::move(sndFile.m_MixPlugins[src]);
  1276. sndFile.m_MixPlugins[src] = SNDMIXPLUGIN();
  1277. // Prevent plug from pointing backwards.
  1278. if(!sndFile.m_MixPlugins[dest].IsOutputToMaster())
  1279. {
  1280. PLUGINDEX nOutput = sndFile.m_MixPlugins[dest].GetOutputPlugin();
  1281. if (nOutput <= dest && nOutput != PLUGINDEX_INVALID)
  1282. {
  1283. sndFile.m_MixPlugins[dest].SetOutputToMaster();
  1284. }
  1285. }
  1286. // Update current plug
  1287. IMixPlugin *pPlugin = sndFile.m_MixPlugins[dest].pMixPlugin;
  1288. if(pPlugin != nullptr)
  1289. {
  1290. pPlugin->SetSlot(dest);
  1291. if(pPlugin->GetEditor() != nullptr)
  1292. {
  1293. pPlugin->GetEditor()->SetTitle();
  1294. }
  1295. }
  1296. // Update all other plugs' outputs
  1297. for (PLUGINDEX nPlug = 0; nPlug < src; nPlug++)
  1298. {
  1299. if(!sndFile.m_MixPlugins[nPlug].IsOutputToMaster())
  1300. {
  1301. if(sndFile.m_MixPlugins[nPlug].GetOutputPlugin() == src)
  1302. {
  1303. sndFile.m_MixPlugins[nPlug].SetOutputPlugin(dest);
  1304. }
  1305. }
  1306. }
  1307. // Update channels
  1308. for (CHANNELINDEX nChn = 0; nChn < sndFile.GetNumChannels(); nChn++)
  1309. {
  1310. if (sndFile.ChnSettings[nChn].nMixPlugin == src + 1u)
  1311. {
  1312. sndFile.ChnSettings[nChn].nMixPlugin = dest + 1u;
  1313. }
  1314. }
  1315. // Update instruments
  1316. for (INSTRUMENTINDEX nIns = 1; nIns <= sndFile.GetNumInstruments(); nIns++)
  1317. {
  1318. if (sndFile.Instruments[nIns] && (sndFile.Instruments[nIns]->nMixPlug == src + 1))
  1319. {
  1320. sndFile.Instruments[nIns]->nMixPlug = dest + 1u;
  1321. }
  1322. }
  1323. // Update MODCOMMANDs so that they won't be referring to old indexes (e.g. with NOTE_PC).
  1324. if (bAdjustPat && sndFile.GetModSpecifications().HasNote(NOTE_PC))
  1325. sndFile.Patterns.ForEachModCommand(PlugIndexModifier(src + 1, src + 1, int(dest) - int(src)));
  1326. cs.Leave();
  1327. SetPluginModified();
  1328. EndWaitCursor();
  1329. return true;
  1330. }
  1331. void CViewGlobals::BuildEmptySlotList(std::vector<PLUGINDEX> &emptySlots)
  1332. {
  1333. const CSoundFile &sndFile = GetDocument()->GetSoundFile();
  1334. emptySlots.clear();
  1335. for(PLUGINDEX nSlot = 0; nSlot < MAX_MIXPLUGINS; nSlot++)
  1336. {
  1337. if(sndFile.m_MixPlugins[nSlot].pMixPlugin == nullptr)
  1338. {
  1339. emptySlots.push_back(nSlot);
  1340. }
  1341. }
  1342. return;
  1343. }
  1344. void CViewGlobals::OnInsertSlot()
  1345. {
  1346. CString prompt;
  1347. CSoundFile &sndFile = GetDocument()->GetSoundFile();
  1348. prompt.Format(_T("Insert empty slot before slot FX%d?"), m_nCurrentPlugin + 1);
  1349. // If last plugin slot is occupied, move it so that the plugin is not lost.
  1350. // This could certainly be improved...
  1351. bool moveLastPlug = false;
  1352. if(sndFile.m_MixPlugins[MAX_MIXPLUGINS - 1].pMixPlugin)
  1353. {
  1354. if(sndFile.m_MixPlugins[MAX_MIXPLUGINS - 2].pMixPlugin == nullptr)
  1355. {
  1356. moveLastPlug = true;
  1357. } else
  1358. {
  1359. prompt += _T("\nWarning: plugin data in last slot will be lost.");
  1360. }
  1361. }
  1362. if(Reporting::Confirm(prompt) == cnfYes)
  1363. {
  1364. // Delete last plug...
  1365. if(sndFile.m_MixPlugins[MAX_MIXPLUGINS - 1].pMixPlugin)
  1366. {
  1367. if(moveLastPlug)
  1368. {
  1369. MovePlug(MAX_MIXPLUGINS - 1, MAX_MIXPLUGINS - 2, true);
  1370. } else
  1371. {
  1372. sndFile.m_MixPlugins[MAX_MIXPLUGINS - 1].Destroy();
  1373. MemsetZero(sndFile.m_MixPlugins[MAX_MIXPLUGINS - 1].Info);
  1374. }
  1375. }
  1376. // Update MODCOMMANDs so that they won't be referring to old indexes (e.g. with NOTE_PC).
  1377. if(sndFile.GetModSpecifications().HasNote(NOTE_PC))
  1378. sndFile.Patterns.ForEachModCommand(PlugIndexModifier(m_nCurrentPlugin + 1, MAX_MIXPLUGINS - 1, 1));
  1379. for(PLUGINDEX nSlot = MAX_MIXPLUGINS - 1; nSlot > m_nCurrentPlugin; nSlot--)
  1380. {
  1381. if(sndFile.m_MixPlugins[nSlot-1].pMixPlugin)
  1382. {
  1383. MovePlug(nSlot - 1, nSlot, NoPatternAdjust);
  1384. }
  1385. }
  1386. m_CbnPlugin.SetCurSel(m_nCurrentPlugin + 1);
  1387. OnPluginChanged();
  1388. GetDocument()->UpdateAllViews(nullptr, PluginHint().Names().Info());
  1389. SetPluginModified();
  1390. }
  1391. }
  1392. void CViewGlobals::OnClonePlug()
  1393. {
  1394. if(GetCurrentPlugin() == nullptr)
  1395. {
  1396. return;
  1397. }
  1398. CSoundFile &sndFile = GetDocument()->GetSoundFile();
  1399. std::vector<PLUGINDEX> emptySlots;
  1400. BuildEmptySlotList(emptySlots);
  1401. CMoveFXSlotDialog dlg(this, m_nCurrentPlugin, emptySlots, 0, true, !sndFile.m_MixPlugins[m_nCurrentPlugin].IsOutputToMaster());
  1402. if(dlg.DoModal() == IDOK)
  1403. {
  1404. size_t toIndex = dlg.GetSlotIndex();
  1405. do
  1406. {
  1407. const SNDMIXPLUGIN &curPlugin = sndFile.m_MixPlugins[m_nCurrentPlugin];
  1408. SNDMIXPLUGIN &newPlugin = sndFile.m_MixPlugins[emptySlots[toIndex]];
  1409. GetDocument()->ClonePlugin(newPlugin, curPlugin);
  1410. IMixPlugin *mixPlug = newPlugin.pMixPlugin;
  1411. if(mixPlug != nullptr && mixPlug->IsInstrument() && GetDocument()->HasInstrumentForPlugin(emptySlots[toIndex]) == INSTRUMENTINDEX_INVALID)
  1412. {
  1413. GetDocument()->InsertInstrumentForPlugin(emptySlots[toIndex]);
  1414. }
  1415. if(curPlugin.IsOutputToMaster() || toIndex == emptySlots.size() - 1)
  1416. {
  1417. break;
  1418. }
  1419. m_nCurrentPlugin = curPlugin.GetOutputPlugin();
  1420. if(dlg.DoMoveChain())
  1421. {
  1422. toIndex++;
  1423. newPlugin.SetOutputPlugin(emptySlots[toIndex]);
  1424. }
  1425. } while(dlg.DoMoveChain());
  1426. m_CbnPlugin.SetCurSel(dlg.GetSlot());
  1427. OnPluginChanged();
  1428. PopulateChannelPlugins();
  1429. GetDocument()->UpdateAllViews(this, PluginHint().Names(), this);
  1430. SetPluginModified();
  1431. }
  1432. }
  1433. // The plugin param box is only filled when it gets the focus (done here).
  1434. void CViewGlobals::OnFillParamCombo()
  1435. {
  1436. // no need to fill it again.
  1437. if(m_CbnParam.GetCount() > 1)
  1438. return;
  1439. IMixPlugin *pPlugin = GetCurrentPlugin();
  1440. if(pPlugin == nullptr) return;
  1441. const PlugParamIndex nParams = pPlugin->GetNumParameters();
  1442. m_CbnParam.SetRedraw(FALSE);
  1443. m_CbnParam.ResetContent();
  1444. AddPluginParameternamesToCombobox(m_CbnParam, *pPlugin);
  1445. if (m_nCurrentParam >= nParams) m_nCurrentParam = 0;
  1446. m_CbnParam.SetCurSel(m_nCurrentParam);
  1447. m_CbnParam.SetRedraw(TRUE);
  1448. m_CbnParam.Invalidate(FALSE);
  1449. }
  1450. // The preset box is only filled when it gets the focus (done here).
  1451. void CViewGlobals::OnFillProgramCombo()
  1452. {
  1453. // no need to fill it again.
  1454. if(m_CbnPreset.GetCount() > 1)
  1455. return;
  1456. IMixPlugin *pPlugin = GetCurrentPlugin();
  1457. if(pPlugin == nullptr) return;
  1458. FillPluginProgramBox(0, pPlugin->GetNumPrograms() - 1);
  1459. m_CbnPreset.SetCurSel(pPlugin->GetCurrentProgram());
  1460. }
  1461. void CViewGlobals::FillPluginProgramBox(int32 firstProg, int32 lastProg)
  1462. {
  1463. IMixPlugin *pPlugin = GetCurrentPlugin();
  1464. m_CbnPreset.SetRedraw(FALSE);
  1465. m_CbnPreset.ResetContent();
  1466. pPlugin->CacheProgramNames(firstProg, lastProg + 1);
  1467. for (int32 i = firstProg; i <= lastProg; i++)
  1468. {
  1469. m_CbnPreset.SetItemData(m_CbnPreset.AddString(pPlugin->GetFormattedProgramName(i)), i);
  1470. }
  1471. m_CbnPreset.SetRedraw(TRUE);
  1472. m_CbnPreset.Invalidate(FALSE);
  1473. }
  1474. BOOL CViewGlobals::OnToolTipText(UINT, NMHDR *pNMHDR, LRESULT *pResult)
  1475. {
  1476. auto pTTT = reinterpret_cast<TOOLTIPTEXT *>(pNMHDR);
  1477. UINT_PTR id = pNMHDR->idFrom;
  1478. if(pTTT->uFlags & TTF_IDISHWND)
  1479. {
  1480. // idFrom is actually the HWND of the tool
  1481. id = static_cast<UINT_PTR>(::GetDlgCtrlID(reinterpret_cast<HWND>(id)));
  1482. }
  1483. mpt::tstring text;
  1484. const auto &chnSettings = GetDocument()->GetSoundFile().ChnSettings;
  1485. switch(id)
  1486. {
  1487. case IDC_EDIT1:
  1488. case IDC_EDIT3:
  1489. case IDC_EDIT5:
  1490. case IDC_EDIT7:
  1491. text = CModDoc::LinearToDecibels(chnSettings[m_nActiveTab * CHANNELS_IN_TAB + (id - IDC_EDIT1) / 2].nVolume, 64.0);
  1492. break;
  1493. case IDC_SLIDER1:
  1494. case IDC_SLIDER3:
  1495. case IDC_SLIDER5:
  1496. case IDC_SLIDER7:
  1497. text = CModDoc::LinearToDecibels(chnSettings[m_nActiveTab * CHANNELS_IN_TAB + (id - IDC_SLIDER1) / 2].nVolume, 64.0);
  1498. break;
  1499. case IDC_EDIT2:
  1500. case IDC_EDIT4:
  1501. case IDC_EDIT6:
  1502. case IDC_EDIT8:
  1503. text = CModDoc::PanningToString(chnSettings[m_nActiveTab * CHANNELS_IN_TAB + (id - IDC_EDIT2) / 2].nPan, 128);
  1504. break;
  1505. case IDC_SLIDER2:
  1506. case IDC_SLIDER4:
  1507. case IDC_SLIDER6:
  1508. case IDC_SLIDER8:
  1509. text = CModDoc::PanningToString(chnSettings[m_nActiveTab * CHANNELS_IN_TAB + (id - IDC_SLIDER2) / 2].nPan, 128);
  1510. break;
  1511. case IDC_EDIT16:
  1512. {
  1513. const auto gain = GetDocument()->GetSoundFile().m_MixPlugins[m_nCurrentPlugin].GetGain();
  1514. text = CModDoc::LinearToDecibels(gain ? gain : 10, 10.0);
  1515. }
  1516. break;
  1517. case IDC_BUTTON5:
  1518. text = _T("Previous Plugin");
  1519. break;
  1520. case IDC_BUTTON4:
  1521. text = _T("Next Plugin");
  1522. break;
  1523. default:
  1524. return FALSE;
  1525. }
  1526. mpt::String::WriteWinBuf(pTTT->szText) = text;
  1527. *pResult = 0;
  1528. // bring the tooltip window above other popup windows
  1529. ::SetWindowPos(pNMHDR->hwndFrom, HWND_TOP, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE | SWP_NOOWNERZORDER);
  1530. return TRUE; // message was handled
  1531. }
  1532. OPENMPT_NAMESPACE_END