From ac657997d16864b480b3df95b0b8d8beb76fca4e Mon Sep 17 00:00:00 2001 From: Chris Korda Date: Mon, 16 Jun 2014 14:10:17 -0400 Subject: [PATCH] 1.0.07.000 The MIDI target lists were refactored to use a standard grid control. The MIDI target lists now show the current controller values for assigned targets. A MIDI learn target can now be selected by clicking its dialog control if it has one. MIDI learn now supports learning a part's input port and channel, simplifying setup. Setting a bass approach target crashed the app if the song was empty; fixed. Double-clicking a patch file in Explorer started the app but didn't load the patch; fixed. Renaming a measure repeat or a multi-chord selection behaved unexpectedly; fixed. Duplicate chords were being merged even if they were in different sections; fixed. List control tool tips were incorrectly positioned; fixed. --- ChartUndoCodeData.h | 1 + ChordEase.chm | Bin 23450 -> 32290 bytes ChordEase.cpp | 25 ++ ChordEase.dsp | 68 +--- ChordEase.h | 2 + ChordEase.rc | 463 +++++++++++----------- ChordEase.vcproj | 60 +-- ChordEaseView.cpp | 39 +- ChordEaseView.h | 2 + DragVirtualListCtrl.cpp | 18 +- DragVirtualListCtrl.h | 14 + DurationComboBox.cpp | 11 +- DurationComboBox.h | 5 +- Engine.cpp | 49 ++- Engine.h | 2 + Globals.h | 3 +- GridCtrl.cpp | 6 +- GridCtrl.h | 2 +- HelpIDs.h | 88 ++++- HelpResMap.h | 81 +++- ListCtrlExSel.cpp | 60 +++ ListCtrlExSel.h | 13 +- MainFrm.cpp | 172 ++++++--- MainFrm.h | 8 + MidiEventDlg.h | 6 + MidiTargetColDef.h | 16 +- MidiTargetDlg.cpp | 301 +++++++++++++-- MidiTargetDlg.h | 74 ++-- MidiTargetRowDlg.cpp | 200 ---------- MidiTargetRowDlg.h | 99 ----- Part.cpp | 2 + Part.h | 1 + PartMidiTargetDef.h | 1 + PartMidiTargetDlg.cpp | 71 ++-- PartMidiTargetDlg.h | 18 +- PartPageView.h | 6 + PartsBar.h | 6 + PartsListColDef.h | 20 + PartsListCtrl.cpp | 45 ++- PartsListCtrl.h | 3 +- PartsListView.cpp | 57 +-- PartsListView.h | 6 +- Patch.cpp | 6 +- Patch.h | 3 +- PatchAutoInstDlg.cpp | 91 ----- PatchAutoInstDlg.h | 81 ---- PatchBar.h | 6 + PatchMetronomeDlg.cpp | 30 +- PatchMetronomeDlg.h | 10 +- PatchMidiTargetDef.h | 39 +- PatchMidiTargetDlg.cpp | 65 +++- PatchMidiTargetDlg.h | 18 +- PatchPageDlg.cpp | 92 +++++ PatchPageDlg.h | 12 + PianoDlg.cpp | 33 +- PianoDlg.h | 2 - RecordPlayerDlg.cpp | 11 +- RecordPlayerDlg.h | 2 +- RowDlg.cpp | 99 ----- RowDlg.h | 114 ------ RowForm.cpp | 139 ------- RowForm.h | 82 ---- RowView.cpp | 568 --------------------------- RowView.h | 245 ------------ Song.cpp | 17 +- Song.h | 13 +- SongState.cpp | 18 +- SongState.h | 1 + Version.h | 1 + res/ChordEase.rc2 | 4 +- resource.h | 832 ++++++++++++++++++++-------------------- 71 files changed, 1880 insertions(+), 2878 deletions(-) delete mode 100644 MidiTargetRowDlg.cpp delete mode 100644 MidiTargetRowDlg.h create mode 100644 PartsListColDef.h delete mode 100644 PatchAutoInstDlg.cpp delete mode 100644 PatchAutoInstDlg.h delete mode 100644 RowDlg.cpp delete mode 100644 RowDlg.h delete mode 100644 RowForm.cpp delete mode 100644 RowForm.h delete mode 100644 RowView.cpp delete mode 100644 RowView.h diff --git a/ChartUndoCodeData.h b/ChartUndoCodeData.h index 4e54484..e0d83cd 100644 --- a/ChartUndoCodeData.h +++ b/ChartUndoCodeData.h @@ -24,5 +24,6 @@ UCODE_DEF(SECTION_CREATE) UCODE_DEF(SECTION_DELETE) UCODE_DEF(SECTION_PROPS) UCODE_DEF(SONG_PROPS) +UCODE_DEF(MULTI_CHORD_EDIT) #undef UCODE_DEF \ No newline at end of file diff --git a/ChordEase.chm b/ChordEase.chm index ca6fc7d96528f5ad4105130e1dbf27b97e55558c..564138d3a0d30a8580f87d93e1a0a51c04480730 100644 GIT binary patch literal 32290 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 RG6aOsP_q9!qK87<{y&i4xk>;4 delta 15729 zcmZX*18`tL(=Hs_wr$(?#OSH8F4x#_K{(WEn{Xm)j z88FP`vJ%=d5~`pf=}l$MX+@{0C`@fd<{U1p2`;ldGw#$|^~#frgB7L^1pe zQ`1pXmsA7^lqwMTXQ_WvmKFa8wnBxV{4+IF75=RU4e23^MEhS;okvUoG^7(N7~vn6 zmaLM*H!U^LplixN1$br(DOpQ*8$}ZvN6_G0-WNq!W{PiOaY%~lKFJZFeBi<1 z9?8R?O#DH24hagF%oJK$@)q8nPOjz(HjZ{~%n~NwSMhqTU&YrvuhHDU5r5uWi#Vl!V<4)pW;rN}Ig@Z)g$=RF9+T8&( zu-+sT0{j04IZ2dF+}!`A{twz>5lVvdf1_L^vW{*RuI~Q}cR?nrfD=Fl_WFhpCc8mU zfd?iCC+9%m83d+42f1T0OWD|4{5K~%iJFPWe_7LfLlkiTZ;<1EKoU{6|E0~A4i@}> zf;mYfy=?xQGan+k4^oi&Kr)Pom|5Dw-QC8~ibT!b#MRxxoSB7{d2e1&RvdnIVcd*$?X9E`uzROQ2}L1H(j)^E#+`+fybjL=9yo-PjDbJ`9yrRF z%#A<_9yo!VtoI*1<(3TmN6%^{7a>qX2L9m>QcG4q{FiGHDcKv51Tt{NBG@_E4oMsW z>{fvx83mcNaREsP6jY?K0Bsx^p9|U;st*zm(iNf~91A=V>>V@^^c5r=L;yrQc@o78 ze1`@!nH7~%`akCi=AY2~lYhQ{BM68B@Bb(ekRSim|5Zd7|2tvf_+R~>SU3;}5ETsV z2S4)@61i5 z({8DdBp8_}Xzp1FOqpcesEAZI-$+Xbm22IN%Nzcj?B#gsX5muLzHLFuE#1~|aM+XB z{iWFa4RgY}rE;E*sgN!W8T{V7bT!j_m3x)~VH5;J26Qn7^asCoz*FbjW5?E!$C+L` ztZnALog0}QNrQ1BI9AT!_uXXWKel_M2_ftPBDNn1<@H2=0Po`$xkbHmhmIJAa{1`>_MD(e`?-Bol#0 z6XW#rtW)NNv6Yd<-#DD@BIagKRZ^QGG;ZS?lCO#sMa?-2pKSLElEnz@*c|fR3OQvg zwVsAG)iE_RfE__t&nV>4&0$$Qc*S>LJY{v7)_i4-DjN1k2b|%Rh|2}h`4uGP0Tv++ zsz$U4FwWqc5$a6nZkdh`F;V#;@(ye;7y1;^!+w3GH?W(ki2%OAx=w}Fw6)X#X?L1T zg@d`xv<&W6CYGWIWE&eP(X(<>`O})Vnk(=b(rH<3AWt=>%WY=g@F>qTdwNYCOszjZ zTVp5~bj@zMR1vBUHC%ZJo-ah z&5e^J*h3+o2B9Ti0gDmvj}S(e%pR+kZedYDx5`r@k@B^_8vbl z0cZ6J$lDe>3V!lwycsHr48$%GM&hN+WOp*iX=C^Z%!#va2T}rj87ny)-huWKwV!C_ zsNfvoFvn&X^S3*zW&{j6=>5>>b!rAAfGsA1f#rmXJKBPVeX~AOaOKdmg+byEdKK7S zMGV^P>`H5%bYqkIdoV3uIj`lId)ILy;CYdxj7Ex4e5x+jz(myGROfKj26keXhote_ zUHfl2a~o<18`2!~;xlp=kFjRBGJY2?Gp;UK^%4}97#^9h!FZSnaTsn;*6-e36$@g; zhHiz92s4mq_*UOUNEoB1FA(sqq@@s@4jq-A6|HHu0-U;0&?ybSRXId_#~}*_1HAsK zOr3Y>Z#bJK^3gT>F(;Fl8o4m}QSU?!(i+)c%hWjvK({A+D8Ja#L|1)i*=ER|1yJgi zJ60R6Lamr+?X05nSB;uhexm(>8#>V)wXHWI)9qi`c*sE1lqaZsiB}^+Z{ekObwxs` zX_jTw96vSizmyWB(mL#!IESRB0;1ex+ETaV3^YrS|0dSa^`^EK$mym^7uKW6>B^R1 z{7qCo95-R;VNI&)6_Z56ME_045Kx)uMv-D(zZ*{;Eg-MXQ!C+p6^lX}8+r1Mwj#ho zSFBdyu2g>5wgyuzL3!8u8RkfyLTn01ut{pFoxa(k(I;Ioq$JHSC(>!$0=fhS&P%Xy z-+kz$|57NXh`=p&VHX1t2^}`Sgs6QQ`ZZ|~NY3(|xNz|}*g-hpU_s??v%rA}xncu1dMA zDVb2Cua)+fi8_J^ZbMzYJHQGUnX*=};`<3Q-Mn+TG``Hgg>(Jjf+#ev!M*=xY&~@=aK%= z+^CXd@&loUo#yu;Q!ar@#K&TpLJi+hBr7u`W<$`(DP=8%J2{|7<980@%&TQBDV+9C z?xBLn+Ni}WKxCa{#W8*bke7d*eoK!-=??9q45b2Pt-WnA1LijOWqfT-Kv3Q$PxoYN z(s$=&c)oz78OhN$#3HD@w|!e%JjmBLl3#q?Sd(GlZ@hQ`+^3>Dt65Dh+laAKmZ%m3 zR*03*J}IRkk+=dp*1J*e%Y2jzXgaM4c*9Xi13h>tF&T6E*wO`#^*Ji0y5pHx7t_M1 z=T{dDK`lc3z~P*$+71tm=BF+h-@}&70*`aJbGhAigB^&&araN0{SdRH&@kFkKPcVu zYGgAZd-xfrc>Ar*uhp*$zifZ?y*#pA$3gjYQkE1+w&(jlIZfV?kf92{ZBz>i>R%Lp z#Y$n|PuPi%82fM?$S;i8EYfCw@~JcI874y9cf=K<0hV6<5HoZq0`*6e=1tazJRD|V zDnH9{O(OV(KdDo49b?i+QgpJNw^Rm>7{5$akE1)JAyHhuAo)rfA@ZM3CN85t%AJ> zzB>jv4#=K8@b%h+qkYh%Hz3GzktZ~ok{Fp<`^`hPhvQmMvS2;Zz%PyPv=D9%!;OHh z%@vX4;|0-=&hhpLk~*ETWyc;%)>4lhF4=~2De-zb8*s}7+q|$3&diF;7XUf8><>+* z(TO~N7M)NrFi_U8D#ZtDR(GWI)2jKcr{PBNfKsc@wsuy@<^3G$b$yshfeeVDOV1nf zqVYsckf3Su+NDk;g72XB1wfC8`}RN2^_oiH*xSZkQ-!3?qqxbE*DJ&~`L9 z>iWKKm>%2LXE{^rw7cK!pzR7_D#<0Q3*=ar;N4jDFf_8O&-VCzg{!MOOl7GpYup0} zFhIT?!Q{^>G`vOQ0%JwDmO?`GDC;P_Qb+WzEECz)n3C!f^<$s=aOn`&V5dc`bxPA>8$p9IDkU_AS%aznUJ(3807$k_3o7_ zYgIZvN}Gp2Fe06cd`QpX;8zY@KMq+d5Z}KR125>w(x07ZNWmz1#|PltD@@&J;G7;P zn;n2QuD5H3@!gB2J^9~UMd;vWA+w;23*s=1tZPv)*Y#aHi2Ys1>^^u*@Yo;PEKgb| zR|?tBQSROthg(*=GDpcX=i#5K;iqAqMj$Os+>yJQ8C0O8mhQL%djzGV{Pke~d`N^z z_Hp+}4-jTlA}=SpyE&ATMrC>wcN?;h;`;So(k{F#UZfD~kIhVP`3uWS_2ZoWH5`d& z%%buBs$(x#+1W4GrxCknJLJJ4%%BZkvqSkvY-3Q;zdj z!jDs>_!Ss+#Z8Xx9Th4M@Uzqb2;vetvb*@;oo$HjVw@t6pBXCD5Z1!^UHCUNu${Ts zTv_pkMqhdOPS_wyma$P-)RKr#CjzP0i$5lOBntLy$9*6J)ho%_f~jwq>NYO3C4a*Q zQZLr2N_8tGdQ-`@H<}y|ylg9DPK*sd+y2WuayT(7U&R9+#h87Eer4-V(*y z67(sZlsIsulGKtakypW}P->yOJOH}C2|Z=n&cV`bgCLT4h_FeH0g8Qn zoU0gJptd-SUZU1eEOIl#kcT@Jm&3I-GV@{nmDn3W>6ySIwcLBqvc@dPZ9P#%$Tc>d zty|Ccc-GETepgR`RAz*G#35B!-uc$FfrJq*i8yJwLNL9S;9;yB3ESyRf^bk7jM9*^ zs!q~+z+*fCLoWA9ErdC&Jlb>*&V!?SwgkbDN8#B(!aMew3Fj8LINLX0#4FIfz0En_ zi^SPcNVdph)RPgTM&(x`X`eSTg#2DO+0V_>b(0b&tm_R7!>);^H4I^jjKA+Gta2z8 zA!=w~Fn&gwwCfCMc|yMk2=J98snKTpDrEHTmDZF%N8hSypJxZPGxJ8kCdq9sDuH=) zzBj=5sf<&lW8{^!Ghi6sRqg4>b_##oMzprSjF%tCQxf(FXJX6fB;w8&Pf_&_iO{Pr zDnv}ObDsgYzN9uA@(wH7jcrN@Kho_~Q`xbQ3QkQ4mO$j>8P=y`MAag!Wq4UL*N2otKz?svzv#H8H)Rqp4@!t= zu2MQl)1o#C8-w}l4lBq@ja8`a5g(w1)l)?C5c&pOJ6S4`F_7`@q98=1Gn_w-S&`If zS;BEABn*#hrpAQwHYm26qMnhokl0u>#}Vx|T8ab=%9wqllPrt)VnETpX~H?2Y(D99 zJu>HRXz2tcczGSmjH67rgsP%6xiX9XlU7O@@10#D0aMVpHycsFbBrLJ$;^|eV#0d~ zI_?jIW+#}-ZO}C&r=@w3K9OR^C2l9s@AFou6;;xiq+ch!RQQQ@T(;qe-tng;>D_O26$VPfr8%S2uC{~~&ov`Z~R!xy?#}V4%A5?dN<>s_D z*&s;@3`=%_?D`OHzIR|Op^XqI=IFBg)>{RrIiYqFRjn7$T|IPqV z(dH+RC@xv8ofua{-GD}JlPBG~{f!%zR=p>-4bkTB%%61rh_ZH`Hhs{IVKrHKAa~*i zF}b%>052o^JJxE$R2o6ikbqKE?uu(@*wXWrMZEz#Zf~!Vzm$Sst*59gE%nLbZ+gx4 z@jZ5+{Y8>6QLLOK>I-~y64m73t9lMFfZ?4qrPZg+bzj)J$zVuFnxCjNaif#G4d*Xn zz2)6P?|}uOHSXHb*Z8`=%?6g;aVJfBIP>t{gK%Z%U6>RDV~18iz7Pt<{_I5T%g1{E z_oZD%pm=|8oaj+`HjeBRSpE@&U$EBjAs#@}Y)?a~OlGI`&!C986OivBaNONsf-+zrK7%B+WB-X~ z+wS5zeKhaOPM%SB=u#%bIzWLuNe>@&=jRvsG&O>FqWw&bSyY*#q{OguTPHk zCz~3^FFl~6D6#2II+9YR>hdq}%f8J$VDCnNmZ&ISUo&{&qeTF2*ifKIF6iT4#Aw2F z{qCf)Mec?}qsSoFO_K9tyeO-|n4#mUKH$cyglh)Kn^0r4|8xM}lcmRIOWI<4!Q;5x z(<4mc{t}sir2d6p7yrWFSnq8klRyXsn>=BF*KSCoaCWP%Q;6%srgV}l|vHl2WhO?%`!c`ew8&66lbfG6ZN9Z321X4 zp>P^r4K^Q(F~)Um=#vA@VM^$fsVTmUNNA^ai?CR>RgK3`lDk{^xPBh%F0{Yds|;>L z^K)Hoc_}^c046WfJ5t8`qBEU2f)H> z)@6FL#CH`Q+S(hiVX3hnA|ELj`>9F3`~%tET)HiLrbf0whwp&KO5?~$mxg(Z2uHhw zg10DMIhLG;Z$!j*?$4(R|78vc8JgZQvJWiz*G`S=33boG7ddJ_6(_X_@(D8 zOod+e6B-I#fhB?HKR$p(VFwx=_SrayDgy>NkjI>*9|B@~xBuU`)6*Xe3)^!L{BM5v z)eo?Mih3)sK%?9HeeUKqu}7UxYg*Q7#!CdUvvtg6BqSToCLC?_+9R0Ve}b|19L`LK z|G55 zinKvcI962OPfHSDJz9kUV@p1jL7b8Sf&qx$&!T}Lt~*NG3vV;WCx5e*9?%g>^BMGB zc_a%w#p=m^o8EH_%+Lz|TJ~u(D-?VC^c@qUm%tf|6!i)UL;OynX_ZDFU|be}8K)fa z+nj*R1CuH3`i~VRijB9y<1Fccz{kRNc^}^AD`k_!*O7&NcKTSTU89Yd{smaUrMK#a%W`a-e@*ghR6Thrzoof4JR+-uZoLIJ zA{ek{Q-}U+f$zlNDk%-L2l)YMOc%%i(g!pvR=T%Og5M8RrhzIMxqo*vf&ll(U;2>& zYmT1)@9bo3E8+|yMlxJf9y$~F3-naPAB9ojQy-$-;4x}5v0cF+f@)79$2A);ZoW9J!;o5id+>cH+2I;=p&{t7x4; zNv6|TR2v|Z);~E%>0?Z(I1=>5FVP(e(L6?yQA7pN6iPyV>*L8L1yzKE!azk9B-N)X zpNG%P6?7K~p+Lt2vjD+rh60*PwpH8OkXc1Dh-xpR9%!PlLIMnD($DBz-3!;O|R^-8npV@ageMGSJNo(kMqNQBJwioTq%6;I!a#fFyLM#;f)Vm#x0R{yHp94735 zzC0(wxG1RA)d^&O4go9*`*axuMuTw-ZPVqJ2x)wy-BEEk_dCQ9kAt1b)NqOTna>2! z_eY6t45d*QQ`(Ed7AC2lU22XxI;00X+zLEyoN5z?w$SP5iw1;JHktU$OWx~ zAUcCrOKyg;gr4&iyAL}A9~yK%7NN%wLDzB2Um@plE*a)!^8yR*k&vt&=ggtz%dtKs z2GM>!P@|&;d(nj7M*fZ!En|PU`+@wRZD>NWop&Le z4^Dc%+`q)II0y~725LVB*Z8~OdR;vLQuH}=9u8@pQ9ELUXvJsJ6$7as9P$Grs2~08 z#F`>lS`%1elSL%52u&Hbzh6o%~p4We?|p^6jnUn zgx%y{U-z$lg!^?~z(7`gi1nih#e?{Tfl?q4%RCE#UE@L|u=|nhKEn)Hp^D62{|MP7 zhs~D;5gG(R3yY`lBL;m&0F%KLO1SoeIA-tX?lKH`)_g~qfdf#e{GPvITqn|eKGfoe z&;uh^Vs$SA72(})b-2XYOpM>4Crt;5>Viixr^Y5MYTM5Z0<0kyH8q>D=y`?o$Tba! zE6^!hew}G9o1l|t368lJ@Xi~$t&I}U96GK4)mM}3vY|`|A*AScDH9|3iR?&UvmRyg zc5G*)^rZQtbzAtJ?)SI+$yYhzV2T%KCIEmIHhbu%#chQifl4OyY#u?@#a1d;_HzOB z?dJ{+o!W=+sR3-YautD_I7KFF)snV7y($BdGNrK1=&Yv=PvNUe#~b+EZjkTGF9*%+ zso&~vN$s`+Lf0P>M?VDHoL(GT6U6I>7emMF;h~v7EBE55?i1s_?57K9628_*E&<5i zw=wedm14;4;OjTClia6=qnPP-Bj%?c2i-q>&hnl{ZjBTgJC2|z8Ig^mb_u2qg$+ML zv5!SQ86W5#odaC9o__AViTZ**Zh=McH@qOZ_dpV6#rZR7^_-WIF?Nl4247?MGZef*13>YPV1nU!?+3k>a2m7UHjw{WKfiZ{{dqnR=-Y=) z?7WA5s{LngP@~_u@Y-UVXUfk799ikfh^8M>z`fV{CYs1Fo;5!FHo3xZd&7fks+RhW zJ75rbC)aMThv=NwoQ#Zs)u1Mc3JZ6Amt?WB@hVdLq%SevZ8|{OLoue)8A!l?T!hcD zLi&q&^YWUyU1!W}CZ}Joh?K-acZxgPyy-T~+ST=v%MIin(eEd7+IP8|=ri?1#PP>T zxRlMFP@0A7zs$0J@ZR}-l3(@QZMm?@od^5hD#!C;sVI#=%rix%UU6u`5bm4u;kG}Z zNKFa~-?+xdjp({B$y{m+$-<3J;NQ^(MUccv|O)UlCVdaelZtErn` z9?kS^b1iG-ubzZ*`hZeB$vux_D4zXBfu zdHJaKn$x9}`Eoy|s_;AFupGp#j_tNxf$Fl>6y87GnRY?kKo|7;cmKT2WUDfj(Y(U| z=g*_FFG$t!q{UO#@1?1M-=luYZMIO8wQ^O+%c_kwrjNp{oOh)cEvLIk)5NNT-j#*= z{#D$z^53fKygP*(eN6#*;OHb7KlLSgy6Q62S}t>;Xzj2Znj~!D=saC&`+RK~@in2q zT+E=$YcZTI2IeO5RC?=)Zd)fNq$~Bfb!vH^o_;tHB%1$Ji}E>=-^_5SMA}yC9>e6c z&rAZpyjui`!Ef%2sd~@x{{(ik9Sip?@sa8D#dqRHwMMmw=8&iikFqQW&P1NfjToxs zkYu7R?JG5o^Z5lc>#ImjG(4BZJRc@sxH>v{K8CW-f%UoRfRamR>;%iR;u=%FRij(} z?sEmp{(AdP zsWzCaQl|S5+R*;G9cH|%R|z-TU7emJ%3$VwaQPvKg7Ae zw-PP|1LDw27ZjeK$7+Hmsy&ijiWpDcd=@MsPyO06=hA?=_WXGsGn7A!G%~8Ia!u=aZkB`^MVb>v{#U!6~rjB~~ohgxus;R8QZoKI6 zIjX}+$uaUt4|#7J$ILi@?#R6|N&`wu8)(tuT92Y1exl}gt&z6Pui?|1CnGE?wR^X* z!Kvl+(r6fy=O3Eqva83JvYXm7Z*z*a(e%9;i)2-5kYq~tO^!?C^<_TF&^U@M2Dpc* zp3YM;njz3H*=5-*#lCazdY|~)U|@3bIG$2b3Y3`F!Y*sZZdS#xJ&ydcXnn}U0g{$O zZOJ~;_15H~v1?v9s&#uxb9b2V;=^QIPs?UqR8`h~Iyg@5Mn;4NouxB8Qj zdSb3KkY=g5M4cc}CryKBBnxx*+Nw-djjUn^93?;6D|44~udsc@4eTT6(OGKbd2NVU z|Mr+Bqtf-B&#gXdqKaB0KGMjR131g99Exf^aQKMwe?2UB26~ZhYZc9Jxk8EfaR6!P(E~pMk{AHxVVv%dYV^S zj>6n>u}(LqKc;oqX9DavX36cQX6VbA|Ef(oWrBX!y3@a^KD5MWJveU*05>pFXEYWQ zbmHE7S%qR-vO4CG%i@bUN%U7qS7C|-Gsgd(>PjW-ZE+&89&o(lCNK!Wg=k0GHC$Kr zN}bodC1YdtYryj-&Frfkn?*YgSh==-O;c>9P7mZfCR?+8%|_2s%O=At=A|=h9FM!R zvwczx^TC%oyWabO{Bizf3rK+O%z>PuAc*|UwlB*m+ArV8sRqkq4xR`)hq`@R?l4%q ze^D!5pOzNtzt}v{AUr|o^v2WAr3SBDs~Z&o`=sL5aJ^(lMr=Xhxnk|NfY6a^k0P>N&* z7Ea6noIz1MF^ms4SlKq@f!HvskyY|!O<$@?JWWiHjU>%vZ0vmQ&E%_@s59Y?emOVt$cJ zS#Kh=g^~3sB6I)lX(!%6-ZH9u)|R>0Y(C8u)N|VR32h13g$j~vIH&p9D(!mJWY;*i zZ0vO$F62&(D)It4lF>k2Q z^Ag2si~IUt`Tp)9LW+BM1yH`oegU}`3YB|@&lpQ(0GjwGg?Fo(xRD<_&7hIjn}aZ` zhW~&c%0_g>AvwmJ6*Ls>#@1tu?9gZuhFp247_*c;#ruJbuTeUzsvuuA7<(9vUY2CG zNscDT$0@O+W%+WkSyV5Pd8J|*Y91qK6|ss?eQtos%Uq1)x+Lo#0A!KMUQ08 zZ#;!ZfUtKiZzSHwj{>1FO+IAC2(RP26VNGr22^s+2V_`9ex~C!Oaa3q2-9?F$cMjv zO_`Fl_A{5S;SukKSniEvO)ETzmZtau@iJZ0!ORA2R!POk$SUg4q|%##yEHAeJV0T8 zBy0B{mRf&q@^SuDn}!drxUD8Per^ZPKfZp!05K=37jbYD75V2*!dZ>v^atPr`6-*G zF3XY3YAE_%^c~rv*IpGt6_vd=`k6LNHHNU3hQTl0(6c2ny}VEi_vn^4zwL()U79C| z1!NH}4ky}LWHpQqe;oUbD1C-I!=0plW2iFKCj z#hB$7`+PuAQ1MbTZzr^e;5?H9`{TI}pn;E9Y3?WxgV7<>eP$ANUl4%vMBpE%@Ug<( zXVHP(dhZyGp$av0`S$aW337^=&g%lr{(WzqzwT1x7`0PQw`~zSeRj^;(g2|uzZ`SH zZ%-@Vhq4B@yjJBC0ik;rm&lZ-Jkpk=)wMH7B{$G-FeV#YHnJ@9a_yETb_$UkK>j71 zWP2Aarp#HcbcJN~gv?~Crh_X>2)D-M;I#IsN~toZjREzn2nfgwG+U!N97W5D_p-qM z5W{L~Ls61Lb49}cHQxL4Rd!s=^^c6b&5}6&n6Zn-@K@PedcS7^xQaE8IR=IS?iR_D z|GG=0c$bnGN+`IG>83d0!SMHOKuS1f`N-D@#ctNCP&<(c0}^Ij3h>I-iXP<*%+H-l z_|6RjqgN-tFSPaCTISgJJSLB=EN!z40;R{3RTfoHNU3gwXBw5{6ckhyKOas08J{o@ zLXpmFHelhIGc$rVU6b?CoaiJBcD`zlz$JG%KMa3|dnB$Pef-#;7q2AB#EBWi!5gga_3{4Oxf*&tl46@6Mj4!wz zR(~YQyd^`)POesfx(L)<(X$y__&3()oEv<*T0`?fl3PkQv<9X-iu?J(p*qjB{ympg z2Y9}VU7d)(u&tz{kHO-Yi-R#i5c&W{Yww_qB^M z>sS;r5G|~R4_G!>HvBmcWx3x)d2GTwK@EGz6{z78W5jg_W(zh2r%B@xcD63tmSlKVxb+B+6ru&&3lzg>e zk;Dwbu*0tuUZ^ERPI%T&%QZ zodTJf`a?o?e7NaXk#h>yZdQXS~8iXMM!fPIdufP;wQPZ2)m9( zvoNoI5-^N2B88zNf6fC(#0eV{n-m7#t1>2kBOm0U4Ta&bkY-+*1NV2Wk=8EYTVo47 z0Exfcz{u+0OXUP|nAL9-%h=~FsckU>Yl!JDTU=eUg!Gp-?wvmmIg2PDf4*&#d;6T} z2T|E8-OE_4O{`#J`}qdp1H;V^2Nqz%7Wu4pux8~c819fz4G3Zoq(A*j>wceg$vinD zM3DPt<|p?#Bphl`Sa0J#M~?g2dSapoy!+hq3HtS49cg6b*w71GL|tzaK5Vs_+ee8VVL8WBT=Jn!nIyr|7`1f6&J6Bts6QXSWiSyUVDST(%!8XY&Ldt{x zX5(@WpLqK~T?{Y17nCcJubdnbAS#kxsrAz8TJ>AgjkIIjGE(fKcUp1s_eUZrzQ7;) zVQPo>-(QP|9+(vi?){$eK|x+W(;6)0>*)*>qb&fuf&+erGuIyM-EyQ00DsMn>X9C^ zaos3{U|dI4EEEOtjYc5sx8Z-kiH|&ZDxyEX4?;Q`)kOs2P4%)g-Pad5y{qJXPrm)g zV!9jHX|chUCe5+TyxI2HVdw2Mrk!NQqD@I{m|qGFP#8u1W8gyS6y6Iy?h7ggzPa79 z8v1Czcup?vkGu#I;rzle0I^WpM0i0r@@N7hqmLkSB5jC6;WI*Yz`fQcmr_IH*PyLz zGlA}mhEmsx4klZu&C4f(vCj9=19y=&Nqw)2@;Bu-1jH}a*NW}eq9b{SQBifvE3jsT z{Tetu;YcL0p>D@8u=A2$ZAN;WfdGNoKH?Iz`Z__bTGo8gah(M)LtoN$QSYB#e$rZZ zqa5#3_$2v^$dj9cK)BS8Ezr*<|KEfPZy+Q(^+oXP{MUXq?@LRIDWmtJ-fXvtucCe< zdiUcu=FZViA8mV1r86thjUesoVm6@XHkM`_+gphFa#89T!>(Y1RuogG36U zt`t~`R&!XVjo%hj0>Nl~oeZJNl(C9)gs>wI?Q5Y3-HZQKb@|qbcaN^R9HM`CE$ZnJ zd2MdQh?uu?=d9e-{OHbLEywPJ*+GKsw4-Q>wn_Gn_v?$f@qjIX`FtW`9b$H?k`?79Os|Yu}#2>1j zXJ~mD<@S=-#BoH+#kmqP>Y#d}YWHY^`?baZRA+}6U@Cknw-z}Rfb0%WX5Wi+9+mI> z=q#j|XTo2zLxgpvA+7!DQaRtVFXWiYDljelIQA?Yq#IHH%L;-S?)YK(aErLHuK4b} z>-|9}s-405JeO%fa9x}A#W<;YWm6j!jh~S8&mQ0XhQ60zGRxo}WNqSS`9NFCFO{N& za!y+?z>_BBH2pWc|q5W>+6a9J?swC%$C*9b)d`>&Y^qyZX)gAByV805|=Ah&69 z@pG}XR<{ui>A1&(!l8v>W+^MEiBP6w^HOhXv#q1^?y@*HK^gWpnVboaNPhJYgc!&G zSqV#IOqCx-pxTs4P&)>V1qp}@2*_LnNue<)@W>x;^yAEH$q2~{^?KwvAr(;sO-Ab& z;uM_~lzL2*C#dBhrW}3*a#)sOnVXG#>*MZ=66Om;1O&t!3M9(-=kl$6@tfaWlr3qm zo{0e08dhP+ZUe`J?>ySrPYUV<-cjg~D+Mbg0MDHVeG2lf~q_4RSOko4h@-PK#6Q zi2BEGNoc8>X3z^e`a(leeO-0vVqPRvsh3??X};yf!@)M6A=S5Z!$jx}Mv#hT33jB( z8yFI9HgP?+-*wIIM6i@JaMNFw{W+1|9I1>Zpi@_1@-#EJU5LJ8wn#iIYt#esA4nVG z2zro(m@~R9V+D(_dqe9ncs=0rzY2ljBhQy-l}9DX+(&n5`f+ME!h)qk$HPtEdHSKg zLrIY~18b*Kex(zBBIE4RgkhsrPp(^QTY{RCeWmT(rpDTr%g@K>%B;$(p$)XO`xdm? zvv#j`>Cox?8&6t`DO0o&X(NEiXQ92w@jw=Sn_=W4C0nR3>EpY}x5F~x=pnuE`YD&i z4k^K�a-z(<3g+mQqW{n~k}$Ej8j%*d|a<<(gc~hLmj$l{~chZr1qLg>*8>q6~_k zYz|_vl#;4`?yzCv2#+_*ReId)`ZE@CwG$B(dPB;(byU&6#>_`g62<`)V{W{~D8_I_ z4>Ub9d(8`NrJEMUrEv^}qzC`wZ#^!mfUv^0h+V2&O zHBukuEJG>Ny5X#u{W8JK1I4Wut4B!m+axT{8-xAR7SwMRV(^(2?3FdZ#$h@y$X+h(?JTu3n$%jm`|x^HEK-8Fk`iG zbM;~qvR_K_t%1qGVo2_)LY`*LBlK-AwV2?jN^34aJe2b?5giJ*%&gH~I4KI}1P#lj zNeI#|$vP73E&rV!vKsOJ(Hp7>FneZdJ5F>SoT9DW|p?8eHbN-upUPRfhY#ZDq z2(V7qd8+xM@c|6q;Tx@M)XQn7x*(O7$M1FLU(0=?6`H_O3z>HaWi6@3+xj((us3ieJ{Dg$th*n~rLADTF-{a7@8>TwcBsd) zJ&0KAtx|Hrpb^dygPmPM#}Oy94Lh9S__k7>w;d-(zzx-z`^%SAu;t~+5{IU089T!u OnG}T%;sqMy$NvGB%iu)- diff --git a/ChordEase.cpp b/ChordEase.cpp index 7ff9cb2..fabd35e 100644 --- a/ChordEase.cpp +++ b/ChordEase.cpp @@ -467,6 +467,31 @@ CString CChordEaseApp::GetFileTitle(const CString& Path) return(sTitle); } +void CChordEaseApp::InitNoteCombo(CComboBox& Combo, CIntRange Range, int SelIdx) +{ + CString s; + int iSel = -1; + for (CNote iNote = Range.Start; iNote <= Range.End; iNote++) { + Combo.AddString(iNote.MidiName()); + if (iNote == SelIdx) + iSel = iNote - Range.Start; + } + Combo.SetCurSel(iSel); +} + +void CChordEaseApp::InitNumericCombo(CComboBox& Combo, CIntRange Range, int SelIdx) +{ + CString s; + int iSel = -1; + for (int iItem = Range.Start; iItem <= Range.End; iItem++) { + s.Format(_T("%d"), iItem); + Combo.AddString(s); + if (iItem == SelIdx) + iSel = iItem - Range.Start; + } + Combo.SetCurSel(iSel); +} + ///////////////////////////////////////////////////////////////////////////// // CChordEaseApp message map diff --git a/ChordEase.dsp b/ChordEase.dsp index f9cccdc..7ec3d9d 100644 --- a/ChordEase.dsp +++ b/ChordEase.dsp @@ -548,14 +548,6 @@ SOURCE=.\MidiTargetDlg.h # End Source File # Begin Source File -SOURCE=.\MidiTargetRowDlg.cpp -# End Source File -# Begin Source File - -SOURCE=.\MidiTargetRowDlg.h -# End Source File -# Begin Source File - SOURCE=.\MidiWrap.cpp # End Source File # Begin Source File @@ -808,6 +800,10 @@ SOURCE=.\PartsBar.h # End Source File # Begin Source File +SOURCE=.\PartsListColDef.h +# End Source File +# Begin Source File + SOURCE=.\PartsListCtrl.cpp # End Source File # Begin Source File @@ -832,14 +828,6 @@ SOURCE=.\Patch.h # End Source File # Begin Source File -SOURCE=.\PatchAutoInstDlg.cpp -# End Source File -# Begin Source File - -SOURCE=.\PatchAutoInstDlg.h -# End Source File -# Begin Source File - SOURCE=.\PatchBar.cpp # End Source File # Begin Source File @@ -1040,30 +1028,6 @@ SOURCE=.\Round.h # End Source File # Begin Source File -SOURCE=.\RowDlg.cpp -# End Source File -# Begin Source File - -SOURCE=.\RowDlg.h -# End Source File -# Begin Source File - -SOURCE=.\RowForm.cpp -# End Source File -# Begin Source File - -SOURCE=.\RowForm.h -# End Source File -# Begin Source File - -SOURCE=.\RowView.cpp -# End Source File -# Begin Source File - -SOURCE=.\RowView.h -# End Source File -# Begin Source File - SOURCE=.\SafeHandle.cpp # End Source File # Begin Source File @@ -1325,10 +1289,6 @@ SOURCE=.\Wrapx64.h # PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" # Begin Source File -SOURCE=.\res\chordeas.ico -# End Source File -# Begin Source File - SOURCE=.\res\ChordEase.ico # End Source File # Begin Source File @@ -1353,30 +1313,10 @@ SOURCE=.\res\dragsingle.cur # End Source File # Begin Source File -SOURCE=.\res\gear.ico -# End Source File -# Begin Source File - SOURCE=.\res\header_sort.bmp # End Source File # Begin Source File -SOURCE=.\res\ico00001.ico -# End Source File -# Begin Source File - -SOURCE=.\res\icon1.ico -# End Source File -# Begin Source File - -SOURCE=.\res\icon2.ico -# End Source File -# Begin Source File - -SOURCE=.\res\rec_play.ico -# End Source File -# Begin Source File - SOURCE=.\res\RecPlayPause.ico # End Source File # Begin Source File diff --git a/ChordEase.h b/ChordEase.h index 18ae663..0a52c61 100644 --- a/ChordEase.h +++ b/ChordEase.h @@ -86,6 +86,8 @@ class CChordEaseApp : public CWinAppCK void MakeAbsolutePath(CString& Path); static void ValidateFolder(CDataExchange* pDX, int CtrlID, const CString& Path); BOOL OnToolTipNeedText(UINT id, NMHDR* pNMHDR, LRESULT* pResult); + static void InitNoteCombo(CComboBox& Combo, CIntRange Range, int SelIdx); + static void InitNumericCombo(CComboBox& Combo, CIntRange Range, int SelIdx); // Overrides // ClassWizard generated virtual function overrides diff --git a/ChordEase.rc b/ChordEase.rc index 8c8f11b..db99bf5 100644 --- a/ChordEase.rc +++ b/ChordEase.rc @@ -337,6 +337,15 @@ BEGIN END END +IDM_MIDI_TARGET_CTX MENU DISCARDABLE +BEGIN + POPUP "" + BEGIN + MENUITEM "&Learn", ID_MIDI_LEARN + MENUITEM "&Reset", ID_MIDI_TARGET_RESET + END +END + ///////////////////////////////////////////////////////////////////////////// // @@ -440,28 +449,28 @@ STYLE DS_CONTROL | WS_CHILD | WS_CAPTION CAPTION "Metronome" FONT 8, "MS Sans Serif" BEGIN - CONTROL "Enable",IDC_PATCH_AINST_ENABLE,"Button",BS_AUTOCHECKBOX | - WS_TABSTOP,7,7,38,10 + CONTROL "Enable",IDC_PATCH_METRO_ENABLE,"Button",BS_AUTOCHECKBOX | + BS_NOTIFY | WS_TABSTOP,7,7,38,10 LTEXT "Port:",IDC_STATIC,7,22,16,8 - EDITTEXT IDC_PATCH_AINST_PORT,7,32,34,12,ES_AUTOHSCROLL + EDITTEXT IDC_PATCH_METRO_PORT,7,32,34,12,ES_AUTOHSCROLL LTEXT "Channel:",IDC_STATIC,45,22,29,8 - EDITTEXT IDC_PATCH_AINST_CHANNEL,45,32,34,12,ES_AUTOHSCROLL + EDITTEXT IDC_PATCH_METRO_CHANNEL,45,32,34,12,ES_AUTOHSCROLL LTEXT "Patch:",IDC_STATIC,83,22,22,8 - EDITTEXT IDC_PATCH_AINST_PATCH,83,32,34,12,ES_AUTOHSCROLL + EDITTEXT IDC_PATCH_METRO_PATCH,83,32,34,12,ES_AUTOHSCROLL LTEXT "Volume:",IDC_STATIC,121,22,26,8 - EDITTEXT IDC_PATCH_AINST_VOLUME,121,32,34,12,ES_AUTOHSCROLL + EDITTEXT IDC_PATCH_METRO_VOLUME,121,32,34,12,ES_AUTOHSCROLL GROUPBOX "Normal",IDC_STATIC,7,50,85,40 LTEXT "Note:",IDC_STATIC,14,60,18,8 EDITTEXT IDC_PATCH_METRO_NOTE,14,70,34,12,ES_AUTOHSCROLL LTEXT "Velocity:",IDC_STATIC,52,60,28,8 - EDITTEXT IDC_PATCH_AINST_VELOCITY,52,70,34,12,ES_AUTOHSCROLL + EDITTEXT IDC_PATCH_METRO_VELOCITY,52,70,34,12,ES_AUTOHSCROLL GROUPBOX "Accent",IDC_STATIC,99,50,85,52 LTEXT "Note:",IDC_STATIC,106,60,18,8 EDITTEXT IDC_PATCH_METRO_ACCENT_NOTE,106,70,34,12,ES_AUTOHSCROLL LTEXT "Velocity:",IDC_STATIC,144,60,28,8 EDITTEXT IDC_PATCH_METRO_ACCENT_VEL,144,70,34,12,ES_AUTOHSCROLL CONTROL "Use same note",IDC_PATCH_METRO_ACCENT_SAME_NOTE,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,106,86,63,10 + BS_AUTOCHECKBOX | BS_NOTIFY | WS_TABSTOP,106,86,63,10 END IDD_PART_INPUT DIALOG DISCARDABLE 0, 0, 180, 86 @@ -506,11 +515,11 @@ BEGIN COMBOBOX IDC_PART_OUT_ANTICIPATION,7,47,60,80,CBS_DROPDOWN | WS_VSCROLL | WS_TABSTOP CONTROL "Controllers thru",IDC_PART_OUT_CONTROLS_THRU,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,77,38,63,10 + BS_AUTOCHECKBOX | BS_NOTIFY | WS_TABSTOP,77,38,63,10 CONTROL "Local control",IDC_PART_OUT_LOCAL_CONTROL,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,77,51,57,10 + BS_AUTOCHECKBOX | BS_NOTIFY | WS_TABSTOP,77,51,57,10 CONTROL "Fix held notes",IDC_PART_OUT_FIX_HELD_NOTES,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,7,67,62,10 + BS_AUTOCHECKBOX | BS_NOTIFY | WS_TABSTOP,7,67,62,10 LTEXT "Device:",IDC_STATIC,7,83,26,12 EDITTEXT IDC_PART_OUT_DEVICE_NAME,35,83,138,12,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP @@ -525,7 +534,7 @@ BEGIN LTEXT "Generic interval:",IDC_STATIC,14,18,52,8 EDITTEXT IDC_PART_LEAD_HARM_INTERVAL,14,28,34,12,ES_AUTOHSCROLL CONTROL "Omit melody",IDC_PART_LEAD_HARM_OMIT_MELODY,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,14,46,54,10 + BS_AUTOCHECKBOX | BS_NOTIFY | WS_TABSTOP,14,46,54,10 GROUPBOX "Static harmony range",IDC_STATIC,73,16,88,41 LTEXT "Minimum:",IDC_STATIC,81,27,30,8 EDITTEXT IDC_PART_LEAD_HARM_STATIC_MIN,81,37,34,12,ES_AUTOHSCROLL @@ -547,7 +556,7 @@ BEGIN WS_VSCROLL | WS_TABSTOP CONTROL "Chord change resets alternation", IDC_PART_COMP_CHORD_RESETS_ALT,"Button",BS_AUTOCHECKBOX | - WS_TABSTOP,7,36,116,10 + BS_NOTIFY | WS_TABSTOP,7,36,116,10 GROUPBOX "Arpeggio",IDC_STATIC,7,50,130,55 LTEXT "Period:",IDC_STATIC,14,61,23,8 COMBOBOX IDC_PART_COMP_ARP_PERIOD,14,71,48,80,CBS_DROPDOWN | @@ -556,7 +565,7 @@ BEGIN COMBOBOX IDC_PART_COMP_ARP_ORDER,67,71,64,80,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP CONTROL "Repeat",IDC_PART_COMP_ARP_REPEAT,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,14,89,39,10 + BS_AUTOCHECKBOX | BS_NOTIFY | WS_TABSTOP,14,89,39,10 END IDD_PART_BASS DIALOG DISCARDABLE 0, 0, 180, 90 @@ -567,7 +576,7 @@ BEGIN LTEXT "Lowest note:",IDC_STATIC,7,7,42,8 EDITTEXT IDC_PART_BASS_LOWEST,7,17,34,12,ES_AUTOHSCROLL CONTROL "Slash chords",IDC_PART_BASS_SLASH_CHORDS,"Button", - BS_AUTOCHECKBOX | WS_TABSTOP,58,19,57,10 + BS_AUTOCHECKBOX | BS_NOTIFY | WS_TABSTOP,58,19,57,10 LTEXT "Approach length:",IDC_STATIC,7,37,55,8 COMBOBOX IDC_PART_BASS_APPROACH_LENGTH,7,47,60,80,CBS_DROPDOWN | WS_VSCROLL | WS_TABSTOP @@ -614,13 +623,6 @@ BEGIN PUSHBUTTON "&Default Font",IDC_OPT_CHART_DEFAULT_FONT,64,47,60,14 END -IDD_ROW_FORM DIALOGEX 0, 0, 240, 137 -STYLE WS_CHILD | WS_CLIPCHILDREN -EXSTYLE WS_EX_CONTROLPARENT -FONT 8, "MS Sans Serif", 0, 0, 0x1 -BEGIN -END - IDD_PART_MIDI DIALOG DISCARDABLE 0, 0, 180, 90 STYLE DS_CONTROL | WS_CHILD | WS_CLIPCHILDREN | WS_CAPTION CAPTION "MIDI" @@ -628,21 +630,6 @@ FONT 8, "MS Sans Serif" BEGIN END -IDD_MIDI_TARGET_ROW DIALOG DISCARDABLE 0, 0, 270, 14 -STYLE DS_CONTROL | WS_CHILD -FONT 8, "MS Sans Serif" -BEGIN - LTEXT "",IDS_MIDI_TARG_ROW_NAME,2,0,60,12,SS_NOTIFY | - SS_CENTERIMAGE | SS_ENDELLIPSIS - EDITTEXT IDS_MIDI_TARG_ROW_PORT,64,0,30,12,ES_AUTOHSCROLL - EDITTEXT IDS_MIDI_TARG_ROW_CHAN,96,0,30,12,ES_AUTOHSCROLL - COMBOBOX IDS_MIDI_TARG_ROW_EVENT,128,0,40,60,CBS_DROPDOWNLIST | - WS_VSCROLL | WS_TABSTOP - EDITTEXT IDS_MIDI_TARG_ROW_CONTROL,170,0,34,12,ES_AUTOHSCROLL - EDITTEXT IDS_MIDI_TARG_ROW_RANGE_START,206,0,30,12,ES_AUTOHSCROLL - EDITTEXT IDS_MIDI_TARG_ROW_RANGE_END,238,0,30,12,ES_AUTOHSCROLL -END - IDD_MIDI_ASSIGNS DIALOG DISCARDABLE 0, 0, 382, 150 STYLE WS_MAXIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME CAPTION "MIDI Assignments" @@ -681,7 +668,7 @@ CAPTION "Auto" FONT 8, "MS Sans Serif" BEGIN CONTROL "Auto Play",IDC_PART_AUTO_PLAY,"Button",BS_AUTOCHECKBOX | - WS_TABSTOP,7,7,46,10 + BS_NOTIFY | WS_TABSTOP,7,7,46,10 LTEXT "Window:",IDC_STATIC,7,22,29,8 EDITTEXT IDC_PART_AUTO_WINDOW,7,32,34,12,ES_AUTOHSCROLL LTEXT "Velocity:",IDC_STATIC,45,22,28,8 @@ -1154,21 +1141,6 @@ BEGIN 0 END -IDD_MIDI_TARGET_ROW DLGINIT -BEGIN - IDS_MIDI_TARG_ROW_EVENT, 0x403, 5, 0 -0x6f4e, 0x656e, "\000" - IDS_MIDI_TARG_ROW_EVENT, 0x403, 8, 0 -0x6f43, 0x746e, 0x6f72, 0x006c, - IDS_MIDI_TARG_ROW_EVENT, 0x403, 6, 0 -0x6857, 0x6565, 0x006c, - IDS_MIDI_TARG_ROW_EVENT, 0x403, 15, 0 -0x7250, 0x676f, 0x6172, 0x206d, 0x6843, 0x6e61, 0x6567, "\000" - IDS_MIDI_TARG_ROW_EVENT, 0x403, 16, 0 -0x6843, 0x6e61, 0x4120, 0x7466, 0x7265, 0x6f74, 0x6375, 0x0068, - 0 -END - IDD_PART_INPUT DLGINIT BEGIN IDC_PART_IN_NON_DIATONIC, 0x403, 6, 0 @@ -1194,6 +1166,68 @@ BEGIN IDR_CHORDEASEPATCH "Patch\n\nPatch\nPatch Files (*.cep)\n.cep\nChordEasePatch.Document\nChordEase Patch" END +STRINGTABLE PRELOAD DISCARDABLE +BEGIN + IDS_PART_MT_IN_VEL_OFFSET "Input Vel. Offset" + IDS_PART_MT_IN_ZONE_HIGH "Input Zone High" + IDS_PART_MT_IN_ZONE_LOW "Input Zone Low" + IDS_PART_MT_LEAD_HARM_INTERVAL "Lead Harm Interval" + IDS_PART_MT_LEAD_HARM_OMIT_MELODY "Lead Harm Omit Melody" + IDS_PART_MT_LEAD_HARM_STATIC_MAX "Lead Harm Static Max" + IDS_PART_MT_LEAD_HARM_STATIC_MIN "Lead Harm Static Min" + IDS_PART_MT_OUT_ANTICIPATION "Output Harm. Anticipation" + IDS_PART_MT_OUT_FIX_HELD_NOTES "Output Fix Held Notes" + IDS_PART_MT_OUT_PATCH "Output Patch" + IDS_PART_MT_OUT_VOLUME "Output Volume" + IDS_PATCH_BAD_FORMAT "'%1' has an invalid format." + IDS_PATCH_BAR "Patch" + IDS_PATCH_MT_LEAD_IN "Lead-in" + IDS_PATCH_MT_METRO_ENABLE "Metro. Enable" + IDS_PATCH_MT_METRO_VOLUME "Metro. Volume" +END + +STRINGTABLE PRELOAD DISCARDABLE +BEGIN + IDS_PATCH_MT_NEXT_CHORD "Next Chord" + IDS_PATCH_MT_NEXT_SECTION "Next Section" + IDS_PATCH_MT_PAUSE "Pause" + IDS_PATCH_MT_PLAY "Play" + IDS_PATCH_MT_PREV_CHORD "Previous Chord" + IDS_PATCH_MT_REPEAT "Repeat" + IDS_PATCH_MT_REWIND "Rewind" + IDS_PATCH_MT_SONG_POSITION "Song Position" + IDS_PATCH_MT_TEMPO "Tempo" + IDS_PATCH_MT_TEMPO_MULTIPLE "Tempo Multiple" + IDS_PATCH_MT_TRANSPOSE "Transpose" + IDS_RECORD_AS "Record As" + IDS_RECORD_LOCKED "Locked to always record." + IDS_REC_PLAY_COL_CHANNEL "Chan" + IDS_REC_PLAY_COL_DEVICE "Device" + IDS_REC_PLAY_COL_EVENTS "Events" +END + +STRINGTABLE PRELOAD DISCARDABLE +BEGIN + IDS_REC_PLAY_COL_NAME "Name" + IDS_REC_PLAY_COL_PORT "Port" + IDS_REC_PLAY_STOP_PLAYING + "Can't do record playback while song is playing. Stop playing?" + IDS_REC_PLAY_STOP_RECORDING + "Can't do record playback while recording. Stop recording?" + IDS_SECTION_NAME_SPACES "Section name can't contain spaces." + IDS_SEC_LIST_COL_IMPLICIT "Implicit" + IDS_SEC_LIST_COL_INDEX "#" + IDS_SEC_LIST_COL_LENGTH "Length" + IDS_SEC_LIST_COL_NAME "Name" + IDS_SEC_LIST_COL_REPEAT "Repeat" + IDS_SEC_LIST_COL_START "Start" + IDS_SONG_ERR_BAD_BASS_NOTE "Invalid bass note in '%s'." + IDS_SONG_ERR_BAD_CHORD "Invalid chord '%s'." + IDS_SONG_ERR_BAD_CHORD_TYPE "Unknown chord type in '%s'." + IDS_SONG_ERR_BAD_COMMAND "Unknown command '%s' or missing duration." + IDS_SONG_ERR_BAD_DURATION "Invalid duration '%d'." +END + STRINGTABLE PRELOAD DISCARDABLE BEGIN ID_APP_CHECK_FOR_UPDATES "Check for a newer version\nCheck for Updates" @@ -1235,6 +1269,7 @@ STRINGTABLE PRELOAD DISCARDABLE BEGIN ID_MIDI_PANIC "Reset all MIDI notes\nPanic" ID_MIDI_RESET_ALL "Reset all MIDI targets\nReset All Targets" + ID_MIDI_TARGET_RESET "Reset MIDI target assignment to default state" ID_OUT_NOTES_ROTATE_LABELS "Rotate key labels sideways" ID_OUT_NOTES_SHOW_KEY_LABELS "Show note names on keys" ID_OUT_NOTES_SHOW_METRONOME "Show metronome notes" @@ -1248,11 +1283,11 @@ BEGIN ID_PATCH_SAVE_AS "Save the current patch with a new name\nSave Patch As" ID_PIANO_KEY_LABEL_TYPE "Hide piano key labels" ID_PIANO_KEY_LABEL_TYPE2 "Show shortcuts on piano keys" - ID_PIANO_KEY_LABEL_TYPE3 "Show input notes on piano keys" END STRINGTABLE PRELOAD DISCARDABLE BEGIN + ID_PIANO_KEY_LABEL_TYPE3 "Show input notes on piano keys" ID_PIANO_KEY_LABEL_TYPE4 "Show output notes on piano keys" ID_PIANO_KEY_LABEL_TYPE5 "Show intervals above root on piano keys" ID_PIANO_KEY_LABEL_TYPE6 "Show scale tones on piano keys" @@ -1269,11 +1304,11 @@ BEGIN ID_TRANSPORT_RECORD "Record performance\nRecord" ID_TRANSPORT_REPEAT "Repeat song\nRepeat" ID_TRANSPORT_REWIND "Rewind to start of song\nRewind" - ID_VIEW_OUTPUT_NOTES "Show or hide the output notes bar\nToggle Output Notes" END STRINGTABLE PRELOAD DISCARDABLE BEGIN + ID_VIEW_OUTPUT_NOTES "Show or hide the output notes bar\nToggle Output Notes" ID_VIEW_PARTS "Show or hide the parts bar\nToggle Parts Bar" ID_VIEW_PATCH "Show or hide the patch bar\nToggle Patch Bar" ID_VIEW_PIANO "Show or hide the virtual piano\nToggle Piano" @@ -1292,17 +1327,18 @@ BEGIN IDS_CHART_UC_CUT "Cut Chords" IDS_CHART_UC_DELETE "Delete Chords" IDS_CHART_UC_INSERT "Insert Chords" + IDS_CHART_UC_MULTI_CHORD_EDIT "Chord Edit" IDS_CHART_UC_PASTE "Paste Chords" IDS_CHART_UC_REORDER "Drag Chords" IDS_CHART_UC_SECTION_CREATE "Create Section" IDS_CHART_UC_SECTION_DELETE "Delete Section" IDS_CHART_UC_SECTION_PROPS "Section Properties" IDS_CHART_UC_SONG_EDIT "Song Text Edit" - IDS_CHART_UC_SONG_PROPS "Song Properties" END STRINGTABLE PRELOAD DISCARDABLE BEGIN + IDS_CHART_UC_SONG_PROPS "Song Properties" IDS_CHORD_DURATION_OTHER "&Other..." IDS_CKUP_CANT_GET_ADDR "Can't get address '%1'.\n%2" IDS_CKUP_CANT_LOAD_DLL "Can't load library '%1'.\n%2" @@ -1318,11 +1354,11 @@ BEGIN IDS_DEVICE_STATE_OPEN "Open" IDS_EDIT_REDO_FMT "&Redo %s\tCtrl+Y" IDS_EDIT_UNDO_FMT "&Undo %s\tCtrl+Z" - IDS_ENGERR_CANT_CLOSE_MIDI_IN "Can't close MIDI input device '%s'.\n%s" END STRINGTABLE PRELOAD DISCARDABLE BEGIN + IDS_ENGERR_CANT_CLOSE_MIDI_IN "Can't close MIDI input device '%s'.\n%s" IDS_ENGERR_CANT_CLOSE_MIDI_OUT "Can't close MIDI output device '%s'.\n%s" IDS_ENGERR_CANT_OPEN_MIDI_IN "Can't open MIDI input device '%s'.\n%s" IDS_ENGERR_CANT_OPEN_MIDI_OUT "Can't open MIDI output device '%s'.\n%s" @@ -1338,11 +1374,11 @@ BEGIN IDS_ENGERR_START_TIMER_THREAD "Can't start timer thread." IDS_ENGERR_STOP_TIMER_THREAD "Can't stop timer thread." IDS_ENGERR_TOO_MANY_NOTES "Too many notes." - IDS_ENGINE_MIDI_DEVICE_NONE "" END STRINGTABLE PRELOAD DISCARDABLE BEGIN + IDS_ENGINE_MIDI_DEVICE_NONE "" IDS_ENGMSG_MISSING_DEVICE "One or more referenced MIDI devices are missing." IDS_HLINK_CANT_LAUNCH "Can't launch browser." @@ -1359,11 +1395,11 @@ BEGIN IDS_MIDI_EVT_COL_CHANNEL "Chan" IDS_MIDI_EVT_COL_DEVICE "Device" IDS_MIDI_EVT_COL_MESSAGE "Message" - IDS_MIDI_EVT_COL_P1 "P1" END STRINGTABLE PRELOAD DISCARDABLE BEGIN + IDS_MIDI_EVT_COL_P1 "P1" IDS_MIDI_EVT_COL_P2 "P2" IDS_MIDI_EVT_COL_PORT "Port" IDS_MIDI_EVT_COL_TIMESTAMP "Timestamp" @@ -1379,11 +1415,11 @@ BEGIN IDS_MIDI_NMAP_COL_OUT_DEVICE "Output Device" IDS_MIDI_NMAP_COL_OUT_PORT "Output Port" IDS_MIDI_NMAP_COL_PART_INDEX "#" - IDS_MIDI_NMAP_COL_PART_NAME "Part Name" END STRINGTABLE PRELOAD DISCARDABLE BEGIN + IDS_MIDI_NMAP_COL_PART_NAME "Part Name" IDS_MIDI_NMAP_COL_ZONE_HIGH "Zone High" IDS_MIDI_NMAP_COL_ZONE_LOW "Zone Low" IDS_MIDI_OUTPUT_BAR "MIDI Output" @@ -1400,11 +1436,11 @@ BEGIN IDS_MIDI_STAT_EOX "EOX" IDS_MIDI_STAT_KEY_AFT "Key Aft" IDS_MIDI_STAT_MTC_QTR_FRAME "MTC Qtr Frame" - IDS_MIDI_STAT_NOTE_OFF "Note Off" END STRINGTABLE PRELOAD DISCARDABLE BEGIN + IDS_MIDI_STAT_NOTE_OFF "Note Off" IDS_MIDI_STAT_NOTE_ON "Note On" IDS_MIDI_STAT_PATCH "Patch" IDS_MIDI_STAT_SONG_POSITION "Song Position" @@ -1416,17 +1452,83 @@ BEGIN IDS_MIDI_STAT_SYSTEM_WILDCARD "System *" IDS_MIDI_STAT_TUNE_REQUEST "Tune Request" IDS_MIDI_STAT_WHEEL "Wheel" - IDS_MIDI_TARG_ROW_CHAN "Input channel number" - IDS_MIDI_TARG_ROW_CONTROL "Input controller number" - IDS_MIDI_TARG_ROW_EVENT "Input event type" - IDS_MIDI_TARG_ROW_NAME "Target parameter name" - IDS_MIDI_TARG_ROW_PORT "MIDI input device index" + IDS_MIDI_TARG_COL_CHAN "Chan" + IDS_MIDI_TARG_COL_CONTROL "Control" + IDS_MIDI_TARG_COL_EVENT "Event" + IDS_MIDI_TARG_COL_NAME "Target" +END + +STRINGTABLE PRELOAD DISCARDABLE +BEGIN + IDC_MIDI_TARGET_LIST "MIDI target list" + IDC_MIDI_TARG_CHAN "Input channel number" + IDC_MIDI_TARG_CONTROL "Input controller number" + IDC_MIDI_TARG_EVENT "Input event type" + IDC_MIDI_TARG_NAME "Target parameter name" + IDC_MIDI_TARG_PORT "MIDI input device index" + IDC_MIDI_TARG_RANGE_END "End of normalized controller range" + IDC_MIDI_TARG_RANGE_START "Start of normalized controller range" + IDC_MIDI_TARG_VALUE "Current value of MIDI controller" + IDC_OPT_CHART_CHOOSE_FONT "Select chart font" + IDC_OPT_CHART_DEFAULT_FONT "Reset chart font to default" + IDC_OPT_CHART_LINE_MEASURES "Number of measures per line" + IDC_OPT_OTHER_AUTO_CHECK_UPDATES + "Automatically check for updates on startup" +END + +STRINGTABLE PRELOAD DISCARDABLE +BEGIN + IDC_OPT_OTHER_DATA_FOLDER_BROWSE "Browse for application data folder" + IDC_OPT_OTHER_DATA_FOLDER_PATH "Location of application data folder" + IDC_OPT_OTHER_DATA_FOLDER_TYPE "Use default application data folder" + IDC_OPT_OTHER_DATA_FOLDER_TYPE2 "Specify custom application data folder" + IDC_OPT_OTHER_SHOW_TOOLTIPS "Display tooltips" + IDC_OPT_REC_ALWAYS_RECORD "Always record while playing" + IDC_OPT_REC_BUFFER_SIZE "Size of record buffer size, in events" + IDC_OPT_REC_FOLDER_BROWSE "Browse for destination folder" + IDC_OPT_REC_FOLDER_PATH "Location of destination folder" + IDC_OPT_REC_FOLDER_TYPE "Use default destination folder for recordings" + IDC_OPT_REC_FOLDER_TYPE2 + "Specify custom destination folder for recordings" + IDC_OPT_REC_MIDI_FILE_PPQ + "Time resolution of output MIDI file, in pulses per quarter note" + IDC_OPT_REC_PROMPT_FILENAME + "Prompt for filename before recording; else generate filename" + IDC_PARTS_LIST " " + IDC_PART_AUTO_PLAY "Play part automatically" + IDC_PART_AUTO_VELOCITY "Auto-play note velocity" +END + +STRINGTABLE PRELOAD DISCARDABLE +BEGIN + IDC_PART_AUTO_WINDOW "Start of auto-play pitch window" + IDC_PART_BASS_APPROACH_LENGTH + "Fixed bass approach length, as fraction of whole note" + IDC_PART_BASS_APPROACH_TRIGGER "Trigger bass approach to target chord" + IDC_PART_BASS_LOWEST "Lowest bass note allowed" + IDC_PART_BASS_SLASH_CHORDS "Respect slash chord bass notes" + IDC_PART_BASS_TARGET_ALIGNMENT + "Alignment of triggered bass approach target, in measures" + IDC_PART_COMP_ARP_ORDER "Arpeggio note order" + IDC_PART_COMP_ARP_PERIOD "Time between arpeggio notes, in whole notes" + IDC_PART_COMP_ARP_PERIOD_QUANT + "Time between arpeggio notes, in quantized units" + IDC_PART_COMP_ARP_REPEAT "True if arpeggio repeats; false for one-shot" + IDC_PART_COMP_CHORD_RESETS_ALT + "True if chord change resets variant alternation" + IDC_PART_COMP_VARIATION "Chord variation scheme" + IDC_PART_COMP_VOICING "Chord voicing type" + IDC_PART_ENABLE "Enable this part" + IDC_PART_FUNCTION "Part mapping function" + IDC_PART_IN_CC_NOTE "Note input via continuous controller" END STRINGTABLE PRELOAD DISCARDABLE BEGIN - IDS_MIDI_TARG_ROW_RANGE_END "End of normalized controller range" - IDS_MIDI_TARG_ROW_RANGE_START "Start of normalized controller range" + IDS_MIDI_TARG_COL_PORT "Port" + IDS_MIDI_TARG_COL_RANGE_END "End" + IDS_MIDI_TARG_COL_RANGE_START "Start" + IDS_MIDI_TARG_COL_VALUE "Value" IDS_MSGBOX_NO "&No" IDS_MSGBOX_YES "&Yes" IDS_MTEVT_CHAN_AFTERTOUCH "Chan Aftertouch" @@ -1439,12 +1541,12 @@ BEGIN IDS_OPT_OTHER_DATA_FOLDER "Application data folder" IDS_OPT_REC_OUTPUT_FOLDER "Record output folder" IDS_OPT_RESET_ALL "Reset All" - IDS_OPT_RESTORE_DEFAULTS "Restore all options to default settings?" - IDS_OUTPUT_NOTES_BAR "Output Notes" END STRINGTABLE PRELOAD DISCARDABLE BEGIN + IDS_OPT_RESTORE_DEFAULTS "Restore all options to default settings?" + IDS_OUTPUT_NOTES_BAR "Output Notes" IDS_OUT_NOTES_FILTER_ALL "All" IDS_OUT_NOTES_SM_ALL_CHANS "Show output notes for all channels" IDS_OUT_NOTES_SM_ALL_PORTS "Show output notes for all ports" @@ -1459,12 +1561,12 @@ BEGIN IDS_PART_MT_AUTO_PLAY "Auto Play" IDS_PART_MT_AUTO_VELOCITY "Auto Velocity" IDS_PART_MT_AUTO_WINDOW "Auto Window" - IDS_PART_MT_BASS_APPROACH_LENGTH "Bass Approach Length" - IDS_PART_MT_BASS_APPROACH_TRIGGER "Bass Approach Trigger" END STRINGTABLE PRELOAD DISCARDABLE BEGIN + IDS_PART_MT_BASS_APPROACH_LENGTH "Bass Approach Length" + IDS_PART_MT_BASS_APPROACH_TRIGGER "Bass Approach Trigger" IDS_PART_MT_BASS_SLASH_CHORDS "Bass Slash Chords" IDS_PART_MT_BASS_TARGET_ALIGNMENT "Bass Target Alignment" IDS_PART_MT_COMP_ARP_ORDER "Comp Arp Order" @@ -1479,56 +1581,31 @@ BEGIN IDS_PART_MT_IN_CC_NOTE_VEL "Input CC Note Velocity" IDS_PART_MT_IN_NON_DIATONIC "Input Non-Diatonic" IDS_PART_MT_IN_TRANSPOSE "Input Transpose" - IDS_PART_MT_IN_VEL_OFFSET "Input Vel. Offset" - IDS_PART_MT_IN_ZONE_HIGH "Input Zone High" END STRINGTABLE PRELOAD DISCARDABLE BEGIN - IDS_PART_MT_IN_ZONE_LOW "Input Zone Low" - IDS_PART_MT_LEAD_HARM_INTERVAL "Lead Harm Interval" - IDS_PART_MT_LEAD_HARM_OMIT_MELODY "Lead Harm Omit Melody" - IDS_PART_MT_LEAD_HARM_STATIC_MAX "Lead Harm Static Max" - IDS_PART_MT_LEAD_HARM_STATIC_MIN "Lead Harm Static Min" - IDS_PART_MT_OUT_ANTICIPATION "Harmonic Anticipation" - IDS_PART_MT_OUT_PATCH "Output Patch" - IDS_PART_MT_OUT_VOLUME "Output Volume" - IDS_PATCH_BAD_FORMAT "'%1' has an invalid format." - IDS_PATCH_BAR "Patch" - IDS_PATCH_MT_LEAD_IN "Lead-in" - IDS_PATCH_MT_METRO_ENABLE "Metro. Enable" - IDS_PATCH_MT_METRO_VOLUME "Metro. Volume" - IDS_PATCH_MT_NEXT_CHORD "Next Chord" - IDS_PATCH_MT_NEXT_SECTION "Next Section" - IDS_PATCH_MT_PAUSE "Pause" + IDS_SONG_ERR_BAD_KEY "Invalid key signature '%s'." + IDS_SONG_ERR_BAD_METER "Invalid time signature %d/%d." + IDS_SONG_ERR_BAD_MODE "Unknown mode '%s'." + IDS_SONG_ERR_BAD_ROOT "Invalid root in '%s'." + IDS_SONG_ERR_BAD_SCALE "Unknown scale '%s'." + IDS_SONG_ERR_BAD_SYNTAX "Invalid syntax." + IDS_SONG_ERR_BAD_TEMPO "Invalid tempo '%s'." + IDS_SONG_ERR_CHORD_TONE_RANGE "Chord tone out of range: '%s'." + IDS_SONG_ERR_DUP_CHORD_TYPE "Duplicate chord type '%s'." + IDS_SONG_ERR_READ "Error reading '%s' at line %d." + IDS_SONG_ERR_SEC_BAD_REPEAT "Invalid section repeat count." + IDS_SONG_ERR_SEC_BAD_SYNTAX "Invalid section syntax." + IDS_SONG_ERR_SEC_CANT_NEST "Sections can't be nested." + IDS_SONG_ERR_SEC_UNTERMINATED "Missing section terminator." + IDS_THREADS_COL_KERNEL_TIME "Kernel" + IDS_THREADS_COL_PRIORITY "Priority" END STRINGTABLE PRELOAD DISCARDABLE BEGIN - IDC_PART_BASS_APPROACH_LENGTH - "Fixed bass approach length, as fraction of whole note" - IDC_PART_BASS_APPROACH_TRIGGER "Trigger bass approach to target chord" - IDC_PART_BASS_LOWEST "Lowest bass note allowed" - IDC_PART_BASS_SLASH_CHORDS "Respect slash chord bass notes" - IDC_PART_BASS_TARGET_ALIGNMENT - "Alignment of triggered bass approach target, in measures" - IDC_PART_COMP_ARP_ORDER "Arpeggio note order" - IDC_PART_COMP_ARP_PERIOD "Time between arpeggio notes, in whole notes" - IDC_PART_COMP_ARP_PERIOD_QUANT - "Time between arpeggio notes, in quantized units" - IDC_PART_COMP_ARP_REPEAT "True if arpeggio repeats; false for one-shot" - IDC_PART_COMP_CHORD_RESETS_ALT - "True if chord change resets variant alternation" - IDC_PART_COMP_VARIATION "Chord variation scheme" - IDC_PART_COMP_VOICING "Chord voicing type" - IDC_PART_ENABLE "Enable this part" - IDC_PART_FUNCTION "Part mapping function" - IDC_PART_IN_CC_NOTE "Note input via continuous controller" IDC_PART_IN_CC_NOTE_VEL "Velocity of note input via continuous controller" -END - -STRINGTABLE PRELOAD DISCARDABLE -BEGIN IDC_PART_IN_CHAN "Input channel number" IDC_PART_IN_DEVICE_NAME "MIDI input device name" IDC_PART_IN_NON_DIATONIC "Handling for non-diatonic input notes" @@ -1548,73 +1625,50 @@ BEGIN IDC_PART_NAME "Part name" IDC_PART_OUT_ANTICIPATION "Harmonic anticipation, as fraction of whole note" - IDC_PART_OUT_CHAN "Output channel number" END STRINGTABLE PRELOAD DISCARDABLE BEGIN - IDS_PATCH_MT_PLAY "Play" - IDS_PATCH_MT_PREV_CHORD "Previous Chord" - IDS_PATCH_MT_REPEAT "Repeat" - IDS_PATCH_MT_REWIND "Rewind" - IDS_PATCH_MT_SONG_POSITION "Song Position" - IDS_PATCH_MT_TEMPO "Tempo" - IDS_PATCH_MT_TEMPO_MULTIPLE "Tempo Multiple" - IDS_PATCH_MT_TRANSPOSE "Transpose" - IDS_RECORD_AS "Record As" - IDS_RECORD_LOCKED "Locked to always record." - IDS_REC_PLAY_COL_CHANNEL "Chan" - IDS_REC_PLAY_COL_DEVICE "Device" - IDS_REC_PLAY_COL_EVENTS "Events" - IDS_REC_PLAY_COL_NAME "Name" - IDS_REC_PLAY_COL_PORT "Port" - IDS_REC_PLAY_STOP_PLAYING - "Can't do record playback while song is playing. Stop playing?" + IDC_PART_OUT_CHAN "Output channel number" + IDC_PART_OUT_CONTROLS_THRU "Pass controllers through to output" + IDC_PART_OUT_DEVICE_NAME "MIDI output device name" + IDC_PART_OUT_FIX_HELD_NOTES "Correct held notes that become non-diatonic" + IDC_PART_OUT_LOCAL_CONTROL "Enable local control" + IDC_PART_OUT_PATCH "Patch number, or -1 if none" + IDC_PART_OUT_PORT "MIDI output device index" + IDC_PART_OUT_VOLUME "Output volume, or -1 if none" + IDC_PATCH_GEN_KEY "Transposed key signature" + IDC_PATCH_GEN_LEAD_IN "Lead-in length, in measures" + IDC_PATCH_GEN_PPQ "Time resolution, in pulses per quarter note" + IDC_PATCH_GEN_TEMPO "Tempo, in beats per minute" + IDC_PATCH_GEN_TEMPO_MULTIPLE "Tempo multiplier" + IDC_PATCH_GEN_TRANSPOSE "Key transposition, in semitones" + IDC_PATCH_METRO_ACCENT_NOTE "Accented metronome note" + IDC_PATCH_METRO_ACCENT_SAME_NOTE "Use same note for accent" END STRINGTABLE PRELOAD DISCARDABLE BEGIN - IDS_REC_PLAY_STOP_RECORDING - "Can't do record playback while recording. Stop recording?" - IDS_SECTION_NAME_SPACES "Section name can't contain spaces." - IDS_SEC_LIST_COL_IMPLICIT "Implicit" - IDS_SEC_LIST_COL_INDEX "#" - IDS_SEC_LIST_COL_LENGTH "Length" - IDS_SEC_LIST_COL_NAME "Name" - IDS_SEC_LIST_COL_REPEAT "Repeat" - IDS_SEC_LIST_COL_START "Start" - IDS_SONG_BAD_METER "Invalid time signature %d/%d." - IDS_SONG_ERR_BAD_BASS_NOTE "Invalid bass note in '%s'." - IDS_SONG_ERR_BAD_CHORD "Invalid chord '%s'." - IDS_SONG_ERR_BAD_CHORD_TYPE "Unknown chord type in '%s'." - IDS_SONG_ERR_BAD_COMMAND "Unknown command '%s' or missing duration." - IDS_SONG_ERR_BAD_DURATION "Invalid duration '%d'." - IDS_SONG_ERR_BAD_KEY "Invalid key signature '%s'." - IDS_SONG_ERR_BAD_MODE "Unknown mode '%s'." + IDC_PATCH_METRO_ACCENT_VEL "Accented note velocity" + IDC_PATCH_METRO_CHANNEL "Output channel number" + IDC_PATCH_METRO_ENABLE "Enable metronome" + IDC_PATCH_METRO_NOTE "Normal metronome note" + IDC_PATCH_METRO_PATCH "Patch number, or -1 if none" + IDC_PATCH_METRO_PORT "MIDI output device index" + IDC_PATCH_METRO_VELOCITY "Note velocity" + IDC_PATCH_METRO_VOLUME "Output volume, or -1 if none" + IDC_PIANO_CHANNEL "Output channel number" + IDC_PIANO_KEY_COUNT "Number of keys" + IDC_PIANO_PORT "MIDI output device index" + IDC_PIANO_START_NOTE "Keyboard's first note" + IDC_PIANO_VELOCITY "Output velocity" END STRINGTABLE PRELOAD DISCARDABLE BEGIN - IDS_SONG_ERR_BAD_ROOT "Invalid root in '%s'." - IDS_SONG_ERR_BAD_SCALE "Unknown scale '%s'." - IDS_SONG_ERR_BAD_SYNTAX "Invalid syntax." - IDS_SONG_ERR_BAD_TEMPO "Invalid tempo '%s'." - IDS_SONG_ERR_CHORD_TONE_RANGE "Chord tone out of range: '%s'." - IDS_SONG_ERR_DUP_CHORD_TYPE "Duplicate chord type '%s'." - IDS_SONG_ERR_READ "Error reading '%s' at line %d." - IDS_SONG_ERR_SEC_BAD_REPEAT "Invalid section repeat count." - IDS_SONG_ERR_SEC_BAD_SYNTAX "Invalid section syntax." - IDS_SONG_ERR_SEC_CANT_NEST "Sections can't be nested." - IDS_SONG_ERR_SEC_UNTERMINATED "Missing section terminator." - IDS_THREADS_COL_KERNEL_TIME "Kernel" - IDS_THREADS_COL_PRIORITY "Priority" IDS_THREADS_COL_THREAD_ID "ID" IDS_THREADS_COL_USER_TIME "User" IDS_UC_BASE_PATCH "Base Patch" -END - -STRINGTABLE PRELOAD DISCARDABLE -BEGIN IDS_UC_CUT "Cut Parts" IDS_UC_DELETE "Delete Parts" IDS_UC_ENABLE_PART "Enable Part" @@ -1642,83 +1696,6 @@ BEGIN IDS_APP_TEMP_FOLDER_NOT_FOUND "Temporary folder '%1' not found." END -STRINGTABLE PRELOAD DISCARDABLE -BEGIN - IDC_MIDI_TARGET "PatchMidiRow" - IDC_MIDI_TARG_COL_CHAN "Chan" - IDC_MIDI_TARG_COL_CONTROL "Control" - IDC_MIDI_TARG_COL_EVENT "Event" - IDC_MIDI_TARG_COL_NAME "Target" - IDC_MIDI_TARG_COL_PORT "Port" - IDC_MIDI_TARG_COL_RANGE_END "End" - IDC_MIDI_TARG_COL_RANGE_START "Start" - IDC_OPT_CHART_CHOOSE_FONT "Select chart font" - IDC_OPT_CHART_DEFAULT_FONT "Reset chart font to default" - IDC_OPT_CHART_LINE_MEASURES "Number of measures per line" - IDC_OPT_OTHER_AUTO_CHECK_UPDATES - "Automatically check for updates on startup" - IDC_OPT_OTHER_DATA_FOLDER_BROWSE "Browse for application data folder" -END - -STRINGTABLE PRELOAD DISCARDABLE -BEGIN - IDC_OPT_OTHER_DATA_FOLDER_PATH "Location of application data folder" - IDC_OPT_OTHER_DATA_FOLDER_TYPE "Use default application data folder" - IDC_OPT_OTHER_DATA_FOLDER_TYPE2 "Specify custom application data folder" - IDC_OPT_OTHER_SHOW_TOOLTIPS "Display tooltips" - IDC_OPT_REC_ALWAYS_RECORD "Always record while playing" - IDC_OPT_REC_BUFFER_SIZE "Size of record buffer size, in events" - IDC_OPT_REC_FOLDER_BROWSE "Browse for destination folder" - IDC_OPT_REC_FOLDER_PATH "Location of destination folder" - IDC_OPT_REC_FOLDER_TYPE "Use default destination folder for recordings" - IDC_OPT_REC_FOLDER_TYPE2 - "Specify custom destination folder for recordings" - IDC_OPT_REC_MIDI_FILE_PPQ - "Time resolution of output MIDI file, in pulses per quarter note" - IDC_OPT_REC_PROMPT_FILENAME - "Prompt for filename before recording; else generate filename" - IDC_PARTS_LIST " " - IDC_PART_AUTO_PLAY "Play part automatically" - IDC_PART_AUTO_VELOCITY "Auto-play note velocity" - IDC_PART_AUTO_WINDOW "Start of auto-play pitch window" -END - -STRINGTABLE PRELOAD DISCARDABLE -BEGIN - IDC_PART_OUT_CONTROLS_THRU "Pass controllers through to output" - IDC_PART_OUT_DEVICE_NAME "MIDI output device name" - IDC_PART_OUT_FIX_HELD_NOTES "Correct held notes that become non-diatonics" - IDC_PART_OUT_LOCAL_CONTROL "Enable local control" - IDC_PART_OUT_PATCH "Patch number, or -1 if none" - IDC_PART_OUT_PORT "MIDI output device index" - IDC_PART_OUT_VOLUME "Output volume, or -1 if none" - IDC_PATCH_AINST_CHANNEL "Output channel number" - IDC_PATCH_AINST_ENABLE "Enable instrument" - IDC_PATCH_AINST_PATCH "Patch number, or -1 if none" - IDC_PATCH_AINST_PORT "MIDI output device index" - IDC_PATCH_AINST_VELOCITY "Note velocity" - IDC_PATCH_AINST_VOLUME "Output volume, or -1 if none" - IDC_PATCH_GEN_KEY "Transposed key signature" - IDC_PATCH_GEN_LEAD_IN "Lead-in length, in measures" - IDC_PATCH_GEN_PPQ "Time resolution, in pulses per quarter note" -END - -STRINGTABLE PRELOAD DISCARDABLE -BEGIN - IDC_PATCH_GEN_TEMPO "Tempo, in beats per minute" - IDC_PATCH_GEN_TEMPO_MULTIPLE "Tempo multiplier" - IDC_PATCH_GEN_TRANSPOSE "Key transposition, in semitones" - IDC_PATCH_METRO_ACCENT_NOTE "Accented metronome note" - IDC_PATCH_METRO_ACCENT_SAME_NOTE "Use same note for accent" - IDC_PATCH_METRO_ACCENT_VEL "Accented note velocity" - IDC_PATCH_METRO_NOTE "Normal metronome note" - IDC_PIANO_CHANNEL "Output channel number" - IDC_PIANO_KEY_COUNT "Number of keys" - IDC_PIANO_PORT "MIDI output device index" - IDC_PIANO_START_NOTE "Keyboard's first note" - IDC_PIANO_VELOCITY "Output velocity" -END - STRINGTABLE PRELOAD DISCARDABLE BEGIN IDC_SECTION_PROPS_LENGTH "Section length" @@ -1727,11 +1704,11 @@ BEGIN IDC_SECTION_PROPS_START "Section start position" IDC_SONG_PROPS_COMMENTS "Enter one or more comment lines here" IDC_SONG_PROPS_KEY_SIG "Song key signature" - IDC_SONG_PROPS_TEMPO "Song tempo, in beats per minute" END STRINGTABLE PRELOAD DISCARDABLE BEGIN + IDC_SONG_PROPS_TEMPO "Song tempo, in beats per minute" IDC_SONG_PROPS_TIME_SIG_DENOM "Denominator of song's time signature" IDC_SONG_PROPS_TIME_SIG_NUMER "Numerator of song's time signature" IDC_SONG_PROPS_TRANSPOSE "Song transposition amount, in semitones" diff --git a/ChordEase.vcproj b/ChordEase.vcproj index bb6793b..01ea780 100644 --- a/ChordEase.vcproj +++ b/ChordEase.vcproj @@ -1784,14 +1784,6 @@ RelativePath=".\MidiTargetDlg.h" > - - - - @@ -2332,6 +2324,10 @@ RelativePath=".\PartsBar.h" > + + @@ -2356,14 +2352,6 @@ RelativePath=".\Patch.h" > - - - - @@ -2564,30 +2552,6 @@ RelativePath="Round.h" > - - - - - - - - - - - - @@ -3401,6 +3365,10 @@ RelativePath="res\ChordEaseDoc.ico" > + + @@ -3413,6 +3381,18 @@ RelativePath=".\res\header_sort.bmp" > + + + + + + diff --git a/ChordEaseView.cpp b/ChordEaseView.cpp index e0c9e7b..3ca6047 100644 --- a/ChordEaseView.cpp +++ b/ChordEaseView.cpp @@ -14,6 +14,7 @@ 04 07may14 add editing 05 15may14 in SaveUndoState, remove song state's section map 06 02jun14 fix access violation on left button up if empty song + 07 10jun14 add SetChord ChordEase view @@ -723,8 +724,7 @@ bool CChordEaseView::OnDestroyPopupEdit() } if (chord == gEngine.GetChord(iSongChord)) // if chord unchanged return(FALSE); - NotifyUndoableEdit(iSongChord, CHART_UCODE_CHORD_EDIT); - if (!gEngine.SetChord(iSongChord, chord)) + if (!SetChord(m_EditChordIdx, chord)) return(FALSE); UpdateViews(CChordEaseDoc::HINT_CHART); return(TRUE); @@ -919,6 +919,27 @@ bool CChordEaseView::MakeChordPopups(CMenu& Menu, int ChordIdx) return(TRUE); } +bool CChordEaseView::SetChord(int ChordIdx, const CSong::CChord& Chord) +{ + int iSongChord = GetSongChordIndex(ChordIdx); + if (m_ChordSymbol[ChordIdx].m_Name == MEASURE_REPEAT // if repeated measure + || GetSelection().LengthInclusive() > 1 // or multi-chord selection + || gEngine.GetSong().IsMergeable(iSongChord)) { // or merging with adjacent chord + CSongState state; + gEngine.GetSongState(state); + NotifyUndoableEdit(iSongChord, CHART_UCODE_MULTI_CHORD_EDIT); + state.SetChord(GetBeatSelection(), Chord); + if (!gEngine.SetSongState(state)) + return(FALSE); + } else { // single chord edit + NotifyUndoableEdit(iSongChord, CHART_UCODE_CHORD_EDIT); + if (!gEngine.SetChord(iSongChord, Chord)) + return(FALSE); + } + UpdateViews(CChordEaseDoc::HINT_CHART); + return(TRUE); +} + void CChordEaseView::SaveUndoState(CUndoState& State) { // _tprintf(_T("SaveUndoState %d %d\n"), State.GetCtrlID(), State.GetCode()); @@ -951,6 +972,7 @@ void CChordEaseView::SaveUndoState(CUndoState& State) case CHART_UCODE_SECTION_CREATE: case CHART_UCODE_SECTION_DELETE: case CHART_UCODE_SECTION_PROPS: + case CHART_UCODE_MULTI_CHORD_EDIT: { CRefPtr uip; uip.CreateObj(); @@ -1006,6 +1028,7 @@ void CChordEaseView::RestoreUndoState(const CUndoState& State) case CHART_UCODE_SECTION_CREATE: case CHART_UCODE_SECTION_DELETE: case CHART_UCODE_SECTION_PROPS: + case CHART_UCODE_MULTI_CHORD_EDIT: { const CClipboardEditUndoInfo *uip = static_cast(State.GetObj()); @@ -1523,11 +1546,9 @@ void CChordEaseView::OnChordRoot(UINT nID) int iSongChord = GetSongChordIndex(m_EditChordIdx); CSong::CChord ch = gEngine.GetChord(iSongChord); if (note != ch.m_Root) { // if selection changed - NotifyUndoableEdit(iSongChord, CHART_UCODE_CHORD_EDIT); ch.m_Root = note; ch.m_Bass = note; // reset bass note to root - gEngine.SetChord(iSongChord, ch); - UpdateViews(CChordEaseDoc::HINT_CHART); + SetChord(m_EditChordIdx, ch); } } @@ -1538,10 +1559,8 @@ void CChordEaseView::OnChordType(UINT nID) int iSongChord = GetSongChordIndex(m_EditChordIdx); CSong::CChord ch = gEngine.GetChord(iSongChord); if (iType != ch.m_Type) { // if selection changed - NotifyUndoableEdit(iSongChord, CHART_UCODE_CHORD_EDIT); ch.m_Type = iType; - gEngine.SetChord(iSongChord, ch); - UpdateViews(CChordEaseDoc::HINT_CHART); + SetChord(m_EditChordIdx, ch); } } @@ -1552,10 +1571,8 @@ void CChordEaseView::OnChordBass(UINT nID) int iSongChord = GetSongChordIndex(m_EditChordIdx); CSong::CChord ch = gEngine.GetChord(iSongChord); if (note != ch.m_Bass) { // if selection changed - NotifyUndoableEdit(iSongChord, CHART_UCODE_CHORD_EDIT); ch.m_Bass = note; - gEngine.SetChord(iSongChord, ch); - UpdateViews(CChordEaseDoc::HINT_CHART); + SetChord(m_EditChordIdx, ch); } } diff --git a/ChordEaseView.h b/ChordEaseView.h index d11f8cf..bd4f9ac 100644 --- a/ChordEaseView.h +++ b/ChordEaseView.h @@ -10,6 +10,7 @@ 00 12sep13 initial version 01 17apr14 add support for song sections 02 07may14 add editing + 03 10jun14 add SetChord ChordEase view @@ -67,6 +68,7 @@ class CChordEaseView : public CScrollView, public CUndoable bool CanPaste() const; CRect GetChordItemRect(int ChordIdx) const; int GetCurSection() const; + bool SetChord(int ChordIdx, const CSong::CChord& Chord); // Operations public: diff --git a/DragVirtualListCtrl.cpp b/DragVirtualListCtrl.cpp index 852e67e..6407446 100644 --- a/DragVirtualListCtrl.cpp +++ b/DragVirtualListCtrl.cpp @@ -18,6 +18,7 @@ 08 04oct13 add drop position tracking 09 21nov13 derive from extended selection list 10 22nov13 in PreTranslateMessage, do base class if not dragging + 11 12jun14 add drag enable virtual list control with drag reordering @@ -43,6 +44,7 @@ IMPLEMENT_DYNAMIC(CDragVirtualListCtrl, CListCtrlExSel); CDragVirtualListCtrl::CDragVirtualListCtrl() { + m_DragEnable = TRUE; m_Dragging = FALSE; m_TrackDropPos = FALSE; m_ScrollDelta = 0; @@ -113,13 +115,15 @@ END_MESSAGE_MAP() BOOL CDragVirtualListCtrl::OnBegindrag(NMHDR* pNMHDR, LRESULT* pResult) { - NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR; - m_Dragging = TRUE; - SetCapture(); - UpdateCursor(pNMListView->ptAction); - m_DropPos = -1; - if (m_TrackDropPos) - SetFocus(); + if (m_DragEnable) { // if drag enabled + NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR; + m_Dragging = TRUE; + SetCapture(); + UpdateCursor(pNMListView->ptAction); + m_DropPos = -1; + if (m_TrackDropPos) + SetFocus(); + } *pResult = 0; return(FALSE); // let parent handle notification too } diff --git a/DragVirtualListCtrl.h b/DragVirtualListCtrl.h index 10e31ec..1c60429 100644 --- a/DragVirtualListCtrl.h +++ b/DragVirtualListCtrl.h @@ -14,6 +14,7 @@ 04 06jan10 W64: make OnTimer 64-bit compatible 05 04oct13 add drop position tracking 06 21nov13 derive from extended selection list + 07 12jun14 add drag enable virtual list control with drag reordering @@ -44,6 +45,8 @@ class CDragVirtualListCtrl : public CListCtrlExSel // Attributes public: + void SetDragEnable(bool Enable); + bool GetDragEnable() const; int GetDropPos() const; bool IsDragging() const; void TrackDropPos(bool Enable); @@ -80,6 +83,7 @@ class CDragVirtualListCtrl : public CListCtrlExSel }; // Member data + bool m_DragEnable; // true if drag is enabled bool m_Dragging; // true if items are being dragged bool m_TrackDropPos; // true if tracking drop position int m_ScrollDelta; // scroll by this amount per timer tick @@ -93,6 +97,16 @@ class CDragVirtualListCtrl : public CListCtrlExSel void AutoScroll(const CPoint& Cursor); }; +inline void CDragVirtualListCtrl::SetDragEnable(bool Enable) +{ + m_DragEnable = Enable; +} + +inline bool CDragVirtualListCtrl::GetDragEnable() const +{ + return(m_DragEnable); +} + inline int CDragVirtualListCtrl::GetDropPos() const { return(m_DropPos); diff --git a/DurationComboBox.cpp b/DurationComboBox.cpp index b4a83c3..1960064 100644 --- a/DurationComboBox.cpp +++ b/DurationComboBox.cpp @@ -8,6 +8,7 @@ revision history: rev date comments 00 17oct13 initial version + 01 10jun14 let parent handle notifications too note duration combo box @@ -156,8 +157,8 @@ void CDurationComboBox::UpdateValFromString(CString Str) BEGIN_MESSAGE_MAP(CDurationComboBox, CComboBox) //{{AFX_MSG_MAP(CDurationComboBox) - ON_CONTROL_REFLECT(CBN_KILLFOCUS, OnKillfocus) - ON_CONTROL_REFLECT(CBN_SELCHANGE, OnSelchange) + ON_CONTROL_REFLECT_EX(CBN_KILLFOCUS, OnKillfocus) + ON_CONTROL_REFLECT_EX(CBN_SELCHANGE, OnSelchange) //}}AFX_MSG_MAP END_MESSAGE_MAP() @@ -185,14 +186,15 @@ void CDurationComboBox::PreSubclassWindow() } } -void CDurationComboBox::OnKillfocus() +BOOL CDurationComboBox::OnKillfocus() { CString s; GetWindowText(s); UpdateValFromString(s); + return(FALSE); // let parent handle notification too } -void CDurationComboBox::OnSelchange() +BOOL CDurationComboBox::OnSelchange() { int iItem = GetCurSel(); if (iItem >= 0) { @@ -200,6 +202,7 @@ void CDurationComboBox::OnSelchange() GetLBText(iItem, s); UpdateValFromString(s); } + return(FALSE); // let parent handle notification too } BOOL CDurationComboBox::PreTranslateMessage(MSG* pMsg) diff --git a/DurationComboBox.h b/DurationComboBox.h index 7b8266c..09be907 100644 --- a/DurationComboBox.h +++ b/DurationComboBox.h @@ -8,6 +8,7 @@ revision history: rev date comments 00 17oct13 initial version + 01 10jun14 let parent handle notifications too note duration combo box @@ -68,8 +69,8 @@ class CDurationComboBox : public CComboBox // Generated message map functions protected: //{{AFX_MSG(CDurationComboBox) - afx_msg void OnKillfocus(); - afx_msg void OnSelchange(); + afx_msg BOOL OnKillfocus(); + afx_msg BOOL OnSelchange(); //}}AFX_MSG DECLARE_MESSAGE_MAP() diff --git a/Engine.cpp b/Engine.cpp index 3671b4b..a5ab4a4 100644 --- a/Engine.cpp +++ b/Engine.cpp @@ -1,4 +1,4 @@ -]// Copyleft 2013 Chris Korda +// Copyleft 2013 Chris Korda // This program is free software; you can redistribute it and/or modify it // under the terms of the GNU General Public License as published by the Free // Software Foundation; either version 2 of the License, or any later version. @@ -544,6 +544,17 @@ inline int CEngine::NormParamToEnum(double Val, int Enums) return(CLAMP(ival, 0, Enums - 1)); } +inline int CEngine::NormParamToInt(double Val, int Min, int Max) +{ + int ival = round(Val); + return(CLAMP(ival, Min, Max)); +} + +inline int CEngine::NormParamToMidiVal(double Val) +{ + return(NormParamToInt(Val * MIDI_NOTE_MAX, 0, MIDI_NOTE_MAX)); +} + inline bool CEngine::UpdateTrigger(bool Input, bool& State) { if (Input == State) @@ -563,6 +574,7 @@ inline void CEngine::UpdateMidiTarget(CMidiInst Inst, int Event, int Ctrl, int V for (int iTarg = 0; iTarg < CPart::MIDI_TARGETS; iTarg++) { // for each target const CMidiTarget& targ = part.m_MidiTarget[iTarg]; if (targ.IsMatch(Inst, Event, Ctrl)) { // if target matches input parameters + part.m_MidiShadow[iTarg] = static_cast(Val); double pos = targ.GetNormPos(Val); bool TargetChanged = FALSE; switch (iTarg) { @@ -573,16 +585,16 @@ inline void CEngine::UpdateMidiTarget(CMidiInst Inst, int Event, int Ctrl, int V UMT(part.m_Function, NormParamToEnum(pos, CPart::FUNCTIONS)); break; case CPart::MIDI_TARGET_IN_ZONE_LOW: - UMT(part.m_In.ZoneLow, round(pos * MIDI_NOTE_MAX)); + UMT(part.m_In.ZoneLow, NormParamToMidiVal(pos)); break; case CPart::MIDI_TARGET_IN_ZONE_HIGH: - UMT(part.m_In.ZoneHigh, round(pos * MIDI_NOTE_MAX)); + UMT(part.m_In.ZoneHigh, NormParamToMidiVal(pos)); break; case CPart::MIDI_TARGET_IN_TRANSPOSE: - UMT(part.m_In.Transpose, round((pos * 2 - 1) * OCTAVE)); + UMT(part.m_In.Transpose, NormParamToInt((pos * 2 - 1) * OCTAVE, -MIDI_NOTE_MAX, MIDI_NOTE_MAX)); break; case CPart::MIDI_TARGET_IN_VEL_OFFSET: - UMT(part.m_In.VelOffset, round((pos * 2 - 1) * MIDI_NOTE_MAX)); + UMT(part.m_In.VelOffset, NormParamToInt((pos * 2 - 1) * MIDI_NOTE_MAX, -MIDI_NOTE_MAX, MIDI_NOTE_MAX)); break; case CPart::MIDI_TARGET_IN_NON_DIATONIC: UMT(part.m_In.NonDiatonic, NormParamToEnum(pos, CPart::INPUT::NON_DIATONIC_RULES)); @@ -590,7 +602,7 @@ inline void CEngine::UpdateMidiTarget(CMidiInst Inst, int Event, int Ctrl, int V case CPart::MIDI_TARGET_IN_CC_NOTE: { // convert continuous controller position to input note - CNote note(round(pos * MIDI_NOTE_MAX)); + CNote note(NormParamToMidiVal(pos)); // if non-diatonic rule is disable, apply rule to input note int iRule = part.m_In.NonDiatonic; if (iRule == CPart::INPUT::NDR_DISABLE @@ -615,19 +627,22 @@ inline void CEngine::UpdateMidiTarget(CMidiInst Inst, int Event, int Ctrl, int V } break; case CPart::MIDI_TARGET_IN_CC_NOTE_VEL: - m_PartState[iPart].m_InputCCNoteVel = round(pos * MIDI_NOTE_MAX); + m_PartState[iPart].m_InputCCNoteVel = NormParamToMidiVal(pos); break; case CPart::MIDI_TARGET_OUT_PATCH: - UMT(part.m_Out.Patch, round(pos * MIDI_NOTE_MAX)); + UMT(part.m_Out.Patch, NormParamToMidiVal(pos)); OutputPatch(part.m_Out.Inst, part.m_Out.Patch); break; case CPart::MIDI_TARGET_OUT_VOLUME: - UMT(part.m_Out.Volume, round(pos * MIDI_NOTE_MAX)); + UMT(part.m_Out.Volume, NormParamToMidiVal(pos)); OutputControl(part.m_Out.Inst, VOLUME, part.m_Out.Volume); break; case CPart::MIDI_TARGET_OUT_ANTICIPATION: UMT(part.m_Out.Anticipation, pos); break; + case CPart::MIDI_TARGET_OUT_FIX_HELD_NOTES: + UMT(part.m_Out.FixHeldNotes, pos > 0); + break; case CPart::MIDI_TARGET_LEAD_HARM_INTERVAL: UMT(part.m_Lead.Harm.Interval, round(pos * CDiatonic::DEGREES)); break; @@ -671,7 +686,7 @@ inline void CEngine::UpdateMidiTarget(CMidiInst Inst, int Event, int Ctrl, int V m_PartState[iPart].OnTimeChange(part, m_Patch.m_PPQ); break; case CPart::MIDI_TARGET_BASS_APPROACH_TRIGGER: - if (pos > 0) { + if (pos > 0 && m_Song.GetChordCount()) { // if positive transition and song not empty if (!m_PartState[iPart].m_BassApproachTrigger) { // if not already triggered m_PartState[iPart].m_BassApproachTrigger = TRUE; // set triggered state SetBassApproachTarget(iPart); // compute approach target time and chord @@ -687,16 +702,15 @@ inline void CEngine::UpdateMidiTarget(CMidiInst Inst, int Event, int Ctrl, int V UMT(part.m_Auto.Play, pos > 0); break; case CPart::MIDI_TARGET_AUTO_WINDOW: - UMT(part.m_Auto.Window, round(pos * MIDI_NOTE_MAX)); + UMT(part.m_Auto.Window, NormParamToMidiVal(pos)); break; case CPart::MIDI_TARGET_AUTO_VELOCITY: - UMT(part.m_Auto.Velocity, round(pos * MIDI_NOTE_MAX)); + UMT(part.m_Auto.Velocity, NormParamToMidiVal(pos)); break; default: NODEFAULTCASE; // missing target handler } - if (TargetChanged) - OnMidiTargetChange(iPart, iTarg); + OnMidiTargetChange(iPart, MAKELONG(iTarg, TargetChanged)); } } } @@ -704,6 +718,7 @@ inline void CEngine::UpdateMidiTarget(CMidiInst Inst, int Event, int Ctrl, int V for (int iTarg = 0; iTarg < CPatch::MIDI_TARGETS; iTarg++) { // for each target const CMidiTarget& targ = m_Patch.m_MidiTarget[iTarg]; if (targ.IsMatch(Inst, Event, Ctrl)) { // if target matches input parameters + m_Patch.m_MidiShadow[iTarg] = static_cast(Val); double pos = targ.GetNormPos(Val); bool TargetChanged = FALSE; switch (iTarg) { @@ -729,7 +744,7 @@ inline void CEngine::UpdateMidiTarget(CMidiInst Inst, int Event, int Ctrl, int V UMT(m_Patch.m_Metronome.Enable, pos > 0); break; case CPatch::MIDI_TARGET_METRO_VOLUME: - UMT(m_Patch.m_Metronome.Volume, round(pos * MIDI_NOTE_MAX)); + UMT(m_Patch.m_Metronome.Volume, NormParamToMidiVal(pos)); OutputControl(m_Patch.m_Metronome.Inst, VOLUME, m_Patch.m_Metronome.Volume); break; case CPatch::MIDI_TARGET_PLAY: @@ -763,8 +778,8 @@ inline void CEngine::UpdateMidiTarget(CMidiInst Inst, int Event, int Ctrl, int V default: NODEFAULTCASE; // missing target handler } - if (TargetChanged) - OnMidiTargetChange(-1, iTarg); // negative part index indicates patch + // negative part index indicates patch + OnMidiTargetChange(-1, MAKELONG(iTarg, TargetChanged)); } } } diff --git a/Engine.h b/Engine.h index 14ae746..174b552 100644 --- a/Engine.h +++ b/Engine.h @@ -298,6 +298,8 @@ class CEngine : public CEngineMidi { return(TRUE); } static int NormParamToEnum(double Val, int Enums); + static int NormParamToInt(double Val, int Min, int Max); + static int NormParamToMidiVal(double Val); static bool UpdateTrigger(bool Input, bool& State); void UpdateMidiTarget(CMidiInst Inst, int Event, int Ctrl, int Val); int GetChordIndex(int Tick) const; diff --git a/Globals.h b/Globals.h index 6f1b19b..96e09ca 100644 --- a/Globals.h +++ b/Globals.h @@ -191,8 +191,7 @@ enum { // user windows messages UWM_ENGINEERROR, // wParam: string resource ID, lParam: CEngine* UWM_ENGINENOTIFY, // wParam: notification code, lParam: CEngine* UWM_DEVICENODECHANGE, // wParam: none, lParam: none - UWM_MIDIROWEDIT, // wParam: row index, lParam: control ID - UWM_MIDITARGETCHANGE, // wParam: part index, lParam: target index + UWM_MIDITARGETCHANGE, // wParam: part index, lParam: LSW target index, HSW change flag UWM_MIDIINPUTDATA, // wParam: MIDI message and device index in MSB, lParam: timestamp UWM_MIDIOUTPUTDATA, // wParam: MIDI message and device index in MSB, lParam: timestamp UWM_COLORSTATUSBARPANE, // wParam: HDC, lParam: pane index, return COLORREF diff --git a/GridCtrl.cpp b/GridCtrl.cpp index 4fc5edf..b516919 100644 --- a/GridCtrl.cpp +++ b/GridCtrl.cpp @@ -118,9 +118,9 @@ CWnd *CGridCtrl::CreateEditCtrl(LPCTSTR Text, DWORD dwStyle, const RECT& rect, C return(pEdit); } -void CGridCtrl::OnItemChange(int Row, int Col, LPCTSTR Text) +void CGridCtrl::OnItemChange(LPCTSTR Text) { - SetItemText(Row, Col, Text); + SetItemText(m_EditRow, m_EditCol, Text); } BEGIN_MESSAGE_MAP(CGridCtrl, CDragVirtualListCtrl) @@ -228,6 +228,6 @@ void CGridCtrl::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) LRESULT CGridCtrl::OnTextChange(WPARAM wParam, LPARAM lParam) { - OnItemChange(m_EditRow, m_EditCol, LPCTSTR(wParam)); + OnItemChange(LPCTSTR(wParam)); return(0); } diff --git a/GridCtrl.h b/GridCtrl.h index 00ed56d..a3a4909 100644 --- a/GridCtrl.h +++ b/GridCtrl.h @@ -80,7 +80,7 @@ class CGridCtrl : public CDragVirtualListCtrl // Overrideables virtual CWnd *CreateEditCtrl(LPCTSTR Text, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID); - virtual void OnItemChange(int Row, int Col, LPCTSTR Text); + virtual void OnItemChange(LPCTSTR Text); // Helpers void GotoSubitem(int DeltaRow, int DeltaCol); diff --git a/HelpIDs.h b/HelpIDs.h index 0e532db..d9ae5a4 100644 --- a/HelpIDs.h +++ b/HelpIDs.h @@ -8,24 +8,70 @@ #define IDH_File_menu_Open 8 #define IDH_File_menu_Save 9 #define IDH_File_menu_Save_As 10 -#define IDH_File_menu_Exit 11 -#define IDH_Edit_menu_Undo_Redo 12 -#define IDH_Edit_menu_Clipboard 13 -#define IDH_Edit_menu_Cut 14 -#define IDH_Edit_menu_Copy 15 -#define IDH_Edit_menu_Paste 16 -#define IDH_Edit_menu_Insert 17 -#define IDH_Edit_menu_Delete 18 -#define IDH_Transport_menu_Play_Stop 19 -#define IDH_Transport_menu_Pause 20 -#define IDH_Transport_menu_Rewind 21 -#define IDH_Transport_menu_Repeat 22 -#define IDH_Transport_menu_Record 23 -#define IDH_Transport_menu_Next_Section 24 -#define IDH_View_menu_Toolbar 25 -#define IDH_View_menu_Status_bar 26 -#define IDH_Help_menu_Help_topics 27 -#define IDH_Help_menu_ChordEase_on_the_Web 28 -#define IDH_Help_menu_Check_for_updates 29 -#define IDH_Help_menu_About 30 -#define IDH_Loose_ends_Shortcuts 31 +#define IDH_File_menu_Edit_Text 11 +#define IDH_File_menu_Properties 12 +#define IDH_File_menu_Print_Setup 13 +#define IDH_File_menu_Print_Preview 14 +#define IDH_File_menu_Print 15 +#define IDH_File_menu_Exit 16 +#define IDH_Edit_menu_Undo_Redo 17 +#define IDH_Edit_menu_Clipboard 18 +#define IDH_Edit_menu_Cut 19 +#define IDH_Edit_menu_Copy 20 +#define IDH_Edit_menu_Paste 21 +#define IDH_Edit_menu_Insert 22 +#define IDH_Edit_menu_Delete 23 +#define IDH_Edit_menu_Select_All 24 +#define IDH_Edit_menu_Rename 25 +#define IDH_Edit_menu_Create_Section 26 +#define IDH_Edit_menu_Delete_Section 27 +#define IDH_Edit_menu_Section_Properties 28 +#define IDH_Edit_menu_List_Sections 29 +#define IDH_Edit_menu_Options 30 +#define IDH_Patch_menu_New_Patch 31 +#define IDH_Patch_menu_Open_Patch 32 +#define IDH_Patch_menu_Save_Patch 33 +#define IDH_Patch_menu_Save_Patch_As 34 +#define IDH_Transport_menu_Play_Stop 35 +#define IDH_Transport_menu_Pause 36 +#define IDH_Transport_menu_Rewind 37 +#define IDH_Transport_menu_Repeat 38 +#define IDH_Transport_menu_Record 39 +#define IDH_Transport_menu_Next_Section 40 +#define IDH_Transport_menu_Next_Chord 41 +#define IDH_Transport_menu_Previous_Chord 42 +#define IDH_Transport_menu_Auto_Rewind 43 +#define IDH_MIDI_menu_Devices 44 +#define IDH_MIDI_menu_Input 45 +#define IDH_MIDI_menu_Output 46 +#define IDH_MIDI_menu_Learn 47 +#define IDH_MIDI_menu_Assignments 48 +#define IDH_MIDI_menu_Note_Mappings 49 +#define IDH_MIDI_menu_Chase_Events 50 +#define IDH_MIDI_menu_Reset_All_Targets 51 +#define IDH_MIDI_menu_Panic 52 +#define IDH_View_menu_Toolbar 53 +#define IDH_View_menu_Status_bar 54 +#define IDH_View_menu_Patch_bar 55 +#define IDH_View_menu_Parts_bar 56 +#define IDH_View_menu_Piano 57 +#define IDH_View_menu_Output_Notes 58 +#define IDH_View_menu_Threads 59 +#define IDH_View_menu_Record_Player 60 +#define IDH_Help_menu_Help_topics 61 +#define IDH_Help_menu_ChordEase_on_the_Web 62 +#define IDH_Help_menu_Check_for_updates 63 +#define IDH_Help_menu_Demo 64 +#define IDH_Help_menu_About 65 +#define IDH_Options_Chart_Measures_per_line 66 +#define IDH_Options_Chart_Font 67 +#define IDH_Options_Chart_Default_font 68 +#define IDH_Options_Record_Always_record 69 +#define IDH_Options_Record_Prompt_for_filename 70 +#define IDH_Options_Record_Buffer_size 71 +#define IDH_Options_Record_MIDI_file_resolution 72 +#define IDH_Options_Record_Output_folder 73 +#define IDH_Options_Other_Automatically_check_for_updates 74 +#define IDH_Options_Other_Show_tooltips 75 +#define IDH_Options_Other_Application_data_folder 76 +#define IDH_Loose_ends_Shortcuts 77 diff --git a/HelpResMap.h b/HelpResMap.h index c0b4212..d121a46 100644 --- a/HelpResMap.h +++ b/HelpResMap.h @@ -13,22 +13,65 @@ */ -{ID_APP_ABOUT, IDH_Help_menu_About}, -//{ID_APP_CHECK_FOR_UPDATES, IDH_Options_View_Automatically_check_for_updates}, -{ID_APP_EXIT, IDH_File_menu_Exit}, -{ID_APP_HOME_PAGE, IDH_Help_menu_ChordEase_on_the_Web}, -{ID_EDIT_COPY, IDH_Edit_menu_Copy}, -{ID_EDIT_CUT, IDH_Edit_menu_Cut}, -{ID_EDIT_DELETE, IDH_Edit_menu_Delete}, -{ID_EDIT_INSERT, IDH_Edit_menu_Insert}, -//{ID_EDIT_OPTIONS, IDH_Options_View_Zoom_step_size}, -{ID_EDIT_PASTE, IDH_Edit_menu_Paste}, -{ID_EDIT_REDO, IDH_Edit_menu_Undo_Redo}, -//{ID_EDIT_SELECT_ALL, IDH_Navigation_Selecting_audio}, -{ID_EDIT_UNDO, IDH_Edit_menu_Undo_Redo}, -{ID_FILE_NEW, IDH_File_menu_New}, -{ID_FILE_OPEN, IDH_File_menu_Open}, -{ID_FILE_SAVE, IDH_File_menu_Save}, -{ID_FILE_SAVE_AS, IDH_File_menu_Save_As}, -{ID_HELP_FINDER, IDH_Help_menu_Help_topics}, - +{ID_APP_ABOUT, IDH_Help_menu_About}, +{ID_APP_CHECK_FOR_UPDATES, IDH_Help_menu_Check_for_updates}, +{ID_APP_DEMO, IDH_Help_menu_Demo}, +{ID_APP_EXIT, IDH_File_menu_Exit}, +{ID_APP_HOME_PAGE, IDH_Help_menu_ChordEase_on_the_Web}, +{ID_EDIT_COPY, IDH_Edit_menu_Copy}, +{ID_EDIT_CUT, IDH_Edit_menu_Cut}, +{ID_EDIT_DELETE, IDH_Edit_menu_Delete}, +{ID_EDIT_INSERT, IDH_Edit_menu_Insert}, +{ID_EDIT_OPTIONS, IDH_Edit_menu_Options}, +{ID_EDIT_PASTE, IDH_Edit_menu_Paste}, +{ID_EDIT_REDO, IDH_Edit_menu_Undo_Redo}, +{ID_EDIT_RENAME, IDH_Edit_menu_Rename}, +{ID_EDIT_SECTION_CREATE, IDH_Edit_menu_Create_Section}, +{ID_EDIT_SECTION_DELETE, IDH_Edit_menu_Delete_Section}, +{ID_EDIT_SECTION_LIST, IDH_Edit_menu_List_Sections}, +{ID_EDIT_SECTION_PROPS, IDH_Edit_menu_Section_Properties}, +{ID_EDIT_SELECT_ALL, IDH_Edit_menu_Select_All}, +{ID_EDIT_UNDO, IDH_Edit_menu_Undo_Redo}, +{ID_FILE_EDIT, IDH_File_menu_Edit_Text}, +{ID_FILE_NEW, IDH_File_menu_New}, +{ID_FILE_OPEN, IDH_File_menu_Open}, +{ID_FILE_PRINT, IDH_File_menu_Print}, +{ID_FILE_PRINT_PREVIEW, IDH_File_menu_Print_Preview}, +{ID_FILE_PRINT_SETUP, IDH_File_menu_Print_Setup}, +{ID_FILE_PROPERTIES, IDH_File_menu_Properties}, +{ID_FILE_SAVE, IDH_File_menu_Save}, +{ID_FILE_SAVE_AS, IDH_File_menu_Save_As}, +{ID_HELP_FINDER, IDH_Help_menu_Help_topics}, +{ID_MIDI_ASSIGNMENTS, IDH_MIDI_menu_Assignments}, +{ID_MIDI_CHASE_EVENTS, IDH_MIDI_menu_Chase_Events}, +{ID_MIDI_DEVICES, IDH_MIDI_menu_Devices}, +{ID_MIDI_INPUT, IDH_MIDI_menu_Input}, +{ID_MIDI_LEARN, IDH_MIDI_menu_Learn}, +{ID_MIDI_NOTE_MAPPINGS, IDH_MIDI_menu_Note_Mappings}, +{ID_MIDI_OUTPUT, IDH_MIDI_menu_Output}, +{ID_MIDI_PANIC, IDH_MIDI_menu_Panic}, +{ID_MIDI_RESET_ALL, IDH_MIDI_menu_Reset_All_Targets}, +{ID_PATCH_NEW, IDH_Patch_menu_New_Patch}, +{ID_PATCH_OPEN, IDH_Patch_menu_Open_Patch}, +{ID_PATCH_SAVE, IDH_Patch_menu_Save_Patch}, +{ID_PATCH_SAVE_AS, IDH_Patch_menu_Save_Patch_As}, +{ID_TRANSPORT_AUTO_REWIND, IDH_Transport_menu_Auto_Rewind}, +{ID_TRANSPORT_NEXT_CHORD, IDH_Transport_menu_Next_Chord}, +{ID_TRANSPORT_NEXT_SECTION, IDH_Transport_menu_Next_Section}, +{ID_TRANSPORT_PAUSE, IDH_Transport_menu_Pause}, +{ID_TRANSPORT_PLAY, IDH_Transport_menu_Play_Stop}, +{ID_TRANSPORT_PREV_CHORD, IDH_Transport_menu_Previous_Chord}, +{ID_TRANSPORT_RECORD, IDH_Transport_menu_Record}, +{ID_TRANSPORT_REPEAT, IDH_Transport_menu_Repeat}, +{ID_TRANSPORT_REWIND, IDH_Transport_menu_Rewind}, +{ID_VIEW_OUTPUT_NOTES, IDH_View_menu_Output_Notes}, +{ID_VIEW_PARTS, IDH_View_menu_Parts_bar}, +{ID_VIEW_PATCH, IDH_View_menu_Patch_bar}, +{ID_VIEW_PIANO, IDH_View_menu_Piano}, +{ID_VIEW_RECORD_PLAYER, IDH_View_menu_Record_Player}, +{ID_VIEW_STATUS_BAR, IDH_View_menu_Status_bar}, +{ID_VIEW_THREADS, IDH_View_menu_Threads}, +{ID_VIEW_TOOLBAR, IDH_View_menu_Toolbar}, +{IDD_OPT_CHART, IDH_Options_Chart_Measures_per_line}, +{IDD_OPT_OTHER, IDH_Options_Other_Automatically_check_for_updates}, +{IDD_OPT_RECORD, IDH_Options_Record_Always_record}, diff --git a/ListCtrlExSel.cpp b/ListCtrlExSel.cpp index d70f44e..20a8d17 100644 --- a/ListCtrlExSel.cpp +++ b/ListCtrlExSel.cpp @@ -9,6 +9,7 @@ rev date comments 00 21nov13 initial version 01 13feb14 add SetSelected + 02 15jun14 add tool tip support extended selection list control @@ -168,13 +169,72 @@ void CListCtrlExSel::FixContextMenuPoint(CPoint& Point) } } +INT_PTR CListCtrlExSel::OnToolHitTest(CPoint point, TOOLINFO* pTI) const +{ + LVHITTESTINFO hti; + hti.pt = point; + ListView_SubItemHitTest(m_hWnd, &hti); // use macro to avoid const issue + CRect rClient; + GetClientRect(rClient); + pTI->hwnd = m_hWnd; + // return unique ID for each item, causing each item to get its own tool tip; + // clear ID's most significant bit so ID range is valid even if subitem is -1 + pTI->uId = MAKELONG(hti.iItem, hti.iSubItem) & INT_MAX; + pTI->lpszText = LPSTR_TEXTCALLBACK; // request need text notification + pTI->rect = rClient; + return pTI->uId; +} + +void CListCtrlExSel::EnableToolTips(BOOL bEnable) +{ + SendMessage(LVM_SETTOOLTIPS, 0, 0); // disable list's tooltip control + CListCtrl::EnableToolTips(bEnable); +} + +int CListCtrlExSel::GetToolTipText(const LVHITTESTINFO* pHTI, CString& Text) +{ + return(0); +} + ///////////////////////////////////////////////////////////////////////////// // CListCtrlExSel message map BEGIN_MESSAGE_MAP(CListCtrlExSel, CListCtrl) //{{AFX_MSG_MAP(CListCtrlExSel) //}}AFX_MSG_MAP + ON_NOTIFY_EX(TTN_NEEDTEXTW, 0, OnToolTipNeedText) // Unicode handler + ON_NOTIFY_EX(TTN_NEEDTEXTA, 0, OnToolTipNeedText) // ANSI handler END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CListCtrlExSel message handlers + +BOOL CListCtrlExSel::OnToolTipNeedText(UINT id, NMHDR* pNMHDR, LRESULT* pResult) +{ + CPoint pt(GetMessagePos()); + ScreenToClient(&pt); + LVHITTESTINFO hti; + hti.pt = pt; + ListView_SubItemHitTest(m_hWnd, &hti); + CString sText; + int nID = GetToolTipText(&hti, sText); // get resource ID or string from derived class + if (!nID && sText.IsEmpty()) // if neither resource ID nor string provided + return(FALSE); // no tip + USES_CONVERSION; + if (pNMHDR->code == TTN_NEEDTEXTA) { // if ANSI notification + TOOLTIPTEXTA *pTTT = (TOOLTIPTEXTA *)pNMHDR; + if (nID) { // if resource ID provided, use it + pTTT->lpszText = LPSTR(MAKEINTRESOURCE(nID)); + pTTT->hinst = AfxGetResourceHandle(); + } else // use string + strncpy(pTTT->szText, T2CA(sText), _countof(pTTT->szText)); + } else { // Unicode notification + TOOLTIPTEXTW *pTTT = (TOOLTIPTEXTW *)pNMHDR; + if (nID) { // if resource ID provided, use it + pTTT->lpszText = LPWSTR(MAKEINTRESOURCE(nID)); + pTTT->hinst = AfxGetResourceHandle(); + } else // use string + wcsncpy(pTTT->szText, T2CW(sText), _countof(pTTT->szText)); + } + return(TRUE); +} diff --git a/ListCtrlExSel.h b/ListCtrlExSel.h index de7e957..4cfe0bf 100644 --- a/ListCtrlExSel.h +++ b/ListCtrlExSel.h @@ -9,6 +9,7 @@ rev date comments 00 21nov13 initial version 01 13feb14 add SetSelected + 02 15jun14 add tool tip support extended selection list control @@ -32,7 +33,9 @@ class CListCtrlExSel : public CListCtrl { DECLARE_DYNCREATE(CListCtrlExSel) public: +// Construction CListCtrlExSel(); + virtual ~CListCtrlExSel(); // Types struct COL_INFO { // column info @@ -61,20 +64,24 @@ class CListCtrlExSel : public CListCtrl bool SaveColumnWidths(LPCTSTR Section, LPCTSTR Key); bool LoadColumnWidths(LPCTSTR Section, LPCTSTR Key); void FixContextMenuPoint(CPoint& Point); + void EnableToolTips(BOOL bEnable = TRUE); // Overrides // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CListCtrlExSel) + public: + virtual INT_PTR OnToolHitTest(CPoint point, TOOLINFO* pTI) const; //}}AFX_VIRTUAL -// Implementation - virtual ~CListCtrlExSel(); - // Generated message map functions protected: //{{AFX_MSG(CListCtrlExSel) //}}AFX_MSG + afx_msg BOOL OnToolTipNeedText(UINT id, NMHDR* pNMHDR, LRESULT* pResult); DECLARE_MESSAGE_MAP() + +// Overrideables + virtual int GetToolTipText(const LVHITTESTINFO* pHTI, CString& Text); }; ///////////////////////////////////////////////////////////////////////////// diff --git a/MainFrm.cpp b/MainFrm.cpp index 9ec6c2d..55cf5b0 100644 --- a/MainFrm.cpp +++ b/MainFrm.cpp @@ -11,6 +11,7 @@ 01 22apr14 add piano dialog 02 28apr14 in OnEngineNotify, add device state change handler 03 30apr14 add OnDropFiles + 04 04jun14 in CreateEngine, fix opening patch via command line ChordEase main frame @@ -300,17 +301,22 @@ bool CMainFrame::CreateEngine() return(FALSE); CString ChordDictPath(theApp.MakeDataFolderPath(CHORD_DICTIONARY_FILE_NAME, TRUE)); gEngine.ReadChordDictionary(ChordDictPath); // read chord dictionary + bool CmdLinePatchOpen; if (!theApp.m_PatchPath.IsEmpty()) // if patch specified on command line - OpenPatch(theApp.m_PatchPath); - else // patch not specified + CmdLinePatchOpen = OpenPatch(theApp.m_PatchPath); // open command line patch + else { // patch not specified + CmdLinePatchOpen = FALSE; m_PatchDoc.New(); // initialize patch document - CPatch patch; - CString PatchPath(theApp.MakeDataFolderPath(PATCH_INI_FILE_NAME, TRUE)); - if (PathFileExists(PatchPath)) { // if patch file exists - if (!patch.Read(PatchPath)) // read patch file - patch.Reset(); // read failed; avoid partial data } - SetPatch(patch, TRUE); // update ports for current devices + if (!CmdLinePatchOpen) { // if patch wasn't opened via command line + CPatch patch; + CString PatchPath(theApp.MakeDataFolderPath(PATCH_INI_FILE_NAME, TRUE)); + if (PathFileExists(PatchPath)) { // if patch file exists + if (!patch.Read(PatchPath)) // read patch file + patch.Reset(); // read failed; avoid partial data + } + SetPatch(patch, TRUE); // update ports for current devices + } return(TRUE); } @@ -456,6 +462,24 @@ void CMainFrame::UpdateHookMidiOutput() || m_OutputNotesBar.IsWindowVisible() != 0); } +int CMainFrame::GetCtrlMidiTarget(CWnd *pWnd, int& PartIdx) const +{ + CWnd *pParent = pWnd->GetParent(); + ASSERT(pParent != NULL); + if (pParent->IsKindOf(RUNTIME_CLASS(CComboBox))) // if parent is combo box + pWnd = pParent; + UINT nID = pWnd->GetDlgCtrlID(); + int iTarget; + if (m_PatchBar.IsChild(pWnd)) { + iTarget = CPatchMidiTargetDlg::FindTargetByCtrlID(nID); + PartIdx = -1; + } else { + iTarget = CPartMidiTargetDlg::FindTargetByCtrlID(nID); + PartIdx = m_PartsBar.GetCurPart(); + } + return(iTarget); +} + void CMainFrame::ApplyOptions(const COptionsInfo& PrevOpts, bool ForceUpdate) { // if chart measures per line or font changed @@ -466,6 +490,15 @@ void CMainFrame::ApplyOptions(const COptionsInfo& PrevOpts, bool ForceUpdate) // if always record option changed if (m_Options.m_Record.AlwaysRecord != PrevOpts.m_Record.AlwaysRecord) gEngine.Record(m_Options.m_Record.AlwaysRecord != 0); // update record state + if (m_Options.m_Other.ShowTooltips != PrevOpts.m_Other.ShowTooltips || ForceUpdate) + EnableAppToolTips(m_Options.m_Other.ShowTooltips != 0); +} + +void CMainFrame::EnableAppToolTips(bool Enable) +{ + m_PatchBar.GetMidiTargetDlg().EnableToolTips(Enable); + m_PartsBar.GetMidiTargetDlg().EnableToolTips(Enable); + m_PartsBar.GetListCtrl().EnableToolTips(Enable); } bool CMainFrame::CheckForUpdates(bool Explicit) @@ -733,6 +766,8 @@ CString CMainFrame::GetUndoTitle(const CUndoState& State) switch (State.GetCode()) { case UCODE_BASE_PATCH: s = m_PatchBar.GetControlCaption(State.GetCtrlID()); + if (s.IsEmpty()) + s.LoadString(State.GetCtrlID()); // assume ID is list column string break; case UCODE_PART: s = m_PartsBar.GetPageView()->GetControlCaption(State.GetCtrlID()); @@ -1137,39 +1172,50 @@ LRESULT CMainFrame::OnMidiTargetChange(WPARAM wParam, LPARAM lParam) #include "PartMidiTargetDef.h" // map part MIDI targets to part pages }; static const int PatchMidiTargetPage[CPatch::MIDI_TARGETS] = { - #define PATCHMIDITARGETDEF(name, page) CPatchBar::PAGE_##page, + #define PATCHMIDITARGETDEF(name, page, tag) CPatchBar::PAGE_##page, #include "PatchMidiTargetDef.h" // map patch MIDI targets to patch pages }; -// _tprintf(_T("CMainFrame::OnMidiTargetChange %d %d\n"), wParam, lParam); int iPart = static_cast(wParam); - int iTarget = static_cast(lParam); + int iTarget = LOWORD(lParam); + int bChanged = HIWORD(lParam); +// _tprintf(_T("CMainFrame::OnMidiTargetChange %d %d (%d)\n"), iPart, iTarget, bChanged); if (iPart >= 0) { // if part target - int iPage = PartMidiTargetPage[iTarget]; - CPart part(gEngine.GetPart(iPart)); - if (iPage >= 0) { // if valid part page - if (m_MidiChaseEvents) { // if chasing MIDI events - NotifyEdit(0, UCODE_MIDI_CHASE, // save part/page indices + if (iPart == m_PartsBar.GetCurPart() // if current part + && m_PartsBar.GetMidiTargetDlg().IsWindowVisible()) + m_PartsBar.GetMidiTargetDlg().UpdateShadowVal(iTarget); + } else { // patch target + if (m_PatchBar.GetMidiTargetDlg().IsWindowVisible()) + m_PatchBar.GetMidiTargetDlg().UpdateShadowVal(iTarget); + } + if (bChanged) { // if target parameter changed + if (iPart >= 0) { // if part target + int iPage = PartMidiTargetPage[iTarget]; + CPart part(gEngine.GetPart(iPart)); + if (iPage >= 0) { // if valid part page + if (m_MidiChaseEvents && !m_MidiLearn) { // if chasing MIDI events + NotifyEdit(0, UCODE_MIDI_CHASE, // save part/page indices + CUndoable::UE_COALESCE | CUndoable::UE_INSIGNIFICANT); + if (iPart != m_PartsBar.GetCurPart()) + m_PartsBar.SetCurPart(iPart); // chase to target's part + m_PartsBar.SetCurPage(iPage); // chase to target's page + } + if (iPart == m_PartsBar.GetCurPart()) // if target part is showing + m_PartsBar.GetPageView()->UpdatePage(iPage, part); // update page + } else // assume parts list + m_PartsBar.GetListView()->SetSubitems(iPart, part); // update parts list + } else { // patch target + int iPage = PatchMidiTargetPage[iTarget]; + CBasePatch patch; + gEngine.GetBasePatch(patch); + if (m_MidiChaseEvents && !m_MidiLearn) { // if chasing MIDI events + NotifyEdit(0, UCODE_MIDI_CHASE, // save part/page indices CUndoable::UE_COALESCE | CUndoable::UE_INSIGNIFICANT); - if (iPart != m_PartsBar.GetCurPart()) - m_PartsBar.SetCurPart(iPart); // chase to target's part - m_PartsBar.SetCurPage(iPage); // chase to target's page + m_PatchBar.SetCurPage(iPage); // chase to target's page } - if (iPart == m_PartsBar.GetCurPart()) // if target part is showing - m_PartsBar.GetPageView()->UpdatePage(iPage, part); // update page - } else // assume parts list - m_PartsBar.GetListView()->SetSubitems(iPart, part); // update parts list - } else { // patch target - int iPage = PatchMidiTargetPage[iTarget]; - CBasePatch patch; - gEngine.GetBasePatch(patch); - if (m_MidiChaseEvents) { // if chasing MIDI events - NotifyEdit(0, UCODE_MIDI_CHASE, // save part/page indices - CUndoable::UE_COALESCE | CUndoable::UE_INSIGNIFICANT); - m_PatchBar.SetCurPage(iPage); // chase to target's page + m_PatchBar.UpdatePage(iPage, patch); // update page + if (iTarget == CPatch::MIDI_TARGET_TRANSPOSE) // if target is transpose + m_View->UpdateChart(); // update chart view } - m_PatchBar.UpdatePage(iPage, patch); // update page - if (iTarget == CPatch::MIDI_TARGET_TRANSPOSE) // if target is transpose - m_View->UpdateChart(); // update chart view } return(0); } @@ -1186,20 +1232,23 @@ LRESULT CMainFrame::OnMidiInputData(WPARAM wParam, LPARAM lParam) if (pFocusWnd != NULL) { int iTarget = -1; int iPart = -1; - CDialog *pMidiPage = m_PatchBar.GetPage(CPatchBar::PAGE_MidiTarget); - if (pMidiPage->IsChild(pFocusWnd)) { // if patch MIDI control has focus - CMidiTargetRowDlg *pRowDlg = DYNAMIC_DOWNCAST(CMidiTargetRowDlg, pFocusWnd->GetParent()); - if (pRowDlg != NULL) // if control's parent is MIDI row dialog - iTarget = pRowDlg->GetRowIndex(); + CMidiTargetDlg& PatchMTDlg = m_PatchBar.GetMidiTargetDlg(); + // if child of patch MIDI target dialog has focus + if (PatchMTDlg.IsChild(pFocusWnd)) { + iTarget = PatchMTDlg.GetCurSel(); } else { - pMidiPage = m_PartsBar.GetPageView()->GetPage(CPartPageView::PAGE_MidiTarget); - if (pMidiPage->IsChild(pFocusWnd)) { // if part MIDI control has focus + CMidiTargetDlg& PartMTDlg = m_PartsBar.GetMidiTargetDlg(); + // if child of part MIDI target dialog has focus + if (PartMTDlg.IsChild(pFocusWnd)) { iPart = m_PartsBar.GetCurPart(); - if (iPart >= 0) { // if part selected; should be true - CMidiTargetRowDlg *pRowDlg = DYNAMIC_DOWNCAST(CMidiTargetRowDlg, pFocusWnd->GetParent()); - if (pRowDlg != NULL) // if control's parent is MIDI row dialog - iTarget = pRowDlg->GetRowIndex(); - } + if (iPart >= 0) // if part selected; should be true + iTarget = PartMTDlg.GetCurSel(); + } else { // try ordinary patch/part pages + iTarget = GetCtrlMidiTarget(pFocusWnd, iPart); + if (iPart < 0) // if patch target + PatchMTDlg.SetCurSel(iTarget); + else // part target + PartMTDlg.SetCurSel(iTarget); } } if (iTarget >= 0) { // if valid MIDI target @@ -1236,6 +1285,18 @@ LRESULT CMainFrame::OnMidiInputData(WPARAM wParam, LPARAM lParam) SetBasePatch(patch); } } + } else { // MIDI target not found + UINT nID = pFocusWnd->GetDlgCtrlID(); + if (nID == IDC_PART_IN_PORT || nID == IDC_PART_IN_CHAN) { + iPart = m_PartsBar.GetCurPart(); + CPart part(gEngine.GetPart(iPart)); + CMidiInst inst(CMidiEventDlg::GetDevice(wParam), MIDI_CHAN(wParam)); + if (inst != part.m_In.Inst) { + NotifyEdit(nID, UCODE_PART); + part.m_In.Inst = inst; + SetPart(iPart, part); + } + } } } } @@ -1620,8 +1681,23 @@ void CMainFrame::OnMidiLearn() { m_MidiLearn ^= 1; UpdateHookMidiInput(); - m_PatchBar.GetPage(CPatchBar::PAGE_MidiTarget)->SendMessage(WM_COMMAND, ID_MIDI_LEARN); - m_PartsBar.GetPageView()->GetPage(CPartPageView::PAGE_MidiTarget)->SendMessage(WM_COMMAND, ID_MIDI_LEARN); + m_PatchBar.GetMidiTargetDlg().OnLearnChange(); + m_PartsBar.GetMidiTargetDlg().OnLearnChange(); + CWnd *pFocusWnd = GetFocus(); + if (pFocusWnd != NULL) { + CPatchPageDlg *pPage = NULL; + if (m_PatchBar.IsChild(pFocusWnd)) { // if patch bar control has focus + int iPage = m_PatchBar.GetCurPage(); + if (iPage >= 0) + pPage = DYNAMIC_DOWNCAST(CPatchPageDlg, m_PatchBar.GetPage(iPage)); + } else if (m_PartsBar.GetPageView()->IsChild(pFocusWnd)) { // if parts bar control has focus + int iPage = m_PartsBar.GetPageView()->GetCurPage(); + if (iPage >= 0) + pPage = DYNAMIC_DOWNCAST(CPatchPageDlg, m_PartsBar.GetPageView()->GetPage(iPage)); + } + if (pPage != NULL) + pPage->UpdateMidiLearn(); + } } void CMainFrame::OnUpdateMidiLearn(CCmdUI* pCmdUI) diff --git a/MainFrm.h b/MainFrm.h index 07101c8..8b0525b 100644 --- a/MainFrm.h +++ b/MainFrm.h @@ -84,6 +84,8 @@ class CMainFrame : public CFrameWnd, public CUndoable CString GetRecordFilePath() const; void SetRecordFilePath(const CString& Path); bool IsMidiLearn() const; + bool IsMidiChase() const; + int GetCtrlMidiTarget(CWnd *pWnd, int& PartIdx) const; // Operations public: @@ -340,6 +342,7 @@ class CMainFrame : public CFrameWnd, public CUndoable void UpdateHookMidiInput(); void UpdateHookMidiOutput(); void DockControlBarNextTo(CControlBar* pBar, CControlBar* pTargetBar); + void EnableAppToolTips(bool Enable); static UINT CheckForUpdatesThreadFunc(LPVOID Param); // Undo state values @@ -457,6 +460,11 @@ inline bool CMainFrame::IsMidiLearn() const return(m_MidiLearn); } +inline bool CMainFrame::IsMidiChase() const +{ + return(m_MidiChaseEvents); +} + ///////////////////////////////////////////////////////////////////////////// //{{AFX_INSERT_LOCATION}} diff --git a/MidiEventDlg.h b/MidiEventDlg.h index 78cf4ed..cb515e8 100644 --- a/MidiEventDlg.h +++ b/MidiEventDlg.h @@ -59,6 +59,7 @@ class CMidiEventDlg : public CChildDlg void ResizeFilters(int ItemIdx = -1, int ItemWidth = 0); void UpdateDevices(); void Pause(bool Enable); + void EnableToolTips(BOOL bEnable = TRUE); static WPARAM MakeParam(DWORD Device, W64UINT MidiMsg); static int GetDevice(WPARAM wParam); @@ -169,6 +170,11 @@ inline void CMidiEventDlg::SetShowCtrlrNames(bool Enable) m_ShowCtrlrNames = Enable; } +inline void CMidiEventDlg::EnableToolTips(BOOL bEnable) +{ + m_List.EnableToolTips(bEnable); +} + inline WPARAM CMidiEventDlg::MakeParam(DWORD Device, W64UINT MidiMsg) { ASSERT(Device < UCHAR_MAX); // device index limited to range of byte diff --git a/MidiTargetColDef.h b/MidiTargetColDef.h index 07d79a9..0149945 100644 --- a/MidiTargetColDef.h +++ b/MidiTargetColDef.h @@ -13,13 +13,15 @@ */ -MIDITARGETCOLDEF(NAME) -MIDITARGETCOLDEF(PORT) -MIDITARGETCOLDEF(CHAN) -MIDITARGETCOLDEF(EVENT) -MIDITARGETCOLDEF(CONTROL) -MIDITARGETCOLDEF(RANGE_START) -MIDITARGETCOLDEF(RANGE_END) +// name align width +MIDITARGETCOLDEF(NAME, LVCFMT_LEFT, 100) +MIDITARGETCOLDEF(PORT, LVCFMT_LEFT, 50) +MIDITARGETCOLDEF(CHAN, LVCFMT_LEFT, 50) +MIDITARGETCOLDEF(EVENT, LVCFMT_LEFT, 60) +MIDITARGETCOLDEF(CONTROL, LVCFMT_LEFT, 50) +MIDITARGETCOLDEF(RANGE_START, LVCFMT_LEFT, 50) +MIDITARGETCOLDEF(RANGE_END, LVCFMT_LEFT, 50) +MIDITARGETCOLDEF(VALUE, LVCFMT_LEFT, 50) #undef MIDITARGETCOLDEF diff --git a/MidiTargetDlg.cpp b/MidiTargetDlg.cpp index b0c5229..9d15ff0 100644 --- a/MidiTargetDlg.cpp +++ b/MidiTargetDlg.cpp @@ -9,6 +9,7 @@ rev date comments 00 19nov13 initial version 01 23apr14 define columns via macro + 02 12jun14 refactor to use grid control instead of row view MIDI target dialog @@ -21,6 +22,7 @@ #include "ChordEase.h" #include "MainFrm.h" #include "MidiTargetDlg.h" +#include "PopupCombo.h" #ifdef _DEBUG #define new DEBUG_NEW @@ -31,47 +33,167 @@ static char THIS_FILE[] = __FILE__; ///////////////////////////////////////////////////////////////////////////// // CMidiTargetDlg dialog -IMPLEMENT_DYNCREATE(CMidiRowView, CRowView) IMPLEMENT_DYNAMIC(CMidiTargetDlg, CChildDlg); -const CRowView::COLINFO CMidiTargetDlg::m_ColInfo[COLUMNS] = { - #define MIDITARGETCOLDEF(name) {IDS_MIDI_TARG_ROW_##name, IDC_MIDI_TARG_COL_##name}, +const CGridCtrl::COL_INFO CMidiTargetDlg::m_ColInfo[COLUMNS] = { + #define MIDITARGETCOLDEF(name, align, width) {IDS_MIDI_TARG_COL_##name, align, width}, #include "MidiTargetColDef.h" }; -CMidiTargetDlg::CMidiTargetDlg(int ViewID, CWnd* pParent /*=NULL*/) +const int CMidiTargetDlg::m_ColToolTip[COLUMNS] = { + #define MIDITARGETCOLDEF(name, align, width) IDC_MIDI_TARG_##name, + #include "MidiTargetColDef.h" +}; + +CMidiTargetDlg::CMidiTargetDlg(CWnd* pParent /*=NULL*/) : CChildDlg(IDD, pParent) { //{{AFX_DATA_INIT(CMidiTargetDlg) //}}AFX_DATA_INIT - m_ViewID = ViewID; - m_View = NULL; - m_CurSel = -1; + m_List.m_pParent = this; + m_List.SetDragEnable(FALSE); + m_TargetNameID = NULL; + m_ListItemHeight = 0; } -CRowDlg *CMidiRowView::CreateRow(int Idx) +void CMidiTargetDlg::SetTargets(const int *TargetNameID, int Targets) { - CMidiTargetRowDlg *pDlg = new CMidiTargetRowDlg; - pDlg->Create(IDD_MIDI_TARGET_ROW); - return(pDlg); + m_Target.SetSize(Targets); + m_TargetNameID = TargetNameID; + m_List.SetItemCountEx(Targets); } -void CMidiRowView::UpdateRow(int Idx) +void CMidiTargetDlg::SetCurSel(int RowIdx) { - ASSERT(0); // update not supported, so shouldn't get here + if (RowIdx >= 0) { // if valid row + m_List.Select(RowIdx); + m_List.EnsureVisible(RowIdx, FALSE); + } else + m_List.Deselect(); } -void CMidiTargetDlg::SetCurSel(int RowIdx) +void CMidiTargetDlg::OnLearnChange() +{ + m_List.Invalidate(); +} + +BOOL CMidiTargetDlg::CTargetGridCtrl::PreCreateWindow(CREATESTRUCT& cs) +{ + cs.dwExStyle |= WS_EX_CLIENTEDGE; // border with sunken edge + return CGridCtrl::PreCreateWindow(cs); +} + +CWnd *CMidiTargetDlg::CTargetGridCtrl::CreateEditCtrl(LPCTSTR Text, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID) +{ + switch (m_EditCol) { + case CMidiTargetDlg::COL_CHAN: + case CMidiTargetDlg::COL_EVENT: + { + int DropHeight = 100; + const CMidiTarget& targ = m_pParent->GetTarget(m_EditRow); + CPopupCombo *pCombo = CPopupCombo::Factory(0, rect, pParentWnd, nID, DropHeight); + if (pCombo != NULL) { + switch (m_EditCol) { + case CMidiTargetDlg::COL_CHAN: + CChordEaseApp::InitNumericCombo(*pCombo, + CIntRange(1, MIDI_CHANNELS), targ.m_Inst.Chan + 1); + break; + case CMidiTargetDlg::COL_EVENT: + { + for (int iType = 0; iType < CMidiTarget::EVENT_TYPES; iType++) + pCombo->AddString(CMidiTarget::GetEventTypeName(iType)); + pCombo->SetCurSel(targ.m_Event); + } + break; + default: + NODEFAULTCASE; + } + pCombo->ShowDropDown(); + } + return pCombo; + } + case COL_VALUE: + return NULL; + } + return CGridCtrl::CreateEditCtrl(Text, dwStyle, rect, pParentWnd, nID); +} + +void CMidiTargetDlg::CTargetGridCtrl::OnItemChange(LPCTSTR Text) { - if (RowIdx == m_CurSel) // if row unchanged - return; // nothing to do - if (theApp.GetMain()->IsMidiLearn()) { // if learning MIDI assignments - if (m_CurSel >= 0) - GetRow(m_CurSel)->UpdateSelection(); - if (RowIdx >= 0) - GetRow(RowIdx)->UpdateSelection(); + CMidiTarget& targ = m_pParent->GetTarget(m_EditRow); + switch (m_EditCol) { + case COL_PORT: + { + int iPort = _ttoi(Text); + targ.m_Inst.Port = max(iPort, 0); + } + break; + case COL_CHAN: + { + int iChan = _ttoi(Text) - 1; + iChan = CLAMP(iChan, 0, MIDI_CHANNELS - 1); + if (iChan == targ.m_Inst.Chan) // if value unchanged + return; // don't update target + targ.m_Inst.Chan = CLAMP(iChan, 0, MIDI_CHANNELS - 1); + } + break; + case COL_EVENT: + { + CPopupCombo *pCombo = STATIC_DOWNCAST(CPopupCombo, m_EditCtrl); + int iType = pCombo->FindString(0, Text); + if (iType >= 0) { // if text found in combo + if (iType == targ.m_Event) // if value unchanged + return; // don't update target + ASSERT(iType < CMidiTarget::EVENT_TYPES); + targ.m_Event = iType; + } + } + break; + case COL_CONTROL: + { + int ctrl = _ttoi(Text); + targ.m_Control = CLAMP(ctrl, 0, MIDI_NOTE_MAX); + } + break; + case COL_RANGE_START: + targ.m_RangeStart = static_cast(_tstof(Text)); + break; + case COL_RANGE_END: + targ.m_RangeEnd = static_cast(_tstof(Text)); + break; + default: + NODEFAULTCASE; } - m_CurSel = RowIdx; + m_pParent->OnTargetChange(m_EditRow, m_EditCol); // update target +} + +int CMidiTargetDlg::CTargetGridCtrl::GetToolTipText(const LVHITTESTINFO* pHTI, CString& Text) +{ + return(m_pParent->GetToolTipText(pHTI, Text)); +} + +void CMidiTargetDlg::OnTargetChange(int RowIdx, int ColIdx) +{ +} + +int CMidiTargetDlg::GetShadowValue(int RowIdx) +{ + return(0); +} + +int CMidiTargetDlg::GetToolTipText(const LVHITTESTINFO* pHTI, CString& Text) +{ + ASSERT(pHTI->iSubItem < COLUMNS); + if (pHTI->iSubItem < 0) + return(IDC_MIDI_TARGET_LIST); + return(m_ColToolTip[pHTI->iSubItem]); +} + +void CMidiTargetDlg::UpdateShadowVal(int RowIdx) +{ + CRect rItem; + m_List.GetSubItemRect(RowIdx, COL_VALUE, LVIR_LABEL, rItem); + m_List.InvalidateRect(rItem); } void CMidiTargetDlg::DoDataExchange(CDataExchange* pDX) @@ -81,10 +203,6 @@ void CMidiTargetDlg::DoDataExchange(CDataExchange* pDX) //}}AFX_DATA_MAP } -void CMidiTargetDlg::GetTargetToolTip(int RowIdx, int id, NMHDR* pNMHDR) -{ -} - ///////////////////////////////////////////////////////////////////////////// // CMidiTargetDlg message map @@ -92,7 +210,13 @@ BEGIN_MESSAGE_MAP(CMidiTargetDlg, CChildDlg) //{{AFX_MSG_MAP(CMidiTargetDlg) ON_WM_SIZE() ON_WM_CREATE() + ON_NOTIFY(LVN_GETDISPINFO, IDC_MIDI_TARGET_LIST, OnGetdispinfo) + ON_WM_MEASUREITEM() + ON_NOTIFY(NM_CUSTOMDRAW, IDC_MIDI_TARGET_LIST, OnCustomDraw) + ON_WM_CONTEXTMENU() ON_COMMAND(ID_MIDI_LEARN, OnMidiLearn) + ON_UPDATE_COMMAND_UI(ID_MIDI_LEARN, OnUpdateMidiLearn) + ON_COMMAND(ID_MIDI_TARGET_RESET, OnMidiTargetReset) //}}AFX_MSG_MAP END_MESSAGE_MAP() @@ -103,22 +227,30 @@ int CMidiTargetDlg::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CChildDlg::OnCreate(lpCreateStruct) == -1) return -1; - CRuntimeClass *pFactory = RUNTIME_CLASS(CMidiRowView); - m_View = DYNAMIC_DOWNCAST(CMidiRowView, pFactory->CreateObject()); - DWORD dwStyle = AFX_WS_DEFAULT_VIEW | WS_TABSTOP; - DWORD dwExStyle = WS_EX_CONTROLPARENT; // append view to parent's tab order - CRect r(0, 0, 0, 0); - if (!m_View->CreateEx(dwExStyle, NULL, NULL, dwStyle, r, this, m_ViewID)) + DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_TABSTOP | LVS_REPORT + | LVS_SINGLESEL | LVS_NOSORTHEADER | LVS_OWNERDATA; + if (!m_List.Create(dwStyle, CRect(0, 0, 0, 0), this, IDC_MIDI_TARGET_LIST)) return -1; + m_List.SendMessage(WM_SETFONT, WPARAM(GetStockObject(DEFAULT_GUI_FONT))); + // force list to send appropriate notifications for our character size + ListView_SetUnicodeFormat(m_List.m_hWnd, sizeof(TCHAR) == sizeof(WCHAR)); + // increase item height to accomodate popup edit controls; + // temporarily enable checkbox style to get appropriate height + DWORD dwListExStyle = LVS_EX_GRIDLINES | LVS_EX_FULLROWSELECT; + m_List.SetExtendedStyle(dwListExStyle | LVS_EX_CHECKBOXES); // add checkbox style + m_List.ModifyStyle(0, LVS_OWNERDRAWFIXED); // add owner draw style + m_List.MoveWindow(0, 0, 0, 1); // force list to send WM_MEASUREITEM + m_List.SetExtendedStyle(dwListExStyle); // remove checkbox style + m_List.MoveWindow(0, 0, 0, 0); // force list to send WM_MEASUREITEM again + m_List.ModifyStyle(LVS_OWNERDRAWFIXED, 0); // remove owner draw style return 0; } BOOL CMidiTargetDlg::OnInitDialog() { CChildDlg::OnInitDialog(); + m_List.CreateColumns(m_ColInfo, COLUMNS); - m_View->CreateCols(COLUMNS, m_ColInfo); - return TRUE; // return TRUE unless you set the focus to a control // EXCEPTION: OCX Property Pages should return FALSE } @@ -126,12 +258,105 @@ BOOL CMidiTargetDlg::OnInitDialog() void CMidiTargetDlg::OnSize(UINT nType, int cx, int cy) { CChildDlg::OnSize(nType, cx, cy); - if (m_View != NULL) - m_View->MoveWindow(0, 0, cx, cy); + m_List.MoveWindow(0, 0, cx, cy); +} + +void CMidiTargetDlg::OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct) +{ + if (nIDCtl == IDC_MIDI_TARGET_LIST) { + if (!m_ListItemHeight) // if retrieving item height + m_ListItemHeight = lpMeasureItemStruct->itemHeight; // save height + else // reapplying saved item height + lpMeasureItemStruct->itemHeight = m_ListItemHeight; // set height + } +} + +void CMidiTargetDlg::OnGetdispinfo(NMHDR* pNMHDR, LRESULT* pResult) +{ + LV_DISPINFO* pDispInfo = (LV_DISPINFO*)pNMHDR; + LVITEM& item = pDispInfo->item; + int iItem = item.iItem; + if (item.mask & LVIF_TEXT) { + switch (item.iSubItem) { + case COL_NAME: + _tcsncpy(item.pszText, LDS(m_TargetNameID[iItem]), item.cchTextMax); + break; + case COL_PORT: + _stprintf(item.pszText, _T("%d"), m_Target[iItem].m_Inst.Port); + break; + case COL_CHAN: + _stprintf(item.pszText, _T("%d"), m_Target[iItem].m_Inst.Chan + 1); + break; + case COL_EVENT: + _tcsncpy(item.pszText, CMidiTarget::GetEventTypeName(m_Target[iItem].m_Event), item.cchTextMax); + break; + case COL_CONTROL: + _stprintf(item.pszText, _T("%d"), m_Target[iItem].m_Control); + break; + case COL_RANGE_START: + _stprintf(item.pszText, _T("%g"), m_Target[iItem].m_RangeStart); + break; + case COL_RANGE_END: + _stprintf(item.pszText, _T("%g"), m_Target[iItem].m_RangeEnd); + break; + case COL_VALUE: + _stprintf(item.pszText, _T("%d"), GetShadowValue(iItem)); + break; + default: + NODEFAULTCASE; + } + } +} + +void CMidiTargetDlg::OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult) +{ + LPNMLVCUSTOMDRAW plvcd = (LPNMLVCUSTOMDRAW)pNMHDR; + switch (plvcd->nmcd.dwDrawStage) { + case CDDS_PREPAINT : + if (theApp.GetMain()->IsMidiLearn()) // if learning MIDI + *pResult = CDRF_NOTIFYITEMDRAW; // request per-item notifications + else + *pResult = 0; + break; + case CDDS_ITEMPREPAINT: + if (plvcd->nmcd.uItemState & CDIS_SELECTED) { // if item selected + plvcd->clrTextBk = MIDI_LEARN_COLOR; // customize item background color + // trick system into using our custom color instead of selection color + plvcd->nmcd.uItemState &= ~CDIS_SELECTED; // clear item's selected flag + } + *pResult = 0; + break; + default: + *pResult = 0; + } +} + +void CMidiTargetDlg::OnContextMenu(CWnd* pWnd, CPoint point) +{ + m_List.FixContextMenuPoint(point); + CMenu menu; + menu.LoadMenu(IDM_MIDI_TARGET_CTX); + CMenu *mp = menu.GetSubMenu(0); + CPersistDlg::UpdateMenu(this, mp); + mp->TrackPopupMenu(0, point.x, point.y, this); } void CMidiTargetDlg::OnMidiLearn() { - if (m_CurSel >= 0) - GetRow(m_CurSel)->UpdateSelection(); + theApp.GetMain()->SendMessage(WM_COMMAND, ID_MIDI_LEARN); // relay to main frame +} + +void CMidiTargetDlg::OnUpdateMidiLearn(CCmdUI *pCmdUI) +{ + pCmdUI->SetCheck(theApp.GetMain()->IsMidiLearn()); +} + +void CMidiTargetDlg::OnMidiTargetReset() +{ + int iSel = m_List.GetSelection(); + if (iSel >= 0) { + m_Target[iSel].Reset(); + OnTargetChange(iSel, 0); + m_List.Invalidate(); + } } diff --git a/MidiTargetDlg.h b/MidiTargetDlg.h index c544f0e..7f043b1 100644 --- a/MidiTargetDlg.h +++ b/MidiTargetDlg.h @@ -9,6 +9,7 @@ rev date comments 00 19nov13 initial version 01 23apr14 define columns via macro + 02 12jun14 refactor to use grid control instead of row view MIDI target dialog @@ -27,24 +28,38 @@ // CMidiTargetDlg dialog #include "ChildDlg.h" -#include "MidiTargetRowDlg.h" - -class CMidiRowView; +#include "GridCtrl.h" class CMidiTargetDlg : public CChildDlg { DECLARE_DYNAMIC(CMidiTargetDlg); // Construction public: - CMidiTargetDlg(int ViewID, CWnd* pParent = NULL); + CMidiTargetDlg(CWnd* pParent = NULL); + +// Constants + enum { + #define MIDITARGETCOLDEF(name, align, width) COL_##name, + #include "MidiTargetColDef.h" + COLUMNS + }; // Attributes + void SetTargets(const int *TargetNameID, int Targets); static UINT GetTemplateID(); int GetCurSel() const; void SetCurSel(int RowIdx); + CMidiTarget& GetTarget(int RowIdx); + +// Operations + void OnLearnChange(); + void UpdateShadowVal(int RowIdx); + void EnableToolTips(BOOL bEnable = TRUE); // Overrideables - virtual void GetTargetToolTip(int RowIdx, int id, NMHDR* pNMHDR); + virtual void OnTargetChange(int RowIdx, int ColIdx); + virtual int GetShadowValue(int RowIdx); + virtual int GetToolTipText(const LVHITTESTINFO* pHTI, CString& Text); // Overrides // ClassWizard generated virtual function overrides @@ -65,35 +80,40 @@ class CMidiTargetDlg : public CChildDlg virtual BOOL OnInitDialog(); afx_msg void OnSize(UINT nType, int cx, int cy); afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); + afx_msg void OnGetdispinfo(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct); + afx_msg void OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnContextMenu(CWnd* pWnd, CPoint point); afx_msg void OnMidiLearn(); + afx_msg void OnUpdateMidiLearn(CCmdUI *pCmdUI); + afx_msg void OnMidiTargetReset(); //}}AFX_MSG DECLARE_MESSAGE_MAP() // Types + class CTargetGridCtrl : public CGridCtrl { + public: + virtual BOOL PreCreateWindow(CREATESTRUCT& cs); + virtual CWnd *CreateEditCtrl(LPCTSTR Text, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID); + virtual void OnItemChange(LPCTSTR Text); + virtual int GetToolTipText(const LVHITTESTINFO* pHTI, CString& Text); + CMidiTargetDlg *m_pParent; + }; // Constants enum { - #define MIDITARGETCOLDEF(name) COL_##name, - #include "MidiTargetColDef.h" - COLUMNS + MIDI_LEARN_COLOR = RGB(0, 255, 0), }; - static const CRowView::COLINFO m_ColInfo[COLUMNS]; + static const CGridCtrl::COL_INFO m_ColInfo[COLUMNS]; + static const int m_ColToolTip[COLUMNS]; // Data members - int m_ViewID; // control ID of row view - CMidiRowView *m_View; // pointer to MIDI row view - int m_CurSel; // currently selected row, or -1 if none + CTargetGridCtrl m_List; // target grid control + CMidiTargetArray m_Target; // array of targets + const int *m_TargetNameID; // pointer to array of target name string IDs + int m_ListItemHeight; // height of list item, in pixels // Helpers - CMidiTargetRowDlg *GetRow(int Idx); - const CMidiTargetRowDlg *GetRow(int Idx) const; -}; - -class CMidiRowView : public CRowView { -protected: - DECLARE_DYNCREATE(CMidiRowView); - virtual CRowDlg *CreateRow(int Idx); - virtual void UpdateRow(int Idx); }; inline UINT CMidiTargetDlg::GetTemplateID() @@ -101,19 +121,19 @@ inline UINT CMidiTargetDlg::GetTemplateID() return(IDD); } -inline CMidiTargetRowDlg *CMidiTargetDlg::GetRow(int Idx) +inline int CMidiTargetDlg::GetCurSel() const { - return((CMidiTargetRowDlg *)m_View->GetRow(Idx)); + return(m_List.GetSelection()); } -inline const CMidiTargetRowDlg *CMidiTargetDlg::GetRow(int Idx) const +inline CMidiTarget& CMidiTargetDlg::GetTarget(int RowIdx) { - return((CMidiTargetRowDlg *)m_View->GetRow(Idx)); + return(m_Target[RowIdx]); } -inline int CMidiTargetDlg::GetCurSel() const +inline void CMidiTargetDlg::EnableToolTips(BOOL bEnable) { - return(m_CurSel); + m_List.EnableToolTips(bEnable); } //{{AFX_INSERT_LOCATION}} diff --git a/MidiTargetRowDlg.cpp b/MidiTargetRowDlg.cpp deleted file mode 100644 index ba43906..0000000 --- a/MidiTargetRowDlg.cpp +++ /dev/null @@ -1,200 +0,0 @@ -// Copyleft 2013 Chris Korda -// This program is free software; you can redistribute it and/or modify it -// under the terms of the GNU General Public License as published by the Free -// Software Foundation; either version 2 of the License, or any later version. -/* - chris korda - - revision history: - rev date comments - 00 19nov13 initial version - 01 23apr14 rename for clarity - - MIDI target row dialog - -*/ - -// MidiTargetRowDlg.cpp : implementation file -// - -#include "stdafx.h" -#include "ChordEase.h" -#include "MainFrm.h" -#include "MidiTargetRowDlg.h" -#include "MidiTargetDlg.h" - -#ifdef _DEBUG -#define new DEBUG_NEW -#undef THIS_FILE -static char THIS_FILE[] = __FILE__; -#endif - -///////////////////////////////////////////////////////////////////////////// -// CMidiTargetRowDlg dialog - -IMPLEMENT_DYNAMIC(CMidiTargetRowDlg, CRowDlg); - -const COLORREF CMidiTargetRowDlg::m_SelectionColor = RGB(0, 255, 0); -const CBrush CMidiTargetRowDlg::m_SelectionBrush(m_SelectionColor); - -CMidiTargetRowDlg::CMidiTargetRowDlg(CWnd* pParent /*=NULL*/) - : CRowDlg(IDD, pParent) -{ - //{{AFX_DATA_INIT(CMidiTargetRowDlg) - //}}AFX_DATA_INIT -} - -void CMidiTargetRowDlg::GetTarget(CMidiTarget& Target) const -{ - Target.m_Inst.Port = m_Port.GetIntVal(); - Target.m_Inst.Chan = m_Chan.GetIntVal() - 1; - Target.m_Event = m_Event.GetCurSel(); - Target.m_Control = m_Control.GetIntVal(); - Target.m_RangeStart = float(m_RangeStart.GetVal()); - Target.m_RangeEnd = float(m_RangeEnd.GetVal()); -} - -void CMidiTargetRowDlg::SetTarget(const CMidiTarget& Target) -{ - m_Port.SetVal(Target.m_Inst.Port); - m_Chan.SetVal(Target.m_Inst.Chan + 1); - m_Event.SetCurSel(Target.m_Event); - m_Control.SetVal(Target.m_Control); - m_RangeStart.SetVal(Target.m_RangeStart); - m_RangeEnd.SetVal(Target.m_RangeEnd); -} - -void CMidiTargetRowDlg::SetTargetName(LPCTSTR Name) -{ - m_Name.SetWindowText(Name); -} - -void CMidiTargetRowDlg::UpdateSelection() -{ - Invalidate(); -} - -void CMidiTargetRowDlg::Select() -{ - CWnd *pFocus = GetFocus(); - CWnd *pGoto = &m_Port; // go to first control by default - if (pFocus != NULL) { - CWnd *pParent = pFocus->GetParent(); - if (pParent != NULL && pParent->IsKindOf(RUNTIME_CLASS(CMidiTargetRowDlg))) - pGoto = GetDlgItem(pFocus->GetDlgCtrlID()); - } - GotoDlgCtrl(pGoto); -} - -CMidiTargetDlg *CMidiTargetRowDlg::GetTargetDlg() -{ - return(STATIC_DOWNCAST(CMidiTargetDlg, GetView()->GetParent())); -} - -void CMidiTargetRowDlg::DoDataExchange(CDataExchange* pDX) -{ - CRowDlg::DoDataExchange(pDX); - //{{AFX_DATA_MAP(CMidiTargetRowDlg) - DDX_Control(pDX, IDS_MIDI_TARG_ROW_RANGE_START, m_RangeStart); - DDX_Control(pDX, IDS_MIDI_TARG_ROW_RANGE_END, m_RangeEnd); - DDX_Control(pDX, IDS_MIDI_TARG_ROW_PORT, m_Port); - DDX_Control(pDX, IDS_MIDI_TARG_ROW_NAME, m_Name); - DDX_Control(pDX, IDS_MIDI_TARG_ROW_EVENT, m_Event); - DDX_Control(pDX, IDS_MIDI_TARG_ROW_CONTROL, m_Control); - DDX_Control(pDX, IDS_MIDI_TARG_ROW_CHAN, m_Chan); - //}}AFX_DATA_MAP -} - -BEGIN_MESSAGE_MAP(CMidiTargetRowDlg, CRowDlg) - //{{AFX_MSG_MAP(CMidiTargetRowDlg) - ON_WM_CTLCOLOR() - ON_BN_CLICKED(IDS_MIDI_TARG_ROW_NAME, OnClickName) - ON_WM_LBUTTONDOWN() - //}}AFX_MSG_MAP - ON_NOTIFY_RANGE(NEN_CHANGED, 0, USHRT_MAX, OnChangedNumEdit) - ON_CONTROL_RANGE(CBN_SELCHANGE, 0, USHRT_MAX, OnSelChangeCombo) - ON_CONTROL_RANGE(EN_KILLFOCUS, 0, USHRT_MAX, OnCtrlKillFocus) - ON_CONTROL_RANGE(BN_KILLFOCUS, 0, USHRT_MAX, OnCtrlKillFocus) - ON_CONTROL_RANGE(CBN_KILLFOCUS, 0, USHRT_MAX, OnCtrlKillFocus) - ON_CONTROL_RANGE(EN_SETFOCUS, 0, USHRT_MAX, OnCtrlSetFocus) - ON_CONTROL_RANGE(BN_SETFOCUS, 0, USHRT_MAX, OnCtrlSetFocus) - ON_CONTROL_RANGE(CBN_SETFOCUS, 0, USHRT_MAX, OnCtrlSetFocus) - ON_NOTIFY_EX(TTN_NEEDTEXT, 0, OnToolTipNeedText) -END_MESSAGE_MAP() - -///////////////////////////////////////////////////////////////////////////// -// CMidiTargetRowDlg message handlers - -BOOL CMidiTargetRowDlg::OnInitDialog() -{ - CRowDlg::OnInitDialog(); - - EnableToolTips(); - - return TRUE; // return TRUE unless you set the focus to a control - // EXCEPTION: OCX Property Pages should return FALSE -} - -void CMidiTargetRowDlg::OnChangedNumEdit(UINT nID, NMHDR* pNMHDR, LRESULT* pResult) -{ - GetNotifyWnd()->SendMessage(UWM_MIDIROWEDIT, m_RowIdx, nID); - *pResult = 0; -} - -void CMidiTargetRowDlg::OnSelChangeCombo(UINT nID) -{ - GetNotifyWnd()->SendMessage(UWM_MIDIROWEDIT, m_RowIdx, nID); -} - -BOOL CMidiTargetRowDlg::PreTranslateMessage(MSG* pMsg) -{ - if (pMsg->message >= WM_KEYFIRST || pMsg->message <= WM_KEYLAST) { - if (AfxGetMainWnd()->SendMessage(UWM_HANDLEDLGKEY, (WPARAM)pMsg)) - return(TRUE); - } - return CRowDlg::PreTranslateMessage(pMsg); -} - -HBRUSH CMidiTargetRowDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) -{ - HBRUSH hbr = CRowDlg::OnCtlColor(pDC, pWnd, nCtlColor); - if ((pWnd == &m_Name || nCtlColor == CTLCOLOR_DLG) // if caption or dialog - && theApp.GetMain()->IsMidiLearn() // and we're learning MIDI assignments - && GetTargetDlg()->GetCurSel() == m_RowIdx) { // and we're selected target - pDC->SetBkColor(m_SelectionColor); // set selection background color - return static_cast(m_SelectionBrush); // return selection brush - } - return hbr; -} - -void CMidiTargetRowDlg::OnCtrlSetFocus(UINT nID) -{ - GetTargetDlg()->SetCurSel(m_RowIdx); // notify target dialog -} - -void CMidiTargetRowDlg::OnCtrlKillFocus(UINT nID) -{ - CWnd *pWnd = GetFocus(); - if (pWnd != NULL && !IsChild(pWnd)) // if newly focused control isn't ours - GetTargetDlg()->SetCurSel(-1); // notify target dialog -} - -void CMidiTargetRowDlg::OnClickName() -{ - Select(); -} - -void CMidiTargetRowDlg::OnLButtonDown(UINT nFlags, CPoint point) -{ - Select(); - CRowDlg::OnLButtonDown(nFlags, point); -} - -BOOL CMidiTargetRowDlg::OnToolTipNeedText(UINT id, NMHDR* pNMHDR, LRESULT* pResult) -{ - // if tool is for target name, allow derived dialog to override tip - UINT nID = ::GetDlgCtrlID(HWND(pNMHDR->idFrom)); - if (nID == IDS_MIDI_TARG_ROW_NAME) - GetTargetDlg()->GetTargetToolTip(m_RowIdx, id, pNMHDR); - return theApp.OnToolTipNeedText(id, pNMHDR, pResult); -} diff --git a/MidiTargetRowDlg.h b/MidiTargetRowDlg.h deleted file mode 100644 index 63dedb9..0000000 --- a/MidiTargetRowDlg.h +++ /dev/null @@ -1,99 +0,0 @@ -// Copyleft 2013 Chris Korda -// This program is free software; you can redistribute it and/or modify it -// under the terms of the GNU General Public License as published by the Free -// Software Foundation; either version 2 of the License, or any later version. -/* - chris korda - - revision history: - rev date comments - 00 19nov13 initial version - 01 23apr14 rename for clarity - - MIDI target row dialog - -*/ - -#if !defined(AFX_MIDIROWDLG_H__A2704F05_FC3B_4FF6_AAEF_9A1FB2527928__INCLUDED_) -#define AFX_MIDIROWDLG_H__A2704F05_FC3B_4FF6_AAEF_9A1FB2527928__INCLUDED_ - -#if _MSC_VER > 1000 -#pragma once -#endif // _MSC_VER > 1000 -// MidiTargetRowDlg.h : header file -// - -///////////////////////////////////////////////////////////////////////////// -// CMidiTargetRowDlg dialog - -#include "RowDlg.h" -#include "SpinNumEdit.h" -#include "MidiTarget.h" - -class CMidiTargetDlg; - -class CMidiTargetRowDlg : public CRowDlg -{ - DECLARE_DYNAMIC(CMidiTargetRowDlg); -// Construction -public: - CMidiTargetRowDlg(CWnd* pParent = NULL); - -// Attributes - void GetTarget(CMidiTarget& Target) const; - void SetTarget(const CMidiTarget& Target); - void SetTargetName(LPCTSTR Name); - -// Operations - void UpdateSelection(); - void Select(); - -// Overrides - // ClassWizard generated virtual function overrides - //{{AFX_VIRTUAL(CMidiTargetRowDlg) - public: - virtual BOOL PreTranslateMessage(MSG* pMsg); - protected: - virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support - //}}AFX_VIRTUAL - -// Implementation -protected: -// Generated message map functions - //{{AFX_MSG(CMidiTargetRowDlg) - virtual BOOL OnInitDialog(); - afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor); - afx_msg void OnClickName(); - afx_msg void OnLButtonDown(UINT nFlags, CPoint point); - //}}AFX_MSG - afx_msg void OnChangedNumEdit(UINT nID, NMHDR* pNMHDR, LRESULT* pResult); - afx_msg void OnSelChangeCombo(UINT nID); - afx_msg void OnCtrlKillFocus(UINT nID); - afx_msg void OnCtrlSetFocus(UINT nID); - afx_msg BOOL OnToolTipNeedText(UINT id, NMHDR* pNMHDR, LRESULT* pResult); - DECLARE_MESSAGE_MAP() - -// Dialog Data - //{{AFX_DATA(CMidiTargetRowDlg) - enum { IDD = IDD_MIDI_TARGET_ROW }; - CNumEdit m_RangeStart; - CNumEdit m_RangeEnd; - CPortEdit m_Port; - CStatic m_Name; - CComboBox m_Event; - CMidiValEdit m_Control; - CChannelEdit m_Chan; - //}}AFX_DATA - -// Constants - static const COLORREF m_SelectionColor; - static const CBrush m_SelectionBrush; - -// Helpers - CMidiTargetDlg *GetTargetDlg(); -}; - -//{{AFX_INSERT_LOCATION}} -// Microsoft Visual C++ will insert additional declarations immediately before the previous line. - -#endif // !defined(AFX_MIDIROWDLG_H__A2704F05_FC3B_4FF6_AAEF_9A1FB2527928__INCLUDED_) diff --git a/Part.cpp b/Part.cpp index f686744..577197f 100644 --- a/Part.cpp +++ b/Part.cpp @@ -32,6 +32,7 @@ CPart::CPart() { #define PARTDEF(name, init) m_##name = init; #include "PartDef.h" // generate code to initialize members + ZeroMemory(m_MidiShadow, sizeof(m_MidiShadow)); } void CPart::Copy(const CPart& Part) @@ -39,6 +40,7 @@ void CPart::Copy(const CPart& Part) #define PARTDEF(name, init) m_##name = Part.m_##name; #include "PartDef.h" // generate code to copy members CopyMemory(m_MidiTarget, Part.m_MidiTarget, sizeof(m_MidiTarget)); // copy MIDI targets + CopyMemory(m_MidiShadow, Part.m_MidiShadow, sizeof(m_MidiShadow)); } bool CPart::operator==(const CPart& Part) const diff --git a/Part.h b/Part.h index 7709eb4..2bad7a2 100644 --- a/Part.h +++ b/Part.h @@ -123,6 +123,7 @@ class CBasePart { // binary copy OK BASS m_Bass; // bass settings AUTO m_Auto; // auto settings CMidiTarget m_MidiTarget[MIDI_TARGETS]; // array of MIDI targets + char m_MidiShadow[MIDI_TARGETS]; // MIDI controller value for each target }; class CPart : public WObject, public CBasePart { diff --git a/PartMidiTargetDef.h b/PartMidiTargetDef.h index cbf7f87..cfabad1 100644 --- a/PartMidiTargetDef.h +++ b/PartMidiTargetDef.h @@ -26,6 +26,7 @@ PARTMIDITARGETDEF( IN_CC_NOTE_VEL, Input) PARTMIDITARGETDEF( OUT_PATCH, Output) PARTMIDITARGETDEF( OUT_VOLUME, Output) PARTMIDITARGETDEF( OUT_ANTICIPATION, Output) +PARTMIDITARGETDEF( OUT_FIX_HELD_NOTES, Output) PARTMIDITARGETDEF( LEAD_HARM_INTERVAL, Lead) PARTMIDITARGETDEF( LEAD_HARM_OMIT_MELODY, Lead) PARTMIDITARGETDEF( LEAD_HARM_STATIC_MIN, Lead) diff --git a/PartMidiTargetDlg.cpp b/PartMidiTargetDlg.cpp index 424d6a1..7032b3b 100644 --- a/PartMidiTargetDlg.cpp +++ b/PartMidiTargetDlg.cpp @@ -9,6 +9,7 @@ rev date comments 00 19nov13 initial version 01 25may14 override target name tool tip + 02 12jun14 refactor to use grid control instead of row view part MIDI target dialog @@ -33,13 +34,13 @@ static char THIS_FILE[] = __FILE__; IMPLEMENT_DYNAMIC(CPartMidiTargetDlg, CMidiTargetDlg); -const int CPartMidiTargetDlg::m_TargetTipID[] = { +const int CPartMidiTargetDlg::m_TargetCtrlID[] = { #define PARTMIDITARGETDEF(name, page) IDC_PART_##name, #include "PartMidiTargetDef.h" }; CPartMidiTargetDlg::CPartMidiTargetDlg(CWnd* pParent /*=NULL*/) - : CMidiTargetDlg(IDC_PART_MIDI_ROW, pParent) + : CMidiTargetDlg(pParent) { //{{AFX_DATA_INIT(CPartMidiTargetDlg) //}}AFX_DATA_INIT @@ -48,26 +49,55 @@ CPartMidiTargetDlg::CPartMidiTargetDlg(CWnd* pParent /*=NULL*/) void CPartMidiTargetDlg::GetPart(CPart& Part) const { for (int iTarg = 0; iTarg < CPart::MIDI_TARGETS; iTarg++) // for each target - GetRow(iTarg)->GetTarget(Part.m_MidiTarget[iTarg]); // retrieve data from row + Part.m_MidiTarget[iTarg] = m_Target[iTarg]; // retrieve data from row } void CPartMidiTargetDlg::SetPart(const CPart& Part) { for (int iTarg = 0; iTarg < CPart::MIDI_TARGETS; iTarg++) // for each target - GetRow(iTarg)->SetTarget(Part.m_MidiTarget[iTarg]); // update row from data + m_Target[iTarg] = Part.m_MidiTarget[iTarg]; // update row from data + m_List.Invalidate(); } -void CPartMidiTargetDlg::GetTargetToolTip(int RowIdx, int id, NMHDR* pNMHDR) +void CPartMidiTargetDlg::OnTargetChange(int RowIdx, int ColIdx) { - ASSERT(RowIdx >= 0 && RowIdx < _countof(m_TargetTipID)); - int nTipID = m_TargetTipID[RowIdx]; - if (nTipID) { // if alternate tip specified - TOOLTIPTEXT *pTTT = (TOOLTIPTEXT *)pNMHDR; - pTTT->uFlags &= ~TTF_IDISHWND; // idFrom is ID, not window handle - pNMHDR->idFrom = nTipID; // set idFrom to hint resource ID + int iPart = theApp.GetMain()->GetPartsBar().GetCurPart(); + if (CPartsBar::IsValidPartIdx(iPart)) { // if current part is valid + theApp.GetMain()->NotifyEdit( + m_ColInfo[ColIdx].TitleID, UCODE_PART, CUndoable::UE_COALESCE); + CPart part(gEngine.GetPart(iPart)); + GetPart(part); + gEngine.SetPart(iPart, part); } } +int CPartMidiTargetDlg::GetShadowValue(int RowIdx) +{ + int iPart = theApp.GetMain()->GetPartsBar().GetCurPart(); + if (CPartsBar::IsValidPartIdx(iPart)) // if current part is valid + return(gEngine.GetPart(iPart).m_MidiShadow[RowIdx]); + return(0); +} + +int CPartMidiTargetDlg::GetToolTipText(const LVHITTESTINFO* pHTI, CString& Text) +{ + if (pHTI->iSubItem == COL_NAME && pHTI->iItem >= 0) { // if name column and valid row + int nID = GetTargetCtrlID(pHTI->iItem); // get row's control ID + if (nID) // if valid ID + return(nID); + } + return(CMidiTargetDlg::GetToolTipText(pHTI, Text)); +} + +int CPartMidiTargetDlg::FindTargetByCtrlID(int CtrlID) +{ + for (int iTarg = 0; iTarg < CPart::MIDI_TARGETS; iTarg++) { // for each target + if (m_TargetCtrlID[iTarg] == CtrlID) // if control ID found + return(iTarg); + } + return(-1); +} + void CPartMidiTargetDlg::DoDataExchange(CDataExchange* pDX) { CMidiTargetDlg::DoDataExchange(pDX); @@ -81,7 +111,6 @@ void CPartMidiTargetDlg::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CPartMidiTargetDlg, CMidiTargetDlg) //{{AFX_MSG_MAP(CPartMidiTargetDlg) //}}AFX_MSG_MAP - ON_MESSAGE(UWM_MIDIROWEDIT, OnMidiRowEdit) END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// @@ -91,24 +120,8 @@ BOOL CPartMidiTargetDlg::OnInitDialog() { CMidiTargetDlg::OnInitDialog(); - m_View->CreateRows(CPart::MIDI_TARGETS); - m_View->SetNotifyWnd(this); - for (int iTarg = 0; iTarg < CPart::MIDI_TARGETS; iTarg++) // for each target - GetRow(iTarg)->SetTargetName(LDS(CPart::m_MidiTargetNameID[iTarg])); // set name + SetTargets(CPart::m_MidiTargetNameID, CPart::MIDI_TARGETS); return TRUE; // return TRUE unless you set the focus to a control // EXCEPTION: OCX Property Pages should return FALSE } - -LRESULT CPartMidiTargetDlg::OnMidiRowEdit(WPARAM wParam, LPARAM lParam) -{ - int iPart = theApp.GetMain()->GetPartsBar().GetCurPart(); - if (CPartsBar::IsValidPartIdx(iPart)) { // if current part is valid - theApp.GetMain()->NotifyEdit(INT64TO32(lParam), - UCODE_PART, CUndoable::UE_COALESCE); - CPart part(gEngine.GetPart(iPart)); - GetPart(part); - gEngine.SetPart(iPart, part); - } - return(0); -} diff --git a/PartMidiTargetDlg.h b/PartMidiTargetDlg.h index 3f411c3..5b4c979 100644 --- a/PartMidiTargetDlg.h +++ b/PartMidiTargetDlg.h @@ -9,6 +9,7 @@ rev date comments 00 19nov13 initial version 01 25may14 override target name tool tip + 02 12jun14 refactor to use grid control instead of row view part MIDI target dialog @@ -40,9 +41,15 @@ class CPartMidiTargetDlg : public CMidiTargetDlg // Attributes void GetPart(CPart& Part) const; void SetPart(const CPart& Part); + static int GetTargetCtrlID(int TargetIdx); + +// Operations + static int FindTargetByCtrlID(int CtrlID); // Overrides - virtual void GetTargetToolTip(int RowIdx, int id, NMHDR* pNMHDR); + virtual void OnTargetChange(int RowIdx, int ColIdx); + virtual int GetShadowValue(int RowIdx); + virtual int GetToolTipText(const LVHITTESTINFO* pHTI, CString& Text); // Overrides // ClassWizard generated virtual function overrides @@ -61,19 +68,24 @@ class CPartMidiTargetDlg : public CMidiTargetDlg //{{AFX_MSG(CPartMidiTargetDlg) virtual BOOL OnInitDialog(); //}}AFX_MSG - afx_msg LRESULT OnMidiRowEdit(WPARAM wParam, LPARAM lParam); DECLARE_MESSAGE_MAP() // Types // Constants - static const int m_TargetTipID[]; // tool tip ID for each target + static const int m_TargetCtrlID[CPart::MIDI_TARGETS]; // control ID for each target // Data members // Helpers }; +inline int CPartMidiTargetDlg::GetTargetCtrlID(int TargetIdx) +{ + ASSERT(TargetIdx >= 0 && TargetIdx < _countof(m_TargetCtrlID)); + return(m_TargetCtrlID[TargetIdx]); +} + //{{AFX_INSERT_LOCATION}} // Microsoft Visual C++ will insert additional declarations immediately before the previous line. diff --git a/PartPageView.h b/PartPageView.h index 9443d09..bdf0bff 100644 --- a/PartPageView.h +++ b/PartPageView.h @@ -58,6 +58,7 @@ class CPartPageView : public CView CDialog *GetPage(int PageIdx); const CDialog *GetPage(int PageIdx) const; CString GetControlCaption(UINT CtrlID) const; + CPartMidiTargetDlg& GetMidiTargetDlg(); // Operations public: @@ -134,6 +135,11 @@ inline void CPartPageView::UpdateCmdUI(BOOL bDisableIfNoHandler) m_TabDlg.UpdateCmdUI(bDisableIfNoHandler); } +inline CPartMidiTargetDlg& CPartPageView::GetMidiTargetDlg() +{ + return(m_MidiTargetDlg); +} + ///////////////////////////////////////////////////////////////////////////// //{{AFX_INSERT_LOCATION}} diff --git a/PartsBar.h b/PartsBar.h index 4051843..0ba1c10 100644 --- a/PartsBar.h +++ b/PartsBar.h @@ -71,6 +71,7 @@ class CPartsBar : public CMySizingControlBar CString GetPartName(int PartIdx) const; void SetPartName(int PartIdx, LPCTSTR Name); int GetParentPane(HWND hWnd) const; + CPartMidiTargetDlg& GetMidiTargetDlg(); static bool IsValidPartIdx(int PartIdx); static bool IsValidInsertPos(int PartIdx); @@ -222,6 +223,11 @@ inline CString CPartsBar::GetPartName(int PartIdx) const return(GetListCtrl().GetItemText(PartIdx, CPartsListView::COL_PART_NAME)); } +inline CPartMidiTargetDlg& CPartsBar::GetMidiTargetDlg() +{ + return(m_PageView->GetMidiTargetDlg()); +} + ///////////////////////////////////////////////////////////////////////////// //{{AFX_INSERT_LOCATION}} diff --git a/PartsListColDef.h b/PartsListColDef.h new file mode 100644 index 0000000..c471339 --- /dev/null +++ b/PartsListColDef.h @@ -0,0 +1,20 @@ +// Copyleft 2014 Chris Korda +// This program is free software; you can redistribute it and/or modify it +// under the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 of the License, or any later version. +/* + chris korda + + revision history: + rev date comments + 00 20sep13 initial version + + parts list control column definitions + +*/ + +// name align width +LISTCOLDEF( PART_NAME, LVCFMT_LEFT, 70) +LISTCOLDEF( FUNCTION, LVCFMT_LEFT, 60) + +#undef LISTCOLDEF diff --git a/PartsListCtrl.cpp b/PartsListCtrl.cpp index d47e194..e3c3209 100644 --- a/PartsListCtrl.cpp +++ b/PartsListCtrl.cpp @@ -59,19 +59,46 @@ CWnd *CPartsListCtrl::CreateEditCtrl(LPCTSTR Text, DWORD dwStyle, const RECT& re return CGridCtrl::CreateEditCtrl(Text, dwStyle, rect, pParentWnd, nID); } -void CPartsListCtrl::OnItemChange(int Row, int Col, LPCTSTR Text) +void CPartsListCtrl::OnItemChange(LPCTSTR Text) { ASSERT(m_EditCol == CPartsListView::COL_FUNCTION); CPopupCombo *pCombo = STATIC_DOWNCAST(CPopupCombo, m_EditCtrl); int iFunc = pCombo->FindString(0, Text); - int iPart = theApp.GetMain()->GetPartsBar().GetCurPart(); - ASSERT(iPart >= 0); - theApp.GetMain()->NotifyEdit(IDS_PARTS_COL_FUNCTION, - UCODE_PART, CUndoable::UE_COALESCE); - CPart part(gEngine.GetPart(iPart)); - part.m_Function = iFunc; - gEngine.SetPart(iPart, part); - CGridCtrl::OnItemChange(Row, Col, Text); + if (iFunc >= 0) { // if text found in combo + int iPart = theApp.GetMain()->GetPartsBar().GetCurPart(); + ASSERT(iPart >= 0); + CPart part(gEngine.GetPart(iPart)); + if (iFunc != part.m_Function) { // if value changed + ASSERT(iFunc < CPart::FUNCTIONS); + theApp.GetMain()->NotifyEdit(IDS_PARTS_COL_FUNCTION, + UCODE_PART, CUndoable::UE_COALESCE); + part.m_Function = iFunc; + gEngine.SetPart(iPart, part); + CGridCtrl::OnItemChange(Text); + } + } +} + +int CPartsListCtrl::GetToolTipText(const LVHITTESTINFO* pHTI, CString& Text) +{ + UINT nID; + if (pHTI->iItem >= 0) { // if cursor on list item + switch (pHTI->iSubItem) { + case CPartsListView::COL_PART_NAME: + if (pHTI->flags & LVHT_ONITEMSTATEICON) // if cursor on checkbox + nID = IDC_PART_ENABLE; + else + nID = IDC_PART_NAME; + break; + case CPartsListView::COL_FUNCTION: + nID = IDC_PART_FUNCTION; + break; + default: + nID = 0; + } + } else // cursor not on list item + nID = IDS_PARTS_TIP_LIST; + return(nID); } ///////////////////////////////////////////////////////////////////////////// diff --git a/PartsListCtrl.h b/PartsListCtrl.h index 45a80d4..14cc549 100644 --- a/PartsListCtrl.h +++ b/PartsListCtrl.h @@ -55,7 +55,8 @@ class CPartsListCtrl : public CGridCtrl // Overrides virtual CWnd *CreateEditCtrl(LPCTSTR Text, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID); - virtual void OnItemChange(int Row, int Col, LPCTSTR Text); + virtual void OnItemChange(LPCTSTR Text); + virtual int GetToolTipText(const LVHITTESTINFO* pHTI, CString& Text); }; ///////////////////////////////////////////////////////////////////////////// diff --git a/PartsListView.cpp b/PartsListView.cpp index 482868d..0fb5d40 100644 --- a/PartsListView.cpp +++ b/PartsListView.cpp @@ -9,6 +9,7 @@ rev date comments 00 20sep13 initial version 01 23apr14 add tooltip support + 02 12jun14 in OnCreate, set font parts list view @@ -34,8 +35,8 @@ static char THIS_FILE[] = __FILE__; IMPLEMENT_DYNCREATE(CPartsListView, CView) const CPartsListCtrl::COL_INFO CPartsListView::m_ColInfo[COLUMNS] = { - {IDS_PARTS_COL_PART_NAME, LVCFMT_LEFT, 70}, - {IDS_PARTS_COL_FUNCTION, LVCFMT_LEFT, 60}, + #define LISTCOLDEF(name, align, width) {IDS_PARTS_COL_##name, align, width}, + #include "PartsListColDef.h" }; const LPCTSTR CPartsListView::m_FunctionName[FUNCTIONS] = { @@ -153,7 +154,6 @@ BEGIN_MESSAGE_MAP(CPartsListView, CView) ON_NOTIFY(NM_RCLICK, IDC_PARTS_LIST, OnListClick) ON_WM_DESTROY() //}}AFX_MSG_MAP - ON_NOTIFY_EX(TTN_NEEDTEXT, 0, OnToolTipNeedText) END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// @@ -163,20 +163,16 @@ int CPartsListView::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CView::OnCreate(lpCreateStruct) == -1) return -1; - // specify owner draw style, but remove it after list control is created; - // this lets us adjust item height by forcing OnMeasureItem to be called - DWORD style = WS_CHILD | WS_VISIBLE - | LVS_REPORT | LVS_SHOWSELALWAYS | LVS_EDITLABELS - | LVS_NOSORTHEADER | LVS_OWNERDRAWFIXED; + DWORD style = WS_CHILD | WS_VISIBLE | LVS_REPORT + | LVS_SHOWSELALWAYS | LVS_EDITLABELS | LVS_NOSORTHEADER; if (!m_List.Create(style, CRect(0, 0, 0, 0), this, IDC_PARTS_LIST)) return -1; - m_List.ModifyStyle(LVS_OWNERDRAWFIXED, 0); // remove owner draw + m_List.SendMessage(WM_SETFONT, WPARAM(GetStockObject(DEFAULT_GUI_FONT))); DWORD ExStyle = LVS_EX_GRIDLINES | LVS_EX_FULLROWSELECT | LVS_EX_CHECKBOXES; m_List.SetExtendedStyle(ExStyle); m_List.TrackDropPos(TRUE); m_List.CreateColumns(m_ColInfo, COLUMNS); - m_List.LoadColumnWidths(REG_SETTINGS, RK_PARTS_LIST_CW); - EnableToolTips(); + m_List.LoadColumnWidths(REG_SETTINGS, RK_PARTS_LIST_CW); return 0; } @@ -186,12 +182,6 @@ void CPartsListView::OnDestroy() CView::OnDestroy(); } -void CPartsListView::OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct) -{ - if (nIDCtl == IDC_PARTS_LIST) - lpMeasureItemStruct->itemHeight += 3; // increase row height for popup editing -} - void CPartsListView::OnSize(UINT nType, int cx, int cy) { CView::OnSize(nType, cx, cy); @@ -267,36 +257,3 @@ void CPartsListView::OnContextMenu(CWnd* pWnd, CPoint point) CMenu *mp = menu.GetSubMenu(0); mp->TrackPopupMenu(0, point.x, point.y, theApp.GetMain()); } - -BOOL CPartsListView::OnToolTipNeedText(UINT id, NMHDR* pNMHDR, LRESULT* pResult) -{ - if (!theApp.GetMain()->GetOptions().m_Other.ShowTooltips) // if not showing tooltips - return(FALSE); - CPoint pt; - GetCursorPos(&pt); - m_List.ScreenToClient(&pt); // convert cursor to list's client coords - LVHITTESTINFO hti; - hti.pt = pt; - m_List.SubItemHitTest(&hti); - UINT nID; - if (hti.iItem >= 0) { // if cursor on list item - switch (hti.iSubItem) { - case COL_PART_NAME: - if (hti.flags & LVHT_ONITEMSTATEICON) // if cursor on checkbox - nID = IDC_PART_ENABLE; - else - nID = IDC_PART_NAME; - break; - case COL_FUNCTION: - nID = IDC_PART_FUNCTION; - break; - default: - nID = 0; - } - } else // cursor not on list item - nID = IDS_PARTS_TIP_LIST; - TOOLTIPTEXT *pTTT = (TOOLTIPTEXT *)pNMHDR; - pTTT->lpszText = MAKEINTRESOURCE(nID); - pTTT->hinst = AfxGetResourceHandle(); - return(TRUE); -} diff --git a/PartsListView.h b/PartsListView.h index 5942720..8c41a14 100644 --- a/PartsListView.h +++ b/PartsListView.h @@ -48,8 +48,8 @@ class CPartsListView : public CView // Constants enum { // columns - COL_PART_NAME, - COL_FUNCTION, + #define LISTCOLDEF(name, align, width) COL_##name, + #include "PartsListColDef.h" COLUMNS }; enum { // functions @@ -94,11 +94,9 @@ class CPartsListView : public CView afx_msg void OnListItemchanged(NMHDR* pNMHDR, LRESULT* pResult); afx_msg void OnListEndlabeledit(NMHDR* pNMHDR, LRESULT* pResult); afx_msg void OnListReorder(NMHDR* pNMHDR, LRESULT* pResult); - afx_msg void OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct); afx_msg void OnContextMenu(CWnd* pWnd, CPoint point); afx_msg void OnDestroy(); //}}AFX_MSG - afx_msg BOOL OnToolTipNeedText(UINT id, NMHDR* pNMHDR, LRESULT* pResult); DECLARE_MESSAGE_MAP() // Constants diff --git a/Patch.cpp b/Patch.cpp index c259b82..6996ffb 100644 --- a/Patch.cpp +++ b/Patch.cpp @@ -31,12 +31,12 @@ #define RK_MIDI_OUT_PORT_ID _T("MidiOutPortID") const LPCTSTR CBasePatch::m_MidiTargetName[MIDI_TARGETS] = { - #define PATCHMIDITARGETDEF(name, page) _T(#name), + #define PATCHMIDITARGETDEF(name, page, tag) _T(#name), #include "PatchMidiTargetDef.h" // generate table of MIDI target names }; const int CBasePatch::m_MidiTargetNameID[MIDI_TARGETS] = { - #define PATCHMIDITARGETDEF(name, page) IDS_PATCH_MT_##name, + #define PATCHMIDITARGETDEF(name, page, tag) IDS_PATCH_MT_##name, #include "PatchMidiTargetDef.h" // generate table of MIDI target name IDs }; @@ -44,6 +44,7 @@ CPatch::CPatch() { #define PATCHDEF(name, init) m_##name = init; #include "PatchDef.h" // generate code to initialize members + ZeroMemory(m_MidiShadow, sizeof(m_MidiShadow)); } double CBasePatch::GetTempo() const @@ -59,6 +60,7 @@ void CPatch::Copy(const CPatch& Patch) m_Part = Patch.m_Part; // copy part array m_InPortID = Patch.m_InPortID; // copy port ID arrays m_OutPortID = Patch.m_OutPortID; + CopyMemory(m_MidiShadow, Patch.m_MidiShadow, sizeof(m_MidiShadow)); } bool CPatch::operator==(const CPatch& Patch) const diff --git a/Patch.h b/Patch.h index 4853677..de8c826 100644 --- a/Patch.h +++ b/Patch.h @@ -27,7 +27,7 @@ struct CBasePatch { // binary copy OK public: // Constants enum { - #define PATCHMIDITARGETDEF(name, page) MIDI_TARGET_##name, + #define PATCHMIDITARGETDEF(name, page, tag) MIDI_TARGET_##name, #include "PatchMidiTargetDef.h" MIDI_TARGETS, }; @@ -56,6 +56,7 @@ struct CBasePatch { // binary copy OK int m_Transpose; // global transposition, in steps int m_CurPart; // index of current part, or -1 if none CMidiTarget m_MidiTarget[MIDI_TARGETS]; // array of MIDI targets + char m_MidiShadow[MIDI_TARGETS]; // MIDI controller value for each target // Attributes double GetTempo() const; diff --git a/PatchAutoInstDlg.cpp b/PatchAutoInstDlg.cpp deleted file mode 100644 index c3071ac..0000000 --- a/PatchAutoInstDlg.cpp +++ /dev/null @@ -1,91 +0,0 @@ -// Copyleft 2013 Chris Korda -// This program is free software; you can redistribute it and/or modify it -// under the terms of the GNU General Public License as published by the Free -// Software Foundation; either version 2 of the License, or any later version. -/* - chris korda - - revision history: - rev date comments - 00 14sep13 initial version - - patch auto instrument dialog - -*/ - -// PatchAutoInstDlg.cpp : implementation file -// - -#include "stdafx.h" -#include "ChordEase.h" -#include "PatchAutoInstDlg.h" - -#ifdef _DEBUG -#define new DEBUG_NEW -#undef THIS_FILE -static char THIS_FILE[] = __FILE__; -#endif - -///////////////////////////////////////////////////////////////////////////// -// CPatchAutoInstDlg dialog - -IMPLEMENT_DYNAMIC(CPatchAutoInstDlg, CPatchPageDlg); - -CPatchAutoInstDlg::CPatchAutoInstDlg(CWnd* pParent /*=NULL*/) - : CPatchPageDlg(IDD, pParent) -{ - //{{AFX_DATA_INIT(CPatchAutoInstDlg) - //}}AFX_DATA_INIT -} - -void CPatchAutoInstDlg::GetInst(CBasePatch::AUTO_INST& Inst) const -{ - Inst.Enable = m_Enable.GetCheck() != 0; - Inst.Inst.Port = m_Port.GetIntVal(); - Inst.Inst.Chan = m_Channel.GetIntVal() - 1; - Inst.Velocity = m_Velocity.GetIntVal(); - Inst.Patch = m_Patch.GetIntVal(); - Inst.Volume = m_Volume.GetIntVal(); -} - -void CPatchAutoInstDlg::SetInst(const CBasePatch::AUTO_INST& Inst) -{ - m_Enable.SetCheck(Inst.Enable); - m_Port.SetVal(Inst.Inst.Port); - m_Channel.SetVal(Inst.Inst.Chan + 1); - m_Velocity.SetVal(Inst.Velocity); - m_Patch.SetVal(Inst.Patch); - m_Volume.SetVal(Inst.Volume); -} - -void CPatchAutoInstDlg::DoDataExchange(CDataExchange* pDX) -{ - CPatchPageDlg::DoDataExchange(pDX); - //{{AFX_DATA_MAP(CPatchAutoInstDlg) - DDX_Control(pDX, IDC_PATCH_AINST_PATCH, m_Patch); - DDX_Control(pDX, IDC_PATCH_AINST_VELOCITY, m_Velocity); - DDX_Control(pDX, IDC_PATCH_AINST_PORT, m_Port); - DDX_Control(pDX, IDC_PATCH_AINST_ENABLE, m_Enable); - DDX_Control(pDX, IDC_PATCH_AINST_CHANNEL, m_Channel); - DDX_Control(pDX, IDC_PATCH_AINST_VOLUME, m_Volume); - //}}AFX_DATA_MAP -} - -///////////////////////////////////////////////////////////////////////////// -// CPatchAutoInstDlg message map - -BEGIN_MESSAGE_MAP(CPatchAutoInstDlg, CPatchPageDlg) - //{{AFX_MSG_MAP(CPatchAutoInstDlg) - //}}AFX_MSG_MAP -END_MESSAGE_MAP() - -///////////////////////////////////////////////////////////////////////////// -// CPatchAutoInstDlg message handlers - -BOOL CPatchAutoInstDlg::OnInitDialog() -{ - CPatchPageDlg::OnInitDialog(); - - return TRUE; // return TRUE unless you set the focus to a control - // EXCEPTION: OCX Property Pages should return FALSE -} diff --git a/PatchAutoInstDlg.h b/PatchAutoInstDlg.h deleted file mode 100644 index bfd5f8d..0000000 --- a/PatchAutoInstDlg.h +++ /dev/null @@ -1,81 +0,0 @@ -// Copyleft 2013 Chris Korda -// This program is free software; you can redistribute it and/or modify it -// under the terms of the GNU General Public License as published by the Free -// Software Foundation; either version 2 of the License, or any later version. -/* - chris korda - - revision history: - rev date comments - 00 14sep13 initial version - - patch auto instrument dialog - -*/ - -#if !defined(AFX_PATCHAUTOINSTDLG_H__F352C13B_F7F1_4873_8524_D86EEB346600__INCLUDED_) -#define AFX_PATCHAUTOINSTDLG_H__F352C13B_F7F1_4873_8524_D86EEB346600__INCLUDED_ - -#if _MSC_VER > 1000 -#pragma once -#endif // _MSC_VER > 1000 -// PatchAutoInstDlg.h : header file -// - -///////////////////////////////////////////////////////////////////////////// -// CPatchAutoInstDlg dialog - -#include "PatchPageDlg.h" -#include "NumSpin.h" -#include "NoteEdit.h" - -class CPatch; - -class CPatchAutoInstDlg : public CPatchPageDlg -{ - DECLARE_DYNAMIC(CPatchAutoInstDlg); -// Construction -public: - CPatchAutoInstDlg(CWnd* pParent = NULL); - -// Attributes - static UINT GetTemplateID(); - void GetInst(CBasePatch::AUTO_INST& Inst) const; - void SetInst(const CBasePatch::AUTO_INST& Inst); - -// Overrides - // ClassWizard generated virtual function overrides - //{{AFX_VIRTUAL(CPatchAutoInstDlg) - protected: - virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support - //}}AFX_VIRTUAL - -// Implementation -protected: -// Dialog Data - //{{AFX_DATA(CPatchAutoInstDlg) - enum { IDD = IDD_PATCH_METRONOME }; - CPatchEdit m_Patch; - CMidiValEdit m_Velocity; - CPortEdit m_Port; - CButton m_Enable; - CChannelEdit m_Channel; - CPatchEdit m_Volume; - //}}AFX_DATA - -// Generated message map functions - //{{AFX_MSG(CPatchAutoInstDlg) - virtual BOOL OnInitDialog(); - //}}AFX_MSG - DECLARE_MESSAGE_MAP() -}; - -inline UINT CPatchAutoInstDlg::GetTemplateID() -{ - return(IDD); -} - -//{{AFX_INSERT_LOCATION}} -// Microsoft Visual C++ will insert additional declarations immediately before the previous line. - -#endif // !defined(AFX_PATCHAUTOINSTDLG_H__F352C13B_F7F1_4873_8524_D86EEB346600__INCLUDED_) diff --git a/PatchBar.h b/PatchBar.h index 0e4adc4..3dafe1a 100644 --- a/PatchBar.h +++ b/PatchBar.h @@ -54,6 +54,7 @@ class CPatchBar : public CMySizingControlBar CDialog *GetPage(int PageIdx); const CDialog *GetPage(int PageIdx) const; CString GetControlCaption(UINT CtrlID) const; + CPatchMidiTargetDlg& GetMidiTargetDlg(); // Operations public: @@ -117,6 +118,11 @@ inline void CPatchBar::FocusControl(UINT CtrlID) m_TabDlg.FocusControl(CtrlID); } +inline CPatchMidiTargetDlg& CPatchBar::GetMidiTargetDlg() +{ + return(m_MidiTargetDlg); +} + ///////////////////////////////////////////////////////////////////////////// //{{AFX_INSERT_LOCATION}} diff --git a/PatchMetronomeDlg.cpp b/PatchMetronomeDlg.cpp index 94cb561..512a08c 100644 --- a/PatchMetronomeDlg.cpp +++ b/PatchMetronomeDlg.cpp @@ -29,10 +29,10 @@ static char THIS_FILE[] = __FILE__; ///////////////////////////////////////////////////////////////////////////// // CPatchMetronomeDlg dialog -IMPLEMENT_DYNAMIC(CPatchMetronomeDlg, CPatchAutoInstDlg); +IMPLEMENT_DYNAMIC(CPatchMetronomeDlg, CPatchPageDlg); CPatchMetronomeDlg::CPatchMetronomeDlg(CWnd* pParent /*=NULL*/) - : CPatchAutoInstDlg(pParent) + : CPatchPageDlg(IDD, pParent) { //{{AFX_DATA_INIT(CPatchMetronomeDlg) //}}AFX_DATA_INIT @@ -41,7 +41,12 @@ CPatchMetronomeDlg::CPatchMetronomeDlg(CWnd* pParent /*=NULL*/) void CPatchMetronomeDlg::GetPatch(CBasePatch& Patch) const { CBasePatch::METRONOME& Inst = Patch.m_Metronome; - GetInst(Inst); + Inst.Enable = m_Enable.GetCheck() != 0; + Inst.Inst.Port = m_Port.GetIntVal(); + Inst.Inst.Chan = m_Channel.GetIntVal() - 1; + Inst.Velocity = m_Velocity.GetIntVal(); + Inst.Patch = m_Patch.GetIntVal(); + Inst.Volume = m_Volume.GetIntVal(); Inst.Note = m_Note.GetIntVal(); Inst.AccentNote = m_AccentNote.GetIntVal(); Inst.AccentVel = m_AccentVel.GetIntVal(); @@ -51,7 +56,12 @@ void CPatchMetronomeDlg::GetPatch(CBasePatch& Patch) const void CPatchMetronomeDlg::SetPatch(const CBasePatch& Patch) { const CBasePatch::METRONOME& Inst = Patch.m_Metronome; - SetInst(Inst); + m_Enable.SetCheck(Inst.Enable); + m_Port.SetVal(Inst.Inst.Port); + m_Channel.SetVal(Inst.Inst.Chan + 1); + m_Velocity.SetVal(Inst.Velocity); + m_Patch.SetVal(Inst.Patch); + m_Volume.SetVal(Inst.Volume); m_Note.SetVal(Inst.Note); m_AccentNote.SetVal(Inst.AccentNote); m_AccentVel.SetVal(Inst.AccentVel); @@ -60,8 +70,14 @@ void CPatchMetronomeDlg::SetPatch(const CBasePatch& Patch) void CPatchMetronomeDlg::DoDataExchange(CDataExchange* pDX) { - CPatchAutoInstDlg::DoDataExchange(pDX); + CPatchPageDlg::DoDataExchange(pDX); //{{AFX_DATA_MAP(CPatchMetronomeDlg) + DDX_Control(pDX, IDC_PATCH_METRO_PATCH, m_Patch); + DDX_Control(pDX, IDC_PATCH_METRO_VELOCITY, m_Velocity); + DDX_Control(pDX, IDC_PATCH_METRO_PORT, m_Port); + DDX_Control(pDX, IDC_PATCH_METRO_ENABLE, m_Enable); + DDX_Control(pDX, IDC_PATCH_METRO_CHANNEL, m_Channel); + DDX_Control(pDX, IDC_PATCH_METRO_VOLUME, m_Volume); DDX_Control(pDX, IDC_PATCH_METRO_ACCENT_SAME_NOTE, m_AccentSameNote); DDX_Control(pDX, IDC_PATCH_METRO_NOTE, m_Note); DDX_Control(pDX, IDC_PATCH_METRO_ACCENT_NOTE, m_AccentNote); @@ -72,7 +88,7 @@ void CPatchMetronomeDlg::DoDataExchange(CDataExchange* pDX) ///////////////////////////////////////////////////////////////////////////// // CPatchMetronomeDlg message map -BEGIN_MESSAGE_MAP(CPatchMetronomeDlg, CPatchAutoInstDlg) +BEGIN_MESSAGE_MAP(CPatchMetronomeDlg, CPatchPageDlg) //{{AFX_MSG_MAP(CPatchMetronomeDlg) ON_UPDATE_COMMAND_UI(IDC_PATCH_METRO_ACCENT_NOTE, OnUpdateAccentNote) //}}AFX_MSG_MAP @@ -83,7 +99,7 @@ END_MESSAGE_MAP() BOOL CPatchMetronomeDlg::OnInitDialog() { - CPatchAutoInstDlg::OnInitDialog(); + CPatchPageDlg::OnInitDialog(); return TRUE; // return TRUE unless you set the focus to a control // EXCEPTION: OCX Property Pages should return FALSE diff --git a/PatchMetronomeDlg.h b/PatchMetronomeDlg.h index 0807d09..1ce2003 100644 --- a/PatchMetronomeDlg.h +++ b/PatchMetronomeDlg.h @@ -25,9 +25,9 @@ ///////////////////////////////////////////////////////////////////////////// // CPatchMetronomeDlg dialog -#include "PatchAutoInstDlg.h" +#include "PatchPageDlg.h" -class CPatchMetronomeDlg : public CPatchAutoInstDlg +class CPatchMetronomeDlg : public CPatchPageDlg { DECLARE_DYNAMIC(CPatchMetronomeDlg); // Construction @@ -55,6 +55,12 @@ class CPatchMetronomeDlg : public CPatchAutoInstDlg CSpinNoteEdit m_Note; CSpinNoteEdit m_AccentNote; CMidiValEdit m_AccentVel; + CPatchEdit m_Patch; + CMidiValEdit m_Velocity; + CPortEdit m_Port; + CButton m_Enable; + CChannelEdit m_Channel; + CPatchEdit m_Volume; //}}AFX_DATA // Generated message map functions diff --git a/PatchMidiTargetDef.h b/PatchMidiTargetDef.h index 109d1a4..1374774 100644 --- a/PatchMidiTargetDef.h +++ b/PatchMidiTargetDef.h @@ -13,21 +13,30 @@ */ -// name page -PATCHMIDITARGETDEF( TEMPO, General) -PATCHMIDITARGETDEF( TEMPO_MULTIPLE, General) -PATCHMIDITARGETDEF( TRANSPOSE, General) -PATCHMIDITARGETDEF( LEAD_IN, General) -PATCHMIDITARGETDEF( METRO_ENABLE, Metronome) -PATCHMIDITARGETDEF( METRO_VOLUME, Metronome) -PATCHMIDITARGETDEF( PLAY, General) -PATCHMIDITARGETDEF( PAUSE, General) -PATCHMIDITARGETDEF( REWIND, General) -PATCHMIDITARGETDEF( REPEAT, General) -PATCHMIDITARGETDEF( NEXT_SECTION, General) -PATCHMIDITARGETDEF( NEXT_CHORD, General) -PATCHMIDITARGETDEF( PREV_CHORD, General) -PATCHMIDITARGETDEF( SONG_POSITION, General) +// name page tag +PATCHMIDITARGETDEF( TEMPO, General, _GEN_) +PATCHMIDITARGETDEF( TEMPO_MULTIPLE, General, _GEN_) +PATCHMIDITARGETDEF( TRANSPOSE, General, _GEN_) +PATCHMIDITARGETDEF( LEAD_IN, General, _GEN_) +PATCHMIDITARGETDEF( METRO_ENABLE, Metronome, _) +PATCHMIDITARGETDEF( METRO_VOLUME, Metronome, _) +PATCHMIDITARGETDEF( PLAY, General, _GEN_) +PATCHMIDITARGETDEF( PAUSE, General, _GEN_) +PATCHMIDITARGETDEF( REWIND, General, _GEN_) +PATCHMIDITARGETDEF( REPEAT, General, _GEN_) +PATCHMIDITARGETDEF( NEXT_SECTION, General, _GEN_) +PATCHMIDITARGETDEF( NEXT_CHORD, General, _GEN_) +PATCHMIDITARGETDEF( PREV_CHORD, General, _GEN_) +PATCHMIDITARGETDEF( SONG_POSITION, General, _GEN_) #undef PATCHMIDITARGETDEF +// these targets don't have control IDs +#define IDC_PATCH_GEN_PLAY 0 +#define IDC_PATCH_GEN_PAUSE 0 +#define IDC_PATCH_GEN_REWIND 0 +#define IDC_PATCH_GEN_REPEAT 0 +#define IDC_PATCH_GEN_NEXT_SECTION 0 +#define IDC_PATCH_GEN_NEXT_CHORD 0 +#define IDC_PATCH_GEN_PREV_CHORD 0 +#define IDC_PATCH_GEN_SONG_POSITION 0 diff --git a/PatchMidiTargetDlg.cpp b/PatchMidiTargetDlg.cpp index 7fcf06b..114f810 100644 --- a/PatchMidiTargetDlg.cpp +++ b/PatchMidiTargetDlg.cpp @@ -8,6 +8,7 @@ revision history: rev date comments 00 19nov13 initial version + 01 12jun14 refactor to use grid control instead of row view MIDI target dialog @@ -32,8 +33,13 @@ static char THIS_FILE[] = __FILE__; IMPLEMENT_DYNAMIC(CPatchMidiTargetDlg, CMidiTargetDlg); +const int CPatchMidiTargetDlg::m_TargetCtrlID[] = { + #define PATCHMIDITARGETDEF(name, page, tag) IDC_PATCH##tag##name, + #include "PatchMidiTargetDef.h" +}; + CPatchMidiTargetDlg::CPatchMidiTargetDlg(CWnd* pParent /*=NULL*/) - : CMidiTargetDlg(IDC_MIDI_TARGET, pParent) + : CMidiTargetDlg(pParent) { //{{AFX_DATA_INIT(CPatchMidiTargetDlg) //}}AFX_DATA_INIT @@ -42,13 +48,49 @@ CPatchMidiTargetDlg::CPatchMidiTargetDlg(CWnd* pParent /*=NULL*/) void CPatchMidiTargetDlg::GetPatch(CBasePatch& Patch) const { for (int iTarg = 0; iTarg < CPatch::MIDI_TARGETS; iTarg++) // for each target - GetRow(iTarg)->GetTarget(Patch.m_MidiTarget[iTarg]); // retrieve data from row + Patch.m_MidiTarget[iTarg] = m_Target[iTarg]; // retrieve data from row } void CPatchMidiTargetDlg::SetPatch(const CBasePatch& Patch) { for (int iTarg = 0; iTarg < CPatch::MIDI_TARGETS; iTarg++) // for each target - GetRow(iTarg)->SetTarget(Patch.m_MidiTarget[iTarg]); // update row from data + m_Target[iTarg] = Patch.m_MidiTarget[iTarg]; // update row from data + m_List.Invalidate(); +} + +void CPatchMidiTargetDlg::OnTargetChange(int RowIdx, int ColIdx) +{ + theApp.GetMain()->NotifyEdit( + m_ColInfo[ColIdx].TitleID, UCODE_BASE_PATCH, CUndoable::UE_COALESCE); + CBasePatch patch; + gEngine.GetBasePatch(patch); + GetPatch(patch); + gEngine.SetBasePatch(patch); +} + +int CPatchMidiTargetDlg::GetShadowValue(int RowIdx) +{ + ASSERT(RowIdx >= 0 && RowIdx < CPatch::MIDI_TARGETS); + return(gEngine.GetPatch().m_MidiShadow[RowIdx]); +} + +int CPatchMidiTargetDlg::GetToolTipText(const LVHITTESTINFO* pHTI, CString& Text) +{ + if (pHTI->iSubItem == COL_NAME && pHTI->iItem >= 0) { // if name column and valid row + int nID = GetTargetCtrlID(pHTI->iItem); // get row's control ID + if (nID) // if valid ID + return(nID); + } + return(CMidiTargetDlg::GetToolTipText(pHTI, Text)); +} + +int CPatchMidiTargetDlg::FindTargetByCtrlID(int CtrlID) +{ + for (int iTarg = 0; iTarg < CPatch::MIDI_TARGETS; iTarg++) { // for each target + if (m_TargetCtrlID[iTarg] == CtrlID) // if control ID found + return(iTarg); + } + return(-1); } void CPatchMidiTargetDlg::DoDataExchange(CDataExchange* pDX) @@ -64,7 +106,6 @@ void CPatchMidiTargetDlg::DoDataExchange(CDataExchange* pDX) BEGIN_MESSAGE_MAP(CPatchMidiTargetDlg, CMidiTargetDlg) //{{AFX_MSG_MAP(CPatchMidiTargetDlg) //}}AFX_MSG_MAP - ON_MESSAGE(UWM_MIDIROWEDIT, OnMidiRowEdit) END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// @@ -74,22 +115,8 @@ BOOL CPatchMidiTargetDlg::OnInitDialog() { CMidiTargetDlg::OnInitDialog(); - m_View->CreateRows(CPatch::MIDI_TARGETS); - m_View->SetNotifyWnd(this); - for (int iTarg = 0; iTarg < CPatch::MIDI_TARGETS; iTarg++) // for each target - GetRow(iTarg)->SetTargetName(LDS(CPatch::m_MidiTargetNameID[iTarg])); // set name + SetTargets(CPatch::m_MidiTargetNameID, CPatch::MIDI_TARGETS); return TRUE; // return TRUE unless you set the focus to a control // EXCEPTION: OCX Property Pages should return FALSE } - -LRESULT CPatchMidiTargetDlg::OnMidiRowEdit(WPARAM wParam, LPARAM lParam) -{ - theApp.GetMain()->NotifyEdit(INT64TO32(lParam), - UCODE_BASE_PATCH, CUndoable::UE_COALESCE); - CBasePatch patch; - gEngine.GetBasePatch(patch); - GetPatch(patch); - gEngine.SetBasePatch(patch); - return(0); -} diff --git a/PatchMidiTargetDlg.h b/PatchMidiTargetDlg.h index 7f20965..9d7a6f8 100644 --- a/PatchMidiTargetDlg.h +++ b/PatchMidiTargetDlg.h @@ -8,6 +8,7 @@ revision history: rev date comments 00 19nov13 initial version + 01 12jun14 refactor to use grid control instead of row view MIDI target dialog @@ -39,6 +40,15 @@ class CPatchMidiTargetDlg : public CMidiTargetDlg // Attributes void GetPatch(CBasePatch& Patch) const; void SetPatch(const CBasePatch& Patch); + static int GetTargetCtrlID(int TargetIdx); + +// Operations + static int FindTargetByCtrlID(int CtrlID); + +// Overrides + virtual void OnTargetChange(int RowIdx, int ColIdx); + virtual int GetShadowValue(int RowIdx); + virtual int GetToolTipText(const LVHITTESTINFO* pHTI, CString& Text); // Overrides // ClassWizard generated virtual function overrides @@ -57,18 +67,24 @@ class CPatchMidiTargetDlg : public CMidiTargetDlg //{{AFX_MSG(CPatchMidiTargetDlg) virtual BOOL OnInitDialog(); //}}AFX_MSG - afx_msg LRESULT OnMidiRowEdit(WPARAM wParam, LPARAM lParam); DECLARE_MESSAGE_MAP() // Types // Constants + static const int m_TargetCtrlID[CPatch::MIDI_TARGETS]; // control ID for each target // Data members // Helpers }; +inline int CPatchMidiTargetDlg::GetTargetCtrlID(int TargetIdx) +{ + ASSERT(TargetIdx >= 0 && TargetIdx < _countof(m_TargetCtrlID)); + return(m_TargetCtrlID[TargetIdx]); +} + //{{AFX_INSERT_LOCATION}} // Microsoft Visual C++ will insert additional declarations immediately before the previous line. diff --git a/PatchPageDlg.cpp b/PatchPageDlg.cpp index 3002b0a..eee82da 100644 --- a/PatchPageDlg.cpp +++ b/PatchPageDlg.cpp @@ -9,6 +9,7 @@ rev date comments 00 14sep13 initial version 01 22apr14 add tooltip support + 02 10jun14 add MIDI learn patch page dialog @@ -43,17 +44,70 @@ void CPatchPageDlg::UpdateEngine(UINT CtrlID) gEngine.SetBasePatch(patch); } +void CPatchPageDlg::GetSelectionRect(CWnd *pChild, CRect& rSelect) +{ + enum { + BORDER = 3 // selection border, in pixels + }; + ASSERT(pChild != NULL); + CWnd *pParent = pChild->GetParent(); + ASSERT(pParent != NULL); + if (pParent->IsKindOf(RUNTIME_CLASS(CComboBox))) // if parent is combo box + pChild = pParent; + CRect rSel; + pChild->GetWindowRect(rSel); + if (pChild->IsKindOf(RUNTIME_CLASS(CEdit))) { // if child is edit + CWnd *pNext = pChild->GetNextWindow(); + if (pNext != NULL && pNext->IsKindOf(RUNTIME_CLASS(CSpinButtonCtrl))) { + CRect rSpin; + pNext->GetWindowRect(rSpin); + rSel = rSel | rSpin; // union of edit and spin control rects + } + } + ScreenToClient(rSel); + rSel.InflateRect(BORDER, BORDER); + rSelect = rSel; +} + +void CPatchPageDlg::UpdateMidiLearn(CWnd *pChild) +{ + CRect rSel; + GetSelectionRect(pChild, rSel); + InvalidateRect(rSel); +} + +void CPatchPageDlg::UpdateMidiLearn(UINT nID) +{ + CWnd *pWnd = GetDlgItem(nID); + if (pWnd != NULL) + UpdateMidiLearn(pWnd); +} + +void CPatchPageDlg::UpdateMidiLearn() +{ + CWnd *pWnd = GetFocus(); + if (pWnd != NULL && IsChild(pWnd)) + UpdateMidiLearn(pWnd); +} + ///////////////////////////////////////////////////////////////////////////// // CPatchPageDlg message map BEGIN_MESSAGE_MAP(CPatchPageDlg, CScrollDlg) //{{AFX_MSG_MAP(CPatchPageDlg) + ON_WM_PAINT() //}}AFX_MSG_MAP ON_NOTIFY_RANGE(NEN_CHANGED, 0, USHRT_MAX, OnChangedNumEdit) ON_CONTROL_RANGE(BN_CLICKED, 0, USHRT_MAX, OnClickedBtn) ON_CONTROL_RANGE(CBN_SELCHANGE, 0, USHRT_MAX, OnSelChangeCombo) ON_NOTIFY_RANGE(NCBN_DURATION_CHANGED, 0, USHRT_MAX, OnChangedDurationCombo) ON_NOTIFY_EX(TTN_NEEDTEXT, 0, OnToolTipNeedText) + ON_CONTROL_RANGE(EN_SETFOCUS, 0, USHRT_MAX, OnChildSetFocus) + ON_CONTROL_RANGE(BN_SETFOCUS, 0, USHRT_MAX, OnChildSetFocus) + ON_CONTROL_RANGE(CBN_SETFOCUS, 0, USHRT_MAX, OnChildSetFocus) + ON_CONTROL_RANGE(EN_KILLFOCUS, 0, USHRT_MAX, OnChildKillFocus) + ON_CONTROL_RANGE(BN_KILLFOCUS, 0, USHRT_MAX, OnChildKillFocus) + ON_CONTROL_RANGE(CBN_KILLFOCUS, 0, USHRT_MAX, OnChildKillFocus) END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// @@ -95,3 +149,41 @@ BOOL CPatchPageDlg::OnToolTipNeedText(UINT id, NMHDR* pNMHDR, LRESULT* pResult) { return theApp.OnToolTipNeedText(id, pNMHDR, pResult); } + +void CPatchPageDlg::OnPaint() +{ + if (theApp.GetMain()->IsMidiLearn()) { // if learning MIDI assignments + CPaintDC dc(this); // device context for painting + CWnd *pFocusWnd = GetFocus(); + // if focus window is one of our controls + if (pFocusWnd != NULL && IsChild(pFocusWnd)) { + int iPart; // find MIDI target corresponding to control, if any + int iTarget = theApp.GetMain()->GetCtrlMidiTarget(pFocusWnd, iPart); + if (iTarget < 0) { // if target not found + int nID = pFocusWnd->GetDlgCtrlID(); + if (nID == IDC_PART_IN_PORT || nID == IDC_PART_IN_CHAN) + iTarget = INT_MAX; // input port/channel are special targets + } + if (iTarget >= 0) { // if control mapped to MIDI target + COLORREF cSel = RGB(0, 255, 0); + CRect rSel; + GetSelectionRect(pFocusWnd, rSel); + dc.FillSolidRect(rSel, cSel); // highlight control + } + } + } else { // not learning MIDI assignments + Default(); + } +} + +void CPatchPageDlg::OnChildSetFocus(UINT nID) +{ + if (theApp.GetMain()->IsMidiLearn()) // if learning MIDI assignments + UpdateMidiLearn(nID); +} + +void CPatchPageDlg::OnChildKillFocus(UINT nID) +{ + if (theApp.GetMain()->IsMidiLearn()) // if learning MIDI assignments + UpdateMidiLearn(nID); +} diff --git a/PatchPageDlg.h b/PatchPageDlg.h index 0f99e93..f9623fa 100644 --- a/PatchPageDlg.h +++ b/PatchPageDlg.h @@ -9,6 +9,7 @@ rev date comments 00 14sep13 initial version 01 22apr14 add tooltip support + 02 10jun14 add MIDI learn patch page dialog @@ -41,6 +42,9 @@ class CPatchPageDlg : public CScrollDlg virtual void GetPatch(CBasePatch& Patch) const = 0; virtual void UpdateEngine(UINT CtrlID); +// Operations + void UpdateMidiLearn(); + // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CPatchPageDlg) //}}AFX_VIRTUAL @@ -54,13 +58,21 @@ class CPatchPageDlg : public CScrollDlg // Generated message map functions //{{AFX_MSG(CPatchPageDlg) virtual BOOL OnInitDialog(); + afx_msg void OnPaint(); //}}AFX_MSG afx_msg void OnChangedNumEdit(UINT nID, NMHDR* pNMHDR, LRESULT* pResult); afx_msg void OnClickedBtn(UINT nID); afx_msg void OnSelChangeCombo(UINT nID); afx_msg void OnChangedDurationCombo(UINT nID, NMHDR* pNMHDR, LRESULT* pResult); afx_msg BOOL OnToolTipNeedText(UINT id, NMHDR* pNMHDR, LRESULT* pResult); + afx_msg void OnChildSetFocus(UINT nID); + afx_msg void OnChildKillFocus(UINT nID); DECLARE_MESSAGE_MAP() + +// Helpers + void GetSelectionRect(CWnd *pChild, CRect& rSelect); + void UpdateMidiLearn(CWnd *pChild); + void UpdateMidiLearn(UINT nID); }; ///////////////////////////////////////////////////////////////////////////// diff --git a/PianoDlg.cpp b/PianoDlg.cpp index c02616c..6d1dd04 100644 --- a/PianoDlg.cpp +++ b/PianoDlg.cpp @@ -45,31 +45,6 @@ CPianoDlg::CPianoDlg(CWnd* pParent /*=NULL*/) m_WasShown = FALSE; } -void CPianoDlg::InitNoteCombo(CComboBox& Combo, CIntRange Range, int SelIdx) -{ - CString s; - int iSel = -1; - for (CNote iNote = Range.Start; iNote <= Range.End; iNote++) { - Combo.AddString(iNote.MidiName()); - if (iNote == SelIdx) - iSel = iNote - Range.Start; - } - Combo.SetCurSel(iSel); -} - -void CPianoDlg::InitNumericCombo(CComboBox& Combo, CIntRange Range, int SelIdx) -{ - CString s; - int iSel = -1; - for (int iItem = Range.Start; iItem <= Range.End; iItem++) { - s.Format(_T("%d"), iItem); - Combo.AddString(s); - if (iItem == SelIdx) - iSel = iItem - Range.Start; - } - Combo.SetCurSel(iSel); -} - void CPianoDlg::PlayNote(int Note, bool Enable) { if (Note < 0 || Note > MIDI_NOTE_MAX) // if note outside MIDI range @@ -102,10 +77,10 @@ void CPianoDlg::SavePianoState() void CPianoDlg::InitControls() { - InitNumericCombo(m_PortCombo, CIntRange(0, MAX_PORTS - 1), m_State.Port); - InitNumericCombo(m_ChannelCombo, CIntRange(1, MIDI_CHANNELS), m_State.Channel + 1); - InitNoteCombo(m_StartNoteCombo, CIntRange(0, MIDI_NOTE_MAX), m_State.StartNote); - InitNumericCombo(m_KeyCountCombo, CIntRange(MIN_KEYS, MAX_KEYS), m_State.KeyCount); + CChordEaseApp::InitNumericCombo(m_PortCombo, CIntRange(0, MAX_PORTS - 1), m_State.Port); + CChordEaseApp::InitNumericCombo(m_ChannelCombo, CIntRange(1, MIDI_CHANNELS), m_State.Channel + 1); + CChordEaseApp::InitNoteCombo(m_StartNoteCombo, CIntRange(0, MIDI_NOTE_MAX), m_State.StartNote); + CChordEaseApp::InitNumericCombo(m_KeyCountCombo, CIntRange(MIN_KEYS, MAX_KEYS), m_State.KeyCount); m_VelocitySlider.SetRange(0, MIDI_NOTE_MAX); m_VelocitySlider.SetPos(m_State.Velocity); } diff --git a/PianoDlg.h b/PianoDlg.h index a715b0f..71cb0d0 100644 --- a/PianoDlg.h +++ b/PianoDlg.h @@ -129,8 +129,6 @@ class CPianoDlg : public CModelessDlg // Helpers void PlayNote(int Note, bool Enable); - static void InitNoteCombo(CComboBox& Combo, CIntRange Range, int SelIdx); - static void InitNumericCombo(CComboBox& Combo, CIntRange Range, int SelIdx); void ResetPianoState(); void LoadPianoState(); void SavePianoState(); diff --git a/RecordPlayerDlg.cpp b/RecordPlayerDlg.cpp index 6638e33..050001e 100644 --- a/RecordPlayerDlg.cpp +++ b/RecordPlayerDlg.cpp @@ -60,6 +60,7 @@ CRecordPlayerDlg::CRecordPlayerDlg(CWnd* pParent /*=NULL*/) m_StartPos = 0; m_Duration = 0; m_TrackList.m_pParent = this; + m_TrackList.SetDragEnable(FALSE); } CRecordPlayerDlg::CPlay::CPlay(CRecordPlayerDlg& Dlg, bool Enable) : m_Dlg(Dlg) @@ -353,17 +354,17 @@ CWnd *CRecordPlayerDlg::CTrackList::CreateEditCtrl(LPCTSTR Text, DWORD dwStyle, return CGridCtrl::CreateEditCtrl(Text, dwStyle, rect, pParentWnd, nID); } -void CRecordPlayerDlg::CTrackList::OnItemChange(int Row, int Col, LPCTSTR Text) +void CRecordPlayerDlg::CTrackList::OnItemChange(LPCTSTR Text) { - switch (Col) { + switch (m_EditCol) { case CRecordPlayerDlg::COL_PORT: - m_pParent->SetPort(Row, _ttoi(Text)); + m_pParent->SetPort(m_EditRow, _ttoi(Text)); break; case CRecordPlayerDlg::COL_CHANNEL: - m_pParent->SetChannel(Row, _ttoi(Text) - 1); + m_pParent->SetChannel(m_EditRow, _ttoi(Text) - 1); break; } - CGridCtrl::OnItemChange(Row, Col, Text); + CGridCtrl::OnItemChange(Text); } void CRecordPlayerDlg::DoDataExchange(CDataExchange* pDX) diff --git a/RecordPlayerDlg.h b/RecordPlayerDlg.h index 091c63e..b83f86a 100644 --- a/RecordPlayerDlg.h +++ b/RecordPlayerDlg.h @@ -73,7 +73,7 @@ class CRecordPlayerDlg : public CPersistDlg class CTrackList : public CGridCtrl { public: virtual CWnd *CreateEditCtrl(LPCTSTR Text, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID); - virtual void OnItemChange(int Row, int Col, LPCTSTR Text); + virtual void OnItemChange(LPCTSTR Text); CRecordPlayerDlg *m_pParent; }; diff --git a/RowDlg.cpp b/RowDlg.cpp deleted file mode 100644 index 5b93a4e..0000000 --- a/RowDlg.cpp +++ /dev/null @@ -1,99 +0,0 @@ -// Copyleft 2005 Chris Korda -// This program is free software; you can redistribute it and/or modify it -// under the terms of the GNU General Public License as published by the Free -// Software Foundation; either version 2 of the License, or any later version. -/* - chris korda - - revision history: - rev date comments - 00 13aug05 initial version - 01 12sep05 add row position - 02 11jul07 remove row dialog tab message; use DS_CONTROL instead - 03 24mar09 add hook for dialog key handler - 04 20apr10 refactor - 05 06apr12 add shortcut key to reset column widths - - row dialog base class - -*/ - -// RowDlg.cpp : implementation file -// - -#include "stdafx.h" -#include "Resource.h" -#include "RowDlg.h" - -#ifdef _DEBUG -#define new DEBUG_NEW -#undef THIS_FILE -static char THIS_FILE[] = __FILE__; -#endif - -///////////////////////////////////////////////////////////////////////////// -// CRowDlg dialog - -IMPLEMENT_DYNAMIC(CRowDlg, CDialog); - -CRowDlg::CRowDlg(UINT Template, CWnd* pParent /*=NULL*/) - : CDialog(Template, pParent) -{ - //{{AFX_DATA_INIT(CRowDlg) - //}}AFX_DATA_INIT - m_RowIdx = 0; - m_RowPos = 0; -} - -void CRowDlg::DoDataExchange(CDataExchange* pDX) -{ - CDialog::DoDataExchange(pDX); - //{{AFX_DATA_MAP(CRowDlg) - //}}AFX_DATA_MAP -} - -BEGIN_MESSAGE_MAP(CRowDlg, CDialog) - //{{AFX_MSG_MAP(CRowDlg) - //}}AFX_MSG_MAP -END_MESSAGE_MAP() - -///////////////////////////////////////////////////////////////////////////// -// CRowDlg message handlers - -void CRowDlg::OnOK() -{ -} - -void CRowDlg::OnCancel() -{ -} - -BOOL CRowDlg::PreTranslateMessage(MSG* pMsg) -{ - if (pMsg->message >= WM_KEYFIRST && pMsg->message <= WM_KEYLAST) { - CRowView *pView = GetView(); - CWnd *pAccelWnd; - HACCEL Accel = pView->GetAccel(pAccelWnd); - if (pAccelWnd != NULL) { - if (Accel != NULL) { - if (TranslateAccelerator(pAccelWnd->m_hWnd, Accel, pMsg)) - return(TRUE); - } else { - if (pMsg->message == WM_KEYDOWN // for non-system keys only - && pAccelWnd->SendMessage(UWM_HANDLEDLGKEY, (WPARAM)pMsg)) - return(TRUE); - } - } - if (pMsg->message == WM_KEYDOWN && pMsg->wParam == VK_ADD - && (GetKeyState(VK_CONTROL) & GKS_DOWN)) { - pView->ResetColumnWidths(); - return(TRUE); - } - } - // NOTE that the derived row dialog's resource is now assumed to have the - // DS_CONTROL style, which makes a dialog work well as a child of another - // dialog, e.g. by integrating the child dialog's tab layout into the tab - // layout of its parent; in previous versions, the parent handled tabbing - // explicitly, so we detected the tab key here and send a notification. - return CDialog::PreTranslateMessage(pMsg); -} diff --git a/RowDlg.h b/RowDlg.h deleted file mode 100644 index b3a868b..0000000 --- a/RowDlg.h +++ /dev/null @@ -1,114 +0,0 @@ -// Copyleft 2005 Chris Korda -// This program is free software; you can redistribute it and/or modify it -// under the terms of the GNU General Public License as published by the Free -// Software Foundation; either version 2 of the License, or any later version. -/* - chris korda - - revision history: - rev date comments - 00 13aug05 initial version - 01 20apr10 refactor - - row dialog base class - -*/ - -#if !defined(AFX_ROWDLG_H__A2704F05_FC3B_4FF6_AAEF_9A1FB2527928__INCLUDED_) -#define AFX_ROWDLG_H__A2704F05_FC3B_4FF6_AAEF_9A1FB2527928__INCLUDED_ - -#if _MSC_VER > 1000 -#pragma once -#endif // _MSC_VER > 1000 -// RowDlg.h : header file -// - -///////////////////////////////////////////////////////////////////////////// -// CRowDlg dialog - -#include "RowView.h" - -class CRowDlg : public CDialog -{ - DECLARE_DYNAMIC(CRowDlg); -// Construction -public: - CRowDlg(UINT Template, CWnd* pParent = NULL); - -// Attributes - CRowView *GetView() const; - CWnd *GetNotifyWnd() const; - HACCEL GetAccel(CWnd*& AccelWnd) const; - int GetRowIndex() const; - void SetRowIndex(int Idx); - int GetRowPos() const; - void SetRowPos(int Pos); - -// Overrides - // ClassWizard generated virtual function overrides - //{{AFX_VIRTUAL(CRowDlg) - public: - virtual BOOL PreTranslateMessage(MSG* pMsg); - protected: - virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support - virtual void OnOK(); - virtual void OnCancel(); - //}}AFX_VIRTUAL - -// Implementation -protected: -// Generated message map functions - //{{AFX_MSG(CRowDlg) - //}}AFX_MSG - DECLARE_MESSAGE_MAP() - -// Dialog Data - //{{AFX_DATA(CRowDlg) - //}}AFX_DATA - -// Member data - int m_RowIdx; // row's index in parent array - int m_RowPos; // row's display position; may differ from index -}; - -inline CRowView *CRowDlg::GetView() const -{ - CWnd *pForm = GetParent(); - ASSERT(DYNAMIC_DOWNCAST(CRowView, pForm->GetParent())); - return((CRowView *)pForm->GetParent()); -} - -inline CWnd *CRowDlg::GetNotifyWnd() const -{ - return(GetView()->GetNotifyWnd()); -} - -inline HACCEL CRowDlg::GetAccel(CWnd*& AccelWnd) const -{ - return(GetView()->GetAccel(AccelWnd)); -} - -inline int CRowDlg::GetRowIndex() const -{ - return(m_RowIdx); -} - -inline void CRowDlg::SetRowIndex(int Idx) -{ - m_RowIdx = Idx; -} - -inline int CRowDlg::GetRowPos() const -{ - return(m_RowPos); -} - -inline void CRowDlg::SetRowPos(int Pos) -{ - m_RowPos = Pos; -} - -//{{AFX_INSERT_LOCATION}} -// Microsoft Visual C++ will insert additional declarations immediately before the previous line. - -#endif // !defined(AFX_ROWDLG_H__A2704F05_FC3B_4FF6_AAEF_9A1FB2527928__INCLUDED_) diff --git a/RowForm.cpp b/RowForm.cpp deleted file mode 100644 index 2de0a0f..0000000 --- a/RowForm.cpp +++ /dev/null @@ -1,139 +0,0 @@ -// Copyleft 2005 Chris Korda -// This program is free software; you can redistribute it and/or modify it -// under the terms of the GNU General Public License as published by the Free -// Software Foundation; either version 2 of the License, or any later version. -/* - chris korda - - revision history: - rev date comments - 00 17may05 initial version - 01 21jul05 make room for header in CalcWindowRect - 02 07sep05 verify dynamic downcast in CalcWindowRect - 03 22dec06 create dialog bar variant from CRowDialogForm - 04 16jul07 relay keyboard messages to parent - 05 20apr10 refactor - 06 12may11 avoid focus, pass it to parent row view - - row form - -*/ - -// RowForm.cpp : implementation file -// - -#include "stdafx.h" -#include "Resource.h" -#include "RowForm.h" - -#ifdef _DEBUG -#define new DEBUG_NEW -#undef THIS_FILE -static char THIS_FILE[] = __FILE__; -#endif - -///////////////////////////////////////////////////////////////////////////// -// CRowForm - -IMPLEMENT_DYNCREATE(CRowForm, CFormView) - -CRowForm::CRowForm() : - CFormView(IDD_ROW_FORM) -{ - //{{AFX_DATA_INIT(CRowForm) - //}}AFX_DATA_INIT -} - -CRowForm::~CRowForm() -{ -} - -BOOL CRowForm::Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, CCreateContext* pContext) -{ - return CFormView::Create(lpszClassName, lpszWindowName, dwStyle, rect, pParentWnd, nID, pContext); -} - -BOOL CRowForm::PreCreateWindow(CREATESTRUCT& cs) -{ - // override default window class styles CS_HREDRAW and CS_VREDRAW - // otherwise resizing frame redraws entire view, causing flicker - CWinApp *pApp = AfxGetApp(); - cs.lpszClass = AfxRegisterWndClass( // create our own window class - CS_DBLCLKS, // request double-clicks - pApp->LoadStandardCursor(IDC_ARROW), // standard cursor - NULL, // no background brush - pApp->LoadIcon(IDR_MAINFRAME)); // app's icon - return CFormView::PreCreateWindow(cs); -} - -void CRowForm::OnDraw(CDC* pDC) -{ - CRect cb; - pDC->GetClipBox(cb); - pDC->FillSolidRect(cb, GetSysColor(COLOR_3DFACE)); -} - -BEGIN_MESSAGE_MAP(CRowForm, CFormView) - //{{AFX_MSG_MAP(CRowForm) - ON_WM_MOUSEACTIVATE() - ON_WM_HSCROLL() - ON_WM_CREATE() - ON_WM_SETFOCUS() - //}}AFX_MSG_MAP -END_MESSAGE_MAP() - -///////////////////////////////////////////////////////////////////////////// -// CRowForm diagnostics - -#ifdef _DEBUG -void CRowForm::AssertValid() const -{ - CFormView::AssertValid(); -} - -void CRowForm::Dump(CDumpContext& dc) const -{ - CFormView::Dump(dc); -} -#endif //_DEBUG - -///////////////////////////////////////////////////////////////////////////// -// CRowForm message handlers - -int CRowForm::OnCreate(LPCREATESTRUCT lpCreateStruct) -{ - if (CFormView::OnCreate(lpCreateStruct) == -1) - return -1; - - SetScrollSizes(MM_TEXT, CSize(0, 0)); - - return 0; -} - -int CRowForm::OnMouseActivate(CWnd* pDesktopWnd, UINT nHitTest, UINT message) -{ - return MA_ACTIVATE; // don't call base class, prevents assertion -} - -void CRowForm::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) -{ - GetParent()->PostMessage(WM_HSCROLL, MAKELONG(nSBCode, nPos), NULL); - CFormView::OnHScroll(nSBCode, nPos, pScrollBar); -} - -BOOL CRowForm::PreTranslateMessage(MSG* pMsg) -{ - if (pMsg->message >= WM_KEYFIRST && pMsg->message <= WM_KEYLAST) - GetParent()->SendMessage(pMsg->message, pMsg->wParam, pMsg->lParam); - return CFormView::PreTranslateMessage(pMsg); -} - -void CRowForm::OnSetFocus(CWnd* pOldWnd) -{ - // if one of our child row dialogs is deleted while one of its controls - // has focus, we get focus, which causes UI bugs, e.g. opening a recent - // file causes an extra file menu popup; pass focus to parent row view - CWnd *Parent = GetParent(); - if (Parent != NULL) - GetParent()->SetFocus(); -} diff --git a/RowForm.h b/RowForm.h deleted file mode 100644 index cf3dfae..0000000 --- a/RowForm.h +++ /dev/null @@ -1,82 +0,0 @@ -// Copyleft 2005 Chris Korda -// This program is free software; you can redistribute it and/or modify it -// under the terms of the GNU General Public License as published by the Free -// Software Foundation; either version 2 of the License, or any later version. -/* - chris korda - - revision history: - rev date comments - 00 17may05 initial version - 01 22dec06 create dialog bar variant from CRowDialogForm - 02 16jul07 relay keyboard messages to parent - 03 20apr10 refactor - 04 12may11 add OnSetFocus - - row form - -*/ - -#if !defined(AFX_ROWFORM_H__F9AB8865_A0D5_49C5_97A9_9A1E2F74BEB1__INCLUDED_) -#define AFX_ROWFORM_H__F9AB8865_A0D5_49C5_97A9_9A1E2F74BEB1__INCLUDED_ - -#if _MSC_VER > 1000 -#pragma once -#endif // _MSC_VER > 1000 -// RowFormView.h : header file -// - -///////////////////////////////////////////////////////////////////////////// -// CRowForm form view - -#ifndef __AFXEXT_H__ -#include -#endif - -class CRowForm : public CFormView -{ - DECLARE_DYNCREATE(CRowForm) -// Construction -public: - CRowForm(); - virtual BOOL Create(LPCTSTR lpszClassName, LPCTSTR lpszWindowName, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID, CCreateContext* pContext = NULL); - -// Attributes -public: - -// Operations -public: - -// Overrides - // ClassWizard generated virtual function overrides - //{{AFX_VIRTUAL(CRowForm) - public: - virtual BOOL PreCreateWindow(CREATESTRUCT& cs); - virtual BOOL PreTranslateMessage(MSG* pMsg); - virtual void OnDraw(CDC* pDC); // overridden to draw this view - //}}AFX_VIRTUAL - -// Implementation -protected: - virtual ~CRowForm(); -#ifdef _DEBUG - virtual void AssertValid() const; - virtual void Dump(CDumpContext& dc) const; -#endif - -// Generated message map functions - //{{AFX_MSG(CRowForm) - afx_msg int OnMouseActivate(CWnd* pDesktopWnd, UINT nHitTest, UINT message); - afx_msg void OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar); - afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); - afx_msg void OnSetFocus(CWnd* pOldWnd); - //}}AFX_MSG - DECLARE_MESSAGE_MAP() -}; - -///////////////////////////////////////////////////////////////////////////// - -//{{AFX_INSERT_LOCATION}} -// Microsoft Visual C++ will insert additional declarations immediately before the previous line. - -#endif // !defined(AFX_ROWFORM_H__F9AB8865_A0D5_49C5_97A9_9A1E2F74BEB1__INCLUDED_) diff --git a/RowView.cpp b/RowView.cpp deleted file mode 100644 index 815530d..0000000 --- a/RowView.cpp +++ /dev/null @@ -1,568 +0,0 @@ -// Copyleft 2005 Chris Korda -// This program is free software; you can redistribute it and/or modify it -// under the terms of the GNU General Public License as published by the Free -// Software Foundation; either version 2 of the License, or any later version. -/* - chris korda - - revision history: - rev date comments - 00 15jul05 initial version - 01 21jul05 CreateRows must reposition scroll bars - 02 16aug05 add GetActiveRow, IsTabStop - 03 06sep05 disabled controls can't be tab stops - 04 12sep05 in tab handler, get position from row - 05 17feb06 add ReplaceRows and RemoveAllRows - 06 22dec06 create dialog bar variant from CRowDialog - 07 19jan07 in ReplaceRows, allow zero Rows arg - 08 11jul07 remove OnRowFrameTab - 09 11jul07 in ReplaceRows, replace MoveWindow with SetWindowPos - 10 23nov07 support Unicode - 11 20apr10 refactor - 12 14may11 in CreateCols, fix header column aligment - 13 11nov11 add FixContextMenuPos - 14 11nov11 fix keyboard-triggered context menu - 15 24nov11 add get/set scroll position - 16 21jan12 remove lock window update to fix desktop flicker - 17 21jan12 in CreateRows, scroll before updating row dialogs - 18 31mar12 in MoveRow, make defer pos an argument - 19 06apr12 add column resizing - 20 19nov13 in CreateCols, create row dialog via CreateRow - - row view - -*/ - -// RowView.cpp : implementation file -// - -#include "stdafx.h" -#include "Resource.h" -#include "RowView.h" -#include "RowDlg.h" -#include "RowForm.h" -#include "Persist.h" - -#ifdef _DEBUG -#define new DEBUG_NEW -#undef THIS_FILE -static char THIS_FILE[] = __FILE__; -#endif - -///////////////////////////////////////////////////////////////////////////// -// CRowView dialog - -IMPLEMENT_DYNCREATE(CRowView, CView); - -const CRect CRowView::m_Margin(0, 4, 0, 0); - -#define RK_COLUMN_WIDTH _T("CW") // column width registry key suffix - -CRowView::CRowView() -{ - //{{AFX_DATA_INIT(CRowView) - //}}AFX_DATA_INIT - m_Cols = 0; - m_ColInfo = NULL; - m_Form = NULL; - m_TopMargin = 0; - m_HdrHeight = HEADER_HEIGHT; - m_NotifyWnd = NULL; - m_Accel = NULL; - m_AccelWnd = NULL; - m_Reorderable = FALSE; - m_HaveScrollPos = FALSE; - m_ScrollPos = CPoint(0, 0); - m_RowDlgSize = CSize(0, 0); - m_RowFirstCtrlX = 0; - m_RowCtrlCount = 0; -} - -BOOL CRowView::PreCreateWindow(CREATESTRUCT& cs) -{ - // override default window class styles CS_HREDRAW and CS_VREDRAW - // otherwise resizing frame redraws entire view, causing flicker - CWinApp *pApp = AfxGetApp(); - cs.lpszClass = AfxRegisterWndClass( // create our own window class - CS_DBLCLKS, // request double-clicks - pApp->LoadStandardCursor(IDC_ARROW), // standard cursor - NULL, // no background brush - pApp->LoadIcon(IDR_MAINFRAME)); // app's icon - return CView::PreCreateWindow(cs); -} - -bool CRowView::ReadColumnWidths(CColWidthArray& ColWidth) const -{ - int cols = GetCols(); - ColWidth.SetSize(cols); - int nID = GetDlgCtrlID(); - CString s((LPCTSTR)nID); - s += RK_COLUMN_WIDTH; - DWORD ExpectedSize = cols * sizeof(int); - DWORD size = ExpectedSize; - return(CPersist::GetBinary(REG_SETTINGS, s, ColWidth.GetData(), &size) - && size == ExpectedSize); -} - -bool CRowView::WriteColumnWidths() const -{ - int cols = GetCols(); - CColWidthArray ColWidth; - ColWidth.SetSize(cols); - for (int iCol = 0; iCol < cols; iCol++) - ColWidth[iCol] = m_ColState[iCol].CurWidth; - int nID = GetDlgCtrlID(); - CString s((LPCTSTR)nID); - s += RK_COLUMN_WIDTH; - DWORD size = cols * sizeof(int); - return(CPersist::WriteBinary(REG_SETTINGS, s, ColWidth.GetData(), size) != 0); -} - -bool CRowView::CreateCols(int Cols, const COLINFO *ColInfo) -{ - if (Cols <= 0 || m_Cols) - return(FALSE); - // create dummy row before setting m_Cols, so CreateRow method can - // easily identify this case if needed, by testing for m_Cols == 0 - CRowDlg *rp = CreateRow(0); // create dummy row dialog - ASSERT(rp != NULL); - m_ColState.SetSize(Cols); - m_Cols = Cols; - m_ColInfo = ColInfo; - CRect cr, dr; - rp->GetWindowRect(dr); // get row dialog's rectangle - ScreenToClient(dr); - m_RowDlgSize = dr.Size(); - m_RowCtrlCount = Cols; // one row control per column initially - CColWidthArray RegColWidth; - bool HaveRegColWidths = ReadColumnWidths(RegColWidth); - int x = 0; - int TotalCurWidth = 0; - // one extra loop because column widths are set in arrears - for (int iCol = 0; iCol <= Cols; iCol++) { - if (iCol < Cols) { // if not last loop - CWnd *RowCtrl = rp->GetDlgItem(ColInfo[iCol].CtrlID); - RowCtrl->GetWindowRect(cr); - // convert control rectangle to row dialog's coords, not view's, - // else header columns don't line up with controls in some cases - rp->ScreenToClient(cr); // 14may11 added rp - m_ColState[iCol].CtrlInitWidth = cr.Width(); - // if control is an edit control - TCHAR szClassName[16]; - if (GetClassName(RowCtrl->m_hWnd, szClassName, 16) - && !_tcsicmp(szClassName, _T("Edit"))) { - // if next control is a spin control - CWnd *NextCtrl = RowCtrl->GetNextWindow(); - if (NextCtrl != NULL - && GetClassName(NextCtrl->m_hWnd, szClassName, 16) - && !_tcsicmp(szClassName, _T("msctls_updown32"))) { - // only right-aligned spin controls are supported - ASSERT(NextCtrl->GetStyle() & UDS_ALIGNRIGHT); - // spin control requires special handling because it - // doesn't automatically move with its buddy edit control - m_ColState[iCol].SpinCtrlID = NextCtrl->GetDlgCtrlID(); - CRect sr; - NextCtrl->GetWindowRect(sr); - rp->ScreenToClient(sr); - m_ColState[iCol].SpinInitWidth = sr.Width(); - m_RowCtrlCount++; // bump row control count - } - } - } else // last loop - cr.left = dr.Width(); // last column boundary is row dialog width - if (iCol > 0) { // if not first column - int iPrevCol = iCol - 1; - int cx = cr.left - x; // initial default column width - m_ColState[iPrevCol].InitWidth = cx; - if (HaveRegColWidths) - cx = RegColWidth[iPrevCol]; // restore persistent column width - m_ColState[iPrevCol].CurWidth = cx; - TotalCurWidth += cx; - HDITEM item; - item.mask = HDI_TEXT | HDI_FORMAT | HDI_WIDTH; - CString s((LPCTSTR)ColInfo[iPrevCol].TitleID); - item.pszText = s.GetBuffer(0); - item.fmt = HDF_CENTER; - item.cxy = cx; - m_Hdr.InsertItem(iPrevCol, &item); - x = cr.left; - } else // first column - m_RowFirstCtrlX = cr.left; - } - // compensate row dialog width for non-default column widths - int TotalDeltaWidth = TotalCurWidth - x; - m_RowDlgSize.cx += TotalDeltaWidth; - m_Form->SetScrollSizes(MM_TEXT, CSize(0, 0)); // reset scroll bars - MoveHeader(); // set header's initial position - rp->DestroyWindow(); - delete rp; - return(TRUE); -} - -void CRowView::RemoveAllRows() -{ - if (!m_Row.GetSize()) - return; // nothing to do - for (int iRow = 0; iRow < m_Row.GetSize(); iRow++) { - GetRow(iRow)->DestroyWindow(); - delete GetRow(iRow); - } - m_Row.RemoveAll(); - if (m_Form->m_hWnd) { // dialog may have been destroyed - m_Form->SetScrollSizes(MM_TEXT, CSize(0, 0)); // reset scroll bars - MoveHeader(); // set header's initial position - } -} - -inline void CRowView::MoveRow(HDWP DeferPos, CRowDlg& Row, int Pos) -{ - CPoint sp = m_Form->GetScrollPosition(); // compensate for scroll position - UINT flags = SWP_NOSIZE | SWP_SHOWWINDOW; - DeferWindowPos(DeferPos, Row, HWND_BOTTOM, m_Margin.left - sp.x, - m_Margin.top + Pos * m_RowDlgSize.cy - sp.y, 0, 0, flags); -} - -bool CRowView::CreateRows(int Rows) -{ - if (Rows < 0 || m_Form == NULL) // form must already exist - return(FALSE); - // scroll BEFORE updating row dialogs to avoid painting them prematurely - if (Rows) { - // set view's scrollable area and update scroll bars - CSize ViewArea(m_Margin.left + m_RowDlgSize.cx + m_Margin.right, - m_Margin.top + m_RowDlgSize.cy * Rows + m_Margin.bottom); - m_Form->SetScrollSizes(MM_TEXT, ViewArea); - RepositionBars(0, 0, AFX_IDW_PANE_FIRST, CWnd::reposDefault); - // set window's maximum size - CRect cr; - GetClientRect(cr); - if (m_HaveScrollPos) { // if target scroll position is valid - // compensate scroll position for view area and client rect - m_Form->GetClientRect(cr); - CSize MaxScrollPos( - max(ViewArea.cx - cr.Width(), 0), - max(ViewArea.cy - cr.Height(), 0)); - m_ScrollPos.x = CLAMP(m_ScrollPos.x, 0, MaxScrollPos.cx); - m_ScrollPos.y = CLAMP(m_ScrollPos.y, 0, MaxScrollPos.cy); - m_Form->ScrollToPosition(m_ScrollPos); - m_HaveScrollPos = FALSE; - MoveHeader(); - } - } else - m_Form->SetScrollSizes(MM_TEXT, CSize(0, 0)); - // update and add/delete row dialogs - int PrevRows = GetRows(); - int Updates = min(Rows, PrevRows); - HDWP DeferPos = BeginDeferWindowPos(Rows); // defer positions to reduce flicker - for (int iRow = 0; iRow < Updates; iRow++) { // update existing rows - CRowDlg *rp = GetRow(iRow); - int PrevPos = rp->GetRowPos(); // save row position - UpdateRow(iRow); - int pos = m_Reorderable ? rp->GetRowPos() : iRow; - if (pos != PrevPos) // if row position changed - MoveRow(DeferPos, *rp, pos); - } - if (Rows > PrevRows) { // if adding rows - m_Row.SetSize(Rows); - for (int iRow = PrevRows; iRow < Rows; iRow++) { - CRowDlg *rp = CreateRow(iRow); - if (rp == NULL) - return(FALSE); - m_Row[iRow] = rp; - rp->SetParent(m_Form); // row scrolls with view - rp->SetRowIndex(iRow); - int pos = m_Reorderable ? rp->GetRowPos() : iRow; - MoveRow(DeferPos, *rp, pos); - } - // find first column with non-default width, if any - int iCol; - int nCols = GetCols(); - for (iCol = 0; iCol < nCols; iCol++) { - if (m_ColState[iCol].CurWidth != m_ColState[iCol].InitWidth) - break; - } - if (iCol < nCols) { // if column resizing needed - int width = m_ColState[iCol].CurWidth; - m_ColState[iCol].CurWidth = -1; // spoof no-op test - ResizeColumn(iCol, width, PrevRows); // resize column - } - } else { // deleting rows - for (int iRow = Rows; iRow < PrevRows; iRow++) { - GetRow(iRow)->DestroyWindow(); - delete GetRow(iRow); - } - m_Row.SetSize(Rows); - } - EndDeferWindowPos(DeferPos); - m_HaveScrollPos = FALSE; - return(TRUE); -} - -bool CRowView::CreateRows(int Rows, CPoint ScrollPos) -{ - m_HaveScrollPos = TRUE; - m_ScrollPos = ScrollPos; - return(CreateRows(Rows)); -} - -CPoint CRowView::GetScrollPos() const -{ - return(m_Form->GetScrollPosition()); -} - -CRowDlg *CRowView::CreateRow(int Idx) -{ - return(NULL); -} - -void CRowView::UpdateRow(int Idx) -{ -} - -void CRowView::MoveHeader() -{ - CRect fr, vr; - GetClientRect(fr); - m_Form->GetWindowRect(vr); - ScreenToClient(vr); - fr.left -= m_Form->GetScrollPosition().x; - fr.bottom = vr.top; - fr.top = vr.top - m_HdrHeight; - m_Hdr.MoveWindow(fr); -} - -int CRowView::GetActiveRow() const -{ - CWnd *wp = GetFocus(); - if (wp != NULL) { - int rows = GetRows(); - for (int iRow = 0; iRow < rows; iRow++) { - if (GetRow(iRow)->IsChild(wp)) - return(iRow); - } - } - return(-1); -} - -void CRowView::ResizeColumn(int ColIdx, int Width, int FirstRow) -{ - int rows = GetRows(); - int cols = GetCols(); - if (Width == m_ColState[ColIdx].CurWidth) // if column width unchanged - return; - // build control span array - int x = 0; - CCtrlSpanArray CtrlSpan; - CtrlSpan.SetSize(m_RowCtrlCount); // count includes spin controls - int nCtrls = 0; - int iResizedCtrl = 0; - for (int iCol = 0; iCol < cols; iCol++) { - int cx; - COL_STATE& state = m_ColState[iCol]; - if (iCol == ColIdx) { // if column is resizing - cx = Width; - state.CurWidth = cx; // set column's current width - iResizedCtrl = nCtrls; // save index of resized control - } else // column isn't resizing - cx = state.CurWidth; // use column's current width - CTRL_SPAN span; - if (!iCol) // if first column - span.left = m_RowFirstCtrlX; - else // not first column - span.left = x; - int gutter = state.InitWidth - state.CtrlInitWidth; - span.nID = m_ColInfo[iCol].CtrlID; - span.right = span.left + cx - gutter; - span.right = max(span.right, x); // avoid negative width - CtrlSpan[nCtrls] = span; - nCtrls++; - if (state.SpinCtrlID) { // if control has attached spin control - CTRL_SPAN ss; // add spin control's span to span array - const int SPIN_BUDDY_OVERLAP = 2; - ss.nID = state.SpinCtrlID; - ss.left = span.left + cx - gutter - SPIN_BUDDY_OVERLAP; - ss.right = ss.left + m_ColState[iCol].SpinInitWidth; - ss.left = max(ss.left, span.left); // don't extend beyond buddy - CtrlSpan[nCtrls] = ss; - nCtrls++; - } - x += cx; - } - m_RowDlgSize.cx = x; // update row dialog width - // resize row dialogs and their controls - CRect r; - m_Form->ShowWindow(SW_HIDE); // somewhat reduces flicker - HDWP RowDeferPos = BeginDeferWindowPos(rows); - for (int iRow = FirstRow; iRow < rows; iRow++) { - CWnd *pRow = GetRow(iRow); - HDWP DeferPos = BeginDeferWindowPos(nCtrls - iResizedCtrl); - for (int iCtrl = iResizedCtrl; iCtrl < nCtrls; iCtrl++) { - const CTRL_SPAN& span = CtrlSpan[iCtrl]; - CWnd *pCtrl = pRow->GetDlgItem(span.nID); - pCtrl->GetWindowRect(r); - pRow->ScreenToClient(r); - r.left = span.left; - r.right = span.right; - UINT flags = SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOACTIVATE; - DeferPos = DeferWindowPos(DeferPos, pCtrl->m_hWnd, - NULL, r.left, r.top, r.Width(), r.Height(), flags); -//printf("%d %d %d %d\n", r.left, r.top, r.bottom, r.right); - } - EndDeferWindowPos(DeferPos); - UINT flags = SWP_NOMOVE | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOACTIVATE; - RowDeferPos = DeferWindowPos(RowDeferPos, pRow->m_hWnd, - NULL, 0, 0, m_RowDlgSize.cx, m_RowDlgSize.cy, flags); - } - EndDeferWindowPos(RowDeferPos); - m_Form->ShowWindow(SW_SHOW); -} - -void CRowView::SetColumnWidth(int ColIdx, int Width, UINT Flags) -{ - if (Width < 0) // if width is LVSCW_AUTOSIZE - Width = m_ColState[ColIdx].InitWidth; // restore initial width - ResizeColumn(ColIdx, Width); - CSize sz = m_Form->GetTotalSize(); - sz.cx = m_Margin.left + m_RowDlgSize.cx + m_Margin.right; - m_Form->SetScrollSizes(MM_TEXT, sz); - m_Form->UpdateWindow(); // prevents sloppy painting for large row counts - if (Flags & SCW_RESIZE_HEADER) { // if resizing header - int iFirstCol, iLastCol; - if (Flags & SCW_ALL_COLUMNS) { // if resizing all columns - iFirstCol = 0; - iLastCol = GetCols(); - } else { // resize specifed column only - iFirstCol = ColIdx; - iLastCol = ColIdx + 1; - } - for (int iCol = iFirstCol; iCol < iLastCol; iCol++) { - HDITEM item; - item.mask = HDI_WIDTH; - item.cxy = m_ColState[iCol].CurWidth; - m_Hdr.SetItem(iCol, &item); // resize header item to match column - } - } - MoveHeader(); // looks weird but standard behavior, same as list control -} - -void CRowView::ResetColumnWidths() -{ - int cols = GetCols(); - for (int iCol = 1; iCol < cols; iCol++) - m_ColState[iCol].CurWidth = m_ColState[iCol].InitWidth; - m_ColState[0].CurWidth = -1; // spoof no-op test - SetColumnWidth(0, LVSCW_AUTOSIZE, SCW_RESIZE_HEADER | SCW_ALL_COLUMNS); -} - -void CRowView::OnDraw(CDC* pDC) -{ -} - -BEGIN_MESSAGE_MAP(CRowView, CView) - //{{AFX_MSG_MAP(CRowView) - ON_WM_SIZE() - ON_WM_DESTROY() - ON_WM_HSCROLL() - ON_WM_CREATE() - ON_WM_KEYDOWN() - //}}AFX_MSG_MAP - ON_NOTIFY(HDN_ITEMCHANGING, HEADER_ID, OnHdrItemChanging) - ON_NOTIFY(HDN_DIVIDERDBLCLICK, HEADER_ID, OnHdrDividerDblClick) -END_MESSAGE_MAP() - -///////////////////////////////////////////////////////////////////////////// -// CRowView message handlers - -int CRowView::OnCreate(LPCREATESTRUCT lpCreateStruct) -{ - if (CView::OnCreate(lpCreateStruct) == -1) - return -1; - - // create form - CRuntimeClass *pFactory = RUNTIME_CLASS(CRowForm); - m_Form = DYNAMIC_DOWNCAST(CRowForm, pFactory->CreateObject()); - if (m_Form == NULL) - return -1; - DWORD dwStyle = WS_CHILD | WS_VISIBLE; - CRect r(0, 0, 0, 0); // arbitrary initial size - if (!m_Form->Create(NULL, NULL, dwStyle, r, this, 0, NULL)) - return -1; - - // create header control - GetClientRect(r); - r.bottom = m_HdrHeight; - m_Hdr.Create(HDS_HORZ | HDS_FULLDRAG, r, this, HEADER_ID); - m_Hdr.SetFont(GetFont()); - m_Hdr.ShowWindow(SW_SHOW); - m_Hdr.SendMessage(WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), 0); - - return 0; -} - -void CRowView::OnDestroy() -{ - WriteColumnWidths(); - RemoveAllRows(); - CView::OnDestroy(); -} - -void CRowView::OnSize(UINT nType, int cx, int cy) -{ - CView::OnSize(nType, cx, cy); - if (m_Form != NULL) { - CRect r; - GetClientRect(r); - r.top += m_HdrHeight + m_TopMargin; // leave room for header - m_Form->MoveWindow(r); - MoveHeader(); - } -} - -void CRowView::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) -{ - MoveHeader(); - CView::OnHScroll(nSBCode, nPos, pScrollBar); -} - -bool CRowView::FixContextMenuPos(CPoint& point) const -{ - if (point.x == -1 && point.y == -1) { // if menu triggered via keyboard - CRect r; - CWnd *pWnd = GetFocus(); - int rows = GetRows(); - int i; - for (i = 0; i < rows; i++) { - if (GetRow(i)->IsChild(pWnd)) { // if one of our children has focus - pWnd->GetWindowRect(r); // position menu over child window - break; - } - } - if (i >= rows) // if focused window wasn't one of our children - GetWindowRect(r); // position menu in top left corner of view - point = r.TopLeft() + CSize(10, 10); // offset looks nicer - return(TRUE); - } - return(FALSE); -} - -void CRowView::OnHdrItemChanging(NMHDR* pNMHDR, LRESULT* pResult) -{ - LPNMHEADER phdr = (LPNMHEADER)pNMHDR; - if (phdr->pitem->mask & HDI_WIDTH) // if width changed - SetColumnWidth(phdr->iItem, phdr->pitem->cxy, 0); // don't resize header - *pResult = 0; -} - -void CRowView::OnHdrDividerDblClick(NMHDR* pNMHDR, LRESULT* pResult) -{ - LPNMHEADER phdr = (LPNMHEADER)pNMHDR; - SetColumnWidth(phdr->iItem, LVSCW_AUTOSIZE); - *pResult = 0; -} - -void CRowView::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) -{ - if (nChar == VK_ADD && (GetKeyState(VK_CONTROL) & GKS_DOWN)) - ResetColumnWidths(); - CView::OnKeyDown(nChar, nRepCnt, nFlags); -} diff --git a/RowView.h b/RowView.h deleted file mode 100644 index 0de0531..0000000 --- a/RowView.h +++ /dev/null @@ -1,245 +0,0 @@ -// Copyleft 2005 Chris Korda -// This program is free software; you can redistribute it and/or modify it -// under the terms of the GNU General Public License as published by the Free -// Software Foundation; either version 2 of the License, or any later version. -/* - chris korda - - revision history: - rev date comments - 00 15jul05 initial version - 01 17feb06 add ReplaceRows and RemoveAllRows - 02 22dec06 create dialog bar variant from CRowDialog - 03 11jul07 remove OnRowFrameTab - 04 23nov07 support Unicode - 05 20apr10 refactor - 06 11nov11 add FixContextMenuPos - 07 24nov11 add get/set scroll position - 08 21jan12 add row dialog size - 09 31mar12 in MoveRow, make defer pos an argument - 10 06apr12 add column resizing - 11 19nov13 in CreateCols, remove RowDlgID argument - - row view - -*/ - -#if !defined(AFX_ROWVIEW_H__53C410DA_1109_40AF_B567_7D7918C63980__INCLUDED_) -#define AFX_ROWVIEW_H__53C410DA_1109_40AF_B567_7D7918C63980__INCLUDED_ - -#if _MSC_VER > 1000 -#pragma once -#endif // _MSC_VER > 1000 -// RowView.h : header file -// - -///////////////////////////////////////////////////////////////////////////// -// CRowView dialog - -#include "ArrayEx.h" - -class CRowForm; -class CRowDlg; - -class CRowView : public CView -{ - DECLARE_DYNCREATE(CRowView); -// Construction -public: - CRowView(); - -// Types - typedef struct tagCOLINFO { - int CtrlID; - int TitleID; - } COLINFO; - -// Constants - enum { // set column width flags - SCW_RESIZE_HEADER = 0x01, - SCW_ALL_COLUMNS = 0x02, - }; - -// Attributes - int GetRows() const; - int GetCols() const; - CRowDlg *GetRow(int Idx) const; - CHeaderCtrl& GetHeader(); - int GetHeaderHeight() const; - int GetTopMargin() const; - void SetTopMargin(int Margin); - int GetActiveRow() const; - CWnd *GetNotifyWnd() const; - void SetNotifyWnd(CWnd *Wnd); - HACCEL GetAccel(CWnd*& AccelWnd) const; - void SetAccel(HACCEL Accel, CWnd *AccelWnd); - bool GetReorderable() const; - void SetReorderable(bool Enable); - CPoint GetScrollPos() const; - int GetColumnWidth(int ColIdx) const; - void SetColumnWidth(int ColIdx, int Width, UINT Flags = SCW_RESIZE_HEADER); - -// Operations - bool CreateCols(int Cols, const COLINFO *ColInfo); - bool CreateRows(int Rows); - bool CreateRows(int Rows, CPoint ScrollPos); - void RemoveAllRows(); - bool FixContextMenuPos(CPoint& point) const; - void ResetColumnWidths(); - -// Overrides - // ClassWizard generated virtual function overrides - //{{AFX_VIRTUAL(CRowView) - public: - virtual void OnDraw(CDC* pDC); // overridden to draw this view - virtual BOOL PreCreateWindow(CREATESTRUCT& cs); - //}}AFX_VIRTUAL - -// Implementation -protected: -// Generated message map functions - //{{AFX_MSG(CRowView) - afx_msg void OnSize(UINT nType, int cx, int cy); - afx_msg void OnDestroy(); - afx_msg void OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar); - afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); - afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags); - //}}AFX_MSG - afx_msg void OnHdrItemChanging(NMHDR* pNMHDR, LRESULT* pResult); - afx_msg void OnHdrDividerDblClick(NMHDR* pNMHDR, LRESULT* pResult); - DECLARE_MESSAGE_MAP() - -// Types - struct COL_STATE { - int InitWidth; // column's initial width - int CurWidth; // column's current width - int CtrlInitWidth; // initial width of column's control - int SpinCtrlID; // ID of attached spin control, if any - int SpinInitWidth; // initial width of attached spin control - }; - struct CTRL_SPAN { - int nID; // control's resource ID - int left; // span's left coordinate - int right; // span's right coordinate - }; - typedef CArrayEx CRowDlgArray; - typedef CArrayEx CColStateArray; - typedef CArrayEx CCtrlSpanArray; - typedef CArrayEx CColWidthArray; - -// Constants - static const CRect m_Margin; // extra margins around view - enum { - HEADER_ID = 0x1234, // non-zero to distinguish from CListCtrl header - HEADER_HEIGHT = 18, // height of header control, in logical coords - }; - -// Member data - int m_Cols; // number of columns in list - const COLINFO *m_ColInfo; // array of information about each column - CRowForm *m_Form; // pointer to form view, parent of rows - CRowDlgArray m_Row; // array of pointers to row dialogs - CHeaderCtrl m_Hdr; // list's header control - int m_TopMargin; // extra top margin for derived controls - int m_HdrHeight; // height of header control - CWnd *m_NotifyWnd; // notifications are sent to this window - HACCEL m_Accel; // if non-null, row's keyboard accelerators - CWnd *m_AccelWnd; // accelerators are translated by this window - bool m_Reorderable; // true if rows can be reordered - bool m_HaveScrollPos; // true if target scroll position is valid - CPoint m_ScrollPos; // target scroll position if any - CSize m_RowDlgSize; // size of row dialog in client coords - CColStateArray m_ColState; // array of column states - int m_RowFirstCtrlX; // x-coord of row's first control - int m_RowCtrlCount; // total number of controls in row - -// Overridables - virtual CRowDlg *CreateRow(int Idx); - virtual void UpdateRow(int Idx); - -// Overrides - -// Helpers - void MoveHeader(); - void MoveRow(HDWP DeferPos, CRowDlg& Row, int Pos); - void ResizeColumn(int ColIdx, int Width, int FirstRow = 0); - bool ReadColumnWidths(CColWidthArray& ColWidth) const; - bool WriteColumnWidths() const; -}; - -inline int CRowView::GetRows() const -{ - return(m_Row.GetSize()); -} - -inline int CRowView::GetCols() const -{ - return(m_Cols); -} - -inline CRowDlg *CRowView::GetRow(int Idx) const -{ - return((CRowDlg *)m_Row[Idx]); -} - -inline CHeaderCtrl& CRowView::GetHeader() -{ - return(m_Hdr); -} - -inline int CRowView::GetHeaderHeight() const -{ - return(m_HdrHeight); -} - -inline void CRowView::SetTopMargin(int Margin) -{ - m_TopMargin = Margin; -} - -inline int CRowView::GetTopMargin() const -{ - return(m_TopMargin); -} - -inline CWnd *CRowView::GetNotifyWnd() const -{ - return(m_NotifyWnd); -} - -inline void CRowView::SetNotifyWnd(CWnd *Wnd) -{ - m_NotifyWnd = Wnd; -} - -inline HACCEL CRowView::GetAccel(CWnd*& AccelWnd) const -{ - AccelWnd = m_AccelWnd; - return(m_Accel); -} - -inline void CRowView::SetAccel(HACCEL Accel, CWnd *AccelWnd) -{ - m_Accel = Accel; - m_AccelWnd = AccelWnd; -} - -inline bool CRowView::GetReorderable() const -{ - return(m_Reorderable); -} - -inline void CRowView::SetReorderable(bool Enable) -{ - m_Reorderable = Enable; -} - -inline int CRowView::GetColumnWidth(int ColIdx) const -{ - return(m_ColState[ColIdx].CurWidth); -} - -//{{AFX_INSERT_LOCATION}} -// Microsoft Visual C++ will insert additional declarations immediately before the previous line. - -#endif // !defined(AFX_ROWVIEW_H__53C410DA_1109_40AF_B567_7D7918C63980__INCLUDED_) diff --git a/Song.cpp b/Song.cpp index ad19fbb..36f1826 100644 --- a/Song.cpp +++ b/Song.cpp @@ -355,6 +355,21 @@ int CSong::FindSection(int BeatIdx) const return(m_Section.FindBeat(BeatIdx)); } +int CSong::FindSectionByChord(int ChordIdx) const +{ + return(m_Section.FindBeat(m_StartBeat[ChordIdx])); +} + +bool CSong::IsMergeable(int ChordIdx) const +{ + return((ChordIdx > 0 + && m_Chord[ChordIdx - 1].EqualNoDuration(m_Chord[ChordIdx]) + && FindSectionByChord(ChordIdx - 1) == FindSectionByChord(ChordIdx)) + || (ChordIdx < GetChordCount() - 1 + && m_Chord[ChordIdx + 1].EqualNoDuration(m_Chord[ChordIdx]) + && FindSectionByChord(ChordIdx + 1) == FindSectionByChord(ChordIdx))); +} + void CSong::ClosePrevSection(int Beats) { int PrevEnd; // end of previous section @@ -487,7 +502,7 @@ bool CSong::Read(LPCTSTR Path) return(FALSE); } if (!m_Meter.IsValidMeter()) { // if bogus time signature - ReportError(fp, IDS_SONG_BAD_METER, + ReportError(fp, IDS_SONG_ERR_BAD_METER, m_Meter.m_Numerator, m_Meter.m_Denominator); return(FALSE); } diff --git a/Song.h b/Song.h index f28803d..a04b711 100644 --- a/Song.h +++ b/Song.h @@ -39,8 +39,9 @@ class CSong : public WObject { public: CChord(); CChord(int Duration, CNote Root, CNote Bass, int Type); - bool operator==(const CChord& Chord) const; - bool operator!=(const CChord& Chord) const; + bool operator==(const CChord& Chord) const; + bool operator!=(const CChord& Chord) const; + bool EqualNoDuration(const CChord& Chord) const; int m_Duration; // duration in beats CNote m_Root; // normalized root note CNote m_Bass; // normalized bass note @@ -144,6 +145,8 @@ class CSong : public WObject { const CSection& GetSection(int SectionIdx) const; CString GetSectionName(int SectionIdx) const; int FindSection(int BeatIdx) const; + int FindSectionByChord(int ChordIdx) const; + bool IsMergeable(int ChordIdx) const; void GetProperties(CProperties& Props) const; void SetProperties(const CProperties& Props); CString GetComments() const; @@ -279,6 +282,12 @@ inline bool CSong::CChord::operator!=(const CChord& Chord) const return(!operator==(Chord)); } +inline bool CSong::CChord::EqualNoDuration(const CChord& Chord) const +{ + // binary compare; also assumes duration is first member, followed by root + return(!memcmp(&m_Root, &Chord.m_Root, sizeof(CChord) - sizeof(m_Duration))); +} + inline CSong::CChordInfo::CChordInfo() { } diff --git a/SongState.cpp b/SongState.cpp index b23cd51..a5f4f8e 100644 --- a/SongState.cpp +++ b/SongState.cpp @@ -84,9 +84,8 @@ void CSongState::MergeDuplicateChords() const CSong::CChord& chord = m_Chord[iChord]; CSong::CChord& PrevChord = m_Chord[iChord - 1]; // if chord and previous chord are identical except for duration - if (chord.m_Root == PrevChord.m_Root - && chord.m_Bass == PrevChord.m_Bass - && chord.m_Type == PrevChord.m_Type) { + if (chord.EqualNoDuration(PrevChord) + && m_SectionMap[iChord] == m_SectionMap[iChord - 1]) { // and belong to same section PrevChord.m_Duration += chord.m_Duration; // sum durations RemoveAt(iChord); // delete duplicate chord nChords--; @@ -132,7 +131,7 @@ CIntRange CSongState::FindChordRange(CIntRange BeatRange, CIntRange& Offset) con int CSongState::GetSection(int ChordIdx) { int iSection; - if (ChordIdx < GetChordCount()) // if inserting before end of chord array + if (ChordIdx < GetChordCount()) // if index within chord array iSection = m_SectionMap[ChordIdx]; // return this chord's section else { // inserting at end of chord array if (ChordIdx > 0) // if at least one chord @@ -236,6 +235,17 @@ int CSongState::MoveChords(CIntRange BeatRange, int Beat) return(Beat); // return possibly updated beat } +void CSongState::SetChord(CIntRange BeatRange, const CSong::CChord& Chord) +{ + RemoveChords(BeatRange); + MakeSectionMap(); // must rebuild section map before inserting + CSong::CChordArray ch; + ch.SetSize(1); + ch[0] = Chord; // copy caller's chord + ch[0].m_Duration = BeatRange.LengthInclusive(); // same duration as beat range + InsertChords(BeatRange.Start, ch); // insert chord at start of beat range +} + void CSongState::AssignToSection(CIntRange BeatRange, int SectionIdx) { CIntRange ChordRange, Offset; diff --git a/SongState.h b/SongState.h index cfac50b..9a88a6d 100644 --- a/SongState.h +++ b/SongState.h @@ -33,6 +33,7 @@ class CSongState : public WObject { const CSong::CChord& GetChord(int ChordIdx) const; void GetChords(CSong::CChordArray& Chord) const; void GetChords(CIntRange BeatRange, CSong::CChordArray& Chord) const; + void SetChord(CIntRange BeatRange, const CSong::CChord& Chord); // Operations int FindChord(int Beat, int& Offset) const; diff --git a/Version.h b/Version.h index df4938f..935cf76 100644 --- a/Version.h +++ b/Version.h @@ -66,6 +66,7 @@ 56 18may14 1.0.05.000 57 01jun14 1.0.06.000 58 02jun14 1.0.06.001 + 59 15jun14 1.0.07.000 DON'T FORGET TO CHANGE VERSION RESOURCE diff --git a/res/ChordEase.rc2 b/res/ChordEase.rc2 index f4c564f..33ad91d 100644 --- a/res/ChordEase.rc2 +++ b/res/ChordEase.rc2 @@ -16,8 +16,8 @@ // #define MAJOR_VERSION 1 #define MINOR_VERSION 0 -#define MAJOR_SUBVERSION 6 -#define MINOR_SUBVERSION 1 +#define MAJOR_SUBVERSION 7 +#define MINOR_SUBVERSION 0 ///////////////////////////////////////////////////////////////////////////// diff --git a/resource.h b/resource.h index a251de6..ef289a5 100644 --- a/resource.h +++ b/resource.h @@ -17,36 +17,35 @@ #define IDD_MIDI_ASSIGNS 113 #define IDD_MIDI_EVENT 114 #define IDD_MIDI_NOTE_MAP 115 -#define IDD_MIDI_TARGET_ROW 116 -#define IDD_NOTEPAD 117 -#define IDD_OPT_CHART 118 -#define IDD_OPT_OTHER 119 -#define IDD_OPT_RECORD 120 -#define IDD_PART_AUTO 121 -#define IDD_PART_BASS 122 -#define IDD_PART_COMP 123 -#define IDD_PART_INPUT 124 -#define IDD_PART_LEAD 125 -#define IDD_PART_MIDI 126 -#define IDD_PART_OUTPUT 127 -#define IDD_PATCH_GENERAL 128 -#define IDD_PATCH_METRONOME 129 -#define IDD_PIANO 130 -#define IDD_PROGRESS 131 -#define IDD_RECORD_PLAYER 132 -#define IDD_ROW_FORM 133 -#define IDD_SECTION_LIST 134 -#define IDD_SECTION_PROPS 135 -#define IDD_SONG_PROPS 136 -#define IDD_SPLASH 137 -#define IDD_TABBED 138 -#define IDD_THREADS 139 -#define IDM_CHART_CTX 140 -#define IDM_MIDI_ASSIGNS_CTX 141 -#define IDM_MIDI_EVENT_CTX 142 -#define IDM_OUTPUT_NOTES_CTX 143 -#define IDM_PART_LIST_CTX 144 -#define IDM_PIANO_CTX 145 +#define IDD_NOTEPAD 116 +#define IDD_OPT_CHART 117 +#define IDD_OPT_OTHER 118 +#define IDD_OPT_RECORD 119 +#define IDD_PART_AUTO 120 +#define IDD_PART_BASS 121 +#define IDD_PART_COMP 122 +#define IDD_PART_INPUT 123 +#define IDD_PART_LEAD 124 +#define IDD_PART_MIDI 125 +#define IDD_PART_OUTPUT 126 +#define IDD_PATCH_GENERAL 127 +#define IDD_PATCH_METRONOME 128 +#define IDD_PIANO 129 +#define IDD_PROGRESS 130 +#define IDD_RECORD_PLAYER 131 +#define IDD_SECTION_LIST 132 +#define IDD_SECTION_PROPS 133 +#define IDD_SONG_PROPS 134 +#define IDD_SPLASH 135 +#define IDD_TABBED 136 +#define IDD_THREADS 137 +#define IDM_CHART_CTX 138 +#define IDM_MIDI_ASSIGNS_CTX 139 +#define IDM_MIDI_EVENT_CTX 140 +#define IDM_MIDI_TARGET_CTX 141 +#define IDM_OUTPUT_NOTES_CTX 142 +#define IDM_PART_LIST_CTX 143 +#define IDM_PIANO_CTX 144 #define IDS_APP_ABOUT_TEXT 200 #define IDS_APP_CANT_INIT_COM 201 #define IDS_APP_DEMO_CHECK 202 @@ -64,234 +63,237 @@ #define IDS_CHART_UC_CUT 214 #define IDS_CHART_UC_DELETE 215 #define IDS_CHART_UC_INSERT 216 -#define IDS_CHART_UC_PASTE 217 -#define IDS_CHART_UC_REORDER 218 -#define IDS_CHART_UC_SECTION_CREATE 219 -#define IDS_CHART_UC_SECTION_DELETE 220 -#define IDS_CHART_UC_SECTION_PROPS 221 -#define IDS_CHART_UC_SONG_EDIT 222 -#define IDS_CHART_UC_SONG_PROPS 223 -#define IDS_CHORD_DURATION_OTHER 224 -#define IDS_CKUP_CANT_GET_ADDR 225 -#define IDS_CKUP_CANT_LOAD_DLL 226 -#define IDS_CLIPBOARD_CANT_COPY 227 -#define IDS_CLIPBOARD_CANT_PASTE 228 -#define IDS_COMMON_NO 229 -#define IDS_COMMON_YES 230 -#define IDS_DEVICE_BAR 231 -#define IDS_DEVICE_COL_NAME 232 -#define IDS_DEVICE_COL_PORT 233 -#define IDS_DEVICE_COL_STATE 234 -#define IDS_DEVICE_STATE_CLOSED 235 -#define IDS_DEVICE_STATE_OPEN 236 -#define IDS_EDIT_REDO_FMT 237 -#define IDS_EDIT_UNDO_FMT 238 -#define IDS_ENGERR_CANT_CLOSE_MIDI_IN 239 -#define IDS_ENGERR_CANT_CLOSE_MIDI_OUT 240 -#define IDS_ENGERR_CANT_OPEN_MIDI_IN 241 -#define IDS_ENGERR_CANT_OPEN_MIDI_OUT 242 -#define IDS_ENGERR_CANT_PLAY_STOPPED 243 -#define IDS_ENGERR_CANT_START_MIDI_IN 244 -#define IDS_ENGERR_CREATE_TIMER 245 -#define IDS_ENGERR_CREATE_TIMER_EVENT 246 -#define IDS_ENGERR_CREATE_TIMER_THREAD 247 -#define IDS_ENGERR_DESTROY_TIMER 248 -#define IDS_ENGERR_DESTROY_TIMER_THREAD 249 -#define IDS_ENGERR_RECORDING_TRUNCATED 250 -#define IDS_ENGERR_SET_TIMER_RESOLUTION 251 -#define IDS_ENGERR_START_TIMER_THREAD 252 -#define IDS_ENGERR_STOP_TIMER_THREAD 253 -#define IDS_ENGERR_TOO_MANY_NOTES 254 -#define IDS_ENGINE_MIDI_DEVICE_NONE 255 -#define IDS_ENGMSG_MISSING_DEVICE 256 -#define IDS_HLINK_CANT_LAUNCH 257 -#define IDS_INSCH_BASS_ROOT 258 -#define IDS_INSCH_SELECT_DURATION 259 -#define IDS_MIDI_ASS_COL_CHANNEL 260 -#define IDS_MIDI_ASS_COL_CONTROL 261 -#define IDS_MIDI_ASS_COL_DEVICE 262 -#define IDS_MIDI_ASS_COL_EVENT 263 -#define IDS_MIDI_ASS_COL_PART 264 -#define IDS_MIDI_ASS_COL_PART_NAME 265 -#define IDS_MIDI_ASS_COL_PORT 266 -#define IDS_MIDI_ASS_COL_TARGET 267 -#define IDS_MIDI_EVT_COL_CHANNEL 268 -#define IDS_MIDI_EVT_COL_DEVICE 269 -#define IDS_MIDI_EVT_COL_MESSAGE 270 -#define IDS_MIDI_EVT_COL_P1 271 -#define IDS_MIDI_EVT_COL_P2 272 -#define IDS_MIDI_EVT_COL_PORT 273 -#define IDS_MIDI_EVT_COL_TIMESTAMP 274 -#define IDS_MIDI_EVT_PAUSE 275 -#define IDS_MIDI_FILE_FILTER 276 -#define IDS_MIDI_INPUT_BAR 277 -#define IDS_MIDI_NMAP_COL_ENABLE 278 -#define IDS_MIDI_NMAP_COL_FUNCTION 279 -#define IDS_MIDI_NMAP_COL_IN_CHAN 280 -#define IDS_MIDI_NMAP_COL_IN_DEVICE 281 -#define IDS_MIDI_NMAP_COL_IN_PORT 282 -#define IDS_MIDI_NMAP_COL_OUT_CHAN 283 -#define IDS_MIDI_NMAP_COL_OUT_DEVICE 284 -#define IDS_MIDI_NMAP_COL_OUT_PORT 285 -#define IDS_MIDI_NMAP_COL_PART_INDEX 286 -#define IDS_MIDI_NMAP_COL_PART_NAME 287 -#define IDS_MIDI_NMAP_COL_ZONE_HIGH 288 -#define IDS_MIDI_NMAP_COL_ZONE_LOW 289 -#define IDS_MIDI_OUTPUT_BAR 290 -#define IDS_MIDI_RECORDING_FILTER 291 -#define IDS_MIDI_REC_BAD_FORMAT 292 -#define IDS_MIDI_REC_OUT_OF_ORDER 293 -#define IDS_MIDI_STAT_ACTIVE_SENSING 294 -#define IDS_MIDI_STAT_CHANNEL_WILDCARD 295 -#define IDS_MIDI_STAT_CHAN_AFT 296 -#define IDS_MIDI_STAT_CLOCK 297 -#define IDS_MIDI_STAT_CONTINUE 298 -#define IDS_MIDI_STAT_CONTROL 299 -#define IDS_MIDI_STAT_EOX 300 -#define IDS_MIDI_STAT_KEY_AFT 301 -#define IDS_MIDI_STAT_MTC_QTR_FRAME 302 -#define IDS_MIDI_STAT_NOTE_OFF 303 -#define IDS_MIDI_STAT_NOTE_ON 304 -#define IDS_MIDI_STAT_PATCH 305 -#define IDS_MIDI_STAT_SONG_POSITION 306 -#define IDS_MIDI_STAT_SONG_SELECT 307 -#define IDS_MIDI_STAT_START 308 -#define IDS_MIDI_STAT_STOP 309 -#define IDS_MIDI_STAT_SYSEX 310 -#define IDS_MIDI_STAT_SYSTEM_RESET 311 -#define IDS_MIDI_STAT_SYSTEM_WILDCARD 312 -#define IDS_MIDI_STAT_TUNE_REQUEST 313 -#define IDS_MIDI_STAT_WHEEL 314 -#define IDS_MIDI_TARG_ROW_CHAN 315 -#define IDS_MIDI_TARG_ROW_CONTROL 316 -#define IDS_MIDI_TARG_ROW_EVENT 317 -#define IDS_MIDI_TARG_ROW_NAME 318 -#define IDS_MIDI_TARG_ROW_PORT 319 -#define IDS_MIDI_TARG_ROW_RANGE_END 320 -#define IDS_MIDI_TARG_ROW_RANGE_START 321 -#define IDS_MSGBOX_NO 322 -#define IDS_MSGBOX_YES 323 -#define IDS_MTEVT_CHAN_AFTERTOUCH 324 -#define IDS_MTEVT_CONTROL 325 -#define IDS_MTEVT_NONE 326 -#define IDS_MTEVT_PROGRAM_CHANGE 327 -#define IDS_MTEVT_WHEEL 328 -#define IDS_NOT_APPLICABLE 329 -#define IDS_OPTIONS 330 -#define IDS_OPT_OTHER_DATA_FOLDER 331 -#define IDS_OPT_REC_OUTPUT_FOLDER 332 -#define IDS_OPT_RESET_ALL 333 -#define IDS_OPT_RESTORE_DEFAULTS 334 -#define IDS_OUTPUT_NOTES_BAR 335 -#define IDS_OUT_NOTES_FILTER_ALL 336 -#define IDS_OUT_NOTES_SM_ALL_CHANS 337 -#define IDS_OUT_NOTES_SM_ALL_PORTS 338 -#define IDS_OUT_NOTES_SM_FILTER_CHAN 339 -#define IDS_OUT_NOTES_SM_FILTER_PORT 340 -#define IDS_OUT_NOTES_SM_PIANO_SIZE 341 -#define IDS_PARTS_BAR 342 -#define IDS_PARTS_COL_FUNCTION 343 -#define IDS_PARTS_COL_PART_NAME 344 -#define IDS_PARTS_TIP_LIST 345 -#define IDS_PART_FORMAT 346 -#define IDS_PART_MT_AUTO_PLAY 347 -#define IDS_PART_MT_AUTO_VELOCITY 348 -#define IDS_PART_MT_AUTO_WINDOW 349 -#define IDS_PART_MT_BASS_APPROACH_LENGTH 350 -#define IDS_PART_MT_BASS_APPROACH_TRIGGER 351 -#define IDS_PART_MT_BASS_SLASH_CHORDS 352 -#define IDS_PART_MT_BASS_TARGET_ALIGNMENT 353 -#define IDS_PART_MT_COMP_ARP_ORDER 354 -#define IDS_PART_MT_COMP_ARP_PERIOD 355 -#define IDS_PART_MT_COMP_ARP_PERIOD_QUANT 356 -#define IDS_PART_MT_COMP_ARP_REPEAT 357 -#define IDS_PART_MT_COMP_VARIATION 358 -#define IDS_PART_MT_COMP_VOICING 359 -#define IDS_PART_MT_ENABLE 360 -#define IDS_PART_MT_FUNCTION 361 -#define IDS_PART_MT_IN_CC_NOTE 362 -#define IDS_PART_MT_IN_CC_NOTE_VEL 363 -#define IDS_PART_MT_IN_NON_DIATONIC 364 -#define IDS_PART_MT_IN_TRANSPOSE 365 -#define IDS_PART_MT_IN_VEL_OFFSET 366 -#define IDS_PART_MT_IN_ZONE_HIGH 367 -#define IDS_PART_MT_IN_ZONE_LOW 368 -#define IDS_PART_MT_LEAD_HARM_INTERVAL 369 -#define IDS_PART_MT_LEAD_HARM_OMIT_MELODY 370 -#define IDS_PART_MT_LEAD_HARM_STATIC_MAX 371 -#define IDS_PART_MT_LEAD_HARM_STATIC_MIN 372 -#define IDS_PART_MT_OUT_ANTICIPATION 373 -#define IDS_PART_MT_OUT_PATCH 374 -#define IDS_PART_MT_OUT_VOLUME 375 -#define IDS_PATCH_BAD_FORMAT 376 -#define IDS_PATCH_BAR 377 -#define IDS_PATCH_MT_LEAD_IN 378 -#define IDS_PATCH_MT_METRO_ENABLE 379 -#define IDS_PATCH_MT_METRO_VOLUME 380 -#define IDS_PATCH_MT_NEXT_CHORD 381 -#define IDS_PATCH_MT_NEXT_SECTION 382 -#define IDS_PATCH_MT_PAUSE 383 -#define IDS_PATCH_MT_PLAY 384 -#define IDS_PATCH_MT_PREV_CHORD 385 -#define IDS_PATCH_MT_REPEAT 386 -#define IDS_PATCH_MT_REWIND 387 -#define IDS_PATCH_MT_SONG_POSITION 388 -#define IDS_PATCH_MT_TEMPO 389 -#define IDS_PATCH_MT_TEMPO_MULTIPLE 390 -#define IDS_PATCH_MT_TRANSPOSE 391 -#define IDS_RECORD_AS 392 -#define IDS_RECORD_LOCKED 393 -#define IDS_REC_PLAY_COL_CHANNEL 394 -#define IDS_REC_PLAY_COL_DEVICE 395 -#define IDS_REC_PLAY_COL_EVENTS 396 -#define IDS_REC_PLAY_COL_NAME 397 -#define IDS_REC_PLAY_COL_PORT 398 -#define IDS_REC_PLAY_STOP_PLAYING 399 -#define IDS_REC_PLAY_STOP_RECORDING 400 -#define IDS_SECTION_NAME_SPACES 401 -#define IDS_SEC_LIST_COL_IMPLICIT 402 -#define IDS_SEC_LIST_COL_INDEX 403 -#define IDS_SEC_LIST_COL_LENGTH 404 -#define IDS_SEC_LIST_COL_NAME 405 -#define IDS_SEC_LIST_COL_REPEAT 406 -#define IDS_SEC_LIST_COL_START 407 -#define IDS_SONG_BAD_METER 408 -#define IDS_SONG_ERR_BAD_BASS_NOTE 409 -#define IDS_SONG_ERR_BAD_CHORD 410 -#define IDS_SONG_ERR_BAD_CHORD_TYPE 411 -#define IDS_SONG_ERR_BAD_COMMAND 412 -#define IDS_SONG_ERR_BAD_DURATION 413 -#define IDS_SONG_ERR_BAD_KEY 414 -#define IDS_SONG_ERR_BAD_MODE 415 -#define IDS_SONG_ERR_BAD_ROOT 416 -#define IDS_SONG_ERR_BAD_SCALE 417 -#define IDS_SONG_ERR_BAD_SYNTAX 418 -#define IDS_SONG_ERR_BAD_TEMPO 419 -#define IDS_SONG_ERR_CHORD_TONE_RANGE 420 -#define IDS_SONG_ERR_DUP_CHORD_TYPE 421 -#define IDS_SONG_ERR_READ 422 -#define IDS_SONG_ERR_SEC_BAD_REPEAT 423 -#define IDS_SONG_ERR_SEC_BAD_SYNTAX 424 -#define IDS_SONG_ERR_SEC_CANT_NEST 425 -#define IDS_SONG_ERR_SEC_UNTERMINATED 426 -#define IDS_THREADS_COL_KERNEL_TIME 427 -#define IDS_THREADS_COL_PRIORITY 428 -#define IDS_THREADS_COL_THREAD_ID 429 -#define IDS_THREADS_COL_USER_TIME 430 -#define IDS_UC_BASE_PATCH 431 -#define IDS_UC_CUT 432 -#define IDS_UC_DELETE 433 -#define IDS_UC_ENABLE_PART 434 -#define IDS_UC_INSERT 435 -#define IDS_UC_MIDI_CHASE 436 -#define IDS_UC_MIDI_TARGETS 437 -#define IDS_UC_PART 438 -#define IDS_UC_PASTE 439 -#define IDS_UC_RENAME 440 -#define IDS_UC_REORDER 441 -#define IDS_UC_SELECT_PART 442 -#define IDS_UC_SELECT_PART_PAGE 443 -#define IDS_UC_SELECT_PATCH_PAGE 444 +#define IDS_CHART_UC_MULTI_CHORD_EDIT 217 +#define IDS_CHART_UC_PASTE 218 +#define IDS_CHART_UC_REORDER 219 +#define IDS_CHART_UC_SECTION_CREATE 220 +#define IDS_CHART_UC_SECTION_DELETE 221 +#define IDS_CHART_UC_SECTION_PROPS 222 +#define IDS_CHART_UC_SONG_EDIT 223 +#define IDS_CHART_UC_SONG_PROPS 224 +#define IDS_CHORD_DURATION_OTHER 225 +#define IDS_CKUP_CANT_GET_ADDR 226 +#define IDS_CKUP_CANT_LOAD_DLL 227 +#define IDS_CLIPBOARD_CANT_COPY 228 +#define IDS_CLIPBOARD_CANT_PASTE 229 +#define IDS_COMMON_NO 230 +#define IDS_COMMON_YES 231 +#define IDS_DEVICE_BAR 232 +#define IDS_DEVICE_COL_NAME 233 +#define IDS_DEVICE_COL_PORT 234 +#define IDS_DEVICE_COL_STATE 235 +#define IDS_DEVICE_STATE_CLOSED 236 +#define IDS_DEVICE_STATE_OPEN 237 +#define IDS_EDIT_REDO_FMT 238 +#define IDS_EDIT_UNDO_FMT 239 +#define IDS_ENGERR_CANT_CLOSE_MIDI_IN 240 +#define IDS_ENGERR_CANT_CLOSE_MIDI_OUT 241 +#define IDS_ENGERR_CANT_OPEN_MIDI_IN 242 +#define IDS_ENGERR_CANT_OPEN_MIDI_OUT 243 +#define IDS_ENGERR_CANT_PLAY_STOPPED 244 +#define IDS_ENGERR_CANT_START_MIDI_IN 245 +#define IDS_ENGERR_CREATE_TIMER 246 +#define IDS_ENGERR_CREATE_TIMER_EVENT 247 +#define IDS_ENGERR_CREATE_TIMER_THREAD 248 +#define IDS_ENGERR_DESTROY_TIMER 249 +#define IDS_ENGERR_DESTROY_TIMER_THREAD 250 +#define IDS_ENGERR_RECORDING_TRUNCATED 251 +#define IDS_ENGERR_SET_TIMER_RESOLUTION 252 +#define IDS_ENGERR_START_TIMER_THREAD 253 +#define IDS_ENGERR_STOP_TIMER_THREAD 254 +#define IDS_ENGERR_TOO_MANY_NOTES 255 +#define IDS_ENGINE_MIDI_DEVICE_NONE 256 +#define IDS_ENGMSG_MISSING_DEVICE 257 +#define IDS_HLINK_CANT_LAUNCH 258 +#define IDS_INSCH_BASS_ROOT 259 +#define IDS_INSCH_SELECT_DURATION 260 +#define IDS_MIDI_ASS_COL_CHANNEL 261 +#define IDS_MIDI_ASS_COL_CONTROL 262 +#define IDS_MIDI_ASS_COL_DEVICE 263 +#define IDS_MIDI_ASS_COL_EVENT 264 +#define IDS_MIDI_ASS_COL_PART 265 +#define IDS_MIDI_ASS_COL_PART_NAME 266 +#define IDS_MIDI_ASS_COL_PORT 267 +#define IDS_MIDI_ASS_COL_TARGET 268 +#define IDS_MIDI_EVT_COL_CHANNEL 269 +#define IDS_MIDI_EVT_COL_DEVICE 270 +#define IDS_MIDI_EVT_COL_MESSAGE 271 +#define IDS_MIDI_EVT_COL_P1 272 +#define IDS_MIDI_EVT_COL_P2 273 +#define IDS_MIDI_EVT_COL_PORT 274 +#define IDS_MIDI_EVT_COL_TIMESTAMP 275 +#define IDS_MIDI_EVT_PAUSE 276 +#define IDS_MIDI_FILE_FILTER 277 +#define IDS_MIDI_INPUT_BAR 278 +#define IDS_MIDI_NMAP_COL_ENABLE 279 +#define IDS_MIDI_NMAP_COL_FUNCTION 280 +#define IDS_MIDI_NMAP_COL_IN_CHAN 281 +#define IDS_MIDI_NMAP_COL_IN_DEVICE 282 +#define IDS_MIDI_NMAP_COL_IN_PORT 283 +#define IDS_MIDI_NMAP_COL_OUT_CHAN 284 +#define IDS_MIDI_NMAP_COL_OUT_DEVICE 285 +#define IDS_MIDI_NMAP_COL_OUT_PORT 286 +#define IDS_MIDI_NMAP_COL_PART_INDEX 287 +#define IDS_MIDI_NMAP_COL_PART_NAME 288 +#define IDS_MIDI_NMAP_COL_ZONE_HIGH 289 +#define IDS_MIDI_NMAP_COL_ZONE_LOW 290 +#define IDS_MIDI_OUTPUT_BAR 291 +#define IDS_MIDI_RECORDING_FILTER 292 +#define IDS_MIDI_REC_BAD_FORMAT 293 +#define IDS_MIDI_REC_OUT_OF_ORDER 294 +#define IDS_MIDI_STAT_ACTIVE_SENSING 295 +#define IDS_MIDI_STAT_CHANNEL_WILDCARD 296 +#define IDS_MIDI_STAT_CHAN_AFT 297 +#define IDS_MIDI_STAT_CLOCK 298 +#define IDS_MIDI_STAT_CONTINUE 299 +#define IDS_MIDI_STAT_CONTROL 300 +#define IDS_MIDI_STAT_EOX 301 +#define IDS_MIDI_STAT_KEY_AFT 302 +#define IDS_MIDI_STAT_MTC_QTR_FRAME 303 +#define IDS_MIDI_STAT_NOTE_OFF 304 +#define IDS_MIDI_STAT_NOTE_ON 305 +#define IDS_MIDI_STAT_PATCH 306 +#define IDS_MIDI_STAT_SONG_POSITION 307 +#define IDS_MIDI_STAT_SONG_SELECT 308 +#define IDS_MIDI_STAT_START 309 +#define IDS_MIDI_STAT_STOP 310 +#define IDS_MIDI_STAT_SYSEX 311 +#define IDS_MIDI_STAT_SYSTEM_RESET 312 +#define IDS_MIDI_STAT_SYSTEM_WILDCARD 313 +#define IDS_MIDI_STAT_TUNE_REQUEST 314 +#define IDS_MIDI_STAT_WHEEL 315 +#define IDS_MIDI_TARG_COL_CHAN 316 +#define IDS_MIDI_TARG_COL_CONTROL 317 +#define IDS_MIDI_TARG_COL_EVENT 318 +#define IDS_MIDI_TARG_COL_NAME 319 +#define IDS_MIDI_TARG_COL_PORT 320 +#define IDS_MIDI_TARG_COL_RANGE_END 321 +#define IDS_MIDI_TARG_COL_RANGE_START 322 +#define IDS_MIDI_TARG_COL_VALUE 323 +#define IDS_MSGBOX_NO 324 +#define IDS_MSGBOX_YES 325 +#define IDS_MTEVT_CHAN_AFTERTOUCH 326 +#define IDS_MTEVT_CONTROL 327 +#define IDS_MTEVT_NONE 328 +#define IDS_MTEVT_PROGRAM_CHANGE 329 +#define IDS_MTEVT_WHEEL 330 +#define IDS_NOT_APPLICABLE 331 +#define IDS_OPTIONS 332 +#define IDS_OPT_OTHER_DATA_FOLDER 333 +#define IDS_OPT_REC_OUTPUT_FOLDER 334 +#define IDS_OPT_RESET_ALL 335 +#define IDS_OPT_RESTORE_DEFAULTS 336 +#define IDS_OUTPUT_NOTES_BAR 337 +#define IDS_OUT_NOTES_FILTER_ALL 338 +#define IDS_OUT_NOTES_SM_ALL_CHANS 339 +#define IDS_OUT_NOTES_SM_ALL_PORTS 340 +#define IDS_OUT_NOTES_SM_FILTER_CHAN 341 +#define IDS_OUT_NOTES_SM_FILTER_PORT 342 +#define IDS_OUT_NOTES_SM_PIANO_SIZE 343 +#define IDS_PARTS_BAR 344 +#define IDS_PARTS_COL_FUNCTION 345 +#define IDS_PARTS_COL_PART_NAME 346 +#define IDS_PARTS_TIP_LIST 347 +#define IDS_PART_FORMAT 348 +#define IDS_PART_MT_AUTO_PLAY 349 +#define IDS_PART_MT_AUTO_VELOCITY 350 +#define IDS_PART_MT_AUTO_WINDOW 351 +#define IDS_PART_MT_BASS_APPROACH_LENGTH 352 +#define IDS_PART_MT_BASS_APPROACH_TRIGGER 353 +#define IDS_PART_MT_BASS_SLASH_CHORDS 354 +#define IDS_PART_MT_BASS_TARGET_ALIGNMENT 355 +#define IDS_PART_MT_COMP_ARP_ORDER 356 +#define IDS_PART_MT_COMP_ARP_PERIOD 357 +#define IDS_PART_MT_COMP_ARP_PERIOD_QUANT 358 +#define IDS_PART_MT_COMP_ARP_REPEAT 359 +#define IDS_PART_MT_COMP_VARIATION 360 +#define IDS_PART_MT_COMP_VOICING 361 +#define IDS_PART_MT_ENABLE 362 +#define IDS_PART_MT_FUNCTION 363 +#define IDS_PART_MT_IN_CC_NOTE 364 +#define IDS_PART_MT_IN_CC_NOTE_VEL 365 +#define IDS_PART_MT_IN_NON_DIATONIC 366 +#define IDS_PART_MT_IN_TRANSPOSE 367 +#define IDS_PART_MT_IN_VEL_OFFSET 368 +#define IDS_PART_MT_IN_ZONE_HIGH 369 +#define IDS_PART_MT_IN_ZONE_LOW 370 +#define IDS_PART_MT_LEAD_HARM_INTERVAL 371 +#define IDS_PART_MT_LEAD_HARM_OMIT_MELODY 372 +#define IDS_PART_MT_LEAD_HARM_STATIC_MAX 373 +#define IDS_PART_MT_LEAD_HARM_STATIC_MIN 374 +#define IDS_PART_MT_OUT_ANTICIPATION 375 +#define IDS_PART_MT_OUT_FIX_HELD_NOTES 376 +#define IDS_PART_MT_OUT_PATCH 377 +#define IDS_PART_MT_OUT_VOLUME 378 +#define IDS_PATCH_BAD_FORMAT 379 +#define IDS_PATCH_BAR 380 +#define IDS_PATCH_MT_LEAD_IN 381 +#define IDS_PATCH_MT_METRO_ENABLE 382 +#define IDS_PATCH_MT_METRO_VOLUME 383 +#define IDS_PATCH_MT_NEXT_CHORD 384 +#define IDS_PATCH_MT_NEXT_SECTION 385 +#define IDS_PATCH_MT_PAUSE 386 +#define IDS_PATCH_MT_PLAY 387 +#define IDS_PATCH_MT_PREV_CHORD 388 +#define IDS_PATCH_MT_REPEAT 389 +#define IDS_PATCH_MT_REWIND 390 +#define IDS_PATCH_MT_SONG_POSITION 391 +#define IDS_PATCH_MT_TEMPO 392 +#define IDS_PATCH_MT_TEMPO_MULTIPLE 393 +#define IDS_PATCH_MT_TRANSPOSE 394 +#define IDS_RECORD_AS 395 +#define IDS_RECORD_LOCKED 396 +#define IDS_REC_PLAY_COL_CHANNEL 397 +#define IDS_REC_PLAY_COL_DEVICE 398 +#define IDS_REC_PLAY_COL_EVENTS 399 +#define IDS_REC_PLAY_COL_NAME 400 +#define IDS_REC_PLAY_COL_PORT 401 +#define IDS_REC_PLAY_STOP_PLAYING 402 +#define IDS_REC_PLAY_STOP_RECORDING 403 +#define IDS_SECTION_NAME_SPACES 404 +#define IDS_SEC_LIST_COL_IMPLICIT 405 +#define IDS_SEC_LIST_COL_INDEX 406 +#define IDS_SEC_LIST_COL_LENGTH 407 +#define IDS_SEC_LIST_COL_NAME 408 +#define IDS_SEC_LIST_COL_REPEAT 409 +#define IDS_SEC_LIST_COL_START 410 +#define IDS_SONG_ERR_BAD_BASS_NOTE 411 +#define IDS_SONG_ERR_BAD_CHORD 412 +#define IDS_SONG_ERR_BAD_CHORD_TYPE 413 +#define IDS_SONG_ERR_BAD_COMMAND 414 +#define IDS_SONG_ERR_BAD_DURATION 415 +#define IDS_SONG_ERR_BAD_KEY 416 +#define IDS_SONG_ERR_BAD_METER 417 +#define IDS_SONG_ERR_BAD_MODE 418 +#define IDS_SONG_ERR_BAD_ROOT 419 +#define IDS_SONG_ERR_BAD_SCALE 420 +#define IDS_SONG_ERR_BAD_SYNTAX 421 +#define IDS_SONG_ERR_BAD_TEMPO 422 +#define IDS_SONG_ERR_CHORD_TONE_RANGE 423 +#define IDS_SONG_ERR_DUP_CHORD_TYPE 424 +#define IDS_SONG_ERR_READ 425 +#define IDS_SONG_ERR_SEC_BAD_REPEAT 426 +#define IDS_SONG_ERR_SEC_BAD_SYNTAX 427 +#define IDS_SONG_ERR_SEC_CANT_NEST 428 +#define IDS_SONG_ERR_SEC_UNTERMINATED 429 +#define IDS_THREADS_COL_KERNEL_TIME 430 +#define IDS_THREADS_COL_PRIORITY 431 +#define IDS_THREADS_COL_THREAD_ID 432 +#define IDS_THREADS_COL_USER_TIME 433 +#define IDS_UC_BASE_PATCH 434 +#define IDS_UC_CUT 435 +#define IDS_UC_DELETE 436 +#define IDS_UC_ENABLE_PART 437 +#define IDS_UC_INSERT 438 +#define IDS_UC_MIDI_CHASE 439 +#define IDS_UC_MIDI_TARGETS 440 +#define IDS_UC_PART 441 +#define IDS_UC_PASTE 442 +#define IDS_UC_RENAME 443 +#define IDS_UC_REORDER 444 +#define IDS_UC_SELECT_PART 445 +#define IDS_UC_SELECT_PART_PAGE 446 +#define IDS_UC_SELECT_PATCH_PAGE 447 #define IDC_ABOUT_LICENSE 1001 #define IDC_ABOUT_TEXT 1002 #define IDC_ABOUT_URL 1003 @@ -315,124 +317,125 @@ #define IDC_MIDI_ASSIGNS_LIST 1021 #define IDC_MIDI_EVENT_LIST 1022 #define IDC_MIDI_NOTE_MAP_LIST 1023 -#define IDC_MIDI_TARGET 1024 -#define IDC_MIDI_TARG_COL_CHAN 1025 -#define IDC_MIDI_TARG_COL_CONTROL 1026 -#define IDC_MIDI_TARG_COL_EVENT 1027 -#define IDC_MIDI_TARG_COL_NAME 1028 -#define IDC_MIDI_TARG_COL_PORT 1029 -#define IDC_MIDI_TARG_COL_RANGE_END 1030 -#define IDC_MIDI_TARG_COL_RANGE_START 1031 -#define IDC_MSGBOXCHK_CHECK 1032 -#define IDC_MSGBOXCHK_TEXT 1033 -#define IDC_NOTEPAD_EDIT 1034 -#define IDC_OPT_CHART_CHOOSE_FONT 1035 -#define IDC_OPT_CHART_DEFAULT_FONT 1036 -#define IDC_OPT_CHART_LINE_MEASURES 1037 -#define IDC_OPT_OTHER_AUTO_CHECK_UPDATES 1038 -#define IDC_OPT_OTHER_DATA_FOLDER_BROWSE 1039 -#define IDC_OPT_OTHER_DATA_FOLDER_PATH 1040 -#define IDC_OPT_OTHER_DATA_FOLDER_TYPE 1041 -#define IDC_OPT_OTHER_DATA_FOLDER_TYPE2 1042 -#define IDC_OPT_OTHER_SHOW_TOOLTIPS 1043 -#define IDC_OPT_REC_ALWAYS_RECORD 1044 -#define IDC_OPT_REC_BUFFER_SIZE 1045 -#define IDC_OPT_REC_FOLDER_BROWSE 1046 -#define IDC_OPT_REC_FOLDER_PATH 1047 -#define IDC_OPT_REC_FOLDER_TYPE 1048 -#define IDC_OPT_REC_FOLDER_TYPE2 1049 -#define IDC_OPT_REC_MIDI_FILE_PPQ 1050 -#define IDC_OPT_REC_PROMPT_FILENAME 1051 -#define IDC_PARTS_LIST 1052 -#define IDC_PART_AUTO_PLAY 1053 -#define IDC_PART_AUTO_VELOCITY 1054 -#define IDC_PART_AUTO_WINDOW 1055 -#define IDC_PART_BASS_APPROACH_LENGTH 1056 -#define IDC_PART_BASS_APPROACH_TRIGGER 1057 -#define IDC_PART_BASS_LOWEST 1058 -#define IDC_PART_BASS_SLASH_CHORDS 1059 -#define IDC_PART_BASS_TARGET_ALIGNMENT 1060 -#define IDC_PART_COMP_ARP_ORDER 1061 -#define IDC_PART_COMP_ARP_PERIOD 1062 -#define IDC_PART_COMP_ARP_PERIOD_QUANT 1063 -#define IDC_PART_COMP_ARP_REPEAT 1064 -#define IDC_PART_COMP_CHORD_RESETS_ALT 1065 -#define IDC_PART_COMP_VARIATION 1066 -#define IDC_PART_COMP_VOICING 1067 -#define IDC_PART_ENABLE 1068 -#define IDC_PART_FUNCTION 1069 -#define IDC_PART_IN_CC_NOTE 1070 -#define IDC_PART_IN_CC_NOTE_VEL 1071 -#define IDC_PART_IN_CHAN 1072 -#define IDC_PART_IN_DEVICE_NAME 1073 -#define IDC_PART_IN_NON_DIATONIC 1074 -#define IDC_PART_IN_PORT 1075 -#define IDC_PART_IN_TRANSPOSE 1076 -#define IDC_PART_IN_VEL_OFFSET 1077 -#define IDC_PART_IN_ZONE_HIGH 1078 -#define IDC_PART_IN_ZONE_LOW 1079 -#define IDC_PART_LEAD_HARM_INTERVAL 1080 -#define IDC_PART_LEAD_HARM_OMIT_MELODY 1081 -#define IDC_PART_LEAD_HARM_STATIC_MAX 1082 -#define IDC_PART_LEAD_HARM_STATIC_MIN 1083 -#define IDC_PART_MIDI_ROW 1084 -#define IDC_PART_NAME 1085 -#define IDC_PART_OUT_ANTICIPATION 1086 -#define IDC_PART_OUT_CHAN 1087 -#define IDC_PART_OUT_CONTROLS_THRU 1088 -#define IDC_PART_OUT_DEVICE_NAME 1089 -#define IDC_PART_OUT_FIX_HELD_NOTES 1090 -#define IDC_PART_OUT_LOCAL_CONTROL 1091 -#define IDC_PART_OUT_PATCH 1092 -#define IDC_PART_OUT_PORT 1093 -#define IDC_PART_OUT_VOLUME 1094 -#define IDC_PATCH_AINST_CHANNEL 1095 -#define IDC_PATCH_AINST_ENABLE 1096 -#define IDC_PATCH_AINST_PATCH 1097 -#define IDC_PATCH_AINST_PORT 1098 -#define IDC_PATCH_AINST_VELOCITY 1099 -#define IDC_PATCH_AINST_VOLUME 1100 -#define IDC_PATCH_GEN_KEY 1101 -#define IDC_PATCH_GEN_LEAD_IN 1102 -#define IDC_PATCH_GEN_PPQ 1103 -#define IDC_PATCH_GEN_TEMPO 1104 -#define IDC_PATCH_GEN_TEMPO_MULTIPLE 1105 -#define IDC_PATCH_GEN_TRANSPOSE 1106 -#define IDC_PATCH_METRO_ACCENT_NOTE 1107 -#define IDC_PATCH_METRO_ACCENT_SAME_NOTE 1108 -#define IDC_PATCH_METRO_ACCENT_VEL 1109 -#define IDC_PATCH_METRO_NOTE 1110 -#define IDC_PIANO_CHANNEL 1111 -#define IDC_PIANO_KEY_COUNT 1112 -#define IDC_PIANO_PORT 1113 -#define IDC_PIANO_START_NOTE 1114 -#define IDC_PIANO_VELOCITY 1115 -#define IDC_PROGRESS 1116 -#define IDC_PROGRESS_PERCENT 1117 -#define IDC_REC_PLAY_DURATION 1118 -#define IDC_REC_PLAY_EXPORT 1119 -#define IDC_REC_PLAY_OPEN 1120 -#define IDC_REC_PLAY_PLAY 1121 -#define IDC_REC_PLAY_POSITION 1122 -#define IDC_REC_PLAY_POS_SLIDER 1123 -#define IDC_REC_PLAY_SAVE 1124 -#define IDC_REC_PLAY_STOP 1125 -#define IDC_REC_PLAY_TITLE 1126 -#define IDC_REC_PLAY_TRACK_LIST 1127 -#define IDC_SECTION_LIST 1128 -#define IDC_SECTION_PROPS_LENGTH 1129 -#define IDC_SECTION_PROPS_NAME 1130 -#define IDC_SECTION_PROPS_REPEAT 1131 -#define IDC_SECTION_PROPS_START 1132 -#define IDC_SONG_PROPS_COMMENTS 1133 -#define IDC_SONG_PROPS_KEY_SIG 1134 -#define IDC_SONG_PROPS_TEMPO 1135 -#define IDC_SONG_PROPS_TIME_SIG_DENOM 1136 -#define IDC_SONG_PROPS_TIME_SIG_NUMER 1137 -#define IDC_SONG_PROPS_TRANSPOSE 1138 -#define IDC_SPLASH_ICON 1139 -#define IDC_TABDLG_TAB 1140 -#define IDC_THREADS_LIST 1141 +#define IDC_MIDI_TARGET_LIST 1024 +#define IDC_MIDI_TARG_CHAN 1025 +#define IDC_MIDI_TARG_CONTROL 1026 +#define IDC_MIDI_TARG_EVENT 1027 +#define IDC_MIDI_TARG_NAME 1028 +#define IDC_MIDI_TARG_PORT 1029 +#define IDC_MIDI_TARG_RANGE_END 1030 +#define IDC_MIDI_TARG_RANGE_START 1031 +#define IDC_MIDI_TARG_VALUE 1032 +#define IDC_MSGBOXCHK_CHECK 1033 +#define IDC_MSGBOXCHK_TEXT 1034 +#define IDC_NOTEPAD_EDIT 1035 +#define IDC_OPT_CHART_CHOOSE_FONT 1036 +#define IDC_OPT_CHART_DEFAULT_FONT 1037 +#define IDC_OPT_CHART_LINE_MEASURES 1038 +#define IDC_OPT_OTHER_AUTO_CHECK_UPDATES 1039 +#define IDC_OPT_OTHER_DATA_FOLDER_BROWSE 1040 +#define IDC_OPT_OTHER_DATA_FOLDER_PATH 1041 +#define IDC_OPT_OTHER_DATA_FOLDER_TYPE 1042 +#define IDC_OPT_OTHER_DATA_FOLDER_TYPE2 1043 +#define IDC_OPT_OTHER_SHOW_TOOLTIPS 1044 +#define IDC_OPT_REC_ALWAYS_RECORD 1045 +#define IDC_OPT_REC_BUFFER_SIZE 1046 +#define IDC_OPT_REC_FOLDER_BROWSE 1047 +#define IDC_OPT_REC_FOLDER_PATH 1048 +#define IDC_OPT_REC_FOLDER_TYPE 1049 +#define IDC_OPT_REC_FOLDER_TYPE2 1050 +#define IDC_OPT_REC_MIDI_FILE_PPQ 1051 +#define IDC_OPT_REC_PROMPT_FILENAME 1052 +#define IDC_PARTS_LIST 1053 +#define IDC_PART_AUTO_PLAY 1054 +#define IDC_PART_AUTO_VELOCITY 1055 +#define IDC_PART_AUTO_WINDOW 1056 +#define IDC_PART_BASS_APPROACH_LENGTH 1057 +#define IDC_PART_BASS_APPROACH_TRIGGER 1058 +#define IDC_PART_BASS_LOWEST 1059 +#define IDC_PART_BASS_SLASH_CHORDS 1060 +#define IDC_PART_BASS_TARGET_ALIGNMENT 1061 +#define IDC_PART_COMP_ARP_ORDER 1062 +#define IDC_PART_COMP_ARP_PERIOD 1063 +#define IDC_PART_COMP_ARP_PERIOD_QUANT 1064 +#define IDC_PART_COMP_ARP_REPEAT 1065 +#define IDC_PART_COMP_CHORD_RESETS_ALT 1066 +#define IDC_PART_COMP_VARIATION 1067 +#define IDC_PART_COMP_VOICING 1068 +#define IDC_PART_ENABLE 1069 +#define IDC_PART_FUNCTION 1070 +#define IDC_PART_IN_CC_NOTE 1071 +#define IDC_PART_IN_CC_NOTE_VEL 1072 +#define IDC_PART_IN_CHAN 1073 +#define IDC_PART_IN_DEVICE_NAME 1074 +#define IDC_PART_IN_NON_DIATONIC 1075 +#define IDC_PART_IN_PORT 1076 +#define IDC_PART_IN_TRANSPOSE 1077 +#define IDC_PART_IN_VEL_OFFSET 1078 +#define IDC_PART_IN_ZONE_HIGH 1079 +#define IDC_PART_IN_ZONE_LOW 1080 +#define IDC_PART_LEAD_HARM_INTERVAL 1081 +#define IDC_PART_LEAD_HARM_OMIT_MELODY 1082 +#define IDC_PART_LEAD_HARM_STATIC_MAX 1083 +#define IDC_PART_LEAD_HARM_STATIC_MIN 1084 +#define IDC_PART_MIDI_ROW 1085 +#define IDC_PART_NAME 1086 +#define IDC_PART_OUT_ANTICIPATION 1087 +#define IDC_PART_OUT_CHAN 1088 +#define IDC_PART_OUT_CONTROLS_THRU 1089 +#define IDC_PART_OUT_DEVICE_NAME 1090 +#define IDC_PART_OUT_FIX_HELD_NOTES 1091 +#define IDC_PART_OUT_LOCAL_CONTROL 1092 +#define IDC_PART_OUT_PATCH 1093 +#define IDC_PART_OUT_PORT 1094 +#define IDC_PART_OUT_VOLUME 1095 +#define IDC_PATCH_GEN_KEY 1096 +#define IDC_PATCH_GEN_LEAD_IN 1097 +#define IDC_PATCH_GEN_PPQ 1098 +#define IDC_PATCH_GEN_TEMPO 1099 +#define IDC_PATCH_GEN_TEMPO_MULTIPLE 1100 +#define IDC_PATCH_GEN_TRANSPOSE 1101 +#define IDC_PATCH_METRO_ACCENT_NOTE 1102 +#define IDC_PATCH_METRO_ACCENT_SAME_NOTE 1103 +#define IDC_PATCH_METRO_ACCENT_VEL 1104 +#define IDC_PATCH_METRO_CHANNEL 1105 +#define IDC_PATCH_METRO_ENABLE 1106 +#define IDC_PATCH_METRO_NOTE 1107 +#define IDC_PATCH_METRO_PATCH 1108 +#define IDC_PATCH_METRO_PORT 1109 +#define IDC_PATCH_METRO_VELOCITY 1110 +#define IDC_PATCH_METRO_VOLUME 1111 +#define IDC_PIANO_CHANNEL 1112 +#define IDC_PIANO_KEY_COUNT 1113 +#define IDC_PIANO_PORT 1114 +#define IDC_PIANO_START_NOTE 1115 +#define IDC_PIANO_VELOCITY 1116 +#define IDC_PROGRESS 1117 +#define IDC_PROGRESS_PERCENT 1118 +#define IDC_REC_PLAY_DURATION 1119 +#define IDC_REC_PLAY_EXPORT 1120 +#define IDC_REC_PLAY_OPEN 1121 +#define IDC_REC_PLAY_PLAY 1122 +#define IDC_REC_PLAY_POSITION 1123 +#define IDC_REC_PLAY_POS_SLIDER 1124 +#define IDC_REC_PLAY_SAVE 1125 +#define IDC_REC_PLAY_STOP 1126 +#define IDC_REC_PLAY_TITLE 1127 +#define IDC_REC_PLAY_TRACK_LIST 1128 +#define IDC_SECTION_LIST 1129 +#define IDC_SECTION_PROPS_LENGTH 1130 +#define IDC_SECTION_PROPS_NAME 1131 +#define IDC_SECTION_PROPS_REPEAT 1132 +#define IDC_SECTION_PROPS_START 1133 +#define IDC_SONG_PROPS_COMMENTS 1134 +#define IDC_SONG_PROPS_KEY_SIG 1135 +#define IDC_SONG_PROPS_TEMPO 1136 +#define IDC_SONG_PROPS_TIME_SIG_DENOM 1137 +#define IDC_SONG_PROPS_TIME_SIG_NUMER 1138 +#define IDC_SONG_PROPS_TRANSPOSE 1139 +#define IDC_SPLASH_ICON 1140 +#define IDC_TABDLG_TAB 1141 +#define IDC_THREADS_LIST 1142 #define ID_APP_CHECK_FOR_UPDATES 32771 #define ID_APP_DEMO 32772 #define ID_APP_HOME_PAGE 32773 @@ -464,50 +467,51 @@ #define ID_MIDI_OUTPUT 32799 #define ID_MIDI_PANIC 32800 #define ID_MIDI_RESET_ALL 32801 -#define ID_OUT_NOTES_ROTATE_LABELS 32802 -#define ID_OUT_NOTES_SHOW_KEY_LABELS 32803 -#define ID_OUT_NOTES_SHOW_METRONOME 32804 -#define ID_PATCH_MRU_FILE1 32805 -#define ID_PATCH_MRU_FILE2 32806 -#define ID_PATCH_MRU_FILE3 32807 -#define ID_PATCH_MRU_FILE4 32808 -#define ID_PATCH_NEW 32809 -#define ID_PATCH_OPEN 32810 -#define ID_PATCH_SAVE 32811 -#define ID_PATCH_SAVE_AS 32812 -#define ID_PIANO_KEY_LABEL_TYPE 32813 -#define ID_PIANO_KEY_LABEL_TYPE2 32814 -#define ID_PIANO_KEY_LABEL_TYPE3 32815 -#define ID_PIANO_KEY_LABEL_TYPE4 32816 -#define ID_PIANO_KEY_LABEL_TYPE5 32817 -#define ID_PIANO_KEY_LABEL_TYPE6 32818 -#define ID_PIANO_ROTATE_LABELS 32819 -#define ID_PIANO_SHOW_OCTAVES 32820 -#define ID_PIANO_VERTICAL 32821 -#define ID_TRANSPORT_AUTO_REWIND 32822 -#define ID_TRANSPORT_NEXT_CHORD 32823 -#define ID_TRANSPORT_NEXT_SECTION 32824 -#define ID_TRANSPORT_PAUSE 32825 -#define ID_TRANSPORT_PLAY 32826 -#define ID_TRANSPORT_PREV_CHORD 32827 -#define ID_TRANSPORT_RECORD 32828 -#define ID_TRANSPORT_REPEAT 32829 -#define ID_TRANSPORT_REWIND 32830 -#define ID_VIEW_OUTPUT_NOTES 32831 -#define ID_VIEW_PARTS 32832 -#define ID_VIEW_PATCH 32833 -#define ID_VIEW_PIANO 32834 -#define ID_VIEW_RECORD_PLAYER 32835 -#define ID_VIEW_THREADS 32836 +#define ID_MIDI_TARGET_RESET 32802 +#define ID_OUT_NOTES_ROTATE_LABELS 32803 +#define ID_OUT_NOTES_SHOW_KEY_LABELS 32804 +#define ID_OUT_NOTES_SHOW_METRONOME 32805 +#define ID_PATCH_MRU_FILE1 32806 +#define ID_PATCH_MRU_FILE2 32807 +#define ID_PATCH_MRU_FILE3 32808 +#define ID_PATCH_MRU_FILE4 32809 +#define ID_PATCH_NEW 32810 +#define ID_PATCH_OPEN 32811 +#define ID_PATCH_SAVE 32812 +#define ID_PATCH_SAVE_AS 32813 +#define ID_PIANO_KEY_LABEL_TYPE 32814 +#define ID_PIANO_KEY_LABEL_TYPE2 32815 +#define ID_PIANO_KEY_LABEL_TYPE3 32816 +#define ID_PIANO_KEY_LABEL_TYPE4 32817 +#define ID_PIANO_KEY_LABEL_TYPE5 32818 +#define ID_PIANO_KEY_LABEL_TYPE6 32819 +#define ID_PIANO_ROTATE_LABELS 32820 +#define ID_PIANO_SHOW_OCTAVES 32821 +#define ID_PIANO_VERTICAL 32822 +#define ID_TRANSPORT_AUTO_REWIND 32823 +#define ID_TRANSPORT_NEXT_CHORD 32824 +#define ID_TRANSPORT_NEXT_SECTION 32825 +#define ID_TRANSPORT_PAUSE 32826 +#define ID_TRANSPORT_PLAY 32827 +#define ID_TRANSPORT_PREV_CHORD 32828 +#define ID_TRANSPORT_RECORD 32829 +#define ID_TRANSPORT_REPEAT 32830 +#define ID_TRANSPORT_REWIND 32831 +#define ID_VIEW_OUTPUT_NOTES 32832 +#define ID_VIEW_PARTS 32833 +#define ID_VIEW_PATCH 32834 +#define ID_VIEW_PIANO 32835 +#define ID_VIEW_RECORD_PLAYER 32836 +#define ID_VIEW_THREADS 32837 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_3D_CONTROLS 1 -#define _APS_NEXT_RESOURCE_VALUE 445 -#define _APS_NEXT_COMMAND_VALUE 32837 -#define _APS_NEXT_CONTROL_VALUE 1142 +#define _APS_NEXT_RESOURCE_VALUE 448 +#define _APS_NEXT_COMMAND_VALUE 32838 +#define _APS_NEXT_CONTROL_VALUE 1143 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif