View_ins.cpp 80 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954
  1. /*
  2. * view_ins.cpp
  3. * ------------
  4. * Purpose: Instrument 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 "ImageLists.h"
  15. #include "Childfrm.h"
  16. #include "Moddoc.h"
  17. #include "Globals.h"
  18. #include "Ctrl_ins.h"
  19. #include "View_ins.h"
  20. #include "Dlsbank.h"
  21. #include "ChannelManagerDlg.h"
  22. #include "ScaleEnvPointsDlg.h"
  23. #include "../soundlib/MIDIEvents.h"
  24. #include "../soundlib/mod_specifications.h"
  25. #include "../common/mptStringBuffer.h"
  26. #include "FileDialog.h"
  27. OPENMPT_NAMESPACE_BEGIN
  28. namespace
  29. {
  30. const int ENV_POINT_SIZE = 4;
  31. const float ENV_MIN_ZOOM = 2.0f;
  32. const float ENV_MAX_ZOOM = 256.0f;
  33. }
  34. // Non-client toolbar
  35. #define ENV_LEFTBAR_CY Util::ScalePixels(29, m_hWnd)
  36. #define ENV_LEFTBAR_CXSEP Util::ScalePixels(14, m_hWnd)
  37. #define ENV_LEFTBAR_CXSPC Util::ScalePixels(3, m_hWnd)
  38. #define ENV_LEFTBAR_CXBTN Util::ScalePixels(24, m_hWnd)
  39. #define ENV_LEFTBAR_CYBTN Util::ScalePixels(22, m_hWnd)
  40. static constexpr UINT cLeftBarButtons[ENV_LEFTBAR_BUTTONS] =
  41. {
  42. ID_ENVSEL_VOLUME,
  43. ID_ENVSEL_PANNING,
  44. ID_ENVSEL_PITCH,
  45. ID_SEPARATOR,
  46. ID_ENVELOPE_VOLUME,
  47. ID_ENVELOPE_PANNING,
  48. ID_ENVELOPE_PITCH,
  49. ID_ENVELOPE_FILTER,
  50. ID_SEPARATOR,
  51. ID_ENVELOPE_SETLOOP,
  52. ID_ENVELOPE_SUSTAIN,
  53. ID_ENVELOPE_CARRY,
  54. ID_SEPARATOR,
  55. ID_INSTRUMENT_SAMPLEMAP,
  56. ID_SEPARATOR,
  57. ID_ENVELOPE_VIEWGRID,
  58. ID_SEPARATOR,
  59. ID_ENVELOPE_ZOOM_IN,
  60. ID_ENVELOPE_ZOOM_OUT,
  61. ID_SEPARATOR,
  62. ID_ENVELOPE_LOAD,
  63. ID_ENVELOPE_SAVE,
  64. };
  65. IMPLEMENT_SERIAL(CViewInstrument, CModScrollView, 0)
  66. BEGIN_MESSAGE_MAP(CViewInstrument, CModScrollView)
  67. //{{AFX_MSG_MAP(CViewInstrument)
  68. #if !defined(MPT_BUILD_RETRO)
  69. ON_MESSAGE(WM_DPICHANGED, &CViewInstrument::OnDPIChanged)
  70. #endif
  71. ON_WM_ERASEBKGND()
  72. ON_WM_SETFOCUS()
  73. ON_WM_SIZE()
  74. ON_WM_NCCALCSIZE()
  75. ON_WM_NCPAINT()
  76. ON_WM_NCHITTEST()
  77. ON_WM_MOUSEMOVE()
  78. ON_WM_NCMOUSEMOVE()
  79. ON_WM_LBUTTONDOWN()
  80. ON_WM_LBUTTONUP()
  81. ON_WM_LBUTTONDBLCLK()
  82. ON_WM_RBUTTONDOWN()
  83. ON_WM_MBUTTONDOWN()
  84. ON_WM_XBUTTONUP()
  85. ON_WM_NCLBUTTONDOWN()
  86. ON_WM_NCLBUTTONUP()
  87. ON_WM_NCLBUTTONDBLCLK()
  88. ON_WM_DROPFILES()
  89. ON_COMMAND(ID_PREVINSTRUMENT, &CViewInstrument::OnPrevInstrument)
  90. ON_COMMAND(ID_NEXTINSTRUMENT, &CViewInstrument::OnNextInstrument)
  91. ON_COMMAND(ID_ENVELOPE_SETLOOP, &CViewInstrument::OnEnvLoopChanged)
  92. ON_COMMAND(ID_ENVELOPE_SUSTAIN, &CViewInstrument::OnEnvSustainChanged)
  93. ON_COMMAND(ID_ENVELOPE_CARRY, &CViewInstrument::OnEnvCarryChanged)
  94. ON_COMMAND(ID_ENVELOPE_INSERTPOINT, &CViewInstrument::OnEnvInsertPoint)
  95. ON_COMMAND(ID_ENVELOPE_REMOVEPOINT, &CViewInstrument::OnEnvRemovePoint)
  96. ON_COMMAND(ID_ENVELOPE_VOLUME, &CViewInstrument::OnEnvVolChanged)
  97. ON_COMMAND(ID_ENVELOPE_PANNING, &CViewInstrument::OnEnvPanChanged)
  98. ON_COMMAND(ID_ENVELOPE_PITCH, &CViewInstrument::OnEnvPitchChanged)
  99. ON_COMMAND(ID_ENVELOPE_FILTER, &CViewInstrument::OnEnvFilterChanged)
  100. ON_COMMAND(ID_ENVELOPE_VIEWGRID, &CViewInstrument::OnEnvToggleGrid)
  101. ON_COMMAND(ID_ENVELOPE_ZOOM_IN, &CViewInstrument::OnEnvZoomIn)
  102. ON_COMMAND(ID_ENVELOPE_ZOOM_OUT, &CViewInstrument::OnEnvZoomOut)
  103. ON_COMMAND(ID_ENVELOPE_LOAD, &CViewInstrument::OnEnvLoad)
  104. ON_COMMAND(ID_ENVELOPE_SAVE, &CViewInstrument::OnEnvSave)
  105. ON_COMMAND(ID_ENVSEL_VOLUME, &CViewInstrument::OnSelectVolumeEnv)
  106. ON_COMMAND(ID_ENVSEL_PANNING, &CViewInstrument::OnSelectPanningEnv)
  107. ON_COMMAND(ID_ENVSEL_PITCH, &CViewInstrument::OnSelectPitchEnv)
  108. ON_COMMAND(ID_EDIT_COPY, &CViewInstrument::OnEditCopy)
  109. ON_COMMAND(ID_EDIT_PASTE, &CViewInstrument::OnEditPaste)
  110. ON_COMMAND(ID_EDIT_UNDO, &CViewInstrument::OnEditUndo)
  111. ON_COMMAND(ID_EDIT_REDO, &CViewInstrument::OnEditRedo)
  112. ON_COMMAND(ID_INSTRUMENT_SAMPLEMAP, &CViewInstrument::OnEditSampleMap)
  113. ON_COMMAND(ID_ENVELOPE_TOGGLERELEASENODE, &CViewInstrument::OnEnvToggleReleasNode)
  114. ON_COMMAND(ID_ENVELOPE_SCALEPOINTS, &CViewInstrument::OnEnvelopeScalePoints)
  115. ON_MESSAGE(WM_MOD_MIDIMSG, &CViewInstrument::OnMidiMsg)
  116. ON_MESSAGE(WM_MOD_KEYCOMMAND, &CViewInstrument::OnCustomKeyMsg)
  117. ON_UPDATE_COMMAND_UI(ID_EDIT_UNDO, &CViewInstrument::OnUpdateUndo)
  118. ON_UPDATE_COMMAND_UI(ID_EDIT_REDO, &CViewInstrument::OnUpdateRedo)
  119. //}}AFX_MSG_MAP
  120. ON_WM_MOUSEWHEEL()
  121. END_MESSAGE_MAP()
  122. ///////////////////////////////////////////////////////////////
  123. // CViewInstrument operations
  124. CViewInstrument::CViewInstrument()
  125. {
  126. EnableActiveAccessibility();
  127. m_rcClient.bottom = 2;
  128. m_dwNotifyPos.fill(uint32(Notification::PosInvalid));
  129. MemsetZero(m_NcButtonState);
  130. m_bmpEnvBar.Create(&CMainFrame::GetMainFrame()->m_EnvelopeIcons);
  131. m_baPlayingNote.reset();
  132. }
  133. void CViewInstrument::OnInitialUpdate()
  134. {
  135. CModScrollView::OnInitialUpdate();
  136. ModifyStyleEx(0, WS_EX_ACCEPTFILES);
  137. m_zoom = (ENV_POINT_SIZE * m_nDPIx) / 96.0f;
  138. m_envPointSize = Util::ScalePixels(ENV_POINT_SIZE, m_hWnd);
  139. UpdateScrollSize();
  140. UpdateNcButtonState();
  141. EnableToolTips();
  142. }
  143. void CViewInstrument::UpdateScrollSize()
  144. {
  145. CModDoc *pModDoc = GetDocument();
  146. GetClientRect(&m_rcClient);
  147. if(m_rcClient.bottom < 2)
  148. m_rcClient.bottom = 2;
  149. if(pModDoc)
  150. {
  151. SIZE sizeTotal, sizePage, sizeLine;
  152. uint32 maxTick = EnvGetTick(EnvGetLastPoint());
  153. sizeTotal.cx = mpt::saturate_round<int>((maxTick + 2) * m_zoom);
  154. sizeTotal.cy = 1;
  155. sizeLine.cx = mpt::saturate_round<int>(m_zoom);
  156. sizeLine.cy = 2;
  157. sizePage.cx = sizeLine.cx * 4;
  158. sizePage.cy = sizeLine.cy;
  159. SetScrollSizes(MM_TEXT, sizeTotal, sizePage, sizeLine);
  160. }
  161. }
  162. LRESULT CViewInstrument::OnDPIChanged(WPARAM wParam, LPARAM lParam)
  163. {
  164. LRESULT res = CModScrollView::OnDPIChanged(wParam, lParam);
  165. m_envPointSize = Util::ScalePixels(4, m_hWnd);
  166. return res;
  167. }
  168. void CViewInstrument::PrepareUndo(const char *description)
  169. {
  170. GetDocument()->GetInstrumentUndo().PrepareUndo(m_nInstrument, description, m_nEnv);
  171. }
  172. // Set instrument (and moddoc) as modified.
  173. // updateAll: Update all views including this one. Otherwise, only update update other views.
  174. void CViewInstrument::SetModified(InstrumentHint hint, bool updateAll)
  175. {
  176. CModDoc *pModDoc = GetDocument();
  177. pModDoc->SetModified();
  178. pModDoc->UpdateAllViews(nullptr, hint.SetData(m_nInstrument), updateAll ? nullptr : this);
  179. CMainFrame::GetMainFrame()->NotifyAccessibilityUpdate(*this);
  180. }
  181. BOOL CViewInstrument::SetCurrentInstrument(INSTRUMENTINDEX nIns, EnvelopeType nEnv)
  182. {
  183. CModDoc *pModDoc = GetDocument();
  184. Notification::Type type;
  185. if((!pModDoc) || (nIns < 1) || (nIns >= MAX_INSTRUMENTS))
  186. return FALSE;
  187. m_nEnv = nEnv;
  188. m_nInstrument = nIns;
  189. switch(m_nEnv)
  190. {
  191. case ENV_PANNING: type = Notification::PanEnv; break;
  192. case ENV_PITCH: type = Notification::PitchEnv; break;
  193. default: m_nEnv = ENV_VOLUME; type = Notification::VolEnv; break;
  194. }
  195. pModDoc->SetNotifications(type, m_nInstrument);
  196. pModDoc->SetFollowWnd(m_hWnd);
  197. UpdateScrollSize();
  198. UpdateNcButtonState();
  199. InvalidateRect(NULL, FALSE);
  200. return TRUE;
  201. }
  202. void CViewInstrument::OnSetFocus(CWnd *pOldWnd)
  203. {
  204. CScrollView::OnSetFocus(pOldWnd);
  205. SetCurrentInstrument(m_nInstrument, m_nEnv);
  206. }
  207. LRESULT CViewInstrument::OnModViewMsg(WPARAM wParam, LPARAM lParam)
  208. {
  209. switch(wParam)
  210. {
  211. case VIEWMSG_SETCURRENTINSTRUMENT:
  212. SetCurrentInstrument(lParam & 0xFFFF, m_nEnv);
  213. break;
  214. case VIEWMSG_LOADSTATE:
  215. if(lParam)
  216. {
  217. INSTRUMENTVIEWSTATE *pState = (INSTRUMENTVIEWSTATE *)lParam;
  218. if(pState->initialized)
  219. {
  220. m_zoom = pState->zoom;
  221. SetCurrentInstrument(m_nInstrument, pState->nEnv);
  222. m_bGrid = pState->bGrid;
  223. }
  224. }
  225. break;
  226. case VIEWMSG_SAVESTATE:
  227. if(lParam)
  228. {
  229. INSTRUMENTVIEWSTATE *pState = (INSTRUMENTVIEWSTATE *)lParam;
  230. pState->initialized = true;
  231. pState->zoom = m_zoom;
  232. pState->nEnv = m_nEnv;
  233. pState->bGrid = m_bGrid;
  234. }
  235. break;
  236. default:
  237. return CModScrollView::OnModViewMsg(wParam, lParam);
  238. }
  239. return 0;
  240. }
  241. uint32 CViewInstrument::EnvGetTick(int nPoint) const
  242. {
  243. InstrumentEnvelope *envelope = GetEnvelopePtr();
  244. if(envelope == nullptr)
  245. return 0;
  246. if((nPoint >= 0) && (nPoint < (int)envelope->size()))
  247. return envelope->at(nPoint).tick;
  248. else
  249. return 0;
  250. }
  251. uint32 CViewInstrument::EnvGetValue(int nPoint) const
  252. {
  253. InstrumentEnvelope *envelope = GetEnvelopePtr();
  254. if(envelope == nullptr)
  255. return 0;
  256. if(nPoint >= 0 && nPoint < (int)envelope->size())
  257. return envelope->at(nPoint).value;
  258. else
  259. return 0;
  260. }
  261. bool CViewInstrument::EnvSetValue(int nPoint, int32 nTick, int32 nValue, bool moveTail)
  262. {
  263. InstrumentEnvelope *envelope = GetEnvelopePtr();
  264. if(envelope == nullptr || nPoint < 0)
  265. return false;
  266. if(nPoint == 0)
  267. {
  268. nTick = 0;
  269. moveTail = false;
  270. }
  271. int tickDiff = 0;
  272. bool ok = false;
  273. if(nPoint < (int)envelope->size())
  274. {
  275. if(nTick != int32_min)
  276. {
  277. nTick = std::max(0, nTick);
  278. tickDiff = envelope->at(nPoint).tick;
  279. int mintick = (nPoint > 0) ? envelope->at(nPoint - 1).tick : 0;
  280. int maxtick;
  281. if(nPoint + 1 >= (int)envelope->size() || moveTail)
  282. maxtick = std::numeric_limits<decltype(maxtick)>::max();
  283. else
  284. maxtick = envelope->at(nPoint + 1).tick;
  285. // Can't have multiple points on same tick
  286. if(nPoint > 0 && mintick < maxtick - 1)
  287. {
  288. mintick++;
  289. if(nPoint + 1 < (int)envelope->size())
  290. maxtick--;
  291. }
  292. if(nTick < mintick)
  293. nTick = mintick;
  294. if(nTick > maxtick)
  295. nTick = maxtick;
  296. if(nTick != envelope->at(nPoint).tick)
  297. {
  298. envelope->at(nPoint).tick = static_cast<EnvelopeNode::tick_t>(nTick);
  299. ok = true;
  300. }
  301. }
  302. const int maxVal = (GetDocument()->GetModType() != MOD_TYPE_XM || m_nEnv != ENV_PANNING) ? 64 : 63;
  303. if(nValue != int32_min)
  304. {
  305. Limit(nValue, 0, maxVal);
  306. if(nValue != envelope->at(nPoint).value)
  307. {
  308. envelope->at(nPoint).value = static_cast<EnvelopeNode::value_t>(nValue);
  309. ok = true;
  310. }
  311. }
  312. }
  313. if(ok && moveTail)
  314. {
  315. // Move all points after modified point as well.
  316. tickDiff = envelope->at(nPoint).tick - tickDiff;
  317. for(auto it = envelope->begin() + nPoint + 1; it != envelope->end(); it++)
  318. {
  319. it->tick = static_cast<EnvelopeNode::tick_t>(std::max(0, (int)it->tick + tickDiff));
  320. }
  321. }
  322. return ok;
  323. }
  324. uint32 CViewInstrument::EnvGetNumPoints() const
  325. {
  326. InstrumentEnvelope *envelope = GetEnvelopePtr();
  327. if(envelope == nullptr)
  328. return 0;
  329. return envelope->size();
  330. }
  331. uint32 CViewInstrument::EnvGetLastPoint() const
  332. {
  333. uint32 nPoints = EnvGetNumPoints();
  334. if(nPoints > 0)
  335. return nPoints - 1;
  336. return 0;
  337. }
  338. // Return if an envelope flag is set.
  339. bool CViewInstrument::EnvGetFlag(const EnvelopeFlags dwFlag) const
  340. {
  341. InstrumentEnvelope *pEnv = GetEnvelopePtr();
  342. if(pEnv != nullptr)
  343. return pEnv->dwFlags[dwFlag];
  344. return false;
  345. }
  346. uint32 CViewInstrument::EnvGetLoopStart() const
  347. {
  348. InstrumentEnvelope *envelope = GetEnvelopePtr();
  349. if(envelope == nullptr)
  350. return 0;
  351. return envelope->nLoopStart;
  352. }
  353. uint32 CViewInstrument::EnvGetLoopEnd() const
  354. {
  355. InstrumentEnvelope *envelope = GetEnvelopePtr();
  356. if(envelope == nullptr)
  357. return 0;
  358. return envelope->nLoopEnd;
  359. }
  360. uint32 CViewInstrument::EnvGetSustainStart() const
  361. {
  362. InstrumentEnvelope *envelope = GetEnvelopePtr();
  363. if(envelope == nullptr)
  364. return 0;
  365. return envelope->nSustainStart;
  366. }
  367. uint32 CViewInstrument::EnvGetSustainEnd() const
  368. {
  369. InstrumentEnvelope *envelope = GetEnvelopePtr();
  370. if(envelope == nullptr)
  371. return 0;
  372. return envelope->nSustainEnd;
  373. }
  374. bool CViewInstrument::EnvGetVolEnv() const
  375. {
  376. ModInstrument *pIns = GetInstrumentPtr();
  377. if(pIns)
  378. return pIns->VolEnv.dwFlags[ENV_ENABLED] != 0;
  379. return false;
  380. }
  381. bool CViewInstrument::EnvGetPanEnv() const
  382. {
  383. ModInstrument *pIns = GetInstrumentPtr();
  384. if(pIns)
  385. return pIns->PanEnv.dwFlags[ENV_ENABLED] != 0;
  386. return false;
  387. }
  388. bool CViewInstrument::EnvGetPitchEnv() const
  389. {
  390. ModInstrument *pIns = GetInstrumentPtr();
  391. if(pIns)
  392. return ((pIns->PitchEnv.dwFlags & (ENV_ENABLED | ENV_FILTER)) == ENV_ENABLED);
  393. return false;
  394. }
  395. bool CViewInstrument::EnvGetFilterEnv() const
  396. {
  397. ModInstrument *pIns = GetInstrumentPtr();
  398. if(pIns)
  399. return ((pIns->PitchEnv.dwFlags & (ENV_ENABLED | ENV_FILTER)) == (ENV_ENABLED | ENV_FILTER));
  400. return false;
  401. }
  402. bool CViewInstrument::EnvSetLoopStart(int nPoint)
  403. {
  404. InstrumentEnvelope *envelope = GetEnvelopePtr();
  405. if(envelope == nullptr)
  406. return false;
  407. if(nPoint < 0 || nPoint > (int)EnvGetLastPoint())
  408. return false;
  409. if(nPoint != envelope->nLoopStart)
  410. {
  411. envelope->nLoopStart = static_cast<decltype(envelope->nLoopStart)>(nPoint);
  412. if(envelope->nLoopEnd < nPoint)
  413. envelope->nLoopEnd = static_cast<decltype(envelope->nLoopEnd)>(nPoint);
  414. return true;
  415. } else
  416. {
  417. return false;
  418. }
  419. }
  420. bool CViewInstrument::EnvSetLoopEnd(int nPoint)
  421. {
  422. InstrumentEnvelope *envelope = GetEnvelopePtr();
  423. if(envelope == nullptr)
  424. return false;
  425. if(nPoint < 0 || nPoint > (int)EnvGetLastPoint())
  426. return false;
  427. if(nPoint != envelope->nLoopEnd)
  428. {
  429. envelope->nLoopEnd = static_cast<decltype(envelope->nLoopEnd)>(nPoint);
  430. if(envelope->nLoopStart > nPoint)
  431. envelope->nLoopStart = static_cast<decltype(envelope->nLoopStart)>(nPoint);
  432. return true;
  433. } else
  434. {
  435. return false;
  436. }
  437. }
  438. bool CViewInstrument::EnvSetSustainStart(int nPoint)
  439. {
  440. InstrumentEnvelope *envelope = GetEnvelopePtr();
  441. if(envelope == nullptr)
  442. return false;
  443. if(nPoint < 0 || nPoint > (int)EnvGetLastPoint())
  444. return false;
  445. // We won't do any security checks here as GetEnvelopePtr() does that for us.
  446. CSoundFile &sndFile = GetDocument()->GetSoundFile();
  447. if(nPoint != envelope->nSustainStart)
  448. {
  449. envelope->nSustainStart = static_cast<decltype(envelope->nSustainStart)>(nPoint);
  450. if((envelope->nSustainEnd < nPoint) || (sndFile.GetType() & MOD_TYPE_XM))
  451. envelope->nSustainEnd = static_cast<decltype(envelope->nSustainEnd)>(nPoint);
  452. return true;
  453. } else
  454. {
  455. return false;
  456. }
  457. }
  458. bool CViewInstrument::EnvSetSustainEnd(int nPoint)
  459. {
  460. InstrumentEnvelope *envelope = GetEnvelopePtr();
  461. if(envelope == nullptr)
  462. return false;
  463. if(nPoint < 0 || nPoint > (int)EnvGetLastPoint())
  464. return false;
  465. // We won't do any security checks here as GetEnvelopePtr() does that for us.
  466. CSoundFile &sndFile = GetDocument()->GetSoundFile();
  467. if(nPoint != envelope->nSustainEnd)
  468. {
  469. envelope->nSustainEnd = static_cast<decltype(envelope->nSustainEnd)>(nPoint);
  470. if((envelope->nSustainStart > nPoint) || (sndFile.GetType() & MOD_TYPE_XM))
  471. envelope->nSustainStart = static_cast<decltype(envelope->nSustainStart)>(nPoint);
  472. return true;
  473. } else
  474. {
  475. return false;
  476. }
  477. }
  478. bool CViewInstrument::EnvToggleReleaseNode(int nPoint)
  479. {
  480. InstrumentEnvelope *envelope = GetEnvelopePtr();
  481. if(envelope == nullptr)
  482. return false;
  483. if(nPoint < 0 || nPoint >= (int)EnvGetNumPoints())
  484. return false;
  485. // Don't allow release nodes in IT/XM. GetDocument()/... nullptr check is done in GetEnvelopePtr, so no need to check twice.
  486. if(!GetDocument()->GetSoundFile().GetModSpecifications().hasReleaseNode)
  487. {
  488. if(envelope->nReleaseNode != ENV_RELEASE_NODE_UNSET)
  489. {
  490. envelope->nReleaseNode = ENV_RELEASE_NODE_UNSET;
  491. return true;
  492. }
  493. return false;
  494. }
  495. if(envelope->nReleaseNode == nPoint)
  496. {
  497. envelope->nReleaseNode = ENV_RELEASE_NODE_UNSET;
  498. } else
  499. {
  500. envelope->nReleaseNode = static_cast<decltype(envelope->nReleaseNode)>(nPoint);
  501. }
  502. return true;
  503. }
  504. // Enable or disable a flag of the current envelope
  505. bool CViewInstrument::EnvSetFlag(EnvelopeFlags flag, bool enable)
  506. {
  507. InstrumentEnvelope *envelope = GetEnvelopePtr();
  508. if(envelope == nullptr || envelope->empty())
  509. return false;
  510. bool modified = envelope->dwFlags[flag] != enable;
  511. PrepareUndo("Toggle Envelope Flag");
  512. envelope->dwFlags.set(flag, enable);
  513. return modified;
  514. }
  515. bool CViewInstrument::EnvToggleEnv(EnvelopeType envelope, CSoundFile &sndFile, ModInstrument &ins, bool enable, EnvelopeNode::value_t defaultValue, EnvelopeFlags extraFlags)
  516. {
  517. InstrumentEnvelope &env = ins.GetEnvelope(envelope);
  518. const FlagSet<EnvelopeFlags> flags = (ENV_ENABLED | extraFlags);
  519. env.dwFlags.set(flags, enable);
  520. if(enable && env.empty())
  521. {
  522. env.reserve(2);
  523. env.push_back(EnvelopeNode(0, defaultValue));
  524. env.push_back(EnvelopeNode(10, defaultValue));
  525. InvalidateRect(NULL, FALSE);
  526. }
  527. CriticalSection cs;
  528. // Update mixing flags...
  529. for(auto &chn : sndFile.m_PlayState.Chn)
  530. {
  531. if(chn.pModInstrument == &ins)
  532. {
  533. chn.GetEnvelope(envelope).flags.set(flags, enable);
  534. }
  535. }
  536. return true;
  537. }
  538. bool CViewInstrument::EnvSetVolEnv(bool enable)
  539. {
  540. ModInstrument *pIns = GetInstrumentPtr();
  541. if(pIns == nullptr)
  542. return false;
  543. return EnvToggleEnv(ENV_VOLUME, GetDocument()->GetSoundFile(), *pIns, enable, 64);
  544. }
  545. bool CViewInstrument::EnvSetPanEnv(bool enable)
  546. {
  547. ModInstrument *pIns = GetInstrumentPtr();
  548. if(pIns == nullptr)
  549. return false;
  550. return EnvToggleEnv(ENV_PANNING, GetDocument()->GetSoundFile(), *pIns, enable, 32);
  551. }
  552. bool CViewInstrument::EnvSetPitchEnv(bool enable)
  553. {
  554. ModInstrument *pIns = GetInstrumentPtr();
  555. if(pIns == nullptr)
  556. return false;
  557. pIns->PitchEnv.dwFlags.reset(ENV_FILTER);
  558. return EnvToggleEnv(ENV_PITCH, GetDocument()->GetSoundFile(), *pIns, enable, 32);
  559. }
  560. bool CViewInstrument::EnvSetFilterEnv(bool enable)
  561. {
  562. ModInstrument *pIns = GetInstrumentPtr();
  563. if(pIns == nullptr)
  564. return false;
  565. return EnvToggleEnv(ENV_PITCH, GetDocument()->GetSoundFile(), *pIns, enable, 64, ENV_FILTER);
  566. }
  567. uint32 CViewInstrument::DragItemToEnvPoint() const
  568. {
  569. InstrumentEnvelope *pEnv = GetEnvelopePtr();
  570. if(pEnv == nullptr || !m_nDragItem)
  571. return 0;
  572. switch(m_nDragItem)
  573. {
  574. case ENV_DRAGLOOPSTART: return pEnv->nLoopStart;
  575. case ENV_DRAGLOOPEND: return pEnv->nLoopEnd;
  576. case ENV_DRAGSUSTAINSTART: return pEnv->nSustainStart;
  577. case ENV_DRAGSUSTAINEND: return pEnv->nSustainEnd;
  578. default: return m_nDragItem - 1;
  579. }
  580. }
  581. int CViewInstrument::TickToScreen(int tick) const
  582. {
  583. return static_cast<int>((tick * m_zoom) - m_nScrollPosX + m_envPointSize);
  584. }
  585. int CViewInstrument::PointToScreen(int nPoint) const
  586. {
  587. return TickToScreen(EnvGetTick(nPoint));
  588. }
  589. int CViewInstrument::ScreenToTick(int x) const
  590. {
  591. int offset = m_nScrollPosX + x;
  592. if(offset < m_envPointSize)
  593. return 0;
  594. return mpt::saturate_round<int>((offset - m_envPointSize) / m_zoom);
  595. }
  596. int CViewInstrument::ScreenToValue(int y) const
  597. {
  598. if(m_rcClient.bottom < 2)
  599. return ENVELOPE_MIN;
  600. int n = ENVELOPE_MAX - Util::muldivr(y, ENVELOPE_MAX, m_rcClient.bottom - 1);
  601. if(n < ENVELOPE_MIN)
  602. return ENVELOPE_MIN;
  603. if(n > ENVELOPE_MAX)
  604. return ENVELOPE_MAX;
  605. return n;
  606. }
  607. int CViewInstrument::ScreenToPoint(int x0, int y0) const
  608. {
  609. int nPoint = -1;
  610. int64 ydist = int64_max, xdist = int64_max;
  611. int numPoints = EnvGetNumPoints();
  612. for(int i = 0; i < numPoints; i++)
  613. {
  614. int dx = x0 - PointToScreen(i);
  615. int64 dx2 = Util::mul32to64(dx, dx);
  616. if(dx2 <= xdist)
  617. {
  618. int dy = y0 - ValueToScreen(EnvGetValue(i));
  619. int64 dy2 = Util::mul32to64(dy, dy);
  620. if(dx2 < xdist || (dx2 == xdist && dy2 < ydist))
  621. {
  622. nPoint = i;
  623. xdist = dx2;
  624. ydist = dy2;
  625. }
  626. }
  627. }
  628. return nPoint;
  629. }
  630. bool CViewInstrument::GetNcButtonRect(UINT button, CRect &rect) const
  631. {
  632. rect.left = 4;
  633. rect.top = 3;
  634. rect.bottom = rect.top + ENV_LEFTBAR_CYBTN;
  635. if(button >= ENV_LEFTBAR_BUTTONS)
  636. return false;
  637. for(UINT i = 0; i < button; i++)
  638. {
  639. if(cLeftBarButtons[i] == ID_SEPARATOR)
  640. rect.left += ENV_LEFTBAR_CXSEP;
  641. else
  642. rect.left += ENV_LEFTBAR_CXBTN + ENV_LEFTBAR_CXSPC;
  643. }
  644. if(cLeftBarButtons[button] == ID_SEPARATOR)
  645. {
  646. rect.left += ENV_LEFTBAR_CXSEP / 2 - 2;
  647. rect.right = rect.left + 2;
  648. return false;
  649. } else
  650. {
  651. rect.right = rect.left + ENV_LEFTBAR_CXBTN;
  652. }
  653. return true;
  654. }
  655. UINT CViewInstrument::GetNcButtonAtPoint(CPoint point, CRect *outRect) const
  656. {
  657. CRect rect, rcWnd;
  658. UINT button = uint32_max;
  659. GetWindowRect(&rcWnd);
  660. for(UINT i = 0; i < ENV_LEFTBAR_BUTTONS; i++)
  661. {
  662. if(!(m_NcButtonState[i] & NCBTNS_DISABLED) && GetNcButtonRect(i, rect))
  663. {
  664. rect.OffsetRect(rcWnd.left, rcWnd.top);
  665. if(rect.PtInRect(point))
  666. {
  667. button = i;
  668. break;
  669. }
  670. }
  671. }
  672. if(outRect)
  673. *outRect = rect;
  674. return button;
  675. }
  676. void CViewInstrument::UpdateNcButtonState()
  677. {
  678. CModDoc *pModDoc = GetDocument();
  679. if(!pModDoc)
  680. return;
  681. CSoundFile &sndFile = pModDoc->GetSoundFile();
  682. CDC *pDC = NULL;
  683. for (UINT i=0; i<ENV_LEFTBAR_BUTTONS; i++) if (cLeftBarButtons[i] != ID_SEPARATOR)
  684. {
  685. DWORD dwStyle = 0;
  686. switch(cLeftBarButtons[i])
  687. {
  688. case ID_ENVSEL_VOLUME: if (m_nEnv == ENV_VOLUME) dwStyle |= NCBTNS_CHECKED; break;
  689. case ID_ENVSEL_PANNING: if (m_nEnv == ENV_PANNING) dwStyle |= NCBTNS_CHECKED; break;
  690. case ID_ENVSEL_PITCH: if (!(sndFile.GetType() & (MOD_TYPE_IT|MOD_TYPE_MPT))) dwStyle |= NCBTNS_DISABLED;
  691. else if (m_nEnv == ENV_PITCH) dwStyle |= NCBTNS_CHECKED; break;
  692. case ID_ENVELOPE_SETLOOP: if (EnvGetLoop()) dwStyle |= NCBTNS_CHECKED; break;
  693. case ID_ENVELOPE_SUSTAIN: if (EnvGetSustain()) dwStyle |= NCBTNS_CHECKED; break;
  694. case ID_ENVELOPE_CARRY: if (!(sndFile.GetType() & (MOD_TYPE_IT|MOD_TYPE_MPT))) dwStyle |= NCBTNS_DISABLED;
  695. else if (EnvGetCarry()) dwStyle |= NCBTNS_CHECKED; break;
  696. case ID_ENVELOPE_VOLUME: if (EnvGetVolEnv()) dwStyle |= NCBTNS_CHECKED; break;
  697. case ID_ENVELOPE_PANNING: if (EnvGetPanEnv()) dwStyle |= NCBTNS_CHECKED; break;
  698. case ID_ENVELOPE_PITCH: if (!(sndFile.GetType() & (MOD_TYPE_IT|MOD_TYPE_MPT))) dwStyle |= NCBTNS_DISABLED; else
  699. if (EnvGetPitchEnv()) dwStyle |= NCBTNS_CHECKED; break;
  700. case ID_ENVELOPE_FILTER: if (!(sndFile.GetType() & (MOD_TYPE_IT|MOD_TYPE_MPT))) dwStyle |= NCBTNS_DISABLED; else
  701. if (EnvGetFilterEnv()) dwStyle |= NCBTNS_CHECKED; break;
  702. case ID_ENVELOPE_VIEWGRID: if (m_bGrid) dwStyle |= NCBTNS_CHECKED; break;
  703. case ID_ENVELOPE_ZOOM_IN: if (m_zoom >= ENV_MAX_ZOOM) dwStyle |= NCBTNS_DISABLED; break;
  704. case ID_ENVELOPE_ZOOM_OUT: if (m_zoom <= ENV_MIN_ZOOM) dwStyle |= NCBTNS_DISABLED; break;
  705. case ID_ENVELOPE_LOAD:
  706. case ID_ENVELOPE_SAVE: if (GetInstrumentPtr() == nullptr) dwStyle |= NCBTNS_DISABLED; break;
  707. }
  708. if (m_nBtnMouseOver == i)
  709. {
  710. dwStyle |= NCBTNS_MOUSEOVER;
  711. if (m_dwStatus & INSSTATUS_NCLBTNDOWN) dwStyle |= NCBTNS_PUSHED;
  712. }
  713. if (dwStyle != m_NcButtonState[i])
  714. {
  715. m_NcButtonState[i] = dwStyle;
  716. if (!pDC) pDC = GetWindowDC();
  717. DrawNcButton(pDC, i);
  718. }
  719. }
  720. if (pDC) ReleaseDC(pDC);
  721. }
  722. ////////////////////////////////////////////////////////////////////
  723. // CViewInstrument drawing
  724. void CViewInstrument::UpdateView(UpdateHint hint, CObject *pObj)
  725. {
  726. if(pObj == this)
  727. {
  728. return;
  729. }
  730. const InstrumentHint instrHint = hint.ToType<InstrumentHint>();
  731. FlagSet<HintType> hintType = instrHint.GetType();
  732. const INSTRUMENTINDEX updateIns = instrHint.GetInstrument();
  733. if(hintType[HINT_MPTOPTIONS | HINT_MODTYPE]
  734. || (hintType[HINT_ENVELOPE] && (m_nInstrument == updateIns || updateIns == 0)))
  735. {
  736. UpdateScrollSize();
  737. UpdateNcButtonState();
  738. InvalidateRect(NULL, FALSE);
  739. }
  740. }
  741. void CViewInstrument::DrawGrid(CDC *pDC, uint32 speed)
  742. {
  743. bool windowResized = false;
  744. if(m_dcGrid.m_hDC)
  745. {
  746. m_dcGrid.SelectObject(m_pbmpOldGrid);
  747. m_dcGrid.DeleteDC();
  748. m_bmpGrid.DeleteObject();
  749. windowResized = true;
  750. }
  751. if(windowResized || m_bGridForceRedraw || (m_nScrollPosX != m_GridScrollPos) || (speed != (UINT)m_GridSpeed) && speed > 0)
  752. {
  753. m_GridSpeed = speed;
  754. m_GridScrollPos = m_nScrollPosX;
  755. m_bGridForceRedraw = false;
  756. // create a memory based dc for drawing the grid
  757. m_dcGrid.CreateCompatibleDC(pDC);
  758. m_bmpGrid.CreateCompatibleBitmap(pDC, m_rcClient.Width(), m_rcClient.Height());
  759. m_pbmpOldGrid = *m_dcGrid.SelectObject(&m_bmpGrid);
  760. // Do draw
  761. const int width = m_rcClient.Width();
  762. int rowsPerBeat = 1, rowsPerMeasure = 1;
  763. const CModDoc *modDoc = GetDocument();
  764. if(modDoc != nullptr)
  765. {
  766. rowsPerBeat = modDoc->GetSoundFile().m_nDefaultRowsPerBeat;
  767. rowsPerMeasure = modDoc->GetSoundFile().m_nDefaultRowsPerMeasure;
  768. }
  769. // Paint it black!
  770. m_dcGrid.FillSolidRect(&m_rcClient, TrackerSettings::Instance().rgbCustomColors[MODCOLOR_BACKENV]);
  771. const uint32 startTick = (ScreenToTick(0) / speed) * speed;
  772. const uint32 endTick = (ScreenToTick(width) / speed) * speed;
  773. auto oldPen = m_dcGrid.SelectStockObject(DC_PEN);
  774. for(uint32 tick = startTick, row = startTick / speed; tick <= endTick; tick += speed, row++)
  775. {
  776. if(rowsPerMeasure > 0 && row % rowsPerMeasure == 0)
  777. m_dcGrid.SetDCPenColor(RGB(0x80, 0x80, 0x80));
  778. else if(rowsPerBeat > 0 && row % rowsPerBeat == 0)
  779. m_dcGrid.SetDCPenColor(RGB(0x55, 0x55, 0x55));
  780. else
  781. m_dcGrid.SetDCPenColor(RGB(0x33, 0x33, 0x33));
  782. int x = TickToScreen(tick);
  783. m_dcGrid.MoveTo(x, 0);
  784. m_dcGrid.LineTo(x, m_rcClient.bottom);
  785. }
  786. if(oldPen)
  787. m_dcGrid.SelectObject(oldPen);
  788. }
  789. pDC->BitBlt(m_rcClient.left, m_rcClient.top, m_rcClient.Width(), m_rcClient.Height(), &m_dcGrid, 0, 0, SRCCOPY);
  790. }
  791. void CViewInstrument::OnDraw(CDC *pDC)
  792. {
  793. CModDoc *pModDoc = GetDocument();
  794. if((!pModDoc) || (!pDC))
  795. return;
  796. // to avoid flicker, establish a memory dc, draw to it
  797. // and then BitBlt it to the destination "pDC"
  798. //check for window resize
  799. if(m_dcMemMain.GetSafeHdc() && m_rcOldClient != m_rcClient)
  800. {
  801. m_dcMemMain.SelectObject(oldBitmap);
  802. m_dcMemMain.DeleteDC();
  803. m_bmpMemMain.DeleteObject();
  804. }
  805. if(!m_dcMemMain.m_hDC)
  806. {
  807. m_dcMemMain.CreateCompatibleDC(pDC);
  808. if(!m_dcMemMain.m_hDC)
  809. return;
  810. }
  811. if(!m_bmpMemMain.m_hObject)
  812. {
  813. m_bmpMemMain.CreateCompatibleBitmap(pDC, m_rcClient.Width(), m_rcClient.Height());
  814. }
  815. m_rcOldClient = m_rcClient;
  816. oldBitmap = *m_dcMemMain.SelectObject(&m_bmpMemMain);
  817. auto stockBrush = CBrush::FromHandle(GetStockBrush(DC_BRUSH));
  818. if(m_bGrid)
  819. {
  820. DrawGrid(&m_dcMemMain, pModDoc->GetSoundFile().m_PlayState.m_nMusicSpeed);
  821. } else
  822. {
  823. // Paint it black!
  824. m_dcMemMain.FillSolidRect(&m_rcClient, TrackerSettings::Instance().rgbCustomColors[MODCOLOR_BACKENV]);
  825. }
  826. auto oldPen = m_dcMemMain.SelectObject(CMainFrame::penDarkGray);
  827. // Middle line (half volume or pitch / panning center)
  828. const int ymed = (m_rcClient.bottom - 1) / 2;
  829. m_dcMemMain.MoveTo(0, ymed);
  830. m_dcMemMain.LineTo(m_rcClient.right, ymed);
  831. // Drawing Loop Start/End
  832. if(EnvGetLoop())
  833. {
  834. m_dcMemMain.SelectObject(m_nDragItem == ENV_DRAGLOOPSTART ? CMainFrame::penGray99 : CMainFrame::penDarkGray);
  835. int x1 = PointToScreen(EnvGetLoopStart()) - m_envPointSize / 2;
  836. m_dcMemMain.MoveTo(x1, 0);
  837. m_dcMemMain.LineTo(x1, m_rcClient.bottom);
  838. m_dcMemMain.SelectObject(m_nDragItem == ENV_DRAGLOOPEND ? CMainFrame::penGray99 : CMainFrame::penDarkGray);
  839. int x2 = PointToScreen(EnvGetLoopEnd()) + m_envPointSize / 2;
  840. m_dcMemMain.MoveTo(x2, 0);
  841. m_dcMemMain.LineTo(x2, m_rcClient.bottom);
  842. }
  843. // Drawing Sustain Start/End
  844. if(EnvGetSustain())
  845. {
  846. m_dcMemMain.SelectObject(CMainFrame::penHalfDarkGray);
  847. int nspace = m_rcClient.bottom / 4;
  848. int n1 = EnvGetSustainStart();
  849. int x1 = PointToScreen(n1) - m_envPointSize / 2;
  850. int y1 = ValueToScreen(EnvGetValue(n1));
  851. m_dcMemMain.MoveTo(x1, y1 - nspace);
  852. m_dcMemMain.LineTo(x1, y1 + nspace);
  853. int n2 = EnvGetSustainEnd();
  854. int x2 = PointToScreen(n2) + m_envPointSize / 2;
  855. int y2 = ValueToScreen(EnvGetValue(n2));
  856. m_dcMemMain.MoveTo(x2, y2 - nspace);
  857. m_dcMemMain.LineTo(x2, y2 + nspace);
  858. }
  859. uint32 maxpoint = EnvGetNumPoints();
  860. // Drawing Envelope
  861. if(maxpoint)
  862. {
  863. maxpoint--;
  864. m_dcMemMain.SelectObject(GetStockObject(DC_PEN));
  865. m_dcMemMain.SetDCPenColor(TrackerSettings::Instance().rgbCustomColors[MODCOLOR_ENVELOPES]);
  866. uint32 releaseNode = EnvGetReleaseNode();
  867. RECT rect;
  868. for(uint32 i = 0; i <= maxpoint; i++)
  869. {
  870. int x = PointToScreen(i);
  871. int y = ValueToScreen(EnvGetValue(i));
  872. rect.left = x - m_envPointSize + 1;
  873. rect.top = y - m_envPointSize + 1;
  874. rect.right = x + m_envPointSize;
  875. rect.bottom = y + m_envPointSize;
  876. if(i)
  877. m_dcMemMain.LineTo(x, y);
  878. else
  879. m_dcMemMain.MoveTo(x, y);
  880. if(i == releaseNode)
  881. {
  882. m_dcMemMain.SetDCBrushColor(RGB(0xFF, 0x00, 0x00));
  883. m_dcMemMain.FrameRect(&rect, stockBrush);
  884. m_dcMemMain.SetDCPenColor(TrackerSettings::Instance().rgbCustomColors[MODCOLOR_ENVELOPE_RELEASE]);
  885. } else if(i == m_nDragItem - 1)
  886. {
  887. // currently selected env point
  888. m_dcMemMain.SetDCBrushColor(RGB(0xFF, 0xFF, 0x00));
  889. m_dcMemMain.FrameRect(&rect, stockBrush);
  890. } else
  891. {
  892. m_dcMemMain.SetDCBrushColor(RGB(0xFF, 0xFF, 0xFF));
  893. m_dcMemMain.FrameRect(&rect, stockBrush);
  894. }
  895. }
  896. }
  897. DrawPositionMarks();
  898. if(oldPen)
  899. m_dcMemMain.SelectObject(oldPen);
  900. pDC->BitBlt(m_rcClient.left, m_rcClient.top, m_rcClient.Width(), m_rcClient.Height(), &m_dcMemMain, 0, 0, SRCCOPY);
  901. }
  902. uint8 CViewInstrument::EnvGetReleaseNode()
  903. {
  904. InstrumentEnvelope *envelope = GetEnvelopePtr();
  905. if(envelope == nullptr)
  906. return ENV_RELEASE_NODE_UNSET;
  907. return envelope->nReleaseNode;
  908. }
  909. bool CViewInstrument::EnvRemovePoint(uint32 nPoint)
  910. {
  911. CModDoc *pModDoc = GetDocument();
  912. if((pModDoc) && (nPoint <= EnvGetLastPoint()))
  913. {
  914. ModInstrument *pIns = pModDoc->GetSoundFile().Instruments[m_nInstrument];
  915. if(pIns)
  916. {
  917. InstrumentEnvelope *envelope = GetEnvelopePtr();
  918. if(envelope == nullptr || envelope->empty())
  919. return false;
  920. PrepareUndo("Remove Envelope Point");
  921. envelope->erase(envelope->begin() + nPoint);
  922. if (nPoint >= envelope->size()) nPoint = envelope->size() - 1;
  923. if (envelope->nLoopStart > nPoint) envelope->nLoopStart--;
  924. if (envelope->nLoopEnd > nPoint) envelope->nLoopEnd--;
  925. if (envelope->nSustainStart > nPoint) envelope->nSustainStart--;
  926. if (envelope->nSustainEnd > nPoint) envelope->nSustainEnd--;
  927. if (envelope->nReleaseNode>nPoint && envelope->nReleaseNode != ENV_RELEASE_NODE_UNSET) envelope->nReleaseNode--;
  928. envelope->at(0).tick = 0;
  929. if(envelope->size() <= 1)
  930. {
  931. // if only one node is left, just disable the envelope completely
  932. *envelope = InstrumentEnvelope();
  933. }
  934. SetModified(InstrumentHint().Envelope(), true);
  935. return true;
  936. }
  937. }
  938. return false;
  939. }
  940. // Insert point. Returns 0 if error occurred, else point ID + 1.
  941. uint32 CViewInstrument::EnvInsertPoint(int nTick, int nValue)
  942. {
  943. CModDoc *pModDoc = GetDocument();
  944. if(pModDoc && nTick >= 0)
  945. {
  946. InstrumentEnvelope *envelope = GetEnvelopePtr();
  947. if(envelope != nullptr && envelope->size() < pModDoc->GetSoundFile().GetModSpecifications().envelopePointsMax)
  948. {
  949. nValue = Clamp(nValue, ENVELOPE_MIN, ENVELOPE_MAX);
  950. if(std::binary_search(envelope->cbegin(), envelope->cend(), EnvelopeNode(static_cast<EnvelopeNode::tick_t>(nTick), 0),
  951. [] (const EnvelopeNode &l, const EnvelopeNode &r) { return l.tick < r.tick; }))
  952. {
  953. // Don't want to insert a node at the same position as another node.
  954. return 0;
  955. }
  956. uint8 defaultValue;
  957. switch(m_nEnv)
  958. {
  959. case ENV_VOLUME:
  960. defaultValue = ENVELOPE_MAX;
  961. break;
  962. case ENV_PANNING:
  963. defaultValue = ENVELOPE_MID;
  964. break;
  965. case ENV_PITCH:
  966. defaultValue = envelope->dwFlags[ENV_FILTER] ? ENVELOPE_MAX : ENVELOPE_MID;
  967. break;
  968. default:
  969. return 0;
  970. }
  971. PrepareUndo("Insert Envelope Point");
  972. if(envelope->empty())
  973. {
  974. envelope->reserve(2);
  975. envelope->push_back(EnvelopeNode(0, defaultValue));
  976. envelope->dwFlags.set(ENV_ENABLED);
  977. if(nTick == 0)
  978. {
  979. // Can't insert two points on the same tick!
  980. nTick = 16;
  981. }
  982. }
  983. uint32 i = 0;
  984. for(i = 0; i < envelope->size(); i++) if(nTick <= envelope->at(i).tick) break;
  985. envelope->insert(envelope->begin() + i, EnvelopeNode(mpt::saturate_cast<EnvelopeNode::tick_t>(nTick), static_cast<EnvelopeNode::value_t>(nValue)));
  986. if(envelope->nLoopStart >= i) envelope->nLoopStart++;
  987. if(envelope->nLoopEnd >= i) envelope->nLoopEnd++;
  988. if(envelope->nSustainStart >= i) envelope->nSustainStart++;
  989. if(envelope->nSustainEnd >= i) envelope->nSustainEnd++;
  990. if(envelope->nReleaseNode >= i && envelope->nReleaseNode != ENV_RELEASE_NODE_UNSET) envelope->nReleaseNode++;
  991. SetModified(InstrumentHint().Envelope(), true);
  992. return i + 1;
  993. }
  994. }
  995. return 0;
  996. }
  997. void CViewInstrument::DrawPositionMarks()
  998. {
  999. CRect rect;
  1000. for(auto pos : m_dwNotifyPos) if (pos != Notification::PosInvalid)
  1001. {
  1002. rect.top = -2;
  1003. rect.left = TickToScreen(pos);
  1004. rect.right = rect.left + 1;
  1005. rect.bottom = m_rcClient.bottom + 1;
  1006. InvertRect(m_dcMemMain.m_hDC, &rect);
  1007. }
  1008. }
  1009. LRESULT CViewInstrument::OnPlayerNotify(Notification *pnotify)
  1010. {
  1011. Notification::Type type;
  1012. CModDoc *pModDoc = GetDocument();
  1013. if((!pnotify) || (!pModDoc))
  1014. return 0;
  1015. switch(m_nEnv)
  1016. {
  1017. case ENV_PANNING: type = Notification::PanEnv; break;
  1018. case ENV_PITCH: type = Notification::PitchEnv; break;
  1019. default: type = Notification::VolEnv; break;
  1020. }
  1021. if(pnotify->type[Notification::Stop])
  1022. {
  1023. bool invalidate = false;
  1024. for(auto &pos : m_dwNotifyPos)
  1025. {
  1026. if(pos != (uint32)Notification::PosInvalid)
  1027. {
  1028. pos = (uint32)Notification::PosInvalid;
  1029. invalidate = true;
  1030. }
  1031. }
  1032. if(invalidate)
  1033. {
  1034. InvalidateEnvelope();
  1035. }
  1036. m_baPlayingNote.reset();
  1037. } else if(pnotify->type[type] && pnotify->item == m_nInstrument)
  1038. {
  1039. bool update = false;
  1040. for(CHANNELINDEX i = 0; i < MAX_CHANNELS; i++)
  1041. {
  1042. uint32 newpos = (uint32)pnotify->pos[i];
  1043. if(m_dwNotifyPos[i] != newpos)
  1044. {
  1045. update = true;
  1046. break;
  1047. }
  1048. }
  1049. if(update)
  1050. {
  1051. HDC hdc = ::GetDC(m_hWnd);
  1052. DrawPositionMarks();
  1053. for(CHANNELINDEX j = 0; j < MAX_CHANNELS; j++)
  1054. {
  1055. uint32 newpos = (uint32)pnotify->pos[j];
  1056. m_dwNotifyPos[j] = newpos;
  1057. }
  1058. DrawPositionMarks();
  1059. BitBlt(hdc, m_rcClient.left, m_rcClient.top, m_rcClient.Width(), m_rcClient.Height(), m_dcMemMain.GetSafeHdc(), 0, 0, SRCCOPY);
  1060. ::ReleaseDC(m_hWnd, hdc);
  1061. }
  1062. }
  1063. return 0;
  1064. }
  1065. void CViewInstrument::DrawNcButton(CDC *pDC, UINT nBtn)
  1066. {
  1067. CRect rect;
  1068. COLORREF crHi = GetSysColor(COLOR_3DHILIGHT);
  1069. COLORREF crDk = GetSysColor(COLOR_3DSHADOW);
  1070. COLORREF crFc = GetSysColor(COLOR_3DFACE);
  1071. COLORREF c1, c2;
  1072. if(GetNcButtonRect(nBtn, rect))
  1073. {
  1074. DWORD dwStyle = m_NcButtonState[nBtn];
  1075. COLORREF c3, c4;
  1076. int xofs = 0, yofs = 0, nImage = 0;
  1077. c1 = c2 = c3 = c4 = crFc;
  1078. if(!(TrackerSettings::Instance().m_dwPatternSetup & PATTERN_FLATBUTTONS))
  1079. {
  1080. c1 = c3 = crHi;
  1081. c2 = crDk;
  1082. c4 = RGB(0, 0, 0);
  1083. }
  1084. if(dwStyle & (NCBTNS_PUSHED | NCBTNS_CHECKED))
  1085. {
  1086. c1 = crDk;
  1087. c2 = crHi;
  1088. if(!(TrackerSettings::Instance().m_dwPatternSetup & PATTERN_FLATBUTTONS))
  1089. {
  1090. c4 = crHi;
  1091. c3 = (dwStyle & NCBTNS_PUSHED) ? RGB(0, 0, 0) : crDk;
  1092. }
  1093. xofs = yofs = 1;
  1094. } else if((dwStyle & NCBTNS_MOUSEOVER) && (TrackerSettings::Instance().m_dwPatternSetup & PATTERN_FLATBUTTONS))
  1095. {
  1096. c1 = crHi;
  1097. c2 = crDk;
  1098. }
  1099. switch(cLeftBarButtons[nBtn])
  1100. {
  1101. case ID_ENVSEL_VOLUME: nImage = IIMAGE_VOLENV; break;
  1102. case ID_ENVSEL_PANNING: nImage = IIMAGE_PANENV; break;
  1103. case ID_ENVSEL_PITCH: nImage = (dwStyle & NCBTNS_DISABLED) ? IIMAGE_NOPITCHENV : IIMAGE_PITCHENV; break;
  1104. case ID_ENVELOPE_SETLOOP: nImage = IIMAGE_LOOP; break;
  1105. case ID_ENVELOPE_SUSTAIN: nImage = IIMAGE_SUSTAIN; break;
  1106. case ID_ENVELOPE_CARRY: nImage = (dwStyle & NCBTNS_DISABLED) ? IIMAGE_NOCARRY : IIMAGE_CARRY; break;
  1107. case ID_ENVELOPE_VOLUME: nImage = IIMAGE_VOLSWITCH; break;
  1108. case ID_ENVELOPE_PANNING: nImage = IIMAGE_PANSWITCH; break;
  1109. case ID_ENVELOPE_PITCH: nImage = (dwStyle & NCBTNS_DISABLED) ? IIMAGE_NOPITCHSWITCH : IIMAGE_PITCHSWITCH; break;
  1110. case ID_ENVELOPE_FILTER: nImage = (dwStyle & NCBTNS_DISABLED) ? IIMAGE_NOFILTERSWITCH : IIMAGE_FILTERSWITCH; break;
  1111. case ID_INSTRUMENT_SAMPLEMAP: nImage = IIMAGE_SAMPLEMAP; break;
  1112. case ID_ENVELOPE_VIEWGRID: nImage = IIMAGE_GRID; break;
  1113. case ID_ENVELOPE_ZOOM_IN: nImage = (dwStyle & NCBTNS_DISABLED) ? IIMAGE_NOZOOMIN : IIMAGE_ZOOMIN; break;
  1114. case ID_ENVELOPE_ZOOM_OUT: nImage = (dwStyle & NCBTNS_DISABLED) ? IIMAGE_NOZOOMOUT : IIMAGE_ZOOMOUT; break;
  1115. case ID_ENVELOPE_LOAD: nImage = IIMAGE_LOAD; break;
  1116. case ID_ENVELOPE_SAVE: nImage = IIMAGE_SAVE; break;
  1117. }
  1118. pDC->Draw3dRect(rect.left - 1, rect.top - 1, ENV_LEFTBAR_CXBTN + 2, ENV_LEFTBAR_CYBTN + 2, c3, c4);
  1119. pDC->Draw3dRect(rect.left, rect.top, ENV_LEFTBAR_CXBTN, ENV_LEFTBAR_CYBTN, c1, c2);
  1120. rect.DeflateRect(1, 1);
  1121. pDC->FillSolidRect(&rect, crFc);
  1122. rect.left += xofs;
  1123. rect.top += yofs;
  1124. if(dwStyle & NCBTNS_CHECKED)
  1125. m_bmpEnvBar.Draw(pDC, IIMAGE_CHECKED, rect.TopLeft(), ILD_NORMAL);
  1126. m_bmpEnvBar.Draw(pDC, nImage, rect.TopLeft(), ILD_NORMAL);
  1127. } else
  1128. {
  1129. c1 = c2 = crFc;
  1130. if(TrackerSettings::Instance().m_dwPatternSetup & PATTERN_FLATBUTTONS)
  1131. {
  1132. c1 = crDk;
  1133. c2 = crHi;
  1134. }
  1135. pDC->Draw3dRect(rect.left, rect.top, 2, ENV_LEFTBAR_CYBTN, c1, c2);
  1136. }
  1137. }
  1138. void CViewInstrument::OnNcPaint()
  1139. {
  1140. RECT rect;
  1141. CModScrollView::OnNcPaint();
  1142. GetWindowRect(&rect);
  1143. // Assumes there is no other non-client items
  1144. rect.bottom = ENV_LEFTBAR_CY;
  1145. rect.right -= rect.left;
  1146. rect.left = 0;
  1147. rect.top = 0;
  1148. if((rect.left < rect.right) && (rect.top < rect.bottom))
  1149. {
  1150. CDC *pDC = GetWindowDC();
  1151. {
  1152. // Shadow
  1153. auto shadowRect = rect;
  1154. shadowRect.top = shadowRect.bottom - 1;
  1155. pDC->FillSolidRect(&shadowRect, GetSysColor(COLOR_BTNSHADOW));
  1156. }
  1157. rect.bottom--;
  1158. if(rect.top < rect.bottom)
  1159. pDC->FillSolidRect(&rect, GetSysColor(COLOR_BTNFACE));
  1160. if(rect.top + 2 < rect.bottom)
  1161. {
  1162. for(UINT i = 0; i < ENV_LEFTBAR_BUTTONS; i++)
  1163. {
  1164. DrawNcButton(pDC, i);
  1165. }
  1166. }
  1167. ReleaseDC(pDC);
  1168. }
  1169. }
  1170. ////////////////////////////////////////////////////////////////////
  1171. // CViewInstrument messages
  1172. void CViewInstrument::OnSize(UINT nType, int cx, int cy)
  1173. {
  1174. CModScrollView::OnSize(nType, cx, cy);
  1175. if(((nType == SIZE_RESTORED) || (nType == SIZE_MAXIMIZED)) && (cx > 0) && (cy > 0))
  1176. {
  1177. UpdateScrollSize();
  1178. }
  1179. }
  1180. void CViewInstrument::OnNcCalcSize(BOOL bCalcValidRects, NCCALCSIZE_PARAMS *lpncsp)
  1181. {
  1182. CModScrollView::OnNcCalcSize(bCalcValidRects, lpncsp);
  1183. if(lpncsp)
  1184. {
  1185. lpncsp->rgrc[0].top += ENV_LEFTBAR_CY;
  1186. if(lpncsp->rgrc[0].bottom < lpncsp->rgrc[0].top)
  1187. lpncsp->rgrc[0].top = lpncsp->rgrc[0].bottom;
  1188. }
  1189. }
  1190. void CViewInstrument::OnNcMouseMove(UINT nHitTest, CPoint point)
  1191. {
  1192. const auto button = GetNcButtonAtPoint(point);
  1193. if(button != m_nBtnMouseOver)
  1194. {
  1195. CMainFrame *pMainFrm = CMainFrame::GetMainFrame();
  1196. if(pMainFrm)
  1197. {
  1198. CString strText;
  1199. if(button < ENV_LEFTBAR_BUTTONS && cLeftBarButtons[button] != ID_SEPARATOR)
  1200. {
  1201. strText = LoadResourceString(cLeftBarButtons[button]);
  1202. }
  1203. pMainFrm->SetHelpText(strText);
  1204. }
  1205. m_nBtnMouseOver = button;
  1206. UpdateNcButtonState();
  1207. }
  1208. CModScrollView::OnNcMouseMove(nHitTest, point);
  1209. }
  1210. void CViewInstrument::OnNcLButtonDown(UINT uFlags, CPoint point)
  1211. {
  1212. if(m_nBtnMouseOver < ENV_LEFTBAR_BUTTONS)
  1213. {
  1214. m_dwStatus |= INSSTATUS_NCLBTNDOWN;
  1215. if(cLeftBarButtons[m_nBtnMouseOver] != ID_SEPARATOR)
  1216. {
  1217. PostMessage(WM_COMMAND, cLeftBarButtons[m_nBtnMouseOver]);
  1218. UpdateNcButtonState();
  1219. }
  1220. }
  1221. CModScrollView::OnNcLButtonDown(uFlags, point);
  1222. }
  1223. void CViewInstrument::OnNcLButtonUp(UINT uFlags, CPoint point)
  1224. {
  1225. if(m_dwStatus & INSSTATUS_NCLBTNDOWN)
  1226. {
  1227. m_dwStatus &= ~INSSTATUS_NCLBTNDOWN;
  1228. UpdateNcButtonState();
  1229. }
  1230. CModScrollView::OnNcLButtonUp(uFlags, point);
  1231. }
  1232. void CViewInstrument::OnNcLButtonDblClk(UINT uFlags, CPoint point)
  1233. {
  1234. OnNcLButtonDown(uFlags, point);
  1235. }
  1236. LRESULT CViewInstrument::OnNcHitTest(CPoint point)
  1237. {
  1238. CRect rect;
  1239. GetWindowRect(&rect);
  1240. rect.bottom = rect.top + ENV_LEFTBAR_CY;
  1241. if(rect.PtInRect(point))
  1242. {
  1243. return HTBORDER;
  1244. }
  1245. return CModScrollView::OnNcHitTest(point);
  1246. }
  1247. void CViewInstrument::OnMouseMove(UINT, CPoint pt)
  1248. {
  1249. ModInstrument *pIns = GetInstrumentPtr();
  1250. if(pIns == nullptr)
  1251. return;
  1252. bool splitCursor = false;
  1253. if((m_nBtnMouseOver < ENV_LEFTBAR_BUTTONS) || (m_dwStatus & INSSTATUS_NCLBTNDOWN))
  1254. {
  1255. m_dwStatus &= ~INSSTATUS_NCLBTNDOWN;
  1256. m_nBtnMouseOver = 0xFFFF;
  1257. UpdateNcButtonState();
  1258. CMainFrame *pMainFrm = CMainFrame::GetMainFrame();
  1259. if(pMainFrm)
  1260. pMainFrm->SetHelpText(_T(""));
  1261. }
  1262. int nTick = ScreenToTick(pt.x);
  1263. int nVal = Clamp(ScreenToValue(pt.y), ENVELOPE_MIN, ENVELOPE_MAX);
  1264. if(nTick < 0)
  1265. nTick = 0;
  1266. UpdateIndicator(nTick, nVal);
  1267. if((m_dwStatus & INSSTATUS_DRAGGING) && (m_nDragItem))
  1268. {
  1269. if(!m_mouseMoveModified)
  1270. {
  1271. PrepareUndo("Move Envelope Point");
  1272. m_mouseMoveModified = true;
  1273. }
  1274. bool changed = false;
  1275. if(pt.x >= m_rcClient.right - 2)
  1276. nTick++;
  1277. if(IsDragItemEnvPoint())
  1278. {
  1279. // Ctrl pressed -> move tail of envelope
  1280. changed = EnvSetValue(m_nDragItem - 1, nTick, nVal, CMainFrame::GetInputHandler()->CtrlPressed());
  1281. } else
  1282. {
  1283. int nPoint = ScreenToPoint(pt.x, pt.y);
  1284. if (nPoint >= 0) switch(m_nDragItem)
  1285. {
  1286. case ENV_DRAGLOOPSTART:
  1287. changed = EnvSetLoopStart(nPoint);
  1288. splitCursor = true;
  1289. break;
  1290. case ENV_DRAGLOOPEND:
  1291. changed = EnvSetLoopEnd(nPoint);
  1292. splitCursor = true;
  1293. break;
  1294. case ENV_DRAGSUSTAINSTART:
  1295. changed = EnvSetSustainStart(nPoint);
  1296. splitCursor = true;
  1297. break;
  1298. case ENV_DRAGSUSTAINEND:
  1299. changed = EnvSetSustainEnd(nPoint);
  1300. splitCursor = true;
  1301. break;
  1302. }
  1303. }
  1304. if(changed)
  1305. {
  1306. if(pt.x <= 0)
  1307. {
  1308. UpdateScrollSize();
  1309. OnScrollBy(CSize(pt.x - (int)m_zoom, 0), TRUE);
  1310. }
  1311. if(pt.x >= m_rcClient.right - 1)
  1312. {
  1313. UpdateScrollSize();
  1314. OnScrollBy(CSize((int)m_zoom + pt.x - m_rcClient.right, 0), TRUE);
  1315. }
  1316. SetModified(InstrumentHint().Envelope(), true);
  1317. UpdateWindow(); //rewbs: TODO - optimisation here so we don't redraw whole view.
  1318. }
  1319. } else
  1320. {
  1321. CRect rect;
  1322. if(EnvGetSustain())
  1323. {
  1324. int nspace = m_rcClient.bottom / 4;
  1325. rect.top = ValueToScreen(EnvGetValue(EnvGetSustainStart())) - nspace;
  1326. rect.bottom = rect.top + nspace * 2;
  1327. rect.right = PointToScreen(EnvGetSustainStart()) + 1;
  1328. rect.left = rect.right - m_envPointSize * 2;
  1329. if(rect.PtInRect(pt))
  1330. {
  1331. splitCursor = true; // ENV_DRAGSUSTAINSTART;
  1332. } else
  1333. {
  1334. rect.top = ValueToScreen(EnvGetValue(EnvGetSustainEnd())) - nspace;
  1335. rect.bottom = rect.top + nspace * 2;
  1336. rect.left = PointToScreen(EnvGetSustainEnd()) - 1;
  1337. rect.right = rect.left + m_envPointSize * 2;
  1338. if(rect.PtInRect(pt))
  1339. splitCursor = true; // ENV_DRAGSUSTAINEND;
  1340. }
  1341. }
  1342. if(EnvGetLoop())
  1343. {
  1344. rect.top = m_rcClient.top;
  1345. rect.bottom = m_rcClient.bottom;
  1346. rect.right = PointToScreen(EnvGetLoopStart()) + 1;
  1347. rect.left = rect.right - m_envPointSize * 2;
  1348. if(rect.PtInRect(pt))
  1349. {
  1350. splitCursor = true; // ENV_DRAGLOOPSTART;
  1351. } else
  1352. {
  1353. rect.left = PointToScreen(EnvGetLoopEnd()) - 1;
  1354. rect.right = rect.left + m_envPointSize * 2;
  1355. if(rect.PtInRect(pt))
  1356. splitCursor = true; // ENV_DRAGLOOPEND;
  1357. }
  1358. }
  1359. }
  1360. // Update the mouse cursor
  1361. if(splitCursor)
  1362. {
  1363. if(!(m_dwStatus & INSSTATUS_SPLITCURSOR))
  1364. {
  1365. m_dwStatus |= INSSTATUS_SPLITCURSOR;
  1366. if(!(m_dwStatus & INSSTATUS_DRAGGING))
  1367. SetCapture();
  1368. SetCursor(CMainFrame::curVSplit);
  1369. }
  1370. } else
  1371. {
  1372. if(m_dwStatus & INSSTATUS_SPLITCURSOR)
  1373. {
  1374. m_dwStatus &= ~INSSTATUS_SPLITCURSOR;
  1375. SetCursor(CMainFrame::curArrow);
  1376. if(!(m_dwStatus & INSSTATUS_DRAGGING))
  1377. ReleaseCapture();
  1378. }
  1379. }
  1380. }
  1381. void CViewInstrument::UpdateIndicator()
  1382. {
  1383. InstrumentEnvelope *pEnv = GetEnvelopePtr();
  1384. if(pEnv == nullptr || !m_nDragItem)
  1385. return;
  1386. uint32 point = DragItemToEnvPoint();
  1387. if(point < pEnv->size())
  1388. {
  1389. UpdateIndicator(pEnv->at(point).tick, pEnv->at(point).value);
  1390. }
  1391. }
  1392. void CViewInstrument::UpdateIndicator(int tick, int val)
  1393. {
  1394. ModInstrument *pIns = GetInstrumentPtr();
  1395. if(pIns == nullptr)
  1396. return;
  1397. CString s;
  1398. s.Format(TrackerSettings::Instance().cursorPositionInHex ? _T("Tick %X, [%s]") : _T("Tick %d, [%s]"), tick, EnvValueToString(tick, val).GetString());
  1399. CModScrollView::UpdateIndicator(s);
  1400. CMainFrame::GetMainFrame()->NotifyAccessibilityUpdate(*this);
  1401. }
  1402. CString CViewInstrument::EnvValueToString(int tick, int val) const
  1403. {
  1404. const InstrumentEnvelope *env = GetEnvelopePtr();
  1405. const bool hasReleaseNode = env->nReleaseNode != ENV_RELEASE_NODE_UNSET;
  1406. EnvelopeNode releaseNode;
  1407. if(hasReleaseNode)
  1408. {
  1409. releaseNode = env->at(env->nReleaseNode);
  1410. }
  1411. CString s;
  1412. if(!hasReleaseNode || tick <= releaseNode.tick + 1)
  1413. {
  1414. // ticks before release node (or no release node)
  1415. const int displayVal = (m_nEnv != ENV_VOLUME && !(m_nEnv == ENV_PITCH && env->dwFlags[ENV_FILTER])) ? val - 32 : val;
  1416. if(m_nEnv != ENV_PANNING)
  1417. s.Format(_T("%d"), displayVal);
  1418. else // panning envelope: display right/center/left chars
  1419. s.Format(_T("%d %c"), std::abs(displayVal), displayVal > 0 ? _T('R') : (displayVal < 0 ? _T('L') : _T('C')));
  1420. } else
  1421. {
  1422. // ticks after release node
  1423. int displayVal = (val - releaseNode.value) * 2;
  1424. displayVal = (m_nEnv != ENV_VOLUME) ? displayVal - 32 : displayVal;
  1425. s.Format(_T("Rel%c%d"), displayVal > 0 ? _T('+') : _T('-'), std::abs(displayVal));
  1426. }
  1427. return s;
  1428. }
  1429. void CViewInstrument::OnLButtonDown(UINT, CPoint pt)
  1430. {
  1431. m_mouseMoveModified = false;
  1432. if(!(m_dwStatus & INSSTATUS_DRAGGING))
  1433. {
  1434. CRect rect;
  1435. // Look if dragging a point
  1436. uint32 maxpoint = EnvGetLastPoint();
  1437. uint32 oldDragItem = m_nDragItem;
  1438. m_nDragItem = 0;
  1439. const int hitboxSize = static_cast<int>((6 * m_nDPIx) / 96.0f);
  1440. for(uint32 i = 0; i <= maxpoint; i++)
  1441. {
  1442. int x = PointToScreen(i);
  1443. int y = ValueToScreen(EnvGetValue(i));
  1444. rect.SetRect(x - hitboxSize, y - hitboxSize, x + hitboxSize + 1, y + hitboxSize + 1);
  1445. if(rect.PtInRect(pt))
  1446. {
  1447. m_nDragItem = i + 1;
  1448. break;
  1449. }
  1450. }
  1451. if((!m_nDragItem) && (EnvGetSustain()))
  1452. {
  1453. int nspace = m_rcClient.bottom / 4;
  1454. rect.top = ValueToScreen(EnvGetValue(EnvGetSustainStart())) - nspace;
  1455. rect.bottom = rect.top + nspace * 2;
  1456. rect.right = PointToScreen(EnvGetSustainStart()) + 1;
  1457. rect.left = rect.right - m_envPointSize * 2;
  1458. if(rect.PtInRect(pt))
  1459. {
  1460. m_nDragItem = ENV_DRAGSUSTAINSTART;
  1461. } else
  1462. {
  1463. rect.top = ValueToScreen(EnvGetValue(EnvGetSustainEnd())) - nspace;
  1464. rect.bottom = rect.top + nspace * 2;
  1465. rect.left = PointToScreen(EnvGetSustainEnd()) - 1;
  1466. rect.right = rect.left + m_envPointSize * 2;
  1467. if(rect.PtInRect(pt))
  1468. m_nDragItem = ENV_DRAGSUSTAINEND;
  1469. }
  1470. }
  1471. if((!m_nDragItem) && (EnvGetLoop()))
  1472. {
  1473. rect.top = m_rcClient.top;
  1474. rect.bottom = m_rcClient.bottom;
  1475. rect.right = PointToScreen(EnvGetLoopStart()) + 1;
  1476. rect.left = rect.right - m_envPointSize * 2;
  1477. if(rect.PtInRect(pt))
  1478. {
  1479. m_nDragItem = ENV_DRAGLOOPSTART;
  1480. } else
  1481. {
  1482. rect.left = PointToScreen(EnvGetLoopEnd()) - 1;
  1483. rect.right = rect.left + m_envPointSize * 2;
  1484. if(rect.PtInRect(pt))
  1485. m_nDragItem = ENV_DRAGLOOPEND;
  1486. }
  1487. }
  1488. if(m_nDragItem)
  1489. {
  1490. SetCapture();
  1491. m_dwStatus |= INSSTATUS_DRAGGING;
  1492. // refresh active node colour
  1493. InvalidateRect(NULL, FALSE);
  1494. } else
  1495. {
  1496. // Shift-Click: Insert envelope point here
  1497. if(CMainFrame::GetInputHandler()->ShiftPressed())
  1498. {
  1499. if(InsertAtPoint(pt) == 0 && oldDragItem != 0)
  1500. {
  1501. InvalidateRect(NULL, FALSE);
  1502. }
  1503. } else if(oldDragItem)
  1504. {
  1505. InvalidateRect(NULL, FALSE);
  1506. }
  1507. }
  1508. }
  1509. }
  1510. void CViewInstrument::OnLButtonUp(UINT, CPoint)
  1511. {
  1512. m_mouseMoveModified = false;
  1513. if(m_dwStatus & INSSTATUS_SPLITCURSOR)
  1514. {
  1515. m_dwStatus &= ~INSSTATUS_SPLITCURSOR;
  1516. SetCursor(CMainFrame::curArrow);
  1517. }
  1518. if(m_dwStatus & INSSTATUS_DRAGGING)
  1519. {
  1520. m_dwStatus &= ~INSSTATUS_DRAGGING;
  1521. ReleaseCapture();
  1522. }
  1523. }
  1524. void CViewInstrument::OnRButtonDown(UINT flags, CPoint pt)
  1525. {
  1526. const CModDoc *pModDoc = GetDocument();
  1527. if(!pModDoc)
  1528. return;
  1529. const CSoundFile &sndFile = GetDocument()->GetSoundFile();
  1530. if(m_dwStatus & INSSTATUS_DRAGGING)
  1531. return;
  1532. // Ctrl + Right-Click = Delete point
  1533. if(flags & MK_CONTROL)
  1534. {
  1535. OnMButtonDown(flags, pt);
  1536. return;
  1537. }
  1538. CMenu Menu;
  1539. if((pModDoc) && (Menu.LoadMenu(IDR_ENVELOPES)))
  1540. {
  1541. CMenu *pSubMenu = Menu.GetSubMenu(0);
  1542. if(pSubMenu != nullptr)
  1543. {
  1544. m_nDragItem = ScreenToPoint(pt.x, pt.y) + 1;
  1545. const uint32 maxPoint = (sndFile.GetType() == MOD_TYPE_XM) ? 11 : 24;
  1546. const uint32 lastpoint = EnvGetLastPoint();
  1547. const bool forceRelease = !sndFile.GetModSpecifications().hasReleaseNode && (EnvGetReleaseNode() != ENV_RELEASE_NODE_UNSET);
  1548. pSubMenu->EnableMenuItem(ID_ENVELOPE_INSERTPOINT, (lastpoint < maxPoint) ? MF_ENABLED : MF_GRAYED);
  1549. pSubMenu->EnableMenuItem(ID_ENVELOPE_REMOVEPOINT, ((m_nDragItem) && (lastpoint > 0)) ? MF_ENABLED : MF_GRAYED);
  1550. pSubMenu->EnableMenuItem(ID_ENVELOPE_CARRY, (sndFile.GetType() & (MOD_TYPE_IT | MOD_TYPE_MPT)) ? MF_ENABLED : MF_GRAYED);
  1551. pSubMenu->EnableMenuItem(ID_ENVELOPE_TOGGLERELEASENODE, ((sndFile.GetModSpecifications().hasReleaseNode && m_nEnv == ENV_VOLUME) || forceRelease) ? MF_ENABLED : MF_GRAYED);
  1552. pSubMenu->CheckMenuItem(ID_ENVELOPE_SETLOOP, (EnvGetLoop()) ? MF_CHECKED : MF_UNCHECKED);
  1553. pSubMenu->CheckMenuItem(ID_ENVELOPE_SUSTAIN, (EnvGetSustain()) ? MF_CHECKED : MF_UNCHECKED);
  1554. pSubMenu->CheckMenuItem(ID_ENVELOPE_CARRY, (EnvGetCarry()) ? MF_CHECKED : MF_UNCHECKED);
  1555. pSubMenu->CheckMenuItem(ID_ENVELOPE_TOGGLERELEASENODE, (EnvGetReleaseNode() == m_nDragItem - 1) ? MF_CHECKED : MF_UNCHECKED);
  1556. m_ptMenu = pt;
  1557. ClientToScreen(&pt);
  1558. pSubMenu->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, pt.x, pt.y, this);
  1559. }
  1560. }
  1561. }
  1562. void CViewInstrument::OnMButtonDown(UINT, CPoint pt)
  1563. {
  1564. // Middle mouse button: Remove envelope point
  1565. int point = ScreenToPoint(pt.x, pt.y);
  1566. if(point >= 0)
  1567. {
  1568. EnvRemovePoint(point);
  1569. m_nDragItem = point + 1;
  1570. }
  1571. }
  1572. void CViewInstrument::OnPrevInstrument()
  1573. {
  1574. SendCtrlMessage(CTRLMSG_INS_PREVINSTRUMENT);
  1575. }
  1576. void CViewInstrument::OnNextInstrument()
  1577. {
  1578. SendCtrlMessage(CTRLMSG_INS_NEXTINSTRUMENT);
  1579. }
  1580. void CViewInstrument::OnEditSampleMap()
  1581. {
  1582. SendCtrlMessage(CTRLMSG_INS_SAMPLEMAP);
  1583. }
  1584. void CViewInstrument::OnSelectVolumeEnv()
  1585. {
  1586. if(m_nEnv != ENV_VOLUME)
  1587. SetCurrentInstrument(m_nInstrument, ENV_VOLUME);
  1588. }
  1589. void CViewInstrument::OnSelectPanningEnv()
  1590. {
  1591. if(m_nEnv != ENV_PANNING)
  1592. SetCurrentInstrument(m_nInstrument, ENV_PANNING);
  1593. }
  1594. void CViewInstrument::OnSelectPitchEnv()
  1595. {
  1596. if(m_nEnv != ENV_PITCH)
  1597. SetCurrentInstrument(m_nInstrument, ENV_PITCH);
  1598. }
  1599. void CViewInstrument::OnEnvLoopChanged()
  1600. {
  1601. CModDoc *pModDoc = GetDocument();
  1602. PrepareUndo("Toggle Envelope Loop");
  1603. if((pModDoc) && (EnvSetLoop(!EnvGetLoop())))
  1604. {
  1605. InstrumentEnvelope *pEnv = GetEnvelopePtr();
  1606. if(EnvGetLoop() && pEnv != nullptr && pEnv->nLoopEnd == 0)
  1607. {
  1608. // Enabled loop => set loop points if no loop has been specified yet.
  1609. pEnv->nLoopStart = 0;
  1610. pEnv->nLoopEnd = mpt::saturate_cast<decltype(pEnv->nLoopEnd)>(pEnv->size() - 1);
  1611. }
  1612. SetModified(InstrumentHint().Envelope(), true);
  1613. }
  1614. }
  1615. void CViewInstrument::OnEnvSustainChanged()
  1616. {
  1617. CModDoc *pModDoc = GetDocument();
  1618. PrepareUndo("Toggle Envelope Sustain");
  1619. if((pModDoc) && (EnvSetSustain(!EnvGetSustain())))
  1620. {
  1621. InstrumentEnvelope *pEnv = GetEnvelopePtr();
  1622. if(EnvGetSustain() && pEnv != nullptr && pEnv->nSustainStart == pEnv->nSustainEnd && IsDragItemEnvPoint())
  1623. {
  1624. // Enabled sustain loop => set sustain loop points if no sustain loop has been specified yet.
  1625. pEnv->nSustainStart = pEnv->nSustainEnd = mpt::saturate_cast<decltype(pEnv->nSustainEnd)>(m_nDragItem - 1);
  1626. }
  1627. SetModified(InstrumentHint().Envelope(), true);
  1628. }
  1629. }
  1630. void CViewInstrument::OnEnvCarryChanged()
  1631. {
  1632. CModDoc *pModDoc = GetDocument();
  1633. PrepareUndo("Toggle Envelope Carry");
  1634. if((pModDoc) && (EnvSetCarry(!EnvGetCarry())))
  1635. {
  1636. SetModified(InstrumentHint().Envelope(), false);
  1637. UpdateNcButtonState();
  1638. }
  1639. }
  1640. void CViewInstrument::OnEnvToggleReleasNode()
  1641. {
  1642. if(IsDragItemEnvPoint())
  1643. {
  1644. PrepareUndo("Toggle Envelope Release Node");
  1645. if(EnvToggleReleaseNode(m_nDragItem - 1))
  1646. {
  1647. SetModified(InstrumentHint().Envelope(), true);
  1648. }
  1649. }
  1650. }
  1651. void CViewInstrument::OnEnvVolChanged()
  1652. {
  1653. GetDocument()->GetInstrumentUndo().PrepareUndo(m_nInstrument, "Toggle Volume Envelope", ENV_VOLUME);
  1654. if(EnvSetVolEnv(!EnvGetVolEnv()))
  1655. {
  1656. SetModified(InstrumentHint().Envelope(), false);
  1657. UpdateNcButtonState();
  1658. }
  1659. }
  1660. void CViewInstrument::OnEnvPanChanged()
  1661. {
  1662. GetDocument()->GetInstrumentUndo().PrepareUndo(m_nInstrument, "Toggle Panning Envelope", ENV_PANNING);
  1663. if(EnvSetPanEnv(!EnvGetPanEnv()))
  1664. {
  1665. SetModified(InstrumentHint().Envelope(), false);
  1666. UpdateNcButtonState();
  1667. }
  1668. }
  1669. void CViewInstrument::OnEnvPitchChanged()
  1670. {
  1671. GetDocument()->GetInstrumentUndo().PrepareUndo(m_nInstrument, "Toggle Pitch Envelope", ENV_PITCH);
  1672. if(EnvSetPitchEnv(!EnvGetPitchEnv()))
  1673. {
  1674. SetModified(InstrumentHint().Envelope(), false);
  1675. UpdateNcButtonState();
  1676. }
  1677. }
  1678. void CViewInstrument::OnEnvFilterChanged()
  1679. {
  1680. GetDocument()->GetInstrumentUndo().PrepareUndo(m_nInstrument, "Toggle Filter Envelope", ENV_PITCH);
  1681. if(EnvSetFilterEnv(!EnvGetFilterEnv()))
  1682. {
  1683. SetModified(InstrumentHint().Envelope(), false);
  1684. UpdateNcButtonState();
  1685. }
  1686. }
  1687. void CViewInstrument::OnEnvToggleGrid()
  1688. {
  1689. m_bGrid = !m_bGrid;
  1690. if(m_bGrid)
  1691. m_bGridForceRedraw = true;
  1692. CModDoc *pModDoc = GetDocument();
  1693. if(pModDoc)
  1694. pModDoc->UpdateAllViews(nullptr, InstrumentHint(m_nInstrument).Envelope());
  1695. }
  1696. void CViewInstrument::OnEnvRemovePoint()
  1697. {
  1698. if(m_nDragItem > 0)
  1699. {
  1700. EnvRemovePoint(m_nDragItem - 1);
  1701. }
  1702. }
  1703. void CViewInstrument::OnEnvInsertPoint()
  1704. {
  1705. const int tick = ScreenToTick(m_ptMenu.x), value = ScreenToValue(m_ptMenu.y);
  1706. if(!EnvInsertPoint(tick, value))
  1707. {
  1708. // Couldn't insert point, maybe because there's already a point at this tick
  1709. // => Try next tick
  1710. EnvInsertPoint(tick + 1, value);
  1711. }
  1712. }
  1713. bool CViewInstrument::InsertAtPoint(CPoint pt)
  1714. {
  1715. auto item = EnvInsertPoint(ScreenToTick(pt.x), ScreenToValue(pt.y)); // returns point ID + 1 if successful, else 0.
  1716. if(item > 0)
  1717. {
  1718. // Drag point if successful
  1719. SetCapture();
  1720. m_dwStatus |= INSSTATUS_DRAGGING;
  1721. m_nDragItem = item;
  1722. }
  1723. return item > 0;
  1724. }
  1725. void CViewInstrument::OnEditCopy()
  1726. {
  1727. CModDoc *pModDoc = GetDocument();
  1728. if(pModDoc)
  1729. pModDoc->CopyEnvelope(m_nInstrument, m_nEnv);
  1730. }
  1731. void CViewInstrument::OnEditPaste()
  1732. {
  1733. CModDoc *pModDoc = GetDocument();
  1734. PrepareUndo("Paste Envelope");
  1735. if(pModDoc->PasteEnvelope(m_nInstrument, m_nEnv))
  1736. {
  1737. SetModified(InstrumentHint().Envelope(), true);
  1738. } else
  1739. {
  1740. pModDoc->GetInstrumentUndo().RemoveLastUndoStep(m_nInstrument);
  1741. }
  1742. }
  1743. void CViewInstrument::PlayNote(ModCommand::NOTE note)
  1744. {
  1745. CMainFrame *pMainFrm = CMainFrame::GetMainFrame();
  1746. CModDoc *pModDoc = GetDocument();
  1747. if(pModDoc == nullptr || pMainFrm == nullptr)
  1748. {
  1749. return;
  1750. }
  1751. if(note > 0 && note < 128)
  1752. {
  1753. if(m_nInstrument && !m_baPlayingNote[note])
  1754. {
  1755. CSoundFile &sndFile = pModDoc->GetSoundFile();
  1756. ModInstrument *pIns = sndFile.Instruments[m_nInstrument];
  1757. if((!pIns) || (!pIns->Keyboard[note - NOTE_MIN] && !pIns->nMixPlug))
  1758. return;
  1759. {
  1760. if(pMainFrm->GetModPlaying() != pModDoc)
  1761. {
  1762. sndFile.m_SongFlags.set(SONG_PAUSED);
  1763. sndFile.ResetChannels();
  1764. if(!pMainFrm->PlayMod(pModDoc))
  1765. return;
  1766. }
  1767. pModDoc->PlayNote(PlayNoteParam(note).Instrument(m_nInstrument).CheckNNA(m_baPlayingNote), &m_noteChannel);
  1768. }
  1769. CString noteName;
  1770. if(ModCommand::IsNote(note))
  1771. {
  1772. noteName = mpt::ToCString(sndFile.GetNoteName(note, m_nInstrument));
  1773. }
  1774. pMainFrm->SetInfoText(noteName);
  1775. }
  1776. } else
  1777. {
  1778. pModDoc->PlayNote(PlayNoteParam(note).Instrument(m_nInstrument));
  1779. }
  1780. }
  1781. // Drop files from Windows
  1782. void CViewInstrument::OnDropFiles(HDROP hDropInfo)
  1783. {
  1784. const UINT nFiles = ::DragQueryFile(hDropInfo, (UINT)-1, NULL, 0);
  1785. CMainFrame::GetMainFrame()->SetForegroundWindow();
  1786. for(UINT f = 0; f < nFiles; f++)
  1787. {
  1788. UINT size = ::DragQueryFile(hDropInfo, f, nullptr, 0) + 1;
  1789. std::vector<TCHAR> fileName(size, _T('\0'));
  1790. if(::DragQueryFile(hDropInfo, f, fileName.data(), size))
  1791. {
  1792. const mpt::PathString file = mpt::PathString::FromNative(fileName.data());
  1793. PrepareUndo("Replace Envelope");
  1794. if(GetDocument()->LoadEnvelope(m_nInstrument, m_nEnv, file))
  1795. {
  1796. SetModified(InstrumentHint(m_nInstrument).Envelope(), true);
  1797. } else
  1798. {
  1799. GetDocument()->GetInstrumentUndo().RemoveLastUndoStep(m_nInstrument);
  1800. if(SendCtrlMessage(CTRLMSG_INS_OPENFILE, (LPARAM)&file) && f < nFiles - 1)
  1801. {
  1802. // Insert more instrument slots
  1803. if(!SendCtrlMessage(CTRLMSG_INS_NEWINSTRUMENT))
  1804. break;
  1805. }
  1806. }
  1807. }
  1808. }
  1809. ::DragFinish(hDropInfo);
  1810. }
  1811. BOOL CViewInstrument::OnDragonDrop(BOOL doDrop, const DRAGONDROP *dropInfo)
  1812. {
  1813. CModDoc *modDoc = GetDocument();
  1814. bool canDrop = false;
  1815. if((!dropInfo) || (!modDoc))
  1816. return FALSE;
  1817. CSoundFile &sndFile = modDoc->GetSoundFile();
  1818. switch(dropInfo->dropType)
  1819. {
  1820. case DRAGONDROP_INSTRUMENT:
  1821. if(dropInfo->sndFile == &sndFile)
  1822. {
  1823. canDrop = ((dropInfo->dropItem)
  1824. && (dropInfo->dropItem <= sndFile.m_nInstruments)
  1825. && (dropInfo->sndFile == &sndFile));
  1826. } else
  1827. {
  1828. canDrop = ((dropInfo->dropItem)
  1829. && ((dropInfo->dropParam) || (dropInfo->sndFile)));
  1830. }
  1831. break;
  1832. case DRAGONDROP_DLS:
  1833. canDrop = ((dropInfo->dropItem < CTrackApp::gpDLSBanks.size())
  1834. && (CTrackApp::gpDLSBanks[dropInfo->dropItem]));
  1835. break;
  1836. case DRAGONDROP_SOUNDFILE:
  1837. case DRAGONDROP_MIDIINSTR:
  1838. canDrop = !dropInfo->GetPath().empty();
  1839. break;
  1840. }
  1841. const bool insertNew = CMainFrame::GetInputHandler()->ShiftPressed() && sndFile.GetNumInstruments() > 0;
  1842. if(insertNew && !sndFile.CanAddMoreInstruments())
  1843. canDrop = false;
  1844. if(!canDrop || !doDrop)
  1845. return canDrop;
  1846. if(!sndFile.GetNumInstruments() && sndFile.GetModSpecifications().instrumentsMax > 0)
  1847. SendCtrlMessage(CTRLMSG_INS_NEWINSTRUMENT);
  1848. if(!m_nInstrument || m_nInstrument > sndFile.GetNumInstruments())
  1849. return FALSE;
  1850. // Do the drop
  1851. bool modified = false;
  1852. BeginWaitCursor();
  1853. switch(dropInfo->dropType)
  1854. {
  1855. case DRAGONDROP_INSTRUMENT:
  1856. if(dropInfo->sndFile == &sndFile)
  1857. {
  1858. SendCtrlMessage(CTRLMSG_SETCURRENTINSTRUMENT, dropInfo->dropItem);
  1859. } else
  1860. {
  1861. if(insertNew && !SendCtrlMessage(CTRLMSG_INS_NEWINSTRUMENT))
  1862. canDrop = false;
  1863. else
  1864. SendCtrlMessage(CTRLMSG_INS_SONGDROP, reinterpret_cast<LPARAM>(dropInfo));
  1865. }
  1866. break;
  1867. case DRAGONDROP_MIDIINSTR:
  1868. if(CDLSBank::IsDLSBank(dropInfo->GetPath()))
  1869. {
  1870. CDLSBank dlsbank;
  1871. if(dlsbank.Open(dropInfo->GetPath()))
  1872. {
  1873. const DLSINSTRUMENT *pDlsIns;
  1874. UINT nIns = 0, nRgn = 0xFF;
  1875. // Drums
  1876. if(dropInfo->dropItem & 0x80)
  1877. {
  1878. UINT key = dropInfo->dropItem & 0x7F;
  1879. pDlsIns = dlsbank.FindInstrument(true, 0xFFFF, 0xFF, key, &nIns);
  1880. if(pDlsIns)
  1881. nRgn = dlsbank.GetRegionFromKey(nIns, key);
  1882. } else
  1883. // Melodic
  1884. {
  1885. pDlsIns = dlsbank.FindInstrument(false, 0xFFFF, dropInfo->dropItem, 60, &nIns);
  1886. if(pDlsIns)
  1887. nRgn = dlsbank.GetRegionFromKey(nIns, 60);
  1888. }
  1889. canDrop = false;
  1890. if(pDlsIns)
  1891. {
  1892. if(!insertNew || SendCtrlMessage(CTRLMSG_INS_NEWINSTRUMENT))
  1893. {
  1894. CriticalSection cs;
  1895. modDoc->GetInstrumentUndo().PrepareUndo(m_nInstrument, "Replace Instrument");
  1896. canDrop = modified = dlsbank.ExtractInstrument(sndFile, m_nInstrument, nIns, nRgn);
  1897. }
  1898. }
  1899. break;
  1900. }
  1901. }
  1902. // Instrument file -> fall through
  1903. [[fallthrough]];
  1904. case DRAGONDROP_SOUNDFILE:
  1905. if(!insertNew || SendCtrlMessage(CTRLMSG_INS_NEWINSTRUMENT))
  1906. SendCtrlMessage(CTRLMSG_INS_OPENFILE, dropInfo->dropParam);
  1907. break;
  1908. case DRAGONDROP_DLS:
  1909. {
  1910. UINT nIns = dropInfo->dropParam & 0xFFFF;
  1911. uint32 drumRgn = uint32_max;
  1912. // Drums: (0x80000000) | (Region << 16) | (Instrument)
  1913. if(dropInfo->dropParam & 0x80000000)
  1914. drumRgn = (dropInfo->dropParam & 0x7FFF0000) >> 16;
  1915. if(!insertNew || SendCtrlMessage(CTRLMSG_INS_NEWINSTRUMENT))
  1916. {
  1917. CriticalSection cs;
  1918. modDoc->GetInstrumentUndo().PrepareUndo(m_nInstrument, "Replace Instrument");
  1919. canDrop = modified = CTrackApp::gpDLSBanks[dropInfo->dropItem]->ExtractInstrument(sndFile, m_nInstrument, nIns, drumRgn);
  1920. }
  1921. }
  1922. break;
  1923. }
  1924. if(modified)
  1925. {
  1926. SetModified(InstrumentHint().Info().Envelope().Names(), true);
  1927. GetDocument()->UpdateAllViews(nullptr, SampleHint().Info().Names().Data(), this);
  1928. }
  1929. CMDIChildWnd *pMDIFrame = (CMDIChildWnd *)GetParentFrame();
  1930. if(pMDIFrame)
  1931. {
  1932. pMDIFrame->MDIActivate();
  1933. pMDIFrame->SetActiveView(this);
  1934. SetFocus();
  1935. }
  1936. EndWaitCursor();
  1937. return canDrop;
  1938. }
  1939. LRESULT CViewInstrument::OnMidiMsg(WPARAM midiDataParam, LPARAM)
  1940. {
  1941. const uint32 midiData = static_cast<uint32>(midiDataParam);
  1942. CModDoc *modDoc = GetDocument();
  1943. if(modDoc != nullptr)
  1944. {
  1945. modDoc->ProcessMIDI(midiData, m_nInstrument, modDoc->GetSoundFile().GetInstrumentPlugin(m_nInstrument), kCtxViewInstruments);
  1946. MIDIEvents::EventType event = MIDIEvents::GetTypeFromEvent(midiData);
  1947. uint8 midiByte1 = MIDIEvents::GetDataByte1FromEvent(midiData);
  1948. if(event == MIDIEvents::evNoteOn)
  1949. {
  1950. CMainFrame::GetMainFrame()->SetInfoText(mpt::ToCString(modDoc->GetSoundFile().GetNoteName(midiByte1 + NOTE_MIN, m_nInstrument)));
  1951. }
  1952. return 1;
  1953. }
  1954. return 0;
  1955. }
  1956. BOOL CViewInstrument::PreTranslateMessage(MSG *pMsg)
  1957. {
  1958. if(pMsg)
  1959. {
  1960. //We handle keypresses before Windows has a chance to handle them (for alt etc..)
  1961. if((pMsg->message == WM_SYSKEYUP) || (pMsg->message == WM_KEYUP) ||
  1962. (pMsg->message == WM_SYSKEYDOWN) || (pMsg->message == WM_KEYDOWN))
  1963. {
  1964. CInputHandler *ih = CMainFrame::GetInputHandler();
  1965. //Translate message manually
  1966. UINT nChar = static_cast<UINT>(pMsg->wParam);
  1967. UINT nRepCnt = LOWORD(pMsg->lParam);
  1968. UINT nFlags = HIWORD(pMsg->lParam);
  1969. KeyEventType kT = ih->GetKeyEventType(nFlags);
  1970. InputTargetContext ctx = (InputTargetContext)(kCtxViewInstruments);
  1971. if(ih->KeyEvent(ctx, nChar, nRepCnt, nFlags, kT) != kcNull)
  1972. return true; // Mapped to a command, no need to pass message on.
  1973. // Handle Application (menu) key
  1974. if(pMsg->message == WM_KEYDOWN && nChar == VK_APPS)
  1975. {
  1976. CPoint pt(0, 0);
  1977. if(m_nDragItem > 0)
  1978. {
  1979. uint32 point = DragItemToEnvPoint();
  1980. pt.SetPoint(PointToScreen(point), ValueToScreen(EnvGetValue(point)));
  1981. }
  1982. OnRButtonDown(0, pt);
  1983. }
  1984. }
  1985. }
  1986. return CModScrollView::PreTranslateMessage(pMsg);
  1987. }
  1988. LRESULT CViewInstrument::OnCustomKeyMsg(WPARAM wParam, LPARAM)
  1989. {
  1990. CModDoc *pModDoc = GetDocument();
  1991. if(!pModDoc)
  1992. return kcNull;
  1993. CSoundFile &sndFile = pModDoc->GetSoundFile();
  1994. switch(wParam)
  1995. {
  1996. case kcPrevInstrument: OnPrevInstrument(); return wParam;
  1997. case kcNextInstrument: OnNextInstrument(); return wParam;
  1998. case kcEditCopy: OnEditCopy(); return wParam;
  1999. case kcEditPaste: OnEditPaste(); return wParam;
  2000. case kcEditUndo: OnEditUndo(); return wParam;
  2001. case kcEditRedo: OnEditRedo(); return wParam;
  2002. case kcNoteOff: PlayNote(NOTE_KEYOFF); return wParam;
  2003. case kcNoteCut: PlayNote(NOTE_NOTECUT); return wParam;
  2004. case kcInstrumentLoad: SendCtrlMessage(IDC_INSTRUMENT_OPEN); return wParam;
  2005. case kcInstrumentSave: SendCtrlMessage(IDC_INSTRUMENT_SAVEAS); return wParam;
  2006. case kcInstrumentNew: SendCtrlMessage(IDC_INSTRUMENT_NEW); return wParam;
  2007. // envelope editor
  2008. case kcInstrumentEnvelopeLoad: OnEnvLoad(); return wParam;
  2009. case kcInstrumentEnvelopeSave: OnEnvSave(); return wParam;
  2010. case kcInstrumentEnvelopeZoomIn: OnEnvZoomIn(); return wParam;
  2011. case kcInstrumentEnvelopeZoomOut: OnEnvZoomOut(); return wParam;
  2012. case kcInstrumentEnvelopeScale: OnEnvelopeScalePoints(); return wParam;
  2013. case kcInstrumentEnvelopeSwitchToVolume: OnSelectVolumeEnv(); return wParam;
  2014. case kcInstrumentEnvelopeSwitchToPanning: OnSelectPanningEnv(); return wParam;
  2015. case kcInstrumentEnvelopeSwitchToPitch: OnSelectPitchEnv(); return wParam;
  2016. case kcInstrumentEnvelopeToggleVolume: OnEnvVolChanged(); return wParam;
  2017. case kcInstrumentEnvelopeTogglePanning: OnEnvPanChanged(); return wParam;
  2018. case kcInstrumentEnvelopeTogglePitch: OnEnvPitchChanged(); return wParam;
  2019. case kcInstrumentEnvelopeToggleFilter: OnEnvFilterChanged(); return wParam;
  2020. case kcInstrumentEnvelopeToggleLoop: OnEnvLoopChanged(); return wParam;
  2021. case kcInstrumentEnvelopeSelectLoopStart: EnvKbdSelectPoint(ENV_DRAGLOOPSTART); return wParam;
  2022. case kcInstrumentEnvelopeSelectLoopEnd: EnvKbdSelectPoint(ENV_DRAGLOOPEND); return wParam;
  2023. case kcInstrumentEnvelopeToggleSustain: OnEnvSustainChanged(); return wParam;
  2024. case kcInstrumentEnvelopeSelectSustainStart: EnvKbdSelectPoint(ENV_DRAGSUSTAINSTART); return wParam;
  2025. case kcInstrumentEnvelopeSelectSustainEnd: EnvKbdSelectPoint(ENV_DRAGSUSTAINEND); return wParam;
  2026. case kcInstrumentEnvelopeToggleCarry: OnEnvCarryChanged(); return wParam;
  2027. case kcInstrumentEnvelopePointPrev: EnvKbdSelectPoint(ENV_DRAGPREVIOUS); return wParam;
  2028. case kcInstrumentEnvelopePointNext: EnvKbdSelectPoint(ENV_DRAGNEXT); return wParam;
  2029. case kcInstrumentEnvelopePointMoveLeft: EnvKbdMovePointLeft(1); return wParam;
  2030. case kcInstrumentEnvelopePointMoveRight: EnvKbdMovePointRight(1); return wParam;
  2031. case kcInstrumentEnvelopePointMoveLeftCoarse: EnvKbdMovePointLeft(sndFile.m_PlayState.m_nCurrentRowsPerBeat * sndFile.m_PlayState.m_nMusicSpeed); return wParam;
  2032. case kcInstrumentEnvelopePointMoveRightCoarse: EnvKbdMovePointRight(sndFile.m_PlayState.m_nCurrentRowsPerBeat * sndFile.m_PlayState.m_nMusicSpeed); return wParam;
  2033. case kcInstrumentEnvelopePointMoveUp: EnvKbdMovePointVertical(1); return wParam;
  2034. case kcInstrumentEnvelopePointMoveDown: EnvKbdMovePointVertical(-1); return wParam;
  2035. case kcInstrumentEnvelopePointMoveUp8: EnvKbdMovePointVertical(8); return wParam;
  2036. case kcInstrumentEnvelopePointMoveDown8: EnvKbdMovePointVertical(-8); return wParam;
  2037. case kcInstrumentEnvelopePointInsert: EnvKbdInsertPoint(); return wParam;
  2038. case kcInstrumentEnvelopePointRemove: EnvKbdRemovePoint(); return wParam;
  2039. case kcInstrumentEnvelopeSetLoopStart: EnvKbdSetLoopStart(); return wParam;
  2040. case kcInstrumentEnvelopeSetLoopEnd: EnvKbdSetLoopEnd(); return wParam;
  2041. case kcInstrumentEnvelopeSetSustainLoopStart: EnvKbdSetSustainStart(); return wParam;
  2042. case kcInstrumentEnvelopeSetSustainLoopEnd: EnvKbdSetSustainEnd(); return wParam;
  2043. case kcInstrumentEnvelopeToggleReleaseNode: EnvKbdToggleReleaseNode(); return wParam;
  2044. }
  2045. if(wParam >= kcInstrumentStartNotes && wParam <= kcInstrumentEndNotes)
  2046. {
  2047. PlayNote(pModDoc->GetNoteWithBaseOctave(static_cast<int>(wParam - kcInstrumentStartNotes), m_nInstrument));
  2048. return wParam;
  2049. }
  2050. if(wParam >= kcInstrumentStartNoteStops && wParam <= kcInstrumentEndNoteStops)
  2051. {
  2052. ModCommand::NOTE note = pModDoc->GetNoteWithBaseOctave(static_cast<int>(wParam - kcInstrumentStartNoteStops), m_nInstrument);
  2053. if(ModCommand::IsNote(note))
  2054. {
  2055. m_baPlayingNote[note] = false;
  2056. pModDoc->NoteOff(note, false, m_nInstrument, m_noteChannel[note - NOTE_MIN]);
  2057. }
  2058. return wParam;
  2059. }
  2060. return kcNull;
  2061. }
  2062. void CViewInstrument::OnEnvelopeScalePoints()
  2063. {
  2064. CModDoc *pModDoc = GetDocument();
  2065. if(pModDoc == nullptr)
  2066. return;
  2067. const CSoundFile &sndFile = pModDoc->GetSoundFile();
  2068. if(m_nInstrument >= 1
  2069. && m_nInstrument <= sndFile.GetNumInstruments()
  2070. && sndFile.Instruments[m_nInstrument])
  2071. {
  2072. // "Center" y value of the envelope. For panning and pitch, this is 32, for volume and filter it is 0 (minimum).
  2073. int nOffset = ((m_nEnv != ENV_VOLUME) && !GetEnvelopePtr()->dwFlags[ENV_FILTER]) ? 32 : 0;
  2074. CScaleEnvPointsDlg dlg(this, *GetEnvelopePtr(), nOffset);
  2075. if(dlg.DoModal() == IDOK)
  2076. {
  2077. PrepareUndo("Scale Envelope");
  2078. dlg.Apply();
  2079. SetModified(InstrumentHint().Envelope(), true);
  2080. }
  2081. }
  2082. }
  2083. void CViewInstrument::EnvSetZoom(float newZoom)
  2084. {
  2085. m_zoom = Clamp(newZoom, ENV_MIN_ZOOM, ENV_MAX_ZOOM);
  2086. InvalidateRect(NULL, FALSE);
  2087. UpdateScrollSize();
  2088. UpdateNcButtonState();
  2089. }
  2090. ////////////////////////////////////////
  2091. // Envelope Editor - Keyboard actions
  2092. void CViewInstrument::EnvKbdSelectPoint(DragPoints point)
  2093. {
  2094. InstrumentEnvelope *pEnv = GetEnvelopePtr();
  2095. if(pEnv == nullptr)
  2096. return;
  2097. switch(point)
  2098. {
  2099. case ENV_DRAGLOOPSTART:
  2100. case ENV_DRAGLOOPEND:
  2101. if(!pEnv->dwFlags[ENV_LOOP])
  2102. return;
  2103. m_nDragItem = point;
  2104. break;
  2105. case ENV_DRAGSUSTAINSTART:
  2106. case ENV_DRAGSUSTAINEND:
  2107. if(!pEnv->dwFlags[ENV_SUSTAIN])
  2108. return;
  2109. m_nDragItem = point;
  2110. break;
  2111. case ENV_DRAGPREVIOUS:
  2112. if(m_nDragItem <= 1 || m_nDragItem > pEnv->size())
  2113. m_nDragItem = pEnv->size();
  2114. else
  2115. m_nDragItem--;
  2116. break;
  2117. case ENV_DRAGNEXT:
  2118. if(m_nDragItem >= pEnv->size())
  2119. m_nDragItem = 1;
  2120. else
  2121. m_nDragItem++;
  2122. break;
  2123. }
  2124. UpdateIndicator();
  2125. InvalidateRect(NULL, FALSE);
  2126. }
  2127. void CViewInstrument::EnvKbdMovePointLeft(int stepsize)
  2128. {
  2129. InstrumentEnvelope *pEnv = GetEnvelopePtr();
  2130. if(pEnv == nullptr)
  2131. return;
  2132. const MODTYPE modType = GetDocument()->GetModType();
  2133. // Move loop points?
  2134. PrepareUndo("Move Envelope Point");
  2135. if(m_nDragItem == ENV_DRAGSUSTAINSTART)
  2136. {
  2137. if(pEnv->nSustainStart <= 0)
  2138. return;
  2139. pEnv->nSustainStart--;
  2140. if(modType == MOD_TYPE_XM)
  2141. pEnv->nSustainEnd = pEnv->nSustainStart;
  2142. } else if(m_nDragItem == ENV_DRAGSUSTAINEND)
  2143. {
  2144. if(pEnv->nSustainEnd <= 0)
  2145. return;
  2146. if(pEnv->nSustainEnd <= pEnv->nSustainStart)
  2147. pEnv->nSustainStart--;
  2148. pEnv->nSustainEnd--;
  2149. } else if(m_nDragItem == ENV_DRAGLOOPSTART)
  2150. {
  2151. if(pEnv->nLoopStart <= 0)
  2152. return;
  2153. pEnv->nLoopStart--;
  2154. } else if(m_nDragItem == ENV_DRAGLOOPEND)
  2155. {
  2156. if(pEnv->nLoopEnd <= 0)
  2157. return;
  2158. if(pEnv->nLoopEnd <= pEnv->nLoopStart)
  2159. pEnv->nLoopStart--;
  2160. pEnv->nLoopEnd--;
  2161. } else
  2162. {
  2163. // Move envelope node
  2164. if(!IsDragItemEnvPoint() || m_nDragItem <= 1)
  2165. {
  2166. GetDocument()->GetInstrumentUndo().RemoveLastUndoStep(m_nInstrument);
  2167. return;
  2168. }
  2169. if(!EnvSetValue(m_nDragItem - 1, pEnv->at(m_nDragItem - 1).tick - stepsize))
  2170. {
  2171. GetDocument()->GetInstrumentUndo().RemoveLastUndoStep(m_nInstrument);
  2172. return;
  2173. }
  2174. }
  2175. UpdateIndicator();
  2176. SetModified(InstrumentHint().Envelope(), true);
  2177. }
  2178. void CViewInstrument::EnvKbdMovePointRight(int stepsize)
  2179. {
  2180. InstrumentEnvelope *pEnv = GetEnvelopePtr();
  2181. if(pEnv == nullptr)
  2182. return;
  2183. const MODTYPE modType = GetDocument()->GetModType();
  2184. // Move loop points?
  2185. PrepareUndo("Move Envelope Point");
  2186. if(m_nDragItem == ENV_DRAGSUSTAINSTART)
  2187. {
  2188. if(pEnv->nSustainStart >= pEnv->size() - 1)
  2189. return;
  2190. if(pEnv->nSustainStart >= pEnv->nSustainEnd)
  2191. pEnv->nSustainEnd++;
  2192. pEnv->nSustainStart++;
  2193. } else if(m_nDragItem == ENV_DRAGSUSTAINEND)
  2194. {
  2195. if(pEnv->nSustainEnd >= pEnv->size() - 1)
  2196. return;
  2197. pEnv->nSustainEnd++;
  2198. if(modType == MOD_TYPE_XM)
  2199. pEnv->nSustainStart = pEnv->nSustainEnd;
  2200. } else if(m_nDragItem == ENV_DRAGLOOPSTART)
  2201. {
  2202. if(pEnv->nLoopStart >= pEnv->size() - 1)
  2203. return;
  2204. if(pEnv->nLoopStart >= pEnv->nLoopEnd)
  2205. pEnv->nLoopEnd++;
  2206. pEnv->nLoopStart++;
  2207. } else if(m_nDragItem == ENV_DRAGLOOPEND)
  2208. {
  2209. if(pEnv->nLoopEnd >= pEnv->size() - 1)
  2210. return;
  2211. pEnv->nLoopEnd++;
  2212. } else
  2213. {
  2214. // Move envelope node
  2215. if(!IsDragItemEnvPoint() || m_nDragItem <= 1)
  2216. {
  2217. GetDocument()->GetInstrumentUndo().RemoveLastUndoStep(m_nInstrument);
  2218. return;
  2219. }
  2220. if(!EnvSetValue(m_nDragItem - 1, pEnv->at(m_nDragItem - 1).tick + stepsize))
  2221. {
  2222. GetDocument()->GetInstrumentUndo().RemoveLastUndoStep(m_nInstrument);
  2223. return;
  2224. }
  2225. }
  2226. UpdateIndicator();
  2227. SetModified(InstrumentHint().Envelope(), true);
  2228. }
  2229. void CViewInstrument::EnvKbdMovePointVertical(int stepsize)
  2230. {
  2231. InstrumentEnvelope *pEnv = GetEnvelopePtr();
  2232. if(pEnv == nullptr || !IsDragItemEnvPoint())
  2233. return;
  2234. int val = pEnv->at(m_nDragItem - 1).value + stepsize;
  2235. PrepareUndo("Move Envelope Point");
  2236. if(EnvSetValue(m_nDragItem - 1, int32_min, val, false))
  2237. {
  2238. UpdateIndicator();
  2239. SetModified(InstrumentHint().Envelope(), true);
  2240. } else
  2241. {
  2242. GetDocument()->GetInstrumentUndo().RemoveLastUndoStep(m_nInstrument);
  2243. }
  2244. }
  2245. void CViewInstrument::EnvKbdInsertPoint()
  2246. {
  2247. InstrumentEnvelope *pEnv = GetEnvelopePtr();
  2248. if(pEnv == nullptr)
  2249. return;
  2250. if(!IsDragItemEnvPoint())
  2251. m_nDragItem = pEnv->size();
  2252. EnvelopeNode::tick_t newTick = 10;
  2253. EnvelopeNode::value_t newVal = m_nEnv == ENV_VOLUME ? ENVELOPE_MAX : ENVELOPE_MID;
  2254. if(m_nDragItem < pEnv->size() && (pEnv->at(m_nDragItem).tick - pEnv->at(m_nDragItem - 1).tick > 1))
  2255. {
  2256. // If some other point than the last is selected: interpolate between this and next point (if there's room between them)
  2257. newTick = (pEnv->at(m_nDragItem - 1).tick + pEnv->at(m_nDragItem).tick) / 2;
  2258. newVal = (pEnv->at(m_nDragItem - 1).value + pEnv->at(m_nDragItem).value) / 2;
  2259. } else if(!pEnv->empty())
  2260. {
  2261. // Last point is selected: add point after last point
  2262. newTick = pEnv->back().tick + 4;
  2263. newVal = pEnv->back().value;
  2264. }
  2265. auto newPoint = EnvInsertPoint(newTick, newVal);
  2266. if(newPoint > 0)
  2267. m_nDragItem = newPoint;
  2268. UpdateIndicator();
  2269. }
  2270. void CViewInstrument::EnvKbdRemovePoint()
  2271. {
  2272. InstrumentEnvelope *pEnv = GetEnvelopePtr();
  2273. if(pEnv == nullptr || !IsDragItemEnvPoint() || pEnv->empty())
  2274. return;
  2275. if(m_nDragItem > pEnv->size())
  2276. m_nDragItem = pEnv->size();
  2277. EnvRemovePoint(m_nDragItem - 1);
  2278. UpdateIndicator();
  2279. }
  2280. void CViewInstrument::EnvKbdSetLoopStart()
  2281. {
  2282. InstrumentEnvelope *pEnv = GetEnvelopePtr();
  2283. if(pEnv == nullptr || !IsDragItemEnvPoint())
  2284. return;
  2285. PrepareUndo("Set Envelope Loop Start");
  2286. if(!EnvGetLoop())
  2287. EnvSetLoopStart(0);
  2288. EnvSetLoopStart(m_nDragItem - 1);
  2289. SetModified(InstrumentHint(m_nInstrument).Envelope(), true);
  2290. }
  2291. void CViewInstrument::EnvKbdSetLoopEnd()
  2292. {
  2293. InstrumentEnvelope *pEnv = GetEnvelopePtr();
  2294. if(pEnv == nullptr || !IsDragItemEnvPoint())
  2295. return;
  2296. PrepareUndo("Set Envelope Loop End");
  2297. if(!EnvGetLoop())
  2298. {
  2299. EnvSetLoop(true);
  2300. EnvSetLoopStart(0);
  2301. }
  2302. EnvSetLoopEnd(m_nDragItem - 1);
  2303. SetModified(InstrumentHint(m_nInstrument).Envelope(), true);
  2304. }
  2305. void CViewInstrument::EnvKbdSetSustainStart()
  2306. {
  2307. InstrumentEnvelope *pEnv = GetEnvelopePtr();
  2308. if(pEnv == nullptr || !IsDragItemEnvPoint())
  2309. return;
  2310. PrepareUndo("Set Envelope Sustain Start");
  2311. if(!EnvGetSustain())
  2312. EnvSetSustain(true);
  2313. EnvSetSustainStart(m_nDragItem - 1);
  2314. SetModified(InstrumentHint(m_nInstrument).Envelope(), true);
  2315. }
  2316. void CViewInstrument::EnvKbdSetSustainEnd()
  2317. {
  2318. InstrumentEnvelope *pEnv = GetEnvelopePtr();
  2319. if(pEnv == nullptr || !IsDragItemEnvPoint())
  2320. return;
  2321. PrepareUndo("Set Envelope Sustain End");
  2322. if(!EnvGetSustain())
  2323. {
  2324. EnvSetSustain(true);
  2325. EnvSetSustainStart(0);
  2326. }
  2327. EnvSetSustainEnd(m_nDragItem - 1);
  2328. SetModified(InstrumentHint(m_nInstrument).Envelope(), true);
  2329. }
  2330. void CViewInstrument::EnvKbdToggleReleaseNode()
  2331. {
  2332. InstrumentEnvelope *pEnv = GetEnvelopePtr();
  2333. if(pEnv == nullptr || !IsDragItemEnvPoint())
  2334. return;
  2335. PrepareUndo("Toggle Release Node");
  2336. if(EnvToggleReleaseNode(m_nDragItem - 1))
  2337. {
  2338. UpdateIndicator();
  2339. SetModified(InstrumentHint().Envelope(), true);
  2340. } else
  2341. {
  2342. GetDocument()->GetInstrumentUndo().RemoveLastUndoStep(m_nInstrument);
  2343. }
  2344. }
  2345. // Get a pointer to the currently active instrument.
  2346. ModInstrument *CViewInstrument::GetInstrumentPtr() const
  2347. {
  2348. CModDoc *pModDoc = GetDocument();
  2349. if(pModDoc == nullptr)
  2350. return nullptr;
  2351. return pModDoc->GetSoundFile().Instruments[m_nInstrument];
  2352. }
  2353. // Get a pointer to the currently selected envelope.
  2354. // This function also implicitely validates the moddoc and soundfile pointers.
  2355. InstrumentEnvelope *CViewInstrument::GetEnvelopePtr() const
  2356. {
  2357. // First do some standard checks...
  2358. ModInstrument *pIns = GetInstrumentPtr();
  2359. if(pIns == nullptr)
  2360. return nullptr;
  2361. return &pIns->GetEnvelope(m_nEnv);
  2362. }
  2363. bool CViewInstrument::CanMovePoint(uint32 envPoint, int step)
  2364. {
  2365. const InstrumentEnvelope *env = GetEnvelopePtr();
  2366. if(env == nullptr)
  2367. return false;
  2368. // Can't move first point
  2369. if(envPoint == 0)
  2370. {
  2371. return false;
  2372. }
  2373. // Can't move left of previous point
  2374. if((step < 0) && (env->at(envPoint).tick - env->at(envPoint - 1).tick <= -step))
  2375. {
  2376. return false;
  2377. }
  2378. // Can't move right of next point
  2379. if((step > 0) && (envPoint < env->size() - 1) && (env->at(envPoint + 1).tick - env->at(envPoint).tick <= step))
  2380. {
  2381. return false;
  2382. }
  2383. return true;
  2384. }
  2385. BOOL CViewInstrument::OnMouseWheel(UINT nFlags, short zDelta, CPoint pt)
  2386. {
  2387. // Ctrl + mouse wheel: envelope zoom.
  2388. if(nFlags == MK_CONTROL)
  2389. {
  2390. // Speed up zoom scrolling by some factor (might need some tuning).
  2391. const float speedUpFactor = std::max(1.0f, m_zoom * 7.0f / ENV_MAX_ZOOM);
  2392. EnvSetZoom(m_zoom + speedUpFactor * (zDelta / WHEEL_DELTA));
  2393. }
  2394. return CModScrollView::OnMouseWheel(nFlags, zDelta, pt);
  2395. }
  2396. void CViewInstrument::OnXButtonUp(UINT nFlags, UINT nButton, CPoint point)
  2397. {
  2398. if(nButton == XBUTTON1)
  2399. OnPrevInstrument();
  2400. else if(nButton == XBUTTON2)
  2401. OnNextInstrument();
  2402. CModScrollView::OnXButtonUp(nFlags, nButton, point);
  2403. }
  2404. void CViewInstrument::OnEnvLoad()
  2405. {
  2406. if(GetInstrumentPtr() == nullptr)
  2407. return;
  2408. FileDialog dlg = OpenFileDialog()
  2409. .DefaultExtension("envelope")
  2410. .ExtensionFilter("Instrument Envelopes (*.envelope)|*.envelope||")
  2411. .WorkingDirectory(TrackerSettings::Instance().PathInstruments.GetWorkingDir());
  2412. if(!dlg.Show(this)) return;
  2413. TrackerSettings::Instance().PathInstruments.SetWorkingDir(dlg.GetWorkingDirectory());
  2414. PrepareUndo("Replace Envelope");
  2415. if(GetDocument()->LoadEnvelope(m_nInstrument, m_nEnv, dlg.GetFirstFile()))
  2416. {
  2417. SetModified(InstrumentHint(m_nInstrument).Envelope(), true);
  2418. } else
  2419. {
  2420. GetDocument()->GetInstrumentUndo().RemoveLastUndoStep(m_nInstrument);
  2421. }
  2422. }
  2423. void CViewInstrument::OnEnvSave()
  2424. {
  2425. const InstrumentEnvelope *env = GetEnvelopePtr();
  2426. if(env == nullptr || env->empty())
  2427. {
  2428. MessageBeep(MB_ICONWARNING);
  2429. return;
  2430. }
  2431. FileDialog dlg = SaveFileDialog()
  2432. .DefaultExtension("envelope")
  2433. .ExtensionFilter("Instrument Envelopes (*.envelope)|*.envelope||")
  2434. .WorkingDirectory(TrackerSettings::Instance().PathInstruments.GetWorkingDir());
  2435. if(!dlg.Show(this)) return;
  2436. TrackerSettings::Instance().PathInstruments.SetWorkingDir(dlg.GetWorkingDirectory());
  2437. if(!GetDocument()->SaveEnvelope(m_nInstrument, m_nEnv, dlg.GetFirstFile()))
  2438. {
  2439. Reporting::Error(MPT_CFORMAT("Unable to save file {}")(dlg.GetFirstFile()), _T("OpenMPT"), this);
  2440. }
  2441. }
  2442. void CViewInstrument::OnUpdateUndo(CCmdUI *pCmdUI)
  2443. {
  2444. CModDoc *pModDoc = GetDocument();
  2445. if((pCmdUI) && (pModDoc))
  2446. {
  2447. pCmdUI->Enable(pModDoc->GetInstrumentUndo().CanUndo(m_nInstrument));
  2448. pCmdUI->SetText(CMainFrame::GetInputHandler()->GetKeyTextFromCommand(kcEditUndo, _T("Undo ") + mpt::ToCString(pModDoc->GetSoundFile().GetCharsetInternal(), pModDoc->GetInstrumentUndo().GetUndoName(m_nInstrument))));
  2449. }
  2450. }
  2451. void CViewInstrument::OnUpdateRedo(CCmdUI *pCmdUI)
  2452. {
  2453. CModDoc *pModDoc = GetDocument();
  2454. if((pCmdUI) && (pModDoc))
  2455. {
  2456. pCmdUI->Enable(pModDoc->GetInstrumentUndo().CanRedo(m_nInstrument));
  2457. pCmdUI->SetText(CMainFrame::GetInputHandler()->GetKeyTextFromCommand(kcEditRedo, _T("Redo ") + mpt::ToCString(pModDoc->GetSoundFile().GetCharsetInternal(), pModDoc->GetInstrumentUndo().GetRedoName(m_nInstrument))));
  2458. }
  2459. }
  2460. void CViewInstrument::OnEditUndo()
  2461. {
  2462. CModDoc *pModDoc = GetDocument();
  2463. if(pModDoc == nullptr)
  2464. return;
  2465. if(pModDoc->GetInstrumentUndo().Undo(m_nInstrument))
  2466. {
  2467. SetModified(InstrumentHint().Info().Envelope().Names(), true);
  2468. }
  2469. }
  2470. void CViewInstrument::OnEditRedo()
  2471. {
  2472. CModDoc *pModDoc = GetDocument();
  2473. if(pModDoc == nullptr)
  2474. return;
  2475. if(pModDoc->GetInstrumentUndo().Redo(m_nInstrument))
  2476. {
  2477. SetModified(InstrumentHint().Info().Envelope().Names(), true);
  2478. }
  2479. }
  2480. INT_PTR CViewInstrument::OnToolHitTest(CPoint point, TOOLINFO *pTI) const
  2481. {
  2482. CRect ncRect;
  2483. ClientToScreen(&point);
  2484. const auto ncButton = GetNcButtonAtPoint(point, &ncRect);
  2485. if(ncButton == uint32_max)
  2486. return CModScrollView::OnToolHitTest(point, pTI);
  2487. auto buttonID = cLeftBarButtons[ncButton];
  2488. ScreenToClient(&ncRect);
  2489. pTI->hwnd = m_hWnd;
  2490. pTI->uId = buttonID;
  2491. pTI->rect = ncRect;
  2492. CString text = LoadResourceString(buttonID);
  2493. CommandID cmd = kcNull;
  2494. switch(buttonID)
  2495. {
  2496. case ID_ENVSEL_VOLUME: cmd = kcInstrumentEnvelopeSwitchToVolume; break;
  2497. case ID_ENVSEL_PANNING: cmd = kcInstrumentEnvelopeSwitchToPanning; break;
  2498. case ID_ENVSEL_PITCH: cmd = kcInstrumentEnvelopeSwitchToPitch; break;
  2499. case ID_ENVELOPE_VOLUME: cmd = kcInstrumentEnvelopeToggleVolume; break;
  2500. case ID_ENVELOPE_PANNING: cmd = kcInstrumentEnvelopeTogglePanning; break;
  2501. case ID_ENVELOPE_PITCH: cmd = kcInstrumentEnvelopeTogglePitch; break;
  2502. case ID_ENVELOPE_FILTER: cmd = kcInstrumentEnvelopeToggleFilter; break;
  2503. case ID_ENVELOPE_SETLOOP: cmd = kcInstrumentEnvelopeToggleLoop; break;
  2504. case ID_ENVELOPE_SUSTAIN: cmd = kcInstrumentEnvelopeToggleSustain; break;
  2505. case ID_ENVELOPE_CARRY: cmd = kcInstrumentEnvelopeToggleCarry; break;
  2506. case ID_INSTRUMENT_SAMPLEMAP: cmd = kcInsNoteMapEditSampleMap; break;
  2507. case ID_ENVELOPE_ZOOM_IN: cmd = kcInstrumentEnvelopeZoomIn; break;
  2508. case ID_ENVELOPE_ZOOM_OUT: cmd = kcInstrumentEnvelopeZoomOut; break;
  2509. case ID_ENVELOPE_LOAD: cmd = kcInstrumentEnvelopeLoad; break;
  2510. case ID_ENVELOPE_SAVE: cmd = kcInstrumentEnvelopeSave; break;
  2511. }
  2512. if(cmd != kcNull)
  2513. {
  2514. auto keyText = CMainFrame::GetInputHandler()->m_activeCommandSet->GetKeyTextFromCommand(cmd, 0);
  2515. if(!keyText.IsEmpty())
  2516. text += MPT_CFORMAT(" ({})")(keyText);
  2517. }
  2518. // MFC will free() the text
  2519. auto size = text.GetLength() + 1;
  2520. TCHAR *textP = static_cast<TCHAR *>(calloc(size, sizeof(TCHAR)));
  2521. std::copy(text.GetString(), text.GetString() + size, textP);
  2522. pTI->lpszText = textP;
  2523. return buttonID;
  2524. }
  2525. // Accessible description for screen readers
  2526. HRESULT CViewInstrument::get_accName(VARIANT varChild, BSTR *pszName)
  2527. {
  2528. const InstrumentEnvelope *env = GetEnvelopePtr();
  2529. if(env == nullptr)
  2530. return CModScrollView::get_accName(varChild, pszName);
  2531. const TCHAR *typeStr = _T("");
  2532. switch(m_nEnv)
  2533. {
  2534. case ENV_VOLUME: typeStr = _T("Volume"); break;
  2535. case ENV_PANNING: typeStr = _T("Panning"); break;
  2536. case ENV_PITCH: typeStr = env->dwFlags[ENV_FILTER] ? _T("Filter") : _T("Pitch"); break;
  2537. }
  2538. CString str;
  2539. if(env->empty() || m_nDragItem == 0)
  2540. {
  2541. str = typeStr;
  2542. if(env->empty())
  2543. str += _T(" envelope has no points");
  2544. else
  2545. str += MPT_CFORMAT(" envelope, {} point{}")(env->size(), env->size() == 1 ? CString(_T("")) : CString(_T("s")));
  2546. } else
  2547. {
  2548. bool isEnvPoint = false;
  2549. auto point = DragItemToEnvPoint();
  2550. auto tick = EnvGetTick(point);
  2551. switch(m_nDragItem)
  2552. {
  2553. case ENV_DRAGLOOPSTART: str = _T("Loop start"); break;
  2554. case ENV_DRAGLOOPEND: str = _T("Loop end"); break;
  2555. case ENV_DRAGSUSTAINSTART: str = _T("Sustain loop start"); break;
  2556. case ENV_DRAGSUSTAINEND: str = _T("Sustain loop end"); break;
  2557. default: isEnvPoint = true;
  2558. }
  2559. if(!isEnvPoint)
  2560. {
  2561. str += MPT_CFORMAT(" at point {}, tick {}")(point + 1, tick);
  2562. } else
  2563. {
  2564. str = MPT_CFORMAT("Point {}, tick {}, {} {}")(point + 1, tick, CString(typeStr), EnvValueToString(EnvGetTick(point), EnvGetValue(point)));
  2565. if(env->dwFlags[ENV_LOOP])
  2566. {
  2567. if(point == env->nLoopStart)
  2568. str += _T(", loop start");
  2569. if(point == env->nLoopEnd)
  2570. str += _T(", loop end");
  2571. }
  2572. if(env->dwFlags[ENV_SUSTAIN])
  2573. {
  2574. if(point == env->nLoopStart)
  2575. str += _T(", sustain loop start");
  2576. if(point == env->nLoopEnd)
  2577. str += _T(", sustain loop end");
  2578. }
  2579. if(env->nReleaseNode == point)
  2580. str += _T(", release node");
  2581. }
  2582. }
  2583. *pszName = str.AllocSysString();
  2584. return S_OK;
  2585. }
  2586. OPENMPT_NAMESPACE_END