From 20d28e80a5c861a9d5f449ea911ab75b4f37ad0d Mon Sep 17 00:00:00 2001 From: Jef Date: Tue, 24 Sep 2024 14:54:57 +0200 Subject: Initial community commit --- Src/Plugins/DSP/dsp_sc/Include/c_datapump.h | 174 + Src/Plugins/DSP/dsp_sc/Include/c_wavein.h | 156 + Src/Plugins/DSP/dsp_sc/NSIS/ShellDispatch.dll | Bin 0 -> 4608 bytes Src/Plugins/DSP/dsp_sc/NSIS/dsp_sc.dll | Bin 0 -> 220672 bytes Src/Plugins/DSP/dsp_sc/NSIS/dsp_sc_license.txt | 186 + Src/Plugins/DSP/dsp_sc/NSIS/dsp_sc_v2.nsi | 438 + Src/Plugins/DSP/dsp_sc/NSIS/modern-install.ico | Bin 0 -> 40585 bytes Src/Plugins/DSP/dsp_sc/NSIS/win.bmp | Bin 0 -> 154544 bytes Src/Plugins/DSP/dsp_sc/Resource/ICY.ICO | Bin 0 -> 4286 bytes Src/Plugins/DSP/dsp_sc/Resource/Script1.rc | 679 ++ Src/Plugins/DSP/dsp_sc/Resource/downarrow.ico | Bin 0 -> 1150 bytes Src/Plugins/DSP/dsp_sc/Resource/kill.ico | Bin 0 -> 1150 bytes Src/Plugins/DSP/dsp_sc/Resource/play.ico | Bin 0 -> 1150 bytes Src/Plugins/DSP/dsp_sc/Resource/refresh.ico | Bin 0 -> 1150 bytes Src/Plugins/DSP/dsp_sc/Resource/resource.h | 327 + Src/Plugins/DSP/dsp_sc/Resource/stop.ico | Bin 0 -> 1150 bytes Src/Plugins/DSP/dsp_sc/api.h | 36 + Src/Plugins/DSP/dsp_sc/crossfader/c_crossfader.cpp | 189 + Src/Plugins/DSP/dsp_sc/crossfader/c_crossfader.h | 33 + .../DSP/dsp_sc/docs/Source_DSP_Changelog.html | 367 + .../DSP/dsp_sc/docs/Source_DSP_Plug-in.html | 326 + .../docs/Source_DSP_Plug-in_Config_Examples.html | 87 + Src/Plugins/DSP/dsp_sc/docs/res/About_tab.png | Bin 0 -> 23732 bytes ...urce_to_a_SHOUTcast_v1_DNAS_Server_(Legacy).png | Bin 0 -> 38941 bytes ...Direct_Source_to_a_SHOUTcast_v2_DNAS_Server.png | Bin 0 -> 38700 bytes .../dsp_sc/docs/res/Input_tab_soundcard_input.png | Bin 0 -> 25373 bytes .../DSP/dsp_sc/docs/res/Input_tab_winamp_input.png | Bin 0 -> 23796 bytes .../docs/res/Output_tab_artwork_tab_v1_enabled.png | Bin 0 -> 20164 bytes .../docs/res/Output_tab_artwork_tab_v2_enabled.png | Bin 0 -> 22202 bytes .../dsp_sc/docs/res/Output_tab_directory_tab.png | Bin 0 -> 23276 bytes .../DSP/dsp_sc/docs/res/Output_tab_encoder_tab.png | Bin 0 -> 22617 bytes .../docs/res/Output_tab_login_tab_v1_enabled.png | Bin 0 -> 23258 bytes .../docs/res/Output_tab_login_tab_v2_enabled.png | Bin 0 -> 23164 bytes .../DSP/dsp_sc/docs/res/Output_tab_logs_tab.png | Bin 0 -> 22510 bytes .../DSP/dsp_sc/docs/res/Output_tab_titles_tab.png | Bin 0 -> 21513 bytes .../docs/res/Output_tag_configuration_error.png | Bin 0 -> 23161 bytes .../docs/res/Select_Source_DSP_in_Winamp.png | Bin 0 -> 28401 bytes Src/Plugins/DSP/dsp_sc/docs/res/Summary_tab.png | Bin 0 -> 28310 bytes Src/Plugins/DSP/dsp_sc/dsp_sc.vcxproj | 424 + Src/Plugins/DSP/dsp_sc/dsp_sc.vcxproj.filters | 188 + Src/Plugins/DSP/dsp_sc/main.cpp | 5804 ++++++++++++ .../DSP/dsp_sc/sc2srclib/Encoders/_ptrlist.h | 411 + .../DSP/dsp_sc/sc2srclib/Encoders/c_encoder.cpp | 95 + .../DSP/dsp_sc/sc2srclib/Encoders/c_encoder.h | 53 + .../dsp_sc/sc2srclib/Encoders/c_encoder_aacp.cpp | 84 + .../DSP/dsp_sc/sc2srclib/Encoders/c_encoder_aacp.h | 37 + .../dsp_sc/sc2srclib/Encoders/c_encoder_fhgaac.cpp | 80 + .../dsp_sc/sc2srclib/Encoders/c_encoder_fhgaac.h | 33 + .../dsp_sc/sc2srclib/Encoders/c_encoder_mp3dll.cpp | 105 + .../dsp_sc/sc2srclib/Encoders/c_encoder_mp3dll.h | 51 + .../dsp_sc/sc2srclib/Encoders/c_encoder_nsv.cpp | 151 + .../DSP/dsp_sc/sc2srclib/Encoders/c_encoder_nsv.h | 76 + .../dsp_sc/sc2srclib/Encoders/c_encoder_ogg.cpp | 117 + .../DSP/dsp_sc/sc2srclib/Encoders/c_encoder_ogg.h | 47 + Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/enc_if.h | 44 + .../DSP/dsp_sc/sc2srclib/Include/c_jobmanager.h | 254 + .../dsp_sc/sc2srclib/Include/c_serial_jobmanager.h | 24 + .../sc2srclib/Include/c_shoutcast_2_output.h | 194 + .../dsp_sc/sc2srclib/Include/shoutcast_output.h | 817 ++ .../DSP/dsp_sc/sc2srclib/lame/include/lame.h | 1323 +++ .../sc2srclib/lame/libmp3lame/lame_global_flags.h | 184 + .../DSP/dsp_sc/sc2srclib/shoutcast_output.cpp | 1636 ++++ .../DSP/dsp_sc/sc2srclib/uvAuth21/uvAuth21.cpp | 90 + .../DSP/dsp_sc/sc2srclib/uvAuth21/uvAuth21.h | 34 + Src/Plugins/DSP/dsp_sc/utils.cpp | 653 ++ Src/Plugins/DSP/dsp_sc/utils.h | 67 + Src/Plugins/DSP/dsp_sps/api__dsp_sps.h | 9 + Src/Plugins/DSP/dsp_sps/constant.bin | Bin 0 -> 247 bytes Src/Plugins/DSP/dsp_sps/dsp_sps.cpp | 294 + Src/Plugins/DSP/dsp_sps/dsp_sps.dsp | 250 + Src/Plugins/DSP/dsp_sps/dsp_sps.dsw | 29 + Src/Plugins/DSP/dsp_sps/dsp_sps.vcxproj | 346 + Src/Plugins/DSP/dsp_sps/dsp_sps.vcxproj.filters | 92 + Src/Plugins/DSP/dsp_sps/dxi/AudioPlugIn.cpp | 283 + Src/Plugins/DSP/dsp_sps/dxi/AudioPlugIn.def | 7 + Src/Plugins/DSP/dsp_sps/dxi/AudioPlugIn.dep | 69 + Src/Plugins/DSP/dsp_sps/dxi/AudioPlugIn.dsp | 271 + Src/Plugins/DSP/dsp_sps/dxi/AudioPlugIn.dsw | 29 + Src/Plugins/DSP/dsp_sps/dxi/AudioPlugIn.h | 47 + Src/Plugins/DSP/dsp_sps/dxi/AudioPlugIn.mak | 312 + Src/Plugins/DSP/dsp_sps/dxi/AudioPlugIn.rc | 202 + .../DSP/dsp_sps/dxi/AudioPlugInPropPage.cpp | 326 + Src/Plugins/DSP/dsp_sps/dxi/AudioPlugInPropPage.h | 68 + Src/Plugins/DSP/dsp_sps/dxi/Filter.cpp | 1139 +++ Src/Plugins/DSP/dsp_sps/dxi/Filter.h | 176 + Src/Plugins/DSP/dsp_sps/dxi/MediaParams.cpp | 465 + Src/Plugins/DSP/dsp_sps/dxi/MediaParams.h | 99 + Src/Plugins/DSP/dsp_sps/dxi/ParamEnvelope.cpp | 403 + Src/Plugins/DSP/dsp_sps/dxi/ParamEnvelope.h | 124 + Src/Plugins/DSP/dsp_sps/dxi/Parameters.h | 47 + Src/Plugins/DSP/dsp_sps/dxi/PlugInApp.cpp | 192 + Src/Plugins/DSP/dsp_sps/dxi/PlugInApp.h | 8 + Src/Plugins/DSP/dsp_sps/dxi/PlugInGUIDs.h | 5 + Src/Plugins/DSP/dsp_sps/dxi/ReadMe.txt | 68 + Src/Plugins/DSP/dsp_sps/dxi/StdAfx.cpp | 9 + Src/Plugins/DSP/dsp_sps/dxi/StdAfx.h | 43 + Src/Plugins/DSP/dsp_sps/dxi/dmoguids.lib | Bin 0 -> 22810 bytes Src/Plugins/DSP/dsp_sps/dxi/include/CakeMedParam.h | 461 + .../DSP/dsp_sps/dxi/include/CakeMedParam_i.c | 53 + Src/Plugins/DSP/dsp_sps/dxi/include/DXi.h | 121 + .../DSP/dsp_sps/dxi/include/DeferZeroFill.h | 33 + Src/Plugins/DSP/dsp_sps/dxi/res/AudioPlugIn.rc2 | 13 + Src/Plugins/DSP/dsp_sps/dxi/resource.h | 59 + Src/Plugins/DSP/dsp_sps/function.bin | Bin 0 -> 3130 bytes Src/Plugins/DSP/dsp_sps/general.bin | Bin 0 -> 2574 bytes Src/Plugins/DSP/dsp_sps/operator.bin | Bin 0 -> 699 bytes Src/Plugins/DSP/dsp_sps/res.rc | 210 + Src/Plugins/DSP/dsp_sps/resource.h | 70 + Src/Plugins/DSP/dsp_sps/sps_common.cpp | 574 ++ Src/Plugins/DSP/dsp_sps/sps_common.h | 63 + Src/Plugins/DSP/dsp_sps/sps_configdlg.h | 476 + Src/Plugins/DSP/dsp_sps/version.rc2 | 39 + Src/Plugins/Encoder/enc_fhgaac/ADTSAACEncoder.cpp | 119 + Src/Plugins/Encoder/enc_fhgaac/ADTSAACEncoder.h | 25 + Src/Plugins/Encoder/enc_fhgaac/FhGAACEncoder.cpp | 128 + Src/Plugins/Encoder/enc_fhgaac/FhGAACEncoder.h | 29 + Src/Plugins/Encoder/enc_fhgaac/MP4Writer.cpp | 64 + Src/Plugins/Encoder/enc_fhgaac/MP4Writer.h | 25 + Src/Plugins/Encoder/enc_fhgaac/config.cpp | 258 + Src/Plugins/Encoder/enc_fhgaac/config.h | 64 + Src/Plugins/Encoder/enc_fhgaac/enc_fhgaac.rc | 150 + Src/Plugins/Encoder/enc_fhgaac/enc_fhgaac.sln | 26 + Src/Plugins/Encoder/enc_fhgaac/enc_fhgaac.vcproj | 322 + Src/Plugins/Encoder/enc_fhgaac/encoder_common.h | 9 + .../enc_fhgaac/fraunhofer_wa_prefs_noalpha.bmp | Bin 0 -> 13654 bytes Src/Plugins/Encoder/enc_fhgaac/libirc.lib | Bin 0 -> 375832 bytes Src/Plugins/Encoder/enc_fhgaac/link_control.cpp | 63 + Src/Plugins/Encoder/enc_fhgaac/link_control.h | 7 + Src/Plugins/Encoder/enc_fhgaac/main.cpp | 263 + Src/Plugins/Encoder/enc_fhgaac/mp4FastAAClib.h | 978 ++ Src/Plugins/Encoder/enc_fhgaac/mp4FastAAClib.lib | Bin 0 -> 2912248 bytes Src/Plugins/Encoder/enc_fhgaac/preferences.cpp | 122 + Src/Plugins/Encoder/enc_fhgaac/preferences.h | 18 + .../Encoder/enc_fhgaac/preferences_adts.cpp | 124 + Src/Plugins/Encoder/enc_fhgaac/preferences_mp4.cpp | 152 + Src/Plugins/Encoder/enc_fhgaac/resource.h | 46 + Src/Plugins/Encoder/enc_fhgaac/version.rc2 | 39 + Src/Plugins/Encoder/enc_flac/AudioCoderFlac.cpp | 203 + Src/Plugins/Encoder/enc_flac/AudioCoderFlac.h | 29 + Src/Plugins/Encoder/enc_flac/StreamFileWin32.cpp | 69 + Src/Plugins/Encoder/enc_flac/StreamFileWin32.h | 18 + Src/Plugins/Encoder/enc_flac/api__enc_flac.h | 10 + Src/Plugins/Encoder/enc_flac/enc_flac2.rc | 119 + Src/Plugins/Encoder/enc_flac/enc_flac2.sln | 56 + Src/Plugins/Encoder/enc_flac/enc_flac2.vcxproj | 277 + .../Encoder/enc_flac/enc_flac2.vcxproj.filters | 44 + Src/Plugins/Encoder/enc_flac/main.cpp | 320 + Src/Plugins/Encoder/enc_flac/resource.h | 24 + Src/Plugins/Encoder/enc_flac/version.rc2 | 39 + Src/Plugins/Encoder/enc_lame/24bit.cpp | 147 + Src/Plugins/Encoder/enc_lame/BladeMP3EncDLL.c | 1028 ++ Src/Plugins/Encoder/enc_lame/BladeMP3EncDLL.h | 278 + Src/Plugins/Encoder/enc_lame/MP3Coder.cpp | 196 + Src/Plugins/Encoder/enc_lame/MP3Coder.h | 62 + Src/Plugins/Encoder/enc_lame/enc_lame.rc | 145 + Src/Plugins/Encoder/enc_lame/enc_lame.sln | 30 + Src/Plugins/Encoder/enc_lame/enc_lame.vcxproj | 293 + .../Encoder/enc_lame/enc_lame.vcxproj.filters | 44 + Src/Plugins/Encoder/enc_lame/main.cpp | 503 + Src/Plugins/Encoder/enc_lame/resource.h | 57 + Src/Plugins/Encoder/enc_lame/version.rc2 | 39 + .../Encoder/enc_vorbis/Libs/libogg_static.lib | Bin 0 -> 72376 bytes .../Encoder/enc_vorbis/Libs/vorbis_static.lib | Bin 0 -> 2341802 bytes .../Encoder/enc_vorbis/Libs/vorbisenc_static.lib | Bin 0 -> 90206 bytes Src/Plugins/Encoder/enc_vorbis/enc_vorbis.rc | 100 + Src/Plugins/Encoder/enc_vorbis/enc_vorbis.sln | 60 + Src/Plugins/Encoder/enc_vorbis/enc_vorbis.vcxproj | 297 + .../Encoder/enc_vorbis/enc_vorbis.vcxproj.filters | 29 + Src/Plugins/Encoder/enc_vorbis/main.cpp | 595 ++ .../enc_vorbis/ogg/include/ogg/config_types.h | 25 + .../Encoder/enc_vorbis/ogg/include/ogg/ogg.h | 210 + .../Encoder/enc_vorbis/ogg/include/ogg/os_types.h | 148 + Src/Plugins/Encoder/enc_vorbis/resource.h | 51 + Src/Plugins/Encoder/enc_vorbis/version.rc2 | 39 + .../enc_vorbis/vorbis/include/vorbis/codec.h | 243 + .../enc_vorbis/vorbis/include/vorbis/vorbisenc.h | 112 + .../enc_vorbis/vorbis/include/vorbis/vorbisfile.h | 201 + Src/Plugins/Encoder/enc_wav/ACMEncoder.cpp | 265 + Src/Plugins/Encoder/enc_wav/ACMEncoder.h | 47 + Src/Plugins/Encoder/enc_wav/Config.cpp | 110 + Src/Plugins/Encoder/enc_wav/Config.h | 31 + Src/Plugins/Encoder/enc_wav/Finisher.h | 10 + Src/Plugins/Encoder/enc_wav/WAVEncoder.cpp | 72 + Src/Plugins/Encoder/enc_wav/WAVEncoder.h | 25 + Src/Plugins/Encoder/enc_wav/enc_wav.rc | 111 + Src/Plugins/Encoder/enc_wav/enc_wav.sln | 30 + Src/Plugins/Encoder/enc_wav/enc_wav.vcxproj | 252 + .../Encoder/enc_wav/enc_wav.vcxproj.filters | 50 + Src/Plugins/Encoder/enc_wav/main.cpp | 176 + Src/Plugins/Encoder/enc_wav/preferences.cpp | 1 + Src/Plugins/Encoder/enc_wav/resource.h | 23 + Src/Plugins/Encoder/enc_wav/version.rc2 | 39 + Src/Plugins/Encoder/enc_wma/ASFErr.h | 256 + Src/Plugins/Encoder/enc_wma/AudioCoderWMA.cpp | 468 + Src/Plugins/Encoder/enc_wma/AudioCoderWMA.h | 50 + Src/Plugins/Encoder/enc_wma/config.cpp | 648 ++ Src/Plugins/Encoder/enc_wma/enc_wma.rc | 128 + Src/Plugins/Encoder/enc_wma/enc_wma.sln | 30 + Src/Plugins/Encoder/enc_wma/enc_wma.vcxproj | 276 + .../Encoder/enc_wma/enc_wma.vcxproj.filters | 44 + Src/Plugins/Encoder/enc_wma/main.cpp | 212 + Src/Plugins/Encoder/enc_wma/main.h | 55 + Src/Plugins/Encoder/enc_wma/nserror.h | 1631 ++++ Src/Plugins/Encoder/enc_wma/resource.h | 57 + Src/Plugins/Encoder/enc_wma/version.rc2 | 39 + Src/Plugins/Encoder/enc_wma/wmaudiosdk.h | 1009 ++ .../General/gen_crasher/ExceptionHandler.cpp | 814 ++ Src/Plugins/General/gen_crasher/ExceptionHandler.h | 33 + Src/Plugins/General/gen_crasher/GetWinVer.cpp | 195 + Src/Plugins/General/gen_crasher/GetWinVer.h | 76 + Src/Plugins/General/gen_crasher/MiniVersion.cpp | 267 + Src/Plugins/General/gen_crasher/MiniVersion.h | 69 + Src/Plugins/General/gen_crasher/ReadMe.txt | 3 + Src/Plugins/General/gen_crasher/api__gen_crasher.h | 20 + Src/Plugins/General/gen_crasher/config.cpp | 308 + Src/Plugins/General/gen_crasher/config.h | 62 + Src/Plugins/General/gen_crasher/configDlg.cpp | 457 + Src/Plugins/General/gen_crasher/configDlg.h | 15 + Src/Plugins/General/gen_crasher/crashDlg.cpp | 101 + Src/Plugins/General/gen_crasher/crashDlg.h | 5 + .../gen_crasher/feedback/email/SendEmail.cpp | 364 + .../General/gen_crasher/feedback/email/SendEmail.h | 38 + .../General/gen_crasher/feedback/feedback.rc | 119 + .../General/gen_crasher/feedback/feedback.sln | 30 + .../General/gen_crasher/feedback/feedback.vcproj | 321 + .../General/gen_crasher/feedback/feedback.vcxproj | 253 + .../gen_crasher/feedback/feedback.vcxproj.filters | 79 + Src/Plugins/General/gen_crasher/feedback/main.cpp | 79 + Src/Plugins/General/gen_crasher/feedback/main.h | 19 + .../General/gen_crasher/feedback/manifest.xml | 36 + .../General/gen_crasher/feedback/operations.cpp | 163 + .../General/gen_crasher/feedback/resource.h | 29 + .../General/gen_crasher/feedback/silent.cpp | 134 + .../General/gen_crasher/feedback/smtp/Base64.cpp | 320 + .../General/gen_crasher/feedback/smtp/Base64.h | 63 + .../General/gen_crasher/feedback/smtp/Smtp.cpp | 1468 +++ .../General/gen_crasher/feedback/smtp/Smtp.h | 245 + .../General/gen_crasher/feedback/version.rc2 | 39 + .../General/gen_crasher/feedback/xzip/XZip.cpp | 2915 ++++++ .../General/gen_crasher/feedback/xzip/XZip.h | 324 + Src/Plugins/General/gen_crasher/gen_crasher.rc | 193 + Src/Plugins/General/gen_crasher/gen_crasher.sln | 31 + .../General/gen_crasher/gen_crasher.vcxproj | 290 + .../gen_crasher/gen_crasher.vcxproj.filters | 92 + Src/Plugins/General/gen_crasher/main.cpp | 140 + Src/Plugins/General/gen_crasher/main.h | 24 + Src/Plugins/General/gen_crasher/minidump.h | 29 + Src/Plugins/General/gen_crasher/resource.h | 96 + Src/Plugins/General/gen_crasher/settings.cpp | 251 + Src/Plugins/General/gen_crasher/settings.h | 67 + Src/Plugins/General/gen_crasher/smtpDlg.cpp | 127 + Src/Plugins/General/gen_crasher/smtpDlg.h | 5 + Src/Plugins/General/gen_crasher/version.rc2 | 39 + Src/Plugins/General/gen_ff/AlbumArt.cpp | 561 ++ Src/Plugins/General/gen_ff/AlbumArt.h | 115 + Src/Plugins/General/gen_ff/MediaDownloader.cpp | 307 + Src/Plugins/General/gen_ff/README.txt | 6 + .../General/gen_ff/WinampConfigScriptObject.cpp | 276 + .../General/gen_ff/WinampConfigScriptObject.h | 99 + Src/Plugins/General/gen_ff/api__gen_ff.h | 20 + .../General/gen_ff/bitmaps/library-hilited.png | Bin 0 -> 1307 bytes .../General/gen_ff/bitmaps/library-selected.png | Bin 0 -> 1306 bytes .../General/gen_ff/bitmaps/library-unselected.png | Bin 0 -> 1304 bytes Src/Plugins/General/gen_ff/bitmaps/mb-hilited.png | Bin 0 -> 1424 bytes Src/Plugins/General/gen_ff/bitmaps/mb-selected.png | Bin 0 -> 1382 bytes .../General/gen_ff/bitmaps/mb-unselected.png | Bin 0 -> 1428 bytes .../General/gen_ff/bitmaps/pledit-hover.png | Bin 0 -> 828 bytes .../General/gen_ff/bitmaps/pledit-selected.png | Bin 0 -> 807 bytes .../General/gen_ff/bitmaps/pledit-unselected.png | Bin 0 -> 826 bytes .../General/gen_ff/bitmaps/video-hilited.png | Bin 0 -> 1441 bytes .../General/gen_ff/bitmaps/video-selected.png | Bin 0 -> 1435 bytes .../General/gen_ff/bitmaps/video-unselected.png | Bin 0 -> 1439 bytes Src/Plugins/General/gen_ff/bitmaps/vis-hilited.png | Bin 0 -> 3241 bytes .../General/gen_ff/bitmaps/vis-selected.png | Bin 0 -> 3509 bytes .../General/gen_ff/bitmaps/vis-unselected.png | Bin 0 -> 3037 bytes Src/Plugins/General/gen_ff/embedwndguid.cpp | 200 + Src/Plugins/General/gen_ff/embedwndguid.h | 42 + Src/Plugins/General/gen_ff/ff_ipc.cpp | 535 ++ Src/Plugins/General/gen_ff/ff_ipc.h | 86 + Src/Plugins/General/gen_ff/fsmonitor.cpp | 174 + Src/Plugins/General/gen_ff/fsmonitor.h | 39 + Src/Plugins/General/gen_ff/gen.h | 1 + Src/Plugins/General/gen_ff/gen_ff.rc | 543 ++ Src/Plugins/General/gen_ff/gen_ff.rc2 | 58 + Src/Plugins/General/gen_ff/gen_ff.sln | 204 + Src/Plugins/General/gen_ff/gen_ff.vcxproj | 988 ++ Src/Plugins/General/gen_ff/gen_ff.vcxproj.filters | 2074 ++++ Src/Plugins/General/gen_ff/gen_ff_ipc.h | 20 + Src/Plugins/General/gen_ff/main.cpp | 3527 +++++++ Src/Plugins/General/gen_ff/main.h | 29 + Src/Plugins/General/gen_ff/menuactions.cpp | 787 ++ Src/Plugins/General/gen_ff/menuactions.h | 91 + Src/Plugins/General/gen_ff/minibrowserCOM.cpp | 93 + Src/Plugins/General/gen_ff/minibrowserCOM.h | 25 + Src/Plugins/General/gen_ff/precomp__gen_ff.cpp | 7 + Src/Plugins/General/gen_ff/precomp__gen_ff.h | 76 + Src/Plugins/General/gen_ff/prefs.cpp | 136 + Src/Plugins/General/gen_ff/prefs.h | 30 + Src/Plugins/General/gen_ff/prefs_about.cpp | 98 + Src/Plugins/General/gen_ff/prefs_alpha.cpp | 280 + Src/Plugins/General/gen_ff/prefs_colorthemes.cpp | 101 + Src/Plugins/General/gen_ff/prefs_font.cpp | 860 ++ Src/Plugins/General/gen_ff/prefs_general.cpp | 273 + Src/Plugins/General/gen_ff/resource.h | 261 + Src/Plugins/General/gen_ff/servicelink.cpp | 72 + Src/Plugins/General/gen_ff/skininfo.cpp | 141 + Src/Plugins/General/gen_ff/skininfo.h | 42 + Src/Plugins/General/gen_ff/wa2buckitems.cpp | 96 + Src/Plugins/General/gen_ff/wa2buckitems.h | 70 + Src/Plugins/General/gen_ff/wa2cfgitems.cpp | 306 + Src/Plugins/General/gen_ff/wa2cfgitems.h | 137 + Src/Plugins/General/gen_ff/wa2core.cpp | 930 ++ Src/Plugins/General/gen_ff/wa2core.h | 147 + Src/Plugins/General/gen_ff/wa2coreactions.cpp | 279 + Src/Plugins/General/gen_ff/wa2coreactions.h | 6 + Src/Plugins/General/gen_ff/wa2frontend.cpp | 1344 +++ Src/Plugins/General/gen_ff/wa2frontend.h | 359 + Src/Plugins/General/gen_ff/wa2groupdefs.cpp | 36 + Src/Plugins/General/gen_ff/wa2groupdefs.h | 18 + Src/Plugins/General/gen_ff/wa2playlist.cpp | 2 + Src/Plugins/General/gen_ff/wa2playlist.h | 11 + Src/Plugins/General/gen_ff/wa2pldirobj.cpp | 366 + Src/Plugins/General/gen_ff/wa2pldirobj.h | 105 + Src/Plugins/General/gen_ff/wa2pledit.cpp | 241 + Src/Plugins/General/gen_ff/wa2pledit.h | 68 + Src/Plugins/General/gen_ff/wa2songticker.cpp | 483 + Src/Plugins/General/gen_ff/wa2songticker.h | 108 + Src/Plugins/General/gen_ff/wa2wndembed.cpp | 946 ++ Src/Plugins/General/gen_ff/wa2wndembed.h | 223 + Src/Plugins/General/gen_ff/wasabi.dsp | 1931 ++++ Src/Plugins/General/gen_ff/wasabi.vcproj | 286 + Src/Plugins/General/gen_ff/wasabicfg.h | 491 + Src/Plugins/General/gen_hotkeys/Configdlg.cpp | 471 + Src/Plugins/General/gen_hotkeys/Configdlg.h | 9 + Src/Plugins/General/gen_hotkeys/HOTKEY.CPP | 56 + Src/Plugins/General/gen_hotkeys/HOTKEY.H | 32 + Src/Plugins/General/gen_hotkeys/HotKeyCtl.cpp | 293 + Src/Plugins/General/gen_hotkeys/HotKeyCtl.h | 13 + Src/Plugins/General/gen_hotkeys/RESOURCE.H | 116 + Src/Plugins/General/gen_hotkeys/WINAMP.H | 38 + Src/Plugins/General/gen_hotkeys/Wacommands.cpp | 366 + Src/Plugins/General/gen_hotkeys/Wacommands.h | 50 + Src/Plugins/General/gen_hotkeys/accelBlock.cpp | 47 + Src/Plugins/General/gen_hotkeys/accelBlock.h | 24 + Src/Plugins/General/gen_hotkeys/api__gen_hotkeys.h | 10 + Src/Plugins/General/gen_hotkeys/gen_hotkeys.cpp | 519 + Src/Plugins/General/gen_hotkeys/gen_hotkeys.h | 56 + Src/Plugins/General/gen_hotkeys/gen_hotkeys.rc | 217 + Src/Plugins/General/gen_hotkeys/gen_hotkeys.sln | 29 + .../General/gen_hotkeys/gen_hotkeys.vcxproj | 316 + .../gen_hotkeys/gen_hotkeys.vcxproj.filters | 79 + Src/Plugins/General/gen_hotkeys/version.rc2 | 39 + Src/Plugins/General/gen_hotkeys/wa_hotkeys.h | 91 + Src/Plugins/General/gen_hotkeys/x.bmp | Bin 0 -> 670 bytes Src/Plugins/General/gen_ml/HeaderIconList.cpp | 1115 +++ Src/Plugins/General/gen_ml/IPC.cpp | 1588 ++++ Src/Plugins/General/gen_ml/MediaLibraryCOM.cpp | 100 + Src/Plugins/General/gen_ml/MediaLibraryCOM.h | 19 + Src/Plugins/General/gen_ml/MusicID.cpp | 658 ++ Src/Plugins/General/gen_ml/MusicID.h | 21 + Src/Plugins/General/gen_ml/OnlineMediaCOM.cpp | 83 + Src/Plugins/General/gen_ml/OnlineMediaCOM.h | 19 + Src/Plugins/General/gen_ml/RatingsCOM.cpp | 119 + Src/Plugins/General/gen_ml/RatingsCOM.h | 19 + Src/Plugins/General/gen_ml/SmoothScrollList.cpp | 1837 ++++ Src/Plugins/General/gen_ml/api__gen_ml.h | 68 + Src/Plugins/General/gen_ml/banner.cpp | 215 + Src/Plugins/General/gen_ml/banner.h | 44 + Src/Plugins/General/gen_ml/childwnd.cpp | 235 + Src/Plugins/General/gen_ml/childwnd.h | 38 + Src/Plugins/General/gen_ml/colors.cpp | 207 + Src/Plugins/General/gen_ml/colors.h | 65 + Src/Plugins/General/gen_ml/comboskin.cpp | 148 + Src/Plugins/General/gen_ml/comboskin.h | 18 + Src/Plugins/General/gen_ml/config.cpp | 128 + Src/Plugins/General/gen_ml/config.h | 31 + Src/Plugins/General/gen_ml/fileview.cpp | 2059 ++++ Src/Plugins/General/gen_ml/fileview.h | 23 + Src/Plugins/General/gen_ml/fileview_columns.cpp | 39 + Src/Plugins/General/gen_ml/fileview_compare.cpp | 255 + Src/Plugins/General/gen_ml/fileview_filesystem.cpp | 289 + Src/Plugins/General/gen_ml/fileview_format.cpp | 335 + Src/Plugins/General/gen_ml/fileview_internal.h | 207 + Src/Plugins/General/gen_ml/fileview_menu.cpp | 331 + Src/Plugins/General/gen_ml/fileview_metadata.cpp | 789 ++ Src/Plugins/General/gen_ml/fileview_toolbar.cpp | 338 + Src/Plugins/General/gen_ml/flickerfix.cpp | 101 + .../General/gen_ml/folderborwser_listbox.cpp | 388 + Src/Plugins/General/gen_ml/folderbrowser.cpp | 2271 +++++ Src/Plugins/General/gen_ml/folderbrowser.h | 22 + .../General/gen_ml/folderbrowser_internal.h | 34 + Src/Plugins/General/gen_ml/gaystring.cpp | 182 + Src/Plugins/General/gen_ml/gaystring.h | 44 + Src/Plugins/General/gen_ml/gen_ml.rc | 648 ++ Src/Plugins/General/gen_ml/gen_ml.sln | 92 + Src/Plugins/General/gen_ml/gen_ml.vcxproj | 526 ++ Src/Plugins/General/gen_ml/gen_ml.vcxproj.filters | 540 ++ Src/Plugins/General/gen_ml/graphics.cpp | 130 + Src/Plugins/General/gen_ml/graphics.h | 26 + Src/Plugins/General/gen_ml/imagefilters.cpp | 349 + Src/Plugins/General/gen_ml/imagefilters.h | 29 + Src/Plugins/General/gen_ml/itemlist.cpp | 147 + Src/Plugins/General/gen_ml/itemlist.h | 54 + Src/Plugins/General/gen_ml/klib/khash.h | 528 ++ Src/Plugins/General/gen_ml/listheader.cpp | 295 + Src/Plugins/General/gen_ml/listskin.cpp | 219 + Src/Plugins/General/gen_ml/listskin.h | 36 + Src/Plugins/General/gen_ml/listview.cpp | 128 + Src/Plugins/General/gen_ml/listview.h | 143 + Src/Plugins/General/gen_ml/main.cpp | 1172 +++ Src/Plugins/General/gen_ml/main.h | 142 + Src/Plugins/General/gen_ml/menu.cpp | 20 + Src/Plugins/General/gen_ml/menu.h | 17 + Src/Plugins/General/gen_ml/menufucker.h | 43 + Src/Plugins/General/gen_ml/ml.h | 915 ++ Src/Plugins/General/gen_ml/ml_cloud.cpp | 66 + Src/Plugins/General/gen_ml/ml_cloud.h | 15 + Src/Plugins/General/gen_ml/ml_cloudcolumn.cpp | 80 + Src/Plugins/General/gen_ml/ml_cloudcolumn.h | 34 + Src/Plugins/General/gen_ml/ml_ex/ex.rc | 144 + Src/Plugins/General/gen_ml/ml_ex/ml_ex.dsp | 152 + Src/Plugins/General/gen_ml/ml_ex/ml_ex.dsw | 29 + Src/Plugins/General/gen_ml/ml_ex/resource.h | 62 + Src/Plugins/General/gen_ml/ml_ex/view_ex.cpp | 888 ++ Src/Plugins/General/gen_ml/ml_imagefilter.cpp | 169 + Src/Plugins/General/gen_ml/ml_imagefilter.h | 47 + Src/Plugins/General/gen_ml/ml_imagelist.cpp | 384 + Src/Plugins/General/gen_ml/ml_imagelist.h | 44 + Src/Plugins/General/gen_ml/ml_imageloader.cpp | 689 ++ Src/Plugins/General/gen_ml/ml_imageloader.h | 52 + Src/Plugins/General/gen_ml/ml_ipc.h | 92 + Src/Plugins/General/gen_ml/ml_ipc_0313.h | 1852 ++++ Src/Plugins/General/gen_ml/ml_lib.cpp | 586 ++ Src/Plugins/General/gen_ml/ml_rating.cpp | 125 + Src/Plugins/General/gen_ml/ml_rating.h | 39 + Src/Plugins/General/gen_ml/ml_ratingcolumn.cpp | 874 ++ Src/Plugins/General/gen_ml/ml_ratingcolumn.h | 90 + Src/Plugins/General/gen_ml/mldwm.cpp | 46 + Src/Plugins/General/gen_ml/mldwm.h | 47 + Src/Plugins/General/gen_ml/navigation.cpp | 2733 ++++++ Src/Plugins/General/gen_ml/navigation.h | 267 + Src/Plugins/General/gen_ml/plugin.cpp | 719 ++ Src/Plugins/General/gen_ml/png.rc | 57 + Src/Plugins/General/gen_ml/prefs.cpp | 787 ++ Src/Plugins/General/gen_ml/reflectmsg.cpp | 188 + Src/Plugins/General/gen_ml/reflectmsg.h | 50 + Src/Plugins/General/gen_ml/resource.h | 263 + Src/Plugins/General/gen_ml/resources/checkmark.png | Bin 0 -> 302 bytes .../General/gen_ml/resources/cloud_16_incloud.png | Bin 0 -> 206 bytes .../General/gen_ml/resources/cloud_16_partial.png | Bin 0 -> 226 bytes .../General/gen_ml/resources/cloud_16_unavail.png | Bin 0 -> 232 bytes .../General/gen_ml/resources/cloud_16_upload.png | Bin 0 -> 124 bytes .../gen_ml/resources/cloud_16_uploading.png | Bin 0 -> 228 bytes Src/Plugins/General/gen_ml/resources/dragdrop.cur | Bin 0 -> 326 bytes .../General/gen_ml/resources/filetype_audio_16.png | Bin 0 -> 198 bytes .../General/gen_ml/resources/filetype_audio_32.png | Bin 0 -> 561 bytes .../gen_ml/resources/filetype_playlist_16.png | Bin 0 -> 272 bytes .../gen_ml/resources/filetype_playlist_32.png | Bin 0 -> 338 bytes .../gen_ml/resources/filetype_unknown_16.png | Bin 0 -> 186 bytes .../gen_ml/resources/filetype_unknown_32.png | Bin 0 -> 274 bytes .../General/gen_ml/resources/filetype_video_16.png | Bin 0 -> 210 bytes .../General/gen_ml/resources/filetype_video_32.png | Bin 0 -> 461 bytes .../General/gen_ml/resources/menu_arrow.png | Bin 0 -> 122 bytes .../General/gen_ml/resources/menu_check.png | Bin 0 -> 128 bytes Src/Plugins/General/gen_ml/resources/menu_dot.png | Bin 0 -> 130 bytes .../General/gen_ml/resources/menu_scrollarrow.png | Bin 0 -> 132 bytes .../gen_ml/resources/menu_scrollarrow_disabled.png | Bin 0 -> 130 bytes Src/Plugins/General/gen_ml/resources/rating.png | Bin 0 -> 328 bytes Src/Plugins/General/gen_ml/resources/sortarrow.png | Bin 0 -> 182 bytes .../General/gen_ml/resources/splitarrow.png | Bin 0 -> 120 bytes .../gen_ml/resources/splitarrow_pressed.png | Bin 0 -> 120 bytes .../General/gen_ml/resources/stars_invert.bmp | Bin 0 -> 2082 bytes .../gen_ml/resources/ti_default_16x16x16.bmp | Bin 0 -> 568 bytes .../General/gen_ml/resources/ti_labs_16x16x16.bmp | Bin 0 -> 1078 bytes .../gen_ml/resources/tree_closed_16x16x16.bmp | Bin 0 -> 568 bytes .../resources/tree_closed_disabled_16x16x16.bmp | Bin 0 -> 568 bytes .../gen_ml/resources/tree_open_16x16x16.bmp | Bin 0 -> 568 bytes .../General/gen_ml/resources/view_mode_detail.png | Bin 0 -> 157 bytes .../General/gen_ml/resources/view_mode_icon.png | Bin 0 -> 179 bytes .../General/gen_ml/resources/view_mode_list.png | Bin 0 -> 131 bytes Src/Plugins/General/gen_ml/scrollwnd.cpp | 2391 +++++ Src/Plugins/General/gen_ml/scrollwnd.h | 220 + Src/Plugins/General/gen_ml/sendto.cpp | 305 + Src/Plugins/General/gen_ml/sendto.h | 31 + Src/Plugins/General/gen_ml/service.cpp | 147 + Src/Plugins/General/gen_ml/service.h | 49 + Src/Plugins/General/gen_ml/setup.cpp | 39 + Src/Plugins/General/gen_ml/skinexport.cpp | 72 + Src/Plugins/General/gen_ml/skinexport.h | 31 + Src/Plugins/General/gen_ml/skinnedbutton.cpp | 1343 +++ Src/Plugins/General/gen_ml/skinnedbutton.h | 71 + Src/Plugins/General/gen_ml/skinnedcombo.cpp | 444 + Src/Plugins/General/gen_ml/skinnedcombo.h | 42 + Src/Plugins/General/gen_ml/skinneddivider.cpp | 128 + Src/Plugins/General/gen_ml/skinneddivider.h | 34 + Src/Plugins/General/gen_ml/skinneddlg.cpp | 45 + Src/Plugins/General/gen_ml/skinneddlg.h | 29 + Src/Plugins/General/gen_ml/skinnededit.cpp | 787 ++ Src/Plugins/General/gen_ml/skinnededit.h | 54 + Src/Plugins/General/gen_ml/skinnedfolder.cpp | 61 + Src/Plugins/General/gen_ml/skinnedfolder.h | 32 + Src/Plugins/General/gen_ml/skinnedheader.cpp | 832 ++ Src/Plugins/General/gen_ml/skinnedheader.h | 71 + Src/Plugins/General/gen_ml/skinnedlistbox.cpp | 250 + Src/Plugins/General/gen_ml/skinnedlistbox.h | 33 + Src/Plugins/General/gen_ml/skinnedlistview.cpp | 526 ++ Src/Plugins/General/gen_ml/skinnedlistview.h | 46 + Src/Plugins/General/gen_ml/skinnedmenu.cpp | 81 + Src/Plugins/General/gen_ml/skinnedmenu.h | 42 + .../General/gen_ml/skinnedmenuthreadinfo.cpp | 468 + Src/Plugins/General/gen_ml/skinnedmenuthreadinfo.h | 74 + Src/Plugins/General/gen_ml/skinnedmenuwnd.cpp | 1538 +++ Src/Plugins/General/gen_ml/skinnedmenuwnd.h | 97 + Src/Plugins/General/gen_ml/skinnedprogressbar.cpp | 131 + Src/Plugins/General/gen_ml/skinnedprogressbar.h | 33 + Src/Plugins/General/gen_ml/skinnedscrollwnd.cpp | 3285 +++++++ Src/Plugins/General/gen_ml/skinnedscrollwnd.h | 99 + Src/Plugins/General/gen_ml/skinnedstatic.cpp | 135 + Src/Plugins/General/gen_ml/skinnedstatic.h | 30 + Src/Plugins/General/gen_ml/skinnedtooltip.cpp | 190 + Src/Plugins/General/gen_ml/skinnedtooltip.h | 35 + Src/Plugins/General/gen_ml/skinnedwnd.cpp | 569 ++ Src/Plugins/General/gen_ml/skinnedwnd.h | 101 + Src/Plugins/General/gen_ml/skinning.cpp | 151 + Src/Plugins/General/gen_ml/skinning.h | 26 + Src/Plugins/General/gen_ml/stockobjects.cpp | 215 + Src/Plugins/General/gen_ml/stockobjects.h | 42 + Src/Plugins/General/gen_ml/stringvector.cpp | 130 + Src/Plugins/General/gen_ml/stringvector.h | 51 + Src/Plugins/General/gen_ml/unused.cpp | 7 + Src/Plugins/General/gen_ml/util.cpp | 84 + Src/Plugins/General/gen_ml/version.rc2 | 39 + Src/Plugins/General/gen_ml/view_mb.h | 129 + Src/Plugins/General/gen_ml/view_ml.cpp | 1723 ++++ Src/Plugins/General/gen_ml/wa_dlg.cpp | 2 + Src/Plugins/General/gen_ml/webinfo_dlg.cpp | 547 ++ Src/Plugins/General/gen_ml/webinfo_obj.cpp | 438 + Src/Plugins/General/gen_ml/webinfo_obj.h | 72 + Src/Plugins/General/gen_tray/GEN_TRAY.sln | 30 + Src/Plugins/General/gen_tray/GEN_TRAY.vcxproj | 296 + .../General/gen_tray/GEN_TRAY.vcxproj.filters | 64 + Src/Plugins/General/gen_tray/RESOURCE.H | 66 + Src/Plugins/General/gen_tray/TRAYCTL.C | 1526 +++ Src/Plugins/General/gen_tray/WINAMPCMD.H | 62 + Src/Plugins/General/gen_tray/api__gen_tray.h | 8 + Src/Plugins/General/gen_tray/gen_tray.rc | 178 + Src/Plugins/General/gen_tray/icons/compact.bmp | Bin 0 -> 126 bytes Src/Plugins/General/gen_tray/icons/icon1.ico | Bin 0 -> 318 bytes Src/Plugins/General/gen_tray/icons/icon2.ico | Bin 0 -> 318 bytes Src/Plugins/General/gen_tray/icons/icon3.ico | Bin 0 -> 318 bytes Src/Plugins/General/gen_tray/icons/icon4.ico | Bin 0 -> 318 bytes Src/Plugins/General/gen_tray/icons/icon5.ico | Bin 0 -> 318 bytes Src/Plugins/General/gen_tray/icons/icon7.ico | Bin 0 -> 318 bytes Src/Plugins/General/gen_tray/icons/icon8.ico | Bin 0 -> 318 bytes Src/Plugins/General/gen_tray/icons/icon9.ico | Bin 0 -> 318 bytes Src/Plugins/General/gen_tray/version.rc2 | 39 + Src/Plugins/Input/in_avi/ExtendedFileInfo.cpp | 138 + Src/Plugins/Input/in_avi/InfoDialog.cpp | 390 + Src/Plugins/Input/in_avi/PlayThread.cpp | 964 ++ Src/Plugins/Input/in_avi/StreamSelector.h | 32 + Src/Plugins/Input/in_avi/VideoThread.cpp | 428 + Src/Plugins/Input/in_avi/VideoThread.h | 12 + Src/Plugins/Input/in_avi/api.cpp | 50 + Src/Plugins/Input/in_avi/api__in_avi.h | 15 + Src/Plugins/Input/in_avi/http_avi_reader.cpp | 255 + Src/Plugins/Input/in_avi/http_avi_reader.h | 31 + Src/Plugins/Input/in_avi/ifc_aviaudiodecoder.h | 67 + Src/Plugins/Input/in_avi/ifc_avivideodecoder.h | 77 + Src/Plugins/Input/in_avi/in_avi.rc | 159 + Src/Plugins/Input/in_avi/in_avi.sln | 31 + Src/Plugins/Input/in_avi/in_avi.vcxproj | 284 + Src/Plugins/Input/in_avi/in_avi.vcxproj.filters | 158 + Src/Plugins/Input/in_avi/interfaces.h | 4 + Src/Plugins/Input/in_avi/main.cpp | 330 + Src/Plugins/Input/in_avi/main.h | 19 + Src/Plugins/Input/in_avi/player.h | 3 + Src/Plugins/Input/in_avi/resource.h | 50 + Src/Plugins/Input/in_avi/svc_avidecoder.h | 74 + Src/Plugins/Input/in_avi/version.rc2 | 39 + Src/Plugins/Input/in_avi/win32_avi_reader.cpp | 174 + Src/Plugins/Input/in_avi/win32_avi_reader.h | 36 + Src/Plugins/Input/in_cdda/AUDIO.Cpp | 112 + Src/Plugins/Input/in_cdda/AUDIO.H | 6 + Src/Plugins/Input/in_cdda/CDDB.Cpp | 1709 ++++ Src/Plugins/Input/in_cdda/CDDB.H | 184 + Src/Plugins/Input/in_cdda/CDDBInterface.h | 10 + Src/Plugins/Input/in_cdda/CDPlay.cpp | 1 + Src/Plugins/Input/in_cdda/CDPlay.h | 34 + Src/Plugins/Input/in_cdda/CDText.cpp | 265 + Src/Plugins/Input/in_cdda/CONFIG.Cpp | 261 + Src/Plugins/Input/in_cdda/DAEPlay.cpp | 423 + Src/Plugins/Input/in_cdda/DAEPlay.h | 112 + Src/Plugins/Input/in_cdda/DB.Cpp | 306 + Src/Plugins/Input/in_cdda/EditCDInfo.cpp | 447 + Src/Plugins/Input/in_cdda/ExtendedFileInfo.cpp | 1241 +++ Src/Plugins/Input/in_cdda/ExtendedRead.cpp | 145 + Src/Plugins/Input/in_cdda/FuncTypedefs.h | 94 + Src/Plugins/Input/in_cdda/MAIN.H | 81 + Src/Plugins/Input/in_cdda/MCI.Cpp | 152 + Src/Plugins/Input/in_cdda/MCIPlay.h | 182 + Src/Plugins/Input/in_cdda/Main.cpp | 598 ++ Src/Plugins/Input/in_cdda/PlayStatus.cpp | 23 + Src/Plugins/Input/in_cdda/PlayStatus.h | 28 + Src/Plugins/Input/in_cdda/VeritasPlay.cpp | 636 ++ Src/Plugins/Input/in_cdda/VeritasPlay.h | 122 + Src/Plugins/Input/in_cdda/WindacPlay.cpp | 321 + Src/Plugins/Input/in_cdda/WindacPlay.h | 77 + Src/Plugins/Input/in_cdda/api__in_cdda.h | 22 + Src/Plugins/Input/in_cdda/cddbevnt.cpp | 178 + Src/Plugins/Input/in_cdda/cddbevnt.h | 63 + Src/Plugins/Input/in_cdda/cddbui.cpp | 997 ++ Src/Plugins/Input/in_cdda/cddbui.h | 46 + Src/Plugins/Input/in_cdda/discid.cpp | 168 + Src/Plugins/Input/in_cdda/grabwnd.cpp | 74 + Src/Plugins/Input/in_cdda/grabwnd.h | 18 + Src/Plugins/Input/in_cdda/in_cdda.rc | 366 + Src/Plugins/Input/in_cdda/in_cdda.sln | 86 + Src/Plugins/Input/in_cdda/in_cdda.vcxproj | 393 + Src/Plugins/Input/in_cdda/in_cdda.vcxproj.filters | 190 + Src/Plugins/Input/in_cdda/inst.nsi | 78 + Src/Plugins/Input/in_cdda/nde_cd.cpp | 720 ++ Src/Plugins/Input/in_cdda/ntddcdrm.h | 549 ++ Src/Plugins/Input/in_cdda/resource.h | 136 + Src/Plugins/Input/in_cdda/scsi_id.cpp | 124 + Src/Plugins/Input/in_cdda/util.cpp | 23 + Src/Plugins/Input/in_cdda/version.rc2 | 39 + Src/Plugins/Input/in_cdda/windac/Aspi.h | 410 + Src/Plugins/Input/in_cdda/windac/Aspifunc.cpp | 378 + Src/Plugins/Input/in_cdda/windac/Aspifunc.h | 101 + Src/Plugins/Input/in_cdda/windac/Dac32.cpp | 2667 ++++++ Src/Plugins/Input/in_cdda/windac/Dac32.dsp | 127 + Src/Plugins/Input/in_cdda/windac/Dac32.h | 775 ++ Src/Plugins/Input/in_cdda/windac/Dac32.mak | 285 + Src/Plugins/Input/in_cdda/windac/Dac32.rc | 121 + Src/Plugins/Input/in_cdda/windac/NTScsi.cpp | 501 + Src/Plugins/Input/in_cdda/windac/NTScsi.h | 147 + Src/Plugins/Input/in_cdda/windac/RESOURCE.H | 17 + Src/Plugins/Input/in_cdda/windac/SCSIDEFS.H | 252 + Src/Plugins/Input/in_cdda/windac/Winaspi.h | 229 + Src/Plugins/Input/in_cdda/workorder.cpp | 53 + Src/Plugins/Input/in_cdda/workorder.h | 10 + Src/Plugins/Input/in_dshow/CSampleCB.h | 11 + Src/Plugins/Input/in_dshow/CWAAudioRenderer.cpp | 70 + Src/Plugins/Input/in_dshow/CWAAudioRenderer.h | 30 + Src/Plugins/Input/in_dshow/CWAVideoRenderer.cpp | 131 + Src/Plugins/Input/in_dshow/CWAVideoRenderer.h | 32 + Src/Plugins/Input/in_dshow/DSTrackSelector.cpp | 89 + Src/Plugins/Input/in_dshow/DSTrackSelector.h | 18 + Src/Plugins/Input/in_dshow/Header.cpp | 49 + Src/Plugins/Input/in_dshow/Header.h | 22 + Src/Plugins/Input/in_dshow/IN2.H | 1 + Src/Plugins/Input/in_dshow/Main.cpp | 1575 ++++ Src/Plugins/Input/in_dshow/Main.h | 37 + Src/Plugins/Input/in_dshow/audioswitch.cpp | 1791 ++++ Src/Plugins/Input/in_dshow/audioswitch.h | 216 + Src/Plugins/Input/in_dshow/base/amextra.cpp | 111 + Src/Plugins/Input/in_dshow/base/amextra.h | 56 + Src/Plugins/Input/in_dshow/base/amfilter.cpp | 5358 +++++++++++ Src/Plugins/Input/in_dshow/base/amfilter.h | 1587 ++++ Src/Plugins/Input/in_dshow/base/amvideo.cpp | 275 + Src/Plugins/Input/in_dshow/base/arithutil.cpp | 360 + Src/Plugins/Input/in_dshow/base/cache.h | 74 + Src/Plugins/Input/in_dshow/base/checkbmi.h | 120 + Src/Plugins/Input/in_dshow/base/combase.cpp | 265 + Src/Plugins/Input/in_dshow/base/combase.h | 305 + Src/Plugins/Input/in_dshow/base/cprop.cpp | 383 + Src/Plugins/Input/in_dshow/base/cprop.h | 95 + Src/Plugins/Input/in_dshow/base/ctlutil.cpp | 2541 +++++ Src/Plugins/Input/in_dshow/base/ctlutil.h | 923 ++ Src/Plugins/Input/in_dshow/base/ddmm.cpp | 129 + Src/Plugins/Input/in_dshow/base/ddmm.h | 28 + Src/Plugins/Input/in_dshow/base/dllentry.cpp | 373 + Src/Plugins/Input/in_dshow/base/dllsetup.cpp | 694 ++ Src/Plugins/Input/in_dshow/base/dllsetup.h | 46 + Src/Plugins/Input/in_dshow/base/dxmperf.h | 250 + Src/Plugins/Input/in_dshow/base/fourcc.h | 101 + Src/Plugins/Input/in_dshow/base/measure.h | 222 + Src/Plugins/Input/in_dshow/base/msgthrd.h | 120 + Src/Plugins/Input/in_dshow/base/mtype.cpp | 478 + Src/Plugins/Input/in_dshow/base/mtype.h | 89 + Src/Plugins/Input/in_dshow/base/outputq.cpp | 801 ++ Src/Plugins/Input/in_dshow/base/outputq.h | 137 + Src/Plugins/Input/in_dshow/base/perflog.cpp | 347 + Src/Plugins/Input/in_dshow/base/perflog.h | 56 + Src/Plugins/Input/in_dshow/base/perfstruct.h | 194 + Src/Plugins/Input/in_dshow/base/pstream.cpp | 197 + Src/Plugins/Input/in_dshow/base/pstream.h | 114 + Src/Plugins/Input/in_dshow/base/pullpin.cpp | 588 ++ Src/Plugins/Input/in_dshow/base/pullpin.h | 152 + Src/Plugins/Input/in_dshow/base/refclock.cpp | 402 + Src/Plugins/Input/in_dshow/base/refclock.h | 184 + Src/Plugins/Input/in_dshow/base/reftime.h | 116 + Src/Plugins/Input/in_dshow/base/renbase.cpp | 2858 ++++++ Src/Plugins/Input/in_dshow/base/renbase.h | 478 + Src/Plugins/Input/in_dshow/base/schedule.cpp | 284 + Src/Plugins/Input/in_dshow/base/schedule.h | 128 + Src/Plugins/Input/in_dshow/base/seekpt.cpp | 83 + Src/Plugins/Input/in_dshow/base/seekpt.h | 30 + Src/Plugins/Input/in_dshow/base/source.cpp | 522 ++ Src/Plugins/Input/in_dshow/base/source.h | 172 + Src/Plugins/Input/in_dshow/base/streams.h | 202 + Src/Plugins/Input/in_dshow/base/strmctl.cpp | 402 + Src/Plugins/Input/in_dshow/base/strmctl.h | 157 + Src/Plugins/Input/in_dshow/base/sysclock.cpp | 74 + Src/Plugins/Input/in_dshow/base/sysclock.h | 39 + Src/Plugins/Input/in_dshow/base/transfrm.cpp | 1016 ++ Src/Plugins/Input/in_dshow/base/transfrm.h | 304 + Src/Plugins/Input/in_dshow/base/transip.cpp | 974 ++ Src/Plugins/Input/in_dshow/base/transip.h | 250 + Src/Plugins/Input/in_dshow/base/videoctl.cpp | 746 ++ Src/Plugins/Input/in_dshow/base/videoctl.h | 168 + Src/Plugins/Input/in_dshow/base/vtrans.cpp | 468 + Src/Plugins/Input/in_dshow/base/vtrans.h | 143 + Src/Plugins/Input/in_dshow/base/winctrl.cpp | 2081 ++++ Src/Plugins/Input/in_dshow/base/winctrl.h | 224 + Src/Plugins/Input/in_dshow/base/winutil.cpp | 2746 ++++++ Src/Plugins/Input/in_dshow/base/winutil.h | 419 + Src/Plugins/Input/in_dshow/base/wxdebug.cpp | 1474 +++ Src/Plugins/Input/in_dshow/base/wxdebug.h | 359 + Src/Plugins/Input/in_dshow/base/wxlist.cpp | 891 ++ Src/Plugins/Input/in_dshow/base/wxlist.h | 553 ++ Src/Plugins/Input/in_dshow/base/wxutil.cpp | 769 ++ Src/Plugins/Input/in_dshow/base/wxutil.h | 532 ++ Src/Plugins/Input/in_dshow/config.cpp | 124 + Src/Plugins/Input/in_dshow/dshowrender.cpp | 583 ++ Src/Plugins/Input/in_dshow/header_asf.cpp | 202 + Src/Plugins/Input/in_dshow/header_asf.h | 14 + Src/Plugins/Input/in_dshow/header_avi.cpp | 283 + Src/Plugins/Input/in_dshow/header_avi.h | 25 + Src/Plugins/Input/in_dshow/header_mpg.cpp | 234 + Src/Plugins/Input/in_dshow/header_mpg.h | 43 + Src/Plugins/Input/in_dshow/header_wav.cpp | 220 + Src/Plugins/Input/in_dshow/header_wav.h | 24 + Src/Plugins/Input/in_dshow/in_dshow.dsp | 201 + Src/Plugins/Input/in_dshow/in_dshow.dsw | 29 + Src/Plugins/Input/in_dshow/in_dshow.rc | 197 + Src/Plugins/Input/in_dshow/in_dshow.sln | 44 + Src/Plugins/Input/in_dshow/in_dshow.vcxproj | 369 + .../Input/in_dshow/in_dshow.vcxproj.filters | 296 + Src/Plugins/Input/in_dshow/info.cpp | 292 + Src/Plugins/Input/in_dshow/resource.h | 67 + Src/Plugins/Input/in_dshow/version.rc2 | 39 + Src/Plugins/Input/in_flac/About.cpp | 54 + Src/Plugins/Input/in_flac/AlbumArt.cpp | 270 + Src/Plugins/Input/in_flac/AlbumArt.h | 38 + Src/Plugins/Input/in_flac/DecodeThread.cpp | 719 ++ Src/Plugins/Input/in_flac/ExtendedFileInfo.cpp | 424 + Src/Plugins/Input/in_flac/ExtendedRead.cpp | 166 + Src/Plugins/Input/in_flac/FLACFileCallbacks.cpp | 57 + Src/Plugins/Input/in_flac/FLACFileCallbacks.h | 35 + Src/Plugins/Input/in_flac/FileInfo.cpp | 400 + Src/Plugins/Input/in_flac/Metadata.cpp | 577 ++ Src/Plugins/Input/in_flac/Metadata.h | 48 + Src/Plugins/Input/in_flac/Preferences.cpp | 110 + Src/Plugins/Input/in_flac/QuickBuf.h | 50 + Src/Plugins/Input/in_flac/RawReader.cpp | 174 + Src/Plugins/Input/in_flac/RawReader.h | 36 + Src/Plugins/Input/in_flac/Stopper.cpp | 65 + Src/Plugins/Input/in_flac/Stopper.h | 10 + Src/Plugins/Input/in_flac/StreamFileWin32.cpp | 129 + Src/Plugins/Input/in_flac/StreamFileWin32.h | 23 + Src/Plugins/Input/in_flac/api__in_flv.h | 11 + Src/Plugins/Input/in_flac/in_flac.rc | 163 + Src/Plugins/Input/in_flac/in_flac.sln | 105 + Src/Plugins/Input/in_flac/in_flac.vcxproj | 307 + Src/Plugins/Input/in_flac/in_flac.vcxproj.filters | 122 + Src/Plugins/Input/in_flac/main.cpp | 277 + Src/Plugins/Input/in_flac/main.h | 40 + Src/Plugins/Input/in_flac/mkv_flac_decoder.cpp | 204 + Src/Plugins/Input/in_flac/mkv_flac_decoder.h | 65 + Src/Plugins/Input/in_flac/resource.h | 71 + Src/Plugins/Input/in_flac/version.rc2 | 39 + Src/Plugins/Input/in_flv/AMFDispatch.cpp | 238 + Src/Plugins/Input/in_flv/AMFDispatch.h | 28 + Src/Plugins/Input/in_flv/AMFObject.cpp | 319 + Src/Plugins/Input/in_flv/AMFObject.h | 182 + Src/Plugins/Input/in_flv/BackgroundDownloader.cpp | 129 + Src/Plugins/Input/in_flv/BackgroundDownloader.h | 19 + Src/Plugins/Input/in_flv/ExtendedInfo.cpp | 29 + Src/Plugins/Input/in_flv/FLVAudioHeader.cpp | 34 + Src/Plugins/Input/in_flv/FLVAudioHeader.h | 31 + Src/Plugins/Input/in_flv/FLVCOM.cpp | 189 + Src/Plugins/Input/in_flv/FLVCOM.h | 37 + Src/Plugins/Input/in_flv/FLVHeader.cpp | 41 + Src/Plugins/Input/in_flv/FLVHeader.h | 18 + Src/Plugins/Input/in_flv/FLVMetadata.cpp | 112 + Src/Plugins/Input/in_flv/FLVMetadata.h | 24 + Src/Plugins/Input/in_flv/FLVProcessor.cpp | 1 + Src/Plugins/Input/in_flv/FLVProcessor.h | 40 + Src/Plugins/Input/in_flv/FLVReader.cpp | 211 + Src/Plugins/Input/in_flv/FLVReader.h | 49 + Src/Plugins/Input/in_flv/FLVStreamHeader.cpp | 36 + Src/Plugins/Input/in_flv/FLVStreamHeader.h | 27 + Src/Plugins/Input/in_flv/FLVUtil.h | 90 + Src/Plugins/Input/in_flv/FLVVideoHeader.cpp | 24 + Src/Plugins/Input/in_flv/FLVVideoHeader.h | 32 + Src/Plugins/Input/in_flv/FileProcessor.cpp | 201 + Src/Plugins/Input/in_flv/FileProcessor.h | 43 + Src/Plugins/Input/in_flv/PlayThread.cpp | 703 ++ Src/Plugins/Input/in_flv/ProgressiveProcessor.cpp | 34 + Src/Plugins/Input/in_flv/ProgressiveProcessor.h | 16 + Src/Plugins/Input/in_flv/StreamProcessor.cpp | 112 + Src/Plugins/Input/in_flv/StreamProcessor.h | 28 + Src/Plugins/Input/in_flv/VideoThread.cpp | 291 + Src/Plugins/Input/in_flv/VideoThread.h | 21 + Src/Plugins/Input/in_flv/api__in_flv.h | 12 + Src/Plugins/Input/in_flv/ifc_flvaudiodecoder.h | 62 + Src/Plugins/Input/in_flv/ifc_flvvideodecoder.h | 67 + Src/Plugins/Input/in_flv/in_flv.rc | 85 + Src/Plugins/Input/in_flv/in_flv.sln | 30 + Src/Plugins/Input/in_flv/in_flv.vcxproj | 295 + Src/Plugins/Input/in_flv/in_flv.vcxproj.filters | 158 + Src/Plugins/Input/in_flv/main.cpp | 432 + Src/Plugins/Input/in_flv/main.h | 27 + Src/Plugins/Input/in_flv/resource.h | 23 + Src/Plugins/Input/in_flv/svc_flvdecoder.h | 52 + Src/Plugins/Input/in_flv/version.rc2 | 39 + Src/Plugins/Input/in_linein/LineIn.cpp | 50 + Src/Plugins/Input/in_linein/LineIn.h | 25 + Src/Plugins/Input/in_linein/audio.cpp | 113 + Src/Plugins/Input/in_linein/audio.h | 11 + Src/Plugins/Input/in_linein/in_linein.cpp | 191 + Src/Plugins/Input/in_linein/in_linein.rc | 82 + Src/Plugins/Input/in_linein/in_linein.sln | 30 + Src/Plugins/Input/in_linein/in_linein.vcxproj | 248 + .../Input/in_linein/in_linein.vcxproj.filters | 44 + Src/Plugins/Input/in_linein/main.h | 7 + Src/Plugins/Input/in_linein/resource.h | 22 + Src/Plugins/Input/in_linein/version.rc2 | 39 + Src/Plugins/Input/in_midi/CompressionUtility.cpp | 270 + Src/Plugins/Input/in_midi/CompressionUtility.h | 13 + Src/Plugins/Input/in_midi/In2.h | 2 + Src/Plugins/Input/in_midi/cleaner.cpp | 587 ++ Src/Plugins/Input/in_midi/cmf.cpp | 48 + Src/Plugins/Input/in_midi/config.cpp | 1072 +++ Src/Plugins/Input/in_midi/core_api.h | 61 + Src/Plugins/Input/in_midi/cvt.h | 21 + Src/Plugins/Input/in_midi/dmplugin.h | 281 + Src/Plugins/Input/in_midi/dmusicc.h | 784 ++ Src/Plugins/Input/in_midi/dmusicf.h | 2373 +++++ Src/Plugins/Input/in_midi/dmusici.h | 1964 ++++ Src/Plugins/Input/in_midi/fakedsound.cpp | 244 + Src/Plugins/Input/in_midi/fakedsound.h | 3 + Src/Plugins/Input/in_midi/genres.c | 103 + Src/Plugins/Input/in_midi/genres.h | 3 + Src/Plugins/Input/in_midi/gmf.cpp | 27 + Src/Plugins/Input/in_midi/guids.cpp | 2 + Src/Plugins/Input/in_midi/hmi.cpp | 206 + Src/Plugins/Input/in_midi/hmp.cpp | 149 + Src/Plugins/Input/in_midi/in_midi.rc | 568 ++ Src/Plugins/Input/in_midi/in_midi.sln | 31 + Src/Plugins/Input/in_midi/in_midi.vcxproj | 328 + Src/Plugins/Input/in_midi/in_midi.vcxproj.filters | 143 + Src/Plugins/Input/in_midi/info.cpp | 940 ++ Src/Plugins/Input/in_midi/main.cpp | 612 ++ Src/Plugins/Input/in_midi/main.h | 230 + Src/Plugins/Input/in_midi/midi_driver.cpp | 121 + Src/Plugins/Input/in_midi/midifile.cpp | 603 ++ Src/Plugins/Input/in_midi/midifile.h | 89 + Src/Plugins/Input/in_midi/midiinfo.cpp | 326 + Src/Plugins/Input/in_midi/midiout_helper.cpp | 2 + Src/Plugins/Input/in_midi/mids.cpp | 96 + Src/Plugins/Input/in_midi/mus.cpp | 184 + Src/Plugins/Input/in_midi/out_dmusic.cpp | 962 ++ Src/Plugins/Input/in_midi/out_midi.cpp | 888 ++ Src/Plugins/Input/in_midi/resource.h | 224 + Src/Plugins/Input/in_midi/sampling.cpp | 333 + Src/Plugins/Input/in_midi/seq.cpp | 852 ++ Src/Plugins/Input/in_midi/seq.h | 75 + Src/Plugins/Input/in_midi/utils.cpp | 1064 +++ Src/Plugins/Input/in_midi/utils.h | 193 + Src/Plugins/Input/in_midi/version.rc2 | 39 + Src/Plugins/Input/in_midi/wa2.cpp | 820 ++ Src/Plugins/Input/in_midi/wa3.cpp | 395 + Src/Plugins/Input/in_midi/xmi.cpp | 310 + Src/Plugins/Input/in_mkv/ExtendedFileInfo.cpp | 80 + Src/Plugins/Input/in_mkv/InfoDialog.cpp | 247 + Src/Plugins/Input/in_mkv/MKVDuration.cpp | 115 + Src/Plugins/Input/in_mkv/MKVDuration.h | 20 + Src/Plugins/Input/in_mkv/MKVInfo.cpp | 168 + Src/Plugins/Input/in_mkv/MKVInfo.h | 26 + Src/Plugins/Input/in_mkv/MKVPlayer.h | 138 + Src/Plugins/Input/in_mkv/PlayThread.cpp | 902 ++ Src/Plugins/Input/in_mkv/api__in_mkv.cpp | 45 + Src/Plugins/Input/in_mkv/api__in_mkv.h | 9 + Src/Plugins/Input/in_mkv/ifc_mkvaudiodecoder.h | 61 + Src/Plugins/Input/in_mkv/ifc_mkvvideodecoder.h | 69 + Src/Plugins/Input/in_mkv/in_mkv.rc | 153 + Src/Plugins/Input/in_mkv/in_mkv.sln | 31 + Src/Plugins/Input/in_mkv/in_mkv.vcxproj | 285 + Src/Plugins/Input/in_mkv/in_mkv.vcxproj.filters | 155 + Src/Plugins/Input/in_mkv/main.cpp | 242 + Src/Plugins/Input/in_mkv/main.h | 5 + Src/Plugins/Input/in_mkv/resource.h | 40 + Src/Plugins/Input/in_mkv/svc_mkvdecoder.h | 37 + Src/Plugins/Input/in_mkv/version.rc2 | 39 + Src/Plugins/Input/in_mod-openmpt/ExtendedInfo.cpp | 90 + Src/Plugins/Input/in_mod-openmpt/ExtendedRead.cpp | 139 + Src/Plugins/Input/in_mod-openmpt/MODPlayer.h | 44 + Src/Plugins/Input/in_mod-openmpt/MODThread.cpp | 166 + Src/Plugins/Input/in_mod-openmpt/api__in_mod.h | 8 + Src/Plugins/Input/in_mod-openmpt/in_mod-openmpt.rc | 81 + .../Input/in_mod-openmpt/in_mod-openmpt.sln | 182 + .../Input/in_mod-openmpt/in_mod-openmpt.vcxproj | 271 + .../in_mod-openmpt/in_mod-openmpt.vcxproj.filters | 53 + Src/Plugins/Input/in_mod-openmpt/main.cpp | 258 + .../Input/in_mod-openmpt/nxfile-callbacks.cpp | 87 + Src/Plugins/Input/in_mod-openmpt/resource.h | 18 + Src/Plugins/Input/in_mod-openmpt/version.rc2 | 39 + Src/Plugins/Input/in_mod/fir_proc.cpp | 172 + Src/Plugins/Input/in_mod/fir_table.h | 2051 ++++ Src/Plugins/Input/in_mod/mikamp/in_mod.rc | 391 + Src/Plugins/Input/in_mod/mikamp/include/Main.h | 106 + Src/Plugins/Input/in_mod/mikamp/include/in2.h | 1 + Src/Plugins/Input/in_mod/mikamp/int64.lib | Bin 0 -> 11160 bytes Src/Plugins/Input/in_mod/mikamp/mikamp.dsp | 167 + Src/Plugins/Input/in_mod/mikamp/mikamp.sln | 31 + Src/Plugins/Input/in_mod/mikamp/mikamp.vcproj | 416 + Src/Plugins/Input/in_mod/mikamp/playbackconfig.cpp | 78 + Src/Plugins/Input/in_mod/mikamp/resource.h | 149 + Src/Plugins/Input/in_mod/mikamp/src/Config.c | 1320 +++ .../Input/in_mod/mikamp/src/ExtendedRead.cpp | 123 + Src/Plugins/Input/in_mod/mikamp/src/Info.c | 602 ++ Src/Plugins/Input/in_mod/mikamp/src/Main.c | 1067 +++ Src/Plugins/Input/in_mod/mikamp/src/api.h | 11 + Src/Plugins/Input/in_mod/mikamp/src/drv_amp.c | 254 + Src/Plugins/Input/in_mod/mikamp/src/drv_buffer.cpp | 176 + Src/Plugins/Input/in_mod/mikamp/src/drv_buffer.h | 18 + Src/Plugins/Input/in_mod/mikamp/src/rf_wrapper.c | 241 + Src/Plugins/Input/in_mod/mikamp/version.rc2 | 39 + Src/Plugins/Input/in_mp3/AACFrame.cpp | 94 + Src/Plugins/Input/in_mp3/AACFrame.h | 43 + Src/Plugins/Input/in_mp3/AlbumArt.cpp | 283 + Src/Plugins/Input/in_mp3/AlbumArt.h | 39 + Src/Plugins/Input/in_mp3/CVbriHeader.cpp | 327 + Src/Plugins/Input/in_mp3/CVbriHeader.h | 49 + Src/Plugins/Input/in_mp3/CreateFile.cpp | 0 Src/Plugins/Input/in_mp3/CreateFile.h | 6 + Src/Plugins/Input/in_mp3/DXHEAD.C | 54 + Src/Plugins/Input/in_mp3/DXHEAD.H | 43 + Src/Plugins/Input/in_mp3/DecodeThread.cpp | 810 ++ Src/Plugins/Input/in_mp3/DecodeThread.h | 19 + Src/Plugins/Input/in_mp3/ExtendedInfo.cpp | 315 + Src/Plugins/Input/in_mp3/ExtendedRead.cpp | 323 + Src/Plugins/Input/in_mp3/FactoryHelper.h | 25 + Src/Plugins/Input/in_mp3/ID3Info.cpp | 10 + Src/Plugins/Input/in_mp3/ID3v1.cpp | 211 + Src/Plugins/Input/in_mp3/ID3v1.h | 29 + Src/Plugins/Input/in_mp3/ID3v2.cpp | 654 ++ Src/Plugins/Input/in_mp3/ID3v2.h | 33 + Src/Plugins/Input/in_mp3/IN2.H | 4 + Src/Plugins/Input/in_mp3/LAMEinfo.cpp | 398 + Src/Plugins/Input/in_mp3/LAMEinfo.h | 118 + Src/Plugins/Input/in_mp3/Lyrics3.cpp | 193 + Src/Plugins/Input/in_mp3/Lyrics3.h | 25 + Src/Plugins/Input/in_mp3/MP3Info.cpp | 612 ++ Src/Plugins/Input/in_mp3/MP3Info.h | 47 + Src/Plugins/Input/in_mp3/Metadata.cpp | 616 ++ Src/Plugins/Input/in_mp3/Metadata.h | 64 + Src/Plugins/Input/in_mp3/MetadataFactory.cpp | 62 + Src/Plugins/Input/in_mp3/MetadataFactory.h | 24 + Src/Plugins/Input/in_mp3/OFL.cpp | 165 + Src/Plugins/Input/in_mp3/OFL.h | 21 + Src/Plugins/Input/in_mp3/RawMediaReader.cpp | 84 + Src/Plugins/Input/in_mp3/RawMediaReader.h | 30 + Src/Plugins/Input/in_mp3/Stopper.cpp | 44 + Src/Plugins/Input/in_mp3/Stopper.h | 10 + Src/Plugins/Input/in_mp3/StreamInfo.cpp | 9 + Src/Plugins/Input/in_mp3/WasabiMetadata.cpp | 67 + Src/Plugins/Input/in_mp3/WasabiMetadata.h | 30 + Src/Plugins/Input/in_mp3/about.cpp | 576 ++ Src/Plugins/Input/in_mp3/adts.h | 37 + Src/Plugins/Input/in_mp3/adts_mp2.cpp | 400 + Src/Plugins/Input/in_mp3/adts_mp2.h | 58 + Src/Plugins/Input/in_mp3/adts_vlb.cpp | 153 + Src/Plugins/Input/in_mp3/adts_vlb.h | 37 + Src/Plugins/Input/in_mp3/apev2.cpp | 169 + Src/Plugins/Input/in_mp3/apev2.h | 46 + Src/Plugins/Input/in_mp3/api__in_mp3.h | 16 + Src/Plugins/Input/in_mp3/config.cpp | 685 ++ Src/Plugins/Input/in_mp3/config.h | 51 + Src/Plugins/Input/in_mp3/giofile.cpp | 2475 +++++ Src/Plugins/Input/in_mp3/giofile.h | 306 + Src/Plugins/Input/in_mp3/graphics/filterWater.cpp | 452 + Src/Plugins/Input/in_mp3/graphics/filterWater.h | 47 + Src/Plugins/Input/in_mp3/graphics/image.cpp | 209 + Src/Plugins/Input/in_mp3/graphics/image.h | 61 + Src/Plugins/Input/in_mp3/graphics/imageFilters.cpp | 91 + Src/Plugins/Input/in_mp3/graphics/imageFilters.h | 17 + Src/Plugins/Input/in_mp3/id3.cpp | 556 ++ Src/Plugins/Input/in_mp3/id3.h | 43 + Src/Plugins/Input/in_mp3/id3dlg.cpp | 1071 +++ Src/Plugins/Input/in_mp3/ifc_mpeg_stream_reader.h | 45 + Src/Plugins/Input/in_mp3/in_mp3.rc | 503 + Src/Plugins/Input/in_mp3/in_mp3.sln | 102 + Src/Plugins/Input/in_mp3/in_mp3.vcxproj | 386 + Src/Plugins/Input/in_mp3/in_mp3.vcxproj.filters | 218 + Src/Plugins/Input/in_mp3/main.cpp | 342 + Src/Plugins/Input/in_mp3/main.h | 123 + Src/Plugins/Input/in_mp3/mp4.cpp | 44 + Src/Plugins/Input/in_mp3/mpegutil.cpp | 372 + Src/Plugins/Input/in_mp3/mpegutil.h | 9 + Src/Plugins/Input/in_mp3/pdtimer.cpp | 24 + Src/Plugins/Input/in_mp3/pdtimer.h | 9 + Src/Plugins/Input/in_mp3/proxydt.h | 16 + Src/Plugins/Input/in_mp3/resource.h | 253 + .../Input/in_mp3/resources/aboutMP3_256.bmp | Bin 0 -> 24650 bytes Src/Plugins/Input/in_mp3/resources/hand.cur | Bin 0 -> 326 bytes Src/Plugins/Input/in_mp3/resources/llama.bmp | Bin 0 -> 35118 bytes .../Input/in_mp3/resources/logoCodTech.90x42.bmp | Bin 0 -> 4942 bytes .../Input/in_mp3/resources/logoIIS_127x42.bmp | Bin 0 -> 6454 bytes .../Input/in_mp3/resources/logoId3v2_44x42.bmp | Bin 0 -> 2926 bytes .../in_mp3/resources/logoMp3surround_101x42.bmp | Bin 0 -> 12654 bytes Src/Plugins/Input/in_mp3/resources/logoWA.bmp | Bin 0 -> 3474 bytes Src/Plugins/Input/in_mp3/tagz.cpp | 953 ++ Src/Plugins/Input/in_mp3/tagz.h | 26 + Src/Plugins/Input/in_mp3/titlelist.cpp | 62 + Src/Plugins/Input/in_mp3/todo.txt | 97 + Src/Plugins/Input/in_mp3/uvox_3901.cpp | 79 + Src/Plugins/Input/in_mp3/uvox_3901.h | 20 + Src/Plugins/Input/in_mp3/uvox_3902.cpp | 77 + Src/Plugins/Input/in_mp3/uvox_3902.h | 20 + Src/Plugins/Input/in_mp3/version.rc2 | 39 + Src/Plugins/Input/in_mp4/AlbumArt.cpp | 232 + Src/Plugins/Input/in_mp4/AlbumArt.h | 39 + Src/Plugins/Input/in_mp4/AudioSample.h | 80 + Src/Plugins/Input/in_mp4/ExtendedInfo.cpp | 1022 ++ Src/Plugins/Input/in_mp4/ExtendedRead.cpp | 239 + Src/Plugins/Input/in_mp4/Main.cpp | 723 ++ Src/Plugins/Input/in_mp4/Main.h | 76 + Src/Plugins/Input/in_mp4/PlayThread.cpp | 417 + Src/Plugins/Input/in_mp4/RawMediaReader.cpp | 145 + Src/Plugins/Input/in_mp4/RawMediaReader.h | 40 + Src/Plugins/Input/in_mp4/Stopper.cpp | 47 + Src/Plugins/Input/in_mp4/Stopper.h | 11 + Src/Plugins/Input/in_mp4/VideoThread.cpp | 215 + Src/Plugins/Input/in_mp4/VideoThread.h | 10 + Src/Plugins/Input/in_mp4/VirtualIO.cpp | 716 ++ Src/Plugins/Input/in_mp4/VirtualIO.h | 17 + Src/Plugins/Input/in_mp4/api.cpp | 4 + Src/Plugins/Input/in_mp4/api__in_mp4.h | 22 + Src/Plugins/Input/in_mp4/config.cpp | 49 + Src/Plugins/Input/in_mp4/in_mp4.rc | 129 + Src/Plugins/Input/in_mp4/in_mp4.sln | 67 + Src/Plugins/Input/in_mp4/in_mp4.vcxproj | 301 + Src/Plugins/Input/in_mp4/in_mp4.vcxproj.filters | 122 + Src/Plugins/Input/in_mp4/infobox.cpp | 20 + Src/Plugins/Input/in_mp4/mp4.cpp | 98 + Src/Plugins/Input/in_mp4/mpeg4audio.cpp | 1 + Src/Plugins/Input/in_mp4/mpeg4audio.h | 208 + Src/Plugins/Input/in_mp4/mpeg4video.cpp | 1 + Src/Plugins/Input/in_mp4/mpeg4video.h | 92 + Src/Plugins/Input/in_mp4/resource.h | 61 + Src/Plugins/Input/in_mp4/version.rc2 | 39 + Src/Plugins/Input/in_nsv/BigLib.cpp | 1428 +++ Src/Plugins/Input/in_nsv/Main.cpp | 976 ++ Src/Plugins/Input/in_nsv/api.h | 16 + Src/Plugins/Input/in_nsv/config.cpp | 197 + Src/Plugins/Input/in_nsv/in_nsv.rc | 294 + Src/Plugins/Input/in_nsv/in_nsv.sln | 30 + Src/Plugins/Input/in_nsv/in_nsv.vcxproj | 274 + Src/Plugins/Input/in_nsv/in_nsv.vcxproj.filters | 67 + Src/Plugins/Input/in_nsv/infodlg.cpp | 1089 +++ Src/Plugins/Input/in_nsv/nsv_logo.bmp | Bin 0 -> 7480 bytes Src/Plugins/Input/in_nsv/proxydt.h | 16 + Src/Plugins/Input/in_nsv/resource.h | 86 + Src/Plugins/Input/in_nsv/version.rc2 | 39 + Src/Plugins/Input/in_swf/ExtendedFileInfo.cpp | 41 + Src/Plugins/Input/in_swf/FLVExternalInterface.cpp | 75 + Src/Plugins/Input/in_swf/FLVExternalInterface.h | 8 + Src/Plugins/Input/in_swf/FlashDispInterface.h | 7 + Src/Plugins/Input/in_swf/SWFContainer.cpp | 574 ++ Src/Plugins/Input/in_swf/SWFContainer.h | 144 + Src/Plugins/Input/in_swf/SWFParameters.cpp | 104 + Src/Plugins/Input/in_swf/SWFParameters.h | 37 + Src/Plugins/Input/in_swf/SWFThread.cpp | 26 + Src/Plugins/Input/in_swf/XMLString.cpp | 52 + Src/Plugins/Input/in_swf/XMLString.h | 29 + Src/Plugins/Input/in_swf/api.h | 6 + Src/Plugins/Input/in_swf/flash9e.tlh | 347 + Src/Plugins/Input/in_swf/in_swf.rc | 83 + Src/Plugins/Input/in_swf/in_swf.sln | 30 + Src/Plugins/Input/in_swf/in_swf.vcxproj | 258 + Src/Plugins/Input/in_swf/in_swf.vcxproj.filters | 74 + Src/Plugins/Input/in_swf/main.cpp | 447 + Src/Plugins/Input/in_swf/main.h | 24 + Src/Plugins/Input/in_swf/resource.h | 20 + Src/Plugins/Input/in_swf/version.rc2 | 39 + Src/Plugins/Input/in_swf/winampFLV.fla | Bin 0 -> 237056 bytes Src/Plugins/Input/in_vorbis/DlgBase.cpp | 67 + Src/Plugins/Input/in_vorbis/DlgBase.h | 118 + Src/Plugins/Input/in_vorbis/ExtendedRead.cpp | 65 + Src/Plugins/Input/in_vorbis/about.cpp | 264 + Src/Plugins/Input/in_vorbis/api__in_vorbis.h | 21 + Src/Plugins/Input/in_vorbis/c_string.cpp | 96 + Src/Plugins/Input/in_vorbis/c_string.h | 156 + .../Input/in_vorbis/chainedstream_parse.cpp | 82 + Src/Plugins/Input/in_vorbis/config.cpp | 335 + Src/Plugins/Input/in_vorbis/core_api.h | 567 ++ Src/Plugins/Input/in_vorbis/decoder.cpp | 459 + Src/Plugins/Input/in_vorbis/decoder.h | 41 + Src/Plugins/Input/in_vorbis/genres.c | 97 + Src/Plugins/Input/in_vorbis/genres.h | 16 + Src/Plugins/Input/in_vorbis/http.cpp | 596 ++ Src/Plugins/Input/in_vorbis/in_vorbis.rc | 415 + Src/Plugins/Input/in_vorbis/in_vorbis.sln | 65 + Src/Plugins/Input/in_vorbis/in_vorbis.vcxproj | 339 + .../Input/in_vorbis/in_vorbis.vcxproj.filters | 145 + Src/Plugins/Input/in_vorbis/info_.cpp | 658 ++ Src/Plugins/Input/in_vorbis/infobox.cpp | 1457 +++ Src/Plugins/Input/in_vorbis/localfile.cpp | 260 + Src/Plugins/Input/in_vorbis/main.h | 220 + Src/Plugins/Input/in_vorbis/mkv_vorbis_decoder.cpp | 194 + Src/Plugins/Input/in_vorbis/mkv_vorbis_decoder.h | 41 + Src/Plugins/Input/in_vorbis/oggdrop/112.png | Bin 0 -> 782 bytes Src/Plugins/Input/in_vorbis/oggdrop/113.png | Bin 0 -> 817 bytes Src/Plugins/Input/in_vorbis/oggdrop/114.png | Bin 0 -> 753 bytes Src/Plugins/Input/in_vorbis/oggdrop/115.png | Bin 0 -> 705 bytes Src/Plugins/Input/in_vorbis/oggdrop/116.png | Bin 0 -> 725 bytes Src/Plugins/Input/in_vorbis/oggdrop/117.png | Bin 0 -> 809 bytes Src/Plugins/Input/in_vorbis/oggdrop/118.png | Bin 0 -> 824 bytes Src/Plugins/Input/in_vorbis/oggdrop/119.png | Bin 0 -> 829 bytes Src/Plugins/Input/in_vorbis/oggdrop/120.png | Bin 0 -> 720 bytes Src/Plugins/Input/in_vorbis/oggdrop/121.png | Bin 0 -> 719 bytes Src/Plugins/Input/in_vorbis/oggdrop/122.png | Bin 0 -> 727 bytes Src/Plugins/Input/in_vorbis/oggdrop/123.png | Bin 0 -> 755 bytes Src/Plugins/Input/in_vorbis/resource.h | 177 + Src/Plugins/Input/in_vorbis/rf.h | 108 + Src/Plugins/Input/in_vorbis/shaper.cpp | 245 + Src/Plugins/Input/in_vorbis/shaper.h | 30 + Src/Plugins/Input/in_vorbis/unihack.cpp | 2 + Src/Plugins/Input/in_vorbis/vcedit.c | 491 + Src/Plugins/Input/in_vorbis/vcedit.h | 62 + Src/Plugins/Input/in_vorbis/version.rc2 | 39 + Src/Plugins/Input/in_vorbis/wa2.cpp | 1097 +++ Src/Plugins/Input/in_vorbis/winnt_helper.cpp | 2 + Src/Plugins/Input/in_wave/AudioThread.cpp | 330 + Src/Plugins/Input/in_wave/AudioThread.h | 19 + Src/Plugins/Input/in_wave/ExtendedRead.cpp | 80 + Src/Plugins/Input/in_wave/RawReader.cpp | 86 + Src/Plugins/Input/in_wave/RawReader.h | 39 + Src/Plugins/Input/in_wave/VirtualIO.cpp | 98 + Src/Plugins/Input/in_wave/VirtualIO.h | 10 + Src/Plugins/Input/in_wave/api__in_wave.h | 10 + Src/Plugins/Input/in_wave/config.cpp | 128 + Src/Plugins/Input/in_wave/config.h | 6 + Src/Plugins/Input/in_wave/extensions.cpp | 183 + Src/Plugins/Input/in_wave/in_wave.rc | 144 + Src/Plugins/Input/in_wave/in_wave.sln | 40 + Src/Plugins/Input/in_wave/in_wave.vcxproj | 282 + Src/Plugins/Input/in_wave/in_wave.vcxproj.filters | 65 + Src/Plugins/Input/in_wave/main.cpp | 437 + Src/Plugins/Input/in_wave/main.h | 24 + Src/Plugins/Input/in_wave/resource.h | 47 + Src/Plugins/Input/in_wave/version.rc2 | 39 + Src/Plugins/Input/in_wmvdrm/ASXLoader.cpp | 490 + Src/Plugins/Input/in_wmvdrm/ASXLoader.h | 29 + Src/Plugins/Input/in_wmvdrm/AlbumArt.cpp | 202 + Src/Plugins/Input/in_wmvdrm/AlbumArt.h | 39 + Src/Plugins/Input/in_wmvdrm/AllocLayer.cpp | 1 + Src/Plugins/Input/in_wmvdrm/AllocLayer.h | 80 + Src/Plugins/Input/in_wmvdrm/AudioFormat.cpp | 61 + Src/Plugins/Input/in_wmvdrm/AudioFormat.h | 45 + Src/Plugins/Input/in_wmvdrm/AudioLayer.cpp | 253 + Src/Plugins/Input/in_wmvdrm/AudioLayer.h | 51 + Src/Plugins/Input/in_wmvdrm/AudioThread.cpp | 129 + Src/Plugins/Input/in_wmvdrm/AudioThread.h | 40 + Src/Plugins/Input/in_wmvdrm/AutoChar.h | 43 + Src/Plugins/Input/in_wmvdrm/AutoWide.h | 41 + Src/Plugins/Input/in_wmvdrm/BufferLayer.cpp | 95 + Src/Plugins/Input/in_wmvdrm/BufferLayer.h | 26 + Src/Plugins/Input/in_wmvdrm/BufferPool.h | 203 + Src/Plugins/Input/in_wmvdrm/CachedData.h | 48 + Src/Plugins/Input/in_wmvdrm/ClockLayer.cpp | 101 + Src/Plugins/Input/in_wmvdrm/ClockLayer.h | 36 + Src/Plugins/Input/in_wmvdrm/ConfigDialog.cpp | 398 + Src/Plugins/Input/in_wmvdrm/ConfigDialog.h | 4 + Src/Plugins/Input/in_wmvdrm/DESIGN.txt | 34 + Src/Plugins/Input/in_wmvdrm/ExtendedFileInfo.cpp | 597 ++ Src/Plugins/Input/in_wmvdrm/ExtendedRead.cpp | 240 + Src/Plugins/Input/in_wmvdrm/ExtendedRead.h | 30 + Src/Plugins/Input/in_wmvdrm/FactoryHelper.h | 24 + Src/Plugins/Input/in_wmvdrm/FileInfoDialog.cpp | 831 ++ Src/Plugins/Input/in_wmvdrm/FileInfoDialog.h | 41 + Src/Plugins/Input/in_wmvdrm/FileTypes.cpp | 179 + Src/Plugins/Input/in_wmvdrm/FileTypes.h | 88 + Src/Plugins/Input/in_wmvdrm/GainLayer.cpp | 232 + Src/Plugins/Input/in_wmvdrm/GainLayer.h | 32 + Src/Plugins/Input/in_wmvdrm/Main.h | 52 + Src/Plugins/Input/in_wmvdrm/MediaThread.cpp | 110 + Src/Plugins/Input/in_wmvdrm/MediaThread.h | 56 + Src/Plugins/Input/in_wmvdrm/MetaTag.cpp | 96 + Src/Plugins/Input/in_wmvdrm/MetaTag.h | 42 + Src/Plugins/Input/in_wmvdrm/MetaTagFactory.cpp | 67 + Src/Plugins/Input/in_wmvdrm/MetaTagFactory.h | 24 + Src/Plugins/Input/in_wmvdrm/OutputStream.h | 169 + Src/Plugins/Input/in_wmvdrm/PlaylistHandler.cpp | 167 + Src/Plugins/Input/in_wmvdrm/PlaylistHandler.h | 28 + Src/Plugins/Input/in_wmvdrm/RawReader.cpp | 173 + Src/Plugins/Input/in_wmvdrm/RawReader.h | 40 + Src/Plugins/Input/in_wmvdrm/Remaining.h | 68 + Src/Plugins/Input/in_wmvdrm/SeekLayer.cpp | 376 + Src/Plugins/Input/in_wmvdrm/SeekLayer.h | 55 + Src/Plugins/Input/in_wmvdrm/StatusHook.cpp | 54 + Src/Plugins/Input/in_wmvdrm/StatusHook.h | 7 + Src/Plugins/Input/in_wmvdrm/TODO.txt | 8 + Src/Plugins/Input/in_wmvdrm/TagAlias.cpp | 64 + Src/Plugins/Input/in_wmvdrm/TagAlias.h | 7 + Src/Plugins/Input/in_wmvdrm/VideoDataConverter.cpp | 41 + Src/Plugins/Input/in_wmvdrm/VideoDataConverter.h | 18 + Src/Plugins/Input/in_wmvdrm/VideoLayer.cpp | 300 + Src/Plugins/Input/in_wmvdrm/VideoLayer.h | 62 + .../Input/in_wmvdrm/VideoOutputChildDDraw.cpp | 80 + .../Input/in_wmvdrm/VideoOutputChildDDraw.h | 17 + Src/Plugins/Input/in_wmvdrm/VideoThread.cpp | 166 + Src/Plugins/Input/in_wmvdrm/VideoThread.h | 37 + Src/Plugins/Input/in_wmvdrm/WMCallback.cpp | 294 + Src/Plugins/Input/in_wmvdrm/WMCallback.h | 52 + Src/Plugins/Input/in_wmvdrm/WMDRMModule.cpp | 649 ++ Src/Plugins/Input/in_wmvdrm/WMDRMModule.h | 100 + Src/Plugins/Input/in_wmvdrm/WMHandler.cpp | 181 + Src/Plugins/Input/in_wmvdrm/WMHandler.h | 113 + Src/Plugins/Input/in_wmvdrm/WMInformation.cpp | 880 ++ Src/Plugins/Input/in_wmvdrm/WMInformation.h | 89 + Src/Plugins/Input/in_wmvdrm/WMPlaylist.cpp | 45 + Src/Plugins/Input/in_wmvdrm/WMPlaylist.h | 56 + Src/Plugins/Input/in_wmvdrm/WPLLoader.cpp | 128 + Src/Plugins/Input/in_wmvdrm/WPLLoader.h | 19 + Src/Plugins/Input/in_wmvdrm/WaitLayer.cpp | 49 + Src/Plugins/Input/in_wmvdrm/WaitLayer.h | 25 + Src/Plugins/Input/in_wmvdrm/WinampInterface.cpp | 183 + Src/Plugins/Input/in_wmvdrm/WinampInterface.h | 121 + Src/Plugins/Input/in_wmvdrm/XMLString.cpp | 45 + Src/Plugins/Input/in_wmvdrm/XMLString.h | 29 + Src/Plugins/Input/in_wmvdrm/api.cpp | 56 + Src/Plugins/Input/in_wmvdrm/api.h | 42 + Src/Plugins/Input/in_wmvdrm/config.cpp | 118 + Src/Plugins/Input/in_wmvdrm/config.h | 50 + Src/Plugins/Input/in_wmvdrm/directdraw.cpp | 23 + Src/Plugins/Input/in_wmvdrm/directdraw.h | 8 + Src/Plugins/Input/in_wmvdrm/factory_Handler.cpp | 80 + Src/Plugins/Input/in_wmvdrm/factory_Handler.h | 30 + Src/Plugins/Input/in_wmvdrm/in_wm.rc | 331 + Src/Plugins/Input/in_wmvdrm/in_wmvdrm.sln | 30 + Src/Plugins/Input/in_wmvdrm/in_wmvdrm.vcxproj | 361 + .../Input/in_wmvdrm/in_wmvdrm.vcxproj.filters | 330 + Src/Plugins/Input/in_wmvdrm/loadini.cpp | 12 + Src/Plugins/Input/in_wmvdrm/loadini.h | 8 + Src/Plugins/Input/in_wmvdrm/main.cpp | 136 + Src/Plugins/Input/in_wmvdrm/output/AudioOut.h | 38 + Src/Plugins/Input/in_wmvdrm/output/OutPlugin.cpp | 70 + Src/Plugins/Input/in_wmvdrm/output/OutPlugin.h | 28 + Src/Plugins/Input/in_wmvdrm/res_wav/resource.h | 60 + Src/Plugins/Input/in_wmvdrm/resource.h | 266 + Src/Plugins/Input/in_wmvdrm/util.cpp | 169 + Src/Plugins/Input/in_wmvdrm/util.h | 20 + Src/Plugins/Input/in_wmvdrm/version.rc2 | 39 + Src/Plugins/Input/in_wmvdrm/vid_ddraw.cpp | 771 ++ Src/Plugins/Input/in_wmvdrm/vid_ddraw.h | 50 + Src/Plugins/Input/in_wmvdrm/vid_overlay.cpp | 708 ++ Src/Plugins/Input/in_wmvdrm/vid_overlay.h | 55 + Src/Plugins/Input/in_wmvdrm/vidutils.cpp | 130 + Src/Plugins/Input/in_wmvdrm/vidutils.h | 24 + Src/Plugins/Input/in_wv/changes.txt | 7 + Src/Plugins/Input/in_wv/in2.h | 152 + Src/Plugins/Input/in_wv/in_wv.cpp | 2062 ++++ Src/Plugins/Input/in_wv/resource.h | 49 + .../wasabi/Agave/AlbumArt/svc_albumArtProvider.h | 73 + .../Input/in_wv/wasabi/Agave/Config/api_config.h | 123 + .../in_wv/wasabi/Agave/Config/ifc_configgroup.h | 35 + .../in_wv/wasabi/Agave/Config/ifc_configitem.h | 200 + .../in_wv/wasabi/Agave/Language/api_language.h | 315 + .../Input/in_wv/wasabi/Agave/Language/lang.h | 595 ++ Src/Plugins/Input/in_wv/wasabi/Wasabi.cpp | 161 + Src/Plugins/Input/in_wv/wasabi/Wasabi.h | 66 + Src/Plugins/Input/in_wv/wasabi/Winamp/DSP.H | 65 + Src/Plugins/Input/in_wv/wasabi/Winamp/GEN.H | 37 + Src/Plugins/Input/in_wv/wasabi/Winamp/IN2.H | 138 + .../Input/in_wv/wasabi/Winamp/api_audiostream.h | 65 + .../Input/in_wv/wasabi/Winamp/api_decodefile.h | 99 + Src/Plugins/Input/in_wv/wasabi/Winamp/api_random.h | 78 + .../Input/in_wv/wasabi/Winamp/api_wa5component.h | 34 + Src/Plugins/Input/in_wv/wasabi/Winamp/ipc_pe.h | 56 + Src/Plugins/Input/in_wv/wasabi/Winamp/wa_dlg.h | 436 + Src/Plugins/Input/in_wv/wasabi/Winamp/wa_hotkeys.h | 39 + Src/Plugins/Input/in_wv/wasabi/Winamp/wa_ipc.h | 2468 +++++ .../Input/in_wv/wasabi/api/memmgr/api_memmgr.h | 78 + .../Input/in_wv/wasabi/api/service/api_service.h | 155 + .../Input/in_wv/wasabi/api/service/services.h | 68 + .../in_wv/wasabi/api/service/waservicefactory.h | 97 + Src/Plugins/Input/in_wv/wasabi/bfc/dispatch.h | 559 ++ Src/Plugins/Input/in_wv/wasabi/bfc/nsguid.h | 37 + Src/Plugins/Input/in_wv/wasabi/bfc/platform/guid.h | 24 + .../Input/in_wv/wasabi/bfc/platform/platform.h | 500 + .../Input/in_wv/wasabi/bfc/platform/types.h | 78 + .../Input/in_wv/wasabi/bfc/platform/win32.h | 38 + Src/Plugins/Input/in_wv/wasabi/bfc/std_mkncc.h | 11 + Src/Plugins/Input/in_wv/wasabi/nu/AutoChar.h | 133 + Src/Plugins/Input/in_wv/wasabi/nu/AutoWide.h | 86 + Src/Plugins/Input/in_wv/wavpack.rc | 118 + Src/Plugins/Input/in_wv/winamp.vcproj | 237 + Src/Plugins/Library/ml_autotag/main.cpp | 220 + Src/Plugins/Library/ml_autotag/main.h | 35 + Src/Plugins/Library/ml_autotag/metadata.cpp | 25 + Src/Plugins/Library/ml_autotag/ml_autotag.rc | 212 + Src/Plugins/Library/ml_autotag/ml_autotag.sln | 30 + Src/Plugins/Library/ml_autotag/ml_autotag.vcxproj | 306 + .../Library/ml_autotag/ml_autotag.vcxproj.filters | 71 + Src/Plugins/Library/ml_autotag/resource.h | 88 + Src/Plugins/Library/ml_autotag/tagger.cpp | 831 ++ Src/Plugins/Library/ml_autotag/tagger.h | 68 + Src/Plugins/Library/ml_autotag/version.rc2 | 39 + .../Library/ml_bookmarks/api__ml_bookmarks.h | 15 + Src/Plugins/Library/ml_bookmarks/bookmark.cpp | 32 + Src/Plugins/Library/ml_bookmarks/bookmark.h | 18 + Src/Plugins/Library/ml_bookmarks/listview.cpp | 128 + Src/Plugins/Library/ml_bookmarks/listview.h | 144 + Src/Plugins/Library/ml_bookmarks/main.cpp | 122 + Src/Plugins/Library/ml_bookmarks/main.h | 28 + Src/Plugins/Library/ml_bookmarks/ml_bookmarks.rc | 186 + Src/Plugins/Library/ml_bookmarks/ml_bookmarks.sln | 30 + .../Library/ml_bookmarks/ml_bookmarks.vcxproj | 317 + .../ml_bookmarks/ml_bookmarks.vcxproj.filters | 100 + Src/Plugins/Library/ml_bookmarks/resource.h | 46 + .../resources/ti_bookmarks_16x16x16.bmp | Bin 0 -> 568 bytes Src/Plugins/Library/ml_bookmarks/version.rc2 | 39 + Src/Plugins/Library/ml_bookmarks/view.cpp | 1512 +++ Src/Plugins/Library/ml_devices/backBuffer.cpp | 276 + Src/Plugins/Library/ml_devices/backBuffer.h | 64 + Src/Plugins/Library/ml_devices/common.h | 76 + Src/Plugins/Library/ml_devices/config.cpp | 119 + Src/Plugins/Library/ml_devices/config.h | 44 + Src/Plugins/Library/ml_devices/deviceCommands.cpp | 207 + Src/Plugins/Library/ml_devices/deviceCommands.h | 24 + Src/Plugins/Library/ml_devices/deviceHandler.cpp | 206 + Src/Plugins/Library/ml_devices/deviceHandler.h | 56 + .../Library/ml_devices/deviceManagerHandler.cpp | 174 + .../Library/ml_devices/deviceManagerHandler.h | 54 + Src/Plugins/Library/ml_devices/embeddedEditor.cpp | 1240 +++ Src/Plugins/Library/ml_devices/embeddedEditor.h | 79 + Src/Plugins/Library/ml_devices/eventRelay.cpp | 465 + Src/Plugins/Library/ml_devices/eventRelay.h | 116 + Src/Plugins/Library/ml_devices/fillRegion.cpp | 134 + Src/Plugins/Library/ml_devices/fillRegion.h | 51 + .../Library/ml_devices/gen_deviceprovider/common.h | 76 + .../ml_devices/gen_deviceprovider/device.cpp | 871 ++ .../Library/ml_devices/gen_deviceprovider/device.h | 114 + .../gen_deviceprovider/deviceActivity.cpp | 519 + .../ml_devices/gen_deviceprovider/deviceActivity.h | 91 + .../gen_deviceprovider/deviceCommandNodeParser.cpp | 75 + .../gen_deviceprovider/deviceCommandNodeParser.h | 39 + .../gen_deviceprovider/deviceCommandParser.cpp | 192 + .../gen_deviceprovider/deviceCommandParser.h | 47 + .../deviceConnectionNodeParser.cpp | 75 + .../deviceConnectionNodeParser.h | 39 + .../gen_deviceprovider/deviceConnectionParser.cpp | 181 + .../gen_deviceprovider/deviceConnectionParser.h | 46 + .../gen_deviceprovider/deviceIconEditor.cpp | 178 + .../gen_deviceprovider/deviceIconEditor.h | 22 + .../gen_deviceprovider/deviceNodeParser.cpp | 75 + .../gen_deviceprovider/deviceNodeParser.h | 39 + .../ml_devices/gen_deviceprovider/deviceParser.cpp | 306 + .../ml_devices/gen_deviceprovider/deviceParser.h | 54 + .../gen_deviceprovider/deviceTypeNodeParser.cpp | 75 + .../gen_deviceprovider/deviceTypeNodeParser.h | 39 + .../gen_deviceprovider/deviceTypeParser.cpp | 182 + .../gen_deviceprovider/deviceTypeParser.h | 46 + .../ml_devices/gen_deviceprovider/deviceView.cpp | 994 ++ .../ml_devices/gen_deviceprovider/deviceView.h | 15 + .../gen_deviceprovider/gen_deviceprovider.sln | Bin 0 -> 2938 bytes .../gen_deviceprovider/gen_deviceprovider.vcproj | 415 + .../gen_deviceprovider/gen_deviceprovider.vcxproj | 335 + .../gen_deviceprovider.vcxproj.filters | 164 + .../ml_devices/gen_deviceprovider/iconStore.cpp | 289 + .../ml_devices/gen_deviceprovider/iconStore.h | 53 + .../Library/ml_devices/gen_deviceprovider/main.cpp | 2 + .../Library/ml_devices/gen_deviceprovider/main.h | 31 + .../ml_devices/gen_deviceprovider/plugin.cpp | 114 + .../Library/ml_devices/gen_deviceprovider/plugin.h | 21 + .../ml_devices/gen_deviceprovider/provider.cpp | 336 + .../ml_devices/gen_deviceprovider/provider.h | 50 + .../ml_devices/gen_deviceprovider/resource.h | 39 + .../ml_devices/gen_deviceprovider/resources.rc | 186 + .../gen_deviceprovider/stringBuilder.cpp | 85 + .../ml_devices/gen_deviceprovider/stringBuilder.h | 31 + .../ml_devices/gen_deviceprovider/strings.cpp | 243 + .../ml_devices/gen_deviceprovider/strings.h | 82 + .../gen_deviceprovider/supportedCommand.cpp | 108 + .../gen_deviceprovider/supportedCommand.h | 46 + .../ml_devices/gen_deviceprovider/testSuite.cpp | 555 ++ .../ml_devices/gen_deviceprovider/testSuite.h | 67 + .../gen_deviceprovider/testSuiteLoader.cpp | 226 + .../gen_deviceprovider/testSuiteLoader.h | 60 + .../ml_devices/gen_deviceprovider/testprovider.xml | 244 + .../ml_devices/gen_deviceprovider/wasabi.cpp | 139 + .../Library/ml_devices/gen_deviceprovider/wasabi.h | 57 + Src/Plugins/Library/ml_devices/graphics.cpp | 313 + Src/Plugins/Library/ml_devices/graphics.h | 72 + Src/Plugins/Library/ml_devices/image.cpp | 978 ++ Src/Plugins/Library/ml_devices/image.h | 227 + Src/Plugins/Library/ml_devices/imageCache.cpp | 902 ++ Src/Plugins/Library/ml_devices/imageCache.h | 76 + Src/Plugins/Library/ml_devices/infoWidget.cpp | 399 + Src/Plugins/Library/ml_devices/infoWidget.h | 27 + Src/Plugins/Library/ml_devices/listWidget.cpp | 4096 ++++++++ Src/Plugins/Library/ml_devices/listWidget.h | 23 + .../Library/ml_devices/listWidgetCategory.cpp | 202 + .../Library/ml_devices/listWidgetCommand.cpp | 392 + .../Library/ml_devices/listWidgetConnection.cpp | 262 + Src/Plugins/Library/ml_devices/listWidgetGroup.cpp | 135 + .../Library/ml_devices/listWidgetInternal.h | 1020 ++ Src/Plugins/Library/ml_devices/listWidgetItem.cpp | 2854 ++++++ Src/Plugins/Library/ml_devices/listWidgetPaint.cpp | 725 ++ .../Library/ml_devices/listWidgetTooltip.cpp | 592 ++ Src/Plugins/Library/ml_devices/local_menu.cpp | 183 + Src/Plugins/Library/ml_devices/local_menu.h | 48 + Src/Plugins/Library/ml_devices/main.cpp | 5 + Src/Plugins/Library/ml_devices/main.h | 50 + Src/Plugins/Library/ml_devices/managerView.cpp | 994 ++ Src/Plugins/Library/ml_devices/managerView.h | 22 + Src/Plugins/Library/ml_devices/ml_devices.rc | 172 + Src/Plugins/Library/ml_devices/ml_devices.sln | 51 + Src/Plugins/Library/ml_devices/ml_devices.vcxproj | 412 + .../Library/ml_devices/ml_devices.vcxproj.filters | 310 + Src/Plugins/Library/ml_devices/navigation.cpp | 1073 +++ Src/Plugins/Library/ml_devices/navigation.h | 29 + Src/Plugins/Library/ml_devices/navigationIcons.cpp | 343 + Src/Plugins/Library/ml_devices/navigationIcons.h | 21 + Src/Plugins/Library/ml_devices/plugin.cpp | 357 + Src/Plugins/Library/ml_devices/plugin.h | 33 + Src/Plugins/Library/ml_devices/png.rc | 50 + Src/Plugins/Library/ml_devices/resource.h | 105 + .../Library/ml_devices/resources/action-bg.png | Bin 0 -> 163 bytes .../Library/ml_devices/resources/arrows.png | Bin 0 -> 365 bytes .../ml_devices/resources/attach-command-large.png | Bin 0 -> 788 bytes .../ml_devices/resources/attach-command-small.png | Bin 0 -> 239 bytes .../resources/cancel-sync-command-small.png | Bin 0 -> 297 bytes .../Library/ml_devices/resources/command-bg.png | Bin 0 -> 242 bytes .../ml_devices/resources/command-secondary-bg.png | Bin 0 -> 163 bytes .../ml_devices/resources/commands/detach.png | Bin 0 -> 690 bytes .../ml_devices/resources/commands/eject.png | Bin 0 -> 1885 bytes .../ml_devices/resources/commands/settings.png | Bin 0 -> 2042 bytes .../Library/ml_devices/resources/commands/sync.png | Bin 0 -> 819 bytes .../ml_devices/resources/connections/bluetooth.png | Bin 0 -> 340 bytes .../ml_devices/resources/connections/cloud.png | Bin 0 -> 551 bytes .../ml_devices/resources/connections/usb.png | Bin 0 -> 329 bytes .../ml_devices/resources/connections/wifi.png | Bin 0 -> 414 bytes .../ml_devices/resources/detach-command-large.png | Bin 0 -> 840 bytes .../ml_devices/resources/detach-command-small.png | Bin 0 -> 241 bytes .../ml_devices/resources/devices-title-en.png | Bin 0 -> 7572 bytes .../Library/ml_devices/resources/devices.png | Bin 0 -> 64224 bytes .../ml_devices/resources/eject-command-small.png | Bin 0 -> 170 bytes .../Library/ml_devices/resources/food/apple.png | Bin 0 -> 34151 bytes .../Library/ml_devices/resources/food/banana.png | Bin 0 -> 138587 bytes .../Library/ml_devices/resources/food/bread.png | Bin 0 -> 5914 bytes .../Library/ml_devices/resources/food/broccoli.png | Bin 0 -> 52734 bytes .../Library/ml_devices/resources/food/carrot.png | Bin 0 -> 52729 bytes .../Library/ml_devices/resources/food/cereal.png | Bin 0 -> 150983 bytes .../Library/ml_devices/resources/food/cucumber.png | Bin 0 -> 74812 bytes .../Library/ml_devices/resources/food/eggplant.png | Bin 0 -> 84567 bytes .../Library/ml_devices/resources/food/grape.png | Bin 0 -> 63881 bytes .../Library/ml_devices/resources/food/lemon.png | Bin 0 -> 57675 bytes .../Library/ml_devices/resources/food/muffin.png | Bin 0 -> 401367 bytes .../Library/ml_devices/resources/food/orange.png | Bin 0 -> 58604 bytes .../Library/ml_devices/resources/food/pasta.png | Bin 0 -> 126179 bytes .../Library/ml_devices/resources/food/peach.png | Bin 0 -> 28831 bytes .../Library/ml_devices/resources/food/pear.png | Bin 0 -> 66710 bytes .../Library/ml_devices/resources/food/pepper.png | Bin 0 -> 202645 bytes .../Library/ml_devices/resources/food/pretzel.png | Bin 0 -> 182594 bytes .../Library/ml_devices/resources/food/rice.png | Bin 0 -> 237923 bytes .../resources/generic-device-160x160.png | Bin 0 -> 2491 bytes .../ml_devices/resources/generic-device-16x16.png | Bin 0 -> 210 bytes .../Library/ml_devices/resources/item-hover.png | Bin 0 -> 206 bytes .../Library/ml_devices/resources/item-select.png | Bin 0 -> 313 bytes .../ml_devices/resources/progress-large.png | Bin 0 -> 22461 bytes .../ml_devices/resources/progress-small.png | Bin 0 -> 844 bytes .../Library/ml_devices/resources/spacebar.png | Bin 0 -> 537 bytes .../ml_devices/resources/sync-command-large.png | Bin 0 -> 819 bytes .../ml_devices/resources/sync-command-small.png | Bin 0 -> 268 bytes .../ml_devices/resources/unknown-command-large.png | Bin 0 -> 324 bytes .../Library/ml_devices/resources/zoom/192x256.png | Bin 0 -> 5366 bytes .../Library/ml_devices/resources/zoom/24x32.png | Bin 0 -> 347 bytes .../Library/ml_devices/resources/zoom/48x64.png | Bin 0 -> 930 bytes .../Library/ml_devices/resources/zoom/96x128.png | Bin 0 -> 2518 bytes Src/Plugins/Library/ml_devices/statusBar.cpp | 1001 ++ Src/Plugins/Library/ml_devices/statusBar.h | 81 + Src/Plugins/Library/ml_devices/strings.cpp | 195 + Src/Plugins/Library/ml_devices/strings.h | 74 + Src/Plugins/Library/ml_devices/version.rc2 | 39 + Src/Plugins/Library/ml_devices/wasabi.cpp | 107 + Src/Plugins/Library/ml_devices/wasabi.h | 43 + Src/Plugins/Library/ml_devices/welcomeWidget.cpp | 760 ++ Src/Plugins/Library/ml_devices/welcomeWidget.h | 20 + Src/Plugins/Library/ml_devices/widget.cpp | 1284 +++ Src/Plugins/Library/ml_devices/widget.h | 129 + Src/Plugins/Library/ml_devices/widgetHost.cpp | 391 + Src/Plugins/Library/ml_devices/widgetHost.h | 23 + Src/Plugins/Library/ml_devices/widgetStyle.cpp | 261 + Src/Plugins/Library/ml_devices/widgetStyle.h | 159 + Src/Plugins/Library/ml_disc/M3UWriter.cpp | 91 + Src/Plugins/Library/ml_disc/M3UWriter.h | 25 + Src/Plugins/Library/ml_disc/PLSWriter.cpp | 65 + Src/Plugins/Library/ml_disc/PLSWriter.h | 23 + Src/Plugins/Library/ml_disc/ReplayGain.cpp | 95 + Src/Plugins/Library/ml_disc/ReplayGain.h | 16 + Src/Plugins/Library/ml_disc/api__ml_disc.h | 15 + Src/Plugins/Library/ml_disc/banner.cpp | 222 + Src/Plugins/Library/ml_disc/banner.h | 44 + Src/Plugins/Library/ml_disc/cdburn.cpp | 2155 +++++ Src/Plugins/Library/ml_disc/cdrip.cpp | 1061 +++ Src/Plugins/Library/ml_disc/cmdbar_data.cpp | 485 + Src/Plugins/Library/ml_disc/commandbar.cpp | 119 + Src/Plugins/Library/ml_disc/commandbar.h | 48 + Src/Plugins/Library/ml_disc/config.cpp | 129 + Src/Plugins/Library/ml_disc/config.h | 44 + Src/Plugins/Library/ml_disc/copyfiles.cpp | 613 ++ Src/Plugins/Library/ml_disc/copyfiles.h | 27 + Src/Plugins/Library/ml_disc/copyfiles_post.cpp | 138 + Src/Plugins/Library/ml_disc/copyinternal.h | 125 + Src/Plugins/Library/ml_disc/copyprep.cpp | 426 + Src/Plugins/Library/ml_disc/copyprogress.cpp | 360 + Src/Plugins/Library/ml_disc/discInfo.cpp | 328 + Src/Plugins/Library/ml_disc/discInfo.h | 61 + Src/Plugins/Library/ml_disc/drive.cpp | 138 + Src/Plugins/Library/ml_disc/drive.h | 16 + Src/Plugins/Library/ml_disc/driveListBox.cpp | 255 + Src/Plugins/Library/ml_disc/driveListBox.h | 50 + Src/Plugins/Library/ml_disc/drivemngr.cpp | 1910 ++++ Src/Plugins/Library/ml_disc/drivemngr.h | 239 + Src/Plugins/Library/ml_disc/drives.cpp | 243 + Src/Plugins/Library/ml_disc/drives.h | 44 + Src/Plugins/Library/ml_disc/formatfilename.cpp | 269 + Src/Plugins/Library/ml_disc/helpwnd.cpp | 350 + Src/Plugins/Library/ml_disc/infoBox.cpp | 145 + Src/Plugins/Library/ml_disc/infoBox.h | 42 + Src/Plugins/Library/ml_disc/main.cpp | 1273 +++ Src/Plugins/Library/ml_disc/main.h | 222 + Src/Plugins/Library/ml_disc/medium.cpp | 136 + Src/Plugins/Library/ml_disc/medium.h | 17 + Src/Plugins/Library/ml_disc/menu.cpp | 25 + Src/Plugins/Library/ml_disc/menu.h | 13 + Src/Plugins/Library/ml_disc/ml_disc.rc | 939 ++ Src/Plugins/Library/ml_disc/ml_disc.sln | 30 + Src/Plugins/Library/ml_disc/ml_disc.vcxproj | 382 + .../Library/ml_disc/ml_disc.vcxproj.filters | 250 + Src/Plugins/Library/ml_disc/png.rc | 45 + Src/Plugins/Library/ml_disc/prefs.cpp | 351 + Src/Plugins/Library/ml_disc/primosdk_helper.cpp | 109 + Src/Plugins/Library/ml_disc/primosdk_helper.h | 31 + Src/Plugins/Library/ml_disc/questionwnd.cpp | 279 + Src/Plugins/Library/ml_disc/resource.h | 455 + Src/Plugins/Library/ml_disc/resources/cdrom.png | Bin 0 -> 202 bytes .../Library/ml_disc/resources/cdrom_32x32_24.bmp | Bin 0 -> 3126 bytes Src/Plugins/Library/ml_disc/resources/eject1.png | Bin 0 -> 249 bytes Src/Plugins/Library/ml_disc/resources/eject2.png | Bin 0 -> 261 bytes Src/Plugins/Library/ml_disc/resources/eject3.png | Bin 0 -> 132 bytes Src/Plugins/Library/ml_disc/resources/eject4.png | Bin 0 -> 113 bytes Src/Plugins/Library/ml_disc/resources/eject_d.png | Bin 0 -> 345 bytes Src/Plugins/Library/ml_disc/resources/eject_n.png | Bin 0 -> 361 bytes Src/Plugins/Library/ml_disc/resources/eject_p.png | Bin 0 -> 325 bytes .../Library/ml_disc/resources/enqueue_d.png | Bin 0 -> 285 bytes .../ml_disc/resources/enqueue_menu_16x16.png | Bin 0 -> 199 bytes .../Library/ml_disc/resources/enqueue_n.png | Bin 0 -> 270 bytes .../Library/ml_disc/resources/enqueue_p.png | Bin 0 -> 249 bytes .../Library/ml_disc/resources/enqueuem_d.png | Bin 0 -> 250 bytes .../Library/ml_disc/resources/enqueuem_n.png | Bin 0 -> 259 bytes .../Library/ml_disc/resources/enqueuem_p.png | Bin 0 -> 257 bytes Src/Plugins/Library/ml_disc/resources/filecopy.png | Bin 0 -> 2903 bytes .../ml_disc/resources/listbox_back_2x68x24.bmp | Bin 0 -> 598 bytes Src/Plugins/Library/ml_disc/resources/play_d.png | Bin 0 -> 340 bytes .../Library/ml_disc/resources/play_menu_16x16.png | Bin 0 -> 348 bytes Src/Plugins/Library/ml_disc/resources/play_n.png | Bin 0 -> 352 bytes Src/Plugins/Library/ml_disc/resources/play_p.png | Bin 0 -> 352 bytes Src/Plugins/Library/ml_disc/resources/playm_d.png | Bin 0 -> 373 bytes Src/Plugins/Library/ml_disc/resources/playm_n.png | Bin 0 -> 380 bytes Src/Plugins/Library/ml_disc/resources/playm_p.png | Bin 0 -> 386 bytes .../ml_disc/resources/rip&burn_logo_228x25x16.bmp | Bin 0 -> 11456 bytes .../ml_disc/resources/sonic_powered_44x22.bmp | Bin 0 -> 694 bytes Src/Plugins/Library/ml_disc/settings.cpp | 573 ++ Src/Plugins/Library/ml_disc/settings.h | 108 + Src/Plugins/Library/ml_disc/spti.cpp | 239 + Src/Plugins/Library/ml_disc/spti.h | 35 + Src/Plugins/Library/ml_disc/version.rc2 | 39 + Src/Plugins/Library/ml_disc/view_cdrom.cpp | 1014 ++ Src/Plugins/Library/ml_disc/view_container.cpp | 573 ++ Src/Plugins/Library/ml_disc/view_data.cpp | 675 ++ Src/Plugins/Library/ml_disc/view_info.cpp | 127 + Src/Plugins/Library/ml_disc/view_ripburn.cpp | 567 ++ Src/Plugins/Library/ml_disc/view_wait.cpp | 140 + Src/Plugins/Library/ml_downloads/AtomParse.h | 51 + Src/Plugins/Library/ml_downloads/DESIGN.txt | 12 + Src/Plugins/Library/ml_downloads/Defaults.cpp | 43 + Src/Plugins/Library/ml_downloads/Defaults.h | 24 + .../Library/ml_downloads/DownloadManager.cpp | 1 + .../Library/ml_downloads/DownloadStatus.cpp | 158 + Src/Plugins/Library/ml_downloads/DownloadStatus.h | 41 + .../Library/ml_downloads/DownloadThread.cpp | 65 + Src/Plugins/Library/ml_downloads/DownloadThread.h | 27 + .../Library/ml_downloads/DownloadViewCallback.cpp | 125 + .../Library/ml_downloads/DownloadViewCallback.h | 37 + Src/Plugins/Library/ml_downloads/Downloaded.cpp | 141 + Src/Plugins/Library/ml_downloads/Downloaded.h | 86 + .../Library/ml_downloads/DownloadsDialog.cpp | 1657 ++++ Src/Plugins/Library/ml_downloads/DownloadsDialog.h | 11 + .../Library/ml_downloads/DownloadsParse.cpp | 179 + Src/Plugins/Library/ml_downloads/DownloadsParse.h | 13 + Src/Plugins/Library/ml_downloads/Main.cpp | 387 + Src/Plugins/Library/ml_downloads/Main.h | 43 + .../Library/ml_downloads/MessageProcessor.cpp | 7 + .../Library/ml_downloads/MessageProcessor.h | 35 + Src/Plugins/Library/ml_downloads/ParseUtil.cpp | 43 + Src/Plugins/Library/ml_downloads/ParseUtil.h | 8 + Src/Plugins/Library/ml_downloads/Preferences.cpp | 77 + Src/Plugins/Library/ml_downloads/Preferences.h | 6 + Src/Plugins/Library/ml_downloads/RFCDate.cpp | 216 + Src/Plugins/Library/ml_downloads/RFCDate.h | 7 + Src/Plugins/Library/ml_downloads/TODO.txt | 51 + Src/Plugins/Library/ml_downloads/Util.h | 64 + Src/Plugins/Library/ml_downloads/XMLWriter.cpp | 125 + Src/Plugins/Library/ml_downloads/XMLWriter.h | 6 + .../Library/ml_downloads/api__ml_downloads.h | 11 + Src/Plugins/Library/ml_downloads/date.c | 1 + Src/Plugins/Library/ml_downloads/date.h | 20 + Src/Plugins/Library/ml_downloads/db.cpp | 150 + Src/Plugins/Library/ml_downloads/errors.h | 15 + Src/Plugins/Library/ml_downloads/layout.cpp | 215 + Src/Plugins/Library/ml_downloads/layout.h | 43 + Src/Plugins/Library/ml_downloads/ml_downloads.rc | 249 + Src/Plugins/Library/ml_downloads/ml_downloads.sln | 93 + .../Library/ml_downloads/ml_downloads.vcxproj | 370 + .../ml_downloads/ml_downloads.vcxproj.filters | 215 + Src/Plugins/Library/ml_downloads/png.rc | 63 + Src/Plugins/Library/ml_downloads/resource.h | 120 + .../ml_downloads/resources/downloadIcon.bmp | Bin 0 -> 1334 bytes .../ml_downloads/resources/downloadIcon.png | Bin 0 -> 367 bytes .../ml_downloads/resources/downloadIcon1.bmp | Bin 0 -> 1334 bytes .../ml_downloads/resources/downloadIcon2.bmp | Bin 0 -> 1334 bytes .../ml_downloads/resources/downloadIcon3.bmp | Bin 0 -> 1334 bytes Src/Plugins/Library/ml_downloads/util.cpp | 255 + Src/Plugins/Library/ml_downloads/version.rc2 | 39 + Src/Plugins/Library/ml_fanzone.7z | Bin 0 -> 9490 bytes Src/Plugins/Library/ml_fanzone/CMakeLists.txt | 77 + Src/Plugins/Library/ml_fanzone/api__ml_fanzone.h | 15 + .../Library/ml_fanzone/compatibility.manifest | 20 + Src/Plugins/Library/ml_fanzone/listview.cpp | 128 + Src/Plugins/Library/ml_fanzone/listview.h | 144 + Src/Plugins/Library/ml_fanzone/main.cpp | 110 + Src/Plugins/Library/ml_fanzone/main.h | 24 + .../Library/ml_fanzone/ml_fanzone.dll.manifest | 20 + Src/Plugins/Library/ml_fanzone/ml_fanzone.rc | 142 + Src/Plugins/Library/ml_fanzone/ml_fanzone.vcxproj | 354 + .../Library/ml_fanzone/ml_fanzone.vcxproj.filters | 97 + Src/Plugins/Library/ml_fanzone/myCefApp.cpp | 49 + Src/Plugins/Library/ml_fanzone/resource.h | 23 + .../Library/ml_fanzone/resources/FANZONE_16x16.bmp | Bin 0 -> 822 bytes .../Library/ml_fanzone/resources/cefsimple.ico | Bin 0 -> 23558 bytes Src/Plugins/Library/ml_fanzone/resources/small.ico | Bin 0 -> 23558 bytes Src/Plugins/Library/ml_fanzone/version.rc2 | 39 + Src/Plugins/Library/ml_fanzone/view.cpp | 444 + Src/Plugins/Library/ml_history/HistoryAPI.cpp | 93 + Src/Plugins/Library/ml_history/HistoryAPI.h | 12 + .../Library/ml_history/HistoryAPIFactory.cpp | 62 + Src/Plugins/Library/ml_history/HistoryAPIFactory.h | 21 + Src/Plugins/Library/ml_history/JSAPI2_Creator.cpp | 95 + Src/Plugins/Library/ml_history/JSAPI2_Creator.h | 31 + .../Library/ml_history/JSAPI2_HistoryAPI.cpp | 123 + Src/Plugins/Library/ml_history/JSAPI2_HistoryAPI.h | 27 + .../Library/ml_history/JSAPI2_HistoryRecord.cpp | 260 + .../Library/ml_history/JSAPI2_HistoryRecord.h | 46 + .../ml_history/JSAPI2_HistoryRecordList.cpp | 198 + .../Library/ml_history/JSAPI2_HistoryRecordList.h | 37 + Src/Plugins/Library/ml_history/Main.cpp | 349 + Src/Plugins/Library/ml_history/Main.h | 34 + Src/Plugins/Library/ml_history/api__ml_history.h | 18 + Src/Plugins/Library/ml_history/api_history.cpp | 1 + Src/Plugins/Library/ml_history/api_history.h | 34 + Src/Plugins/Library/ml_history/db_error.txt | 6 + Src/Plugins/Library/ml_history/history.h | 35 + Src/Plugins/Library/ml_history/ml_history.cpp | 289 + Src/Plugins/Library/ml_history/ml_history.h | 64 + Src/Plugins/Library/ml_history/ml_history.rc | 264 + Src/Plugins/Library/ml_history/ml_history.sln | 94 + Src/Plugins/Library/ml_history/ml_history.vcxproj | 341 + .../Library/ml_history/ml_history.vcxproj.filters | 164 + Src/Plugins/Library/ml_history/prefs.cpp | 178 + Src/Plugins/Library/ml_history/resource.h | 92 + .../resources/ti_history_items_16x16x16.bmp | Bin 0 -> 568 bytes Src/Plugins/Library/ml_history/version.rc2 | 39 + Src/Plugins/Library/ml_history/view_history.cpp | 2349 +++++ .../Library/ml_hotmixradio/api__ml_hotmixradio.h | 15 + Src/Plugins/Library/ml_hotmixradio/listview.cpp | 128 + Src/Plugins/Library/ml_hotmixradio/listview.h | 144 + Src/Plugins/Library/ml_hotmixradio/main.cpp | 111 + Src/Plugins/Library/ml_hotmixradio/main.h | 24 + .../Library/ml_hotmixradio/ml_hotmixradio.rc | 128 + .../Library/ml_hotmixradio/ml_hotmixradio.vcxproj | 313 + .../ml_hotmixradio/ml_hotmixradio.vcxproj.filters | 88 + Src/Plugins/Library/ml_hotmixradio/resource.h | 20 + .../ml_hotmixradio/resources/HOTMIXRADIO_16x16.bmp | Bin 0 -> 1078 bytes Src/Plugins/Library/ml_hotmixradio/version.rc2 | 39 + Src/Plugins/Library/ml_hotmixradio/view.cpp | 388 + Src/Plugins/Library/ml_impex/ImportPlaylists.cpp | 404 + Src/Plugins/Library/ml_impex/ImporterAPI.cpp | 299 + Src/Plugins/Library/ml_impex/ImporterAPI.h | 13 + Src/Plugins/Library/ml_impex/api__ml_impex.h | 20 + Src/Plugins/Library/ml_impex/api_importer.h | 52 + Src/Plugins/Library/ml_impex/impex.cpp | 625 ++ Src/Plugins/Library/ml_impex/importer.cpp | 327 + Src/Plugins/Library/ml_impex/importer.h | 66 + Src/Plugins/Library/ml_impex/itunesxmlwrite.cpp | 152 + Src/Plugins/Library/ml_impex/itunesxmlwrite.h | 33 + Src/Plugins/Library/ml_impex/ml_impex.rc | 144 + Src/Plugins/Library/ml_impex/ml_impex.sln | 50 + Src/Plugins/Library/ml_impex/ml_impex.vcxproj | 317 + .../Library/ml_impex/ml_impex.vcxproj.filters | 62 + Src/Plugins/Library/ml_impex/resource.h | 37 + Src/Plugins/Library/ml_impex/version.rc2 | 39 + Src/Plugins/Library/ml_local/AlbumArtCache.cpp | 508 + Src/Plugins/Library/ml_local/AlbumArtCache.h | 14 + Src/Plugins/Library/ml_local/AlbumArtContainer.cpp | 80 + Src/Plugins/Library/ml_local/AlbumArtContainer.h | 43 + Src/Plugins/Library/ml_local/AlbumArtFilter.cpp | 1258 +++ Src/Plugins/Library/ml_local/AlbumArtFilter.h | 64 + Src/Plugins/Library/ml_local/AlbumFilter.cpp | 496 + Src/Plugins/Library/ml_local/AlbumFilter.h | 49 + Src/Plugins/Library/ml_local/DBitemrecord.cpp | 173 + Src/Plugins/Library/ml_local/FolderBrowseEx.cpp | 239 + Src/Plugins/Library/ml_local/FolderBrowseEx.h | 97 + Src/Plugins/Library/ml_local/LocalMediaCOM.cpp | 307 + Src/Plugins/Library/ml_local/LocalMediaCOM.h | 21 + Src/Plugins/Library/ml_local/MD5.cpp | 294 + Src/Plugins/Library/ml_local/MD5.h | 18 + Src/Plugins/Library/ml_local/MLDBCallback.h | 76 + Src/Plugins/Library/ml_local/MLString.cpp | 116 + Src/Plugins/Library/ml_local/MLString.h | 54 + Src/Plugins/Library/ml_local/Main.cpp | 586 ++ Src/Plugins/Library/ml_local/Main.h | 71 + Src/Plugins/Library/ml_local/ReIndexUI.cpp | 333 + Src/Plugins/Library/ml_local/SaveQuery.cpp | 406 + Src/Plugins/Library/ml_local/ScanFolderBrowser.cpp | 475 + Src/Plugins/Library/ml_local/ScanFolderBrowser.h | 53 + Src/Plugins/Library/ml_local/SimpleFilter.cpp | 278 + Src/Plugins/Library/ml_local/SimpleFilter.h | 169 + Src/Plugins/Library/ml_local/TitleInfo.cpp | 321 + Src/Plugins/Library/ml_local/ViewFilter.cpp | 431 + Src/Plugins/Library/ml_local/ViewFilter.h | 89 + Src/Plugins/Library/ml_local/add.cpp | 478 + Src/Plugins/Library/ml_local/api__ml_local.h | 15 + Src/Plugins/Library/ml_local/api_mldb.cpp | 3 + Src/Plugins/Library/ml_local/api_mldb.h | 166 + Src/Plugins/Library/ml_local/bgscan.cpp | 940 ++ Src/Plugins/Library/ml_local/contnr.cpp | 979 ++ Src/Plugins/Library/ml_local/contnr.h | 153 + Src/Plugins/Library/ml_local/db.h | 64 + Src/Plugins/Library/ml_local/db_error.txt | 6 + Src/Plugins/Library/ml_local/editinfo.cpp | 816 ++ Src/Plugins/Library/ml_local/editquery.cpp | 1013 ++ Src/Plugins/Library/ml_local/editquery.h | 7 + Src/Plugins/Library/ml_local/evntsink.cpp | 239 + Src/Plugins/Library/ml_local/evntsink.h | 25 + Src/Plugins/Library/ml_local/guess.cpp | 122 + Src/Plugins/Library/ml_local/handleMessage.cpp | 906 ++ Src/Plugins/Library/ml_local/local_menu.cpp | 272 + Src/Plugins/Library/ml_local/local_menu.h | 15 + Src/Plugins/Library/ml_local/metaRecord.h | 44 + Src/Plugins/Library/ml_local/ml_local.cpp | 1394 +++ Src/Plugins/Library/ml_local/ml_local.h | 403 + Src/Plugins/Library/ml_local/ml_local.rc | 1244 +++ Src/Plugins/Library/ml_local/ml_local.sln | 128 + Src/Plugins/Library/ml_local/ml_local.vcxproj | 416 + .../Library/ml_local/ml_local.vcxproj.filters | 328 + Src/Plugins/Library/ml_local/ml_subclass.cpp | 80 + Src/Plugins/Library/ml_local/mldbApi.cpp | 399 + Src/Plugins/Library/ml_local/mldbApi.h | 34 + Src/Plugins/Library/ml_local/mldbApiFactory.cpp | 61 + Src/Plugins/Library/ml_local/mldbApiFactory.h | 23 + Src/Plugins/Library/ml_local/nde_error.txt | 4 + Src/Plugins/Library/ml_local/nde_itemRecord.cpp | 976 ++ Src/Plugins/Library/ml_local/pe_subclass.cpp | 76 + Src/Plugins/Library/ml_local/prefs.cpp | 936 ++ Src/Plugins/Library/ml_local/queries.cpp | 1639 ++++ Src/Plugins/Library/ml_local/queries.txt | 77 + Src/Plugins/Library/ml_local/remove.cpp | 31 + Src/Plugins/Library/ml_local/resource.h | 606 ++ .../Library/ml_local/resources/icn_alb_art.bmp | Bin 0 -> 1210 bytes .../Library/ml_local/resources/icn_columns.bmp | Bin 0 -> 1342 bytes .../Library/ml_local/resources/icn_view_mode.bmp | Bin 0 -> 1298 bytes .../Library/ml_local/resources/nf_simple.bmp | Bin 0 -> 822 bytes .../Library/ml_local/resources/nf_simplealbum.bmp | Bin 0 -> 822 bytes .../Library/ml_local/resources/nf_threefilters.bmp | Bin 0 -> 822 bytes .../Library/ml_local/resources/nf_twofilters.bmp | Bin 0 -> 822 bytes .../Library/ml_local/resources/notfound.png | Bin 0 -> 3645 bytes .../ml_local/resources/ti_audio_16x16x16.bmp | Bin 0 -> 568 bytes .../ml_local/resources/ti_most_played_16x16x16.bmp | Bin 0 -> 568 bytes .../resources/ti_never_played_16x16x16.bmp | Bin 0 -> 568 bytes .../ml_local/resources/ti_podcasts_16x16x16.bmp | Bin 0 -> 568 bytes .../resources/ti_recently_added_16x16x16.bmp | Bin 0 -> 568 bytes .../resources/ti_recently_modified_16x16x16.bmp | Bin 0 -> 578 bytes .../resources/ti_recently_played_16x16x16.bmp | Bin 0 -> 568 bytes .../ml_local/resources/ti_top_rated_16x16x16.bmp | Bin 0 -> 568 bytes .../ml_local/resources/ti_video_16x16x16.bmp | Bin 0 -> 568 bytes Src/Plugins/Library/ml_local/util.cpp | 77 + Src/Plugins/Library/ml_local/version.rc2 | 39 + Src/Plugins/Library/ml_local/view_audio.cpp | 2403 +++++ Src/Plugins/Library/ml_local/view_errorinfo.cpp | 119 + Src/Plugins/Library/ml_local/view_media.cpp | 4051 ++++++++ Src/Plugins/Library/ml_local/view_miniinfo.cpp | 201 + Src/Plugins/Library/ml_local/wa_subclass.cpp | 314 + Src/Plugins/Library/ml_nft/api__ml_nft.h | 15 + Src/Plugins/Library/ml_nft/listview.cpp | 128 + Src/Plugins/Library/ml_nft/listview.h | 144 + Src/Plugins/Library/ml_nft/main.cpp | 111 + Src/Plugins/Library/ml_nft/main.h | 24 + Src/Plugins/Library/ml_nft/ml_nft.rc | 128 + Src/Plugins/Library/ml_nft/ml_nft.vcxproj | 313 + Src/Plugins/Library/ml_nft/ml_nft.vcxproj.filters | 88 + Src/Plugins/Library/ml_nft/resource.h | 20 + Src/Plugins/Library/ml_nft/resources/NFT_16x16.bmp | Bin 0 -> 822 bytes .../Library/ml_nft/resources/_NFT_16x16.png | Bin 0 -> 456 bytes Src/Plugins/Library/ml_nft/version.rc2 | 39 + Src/Plugins/Library/ml_nft/view.cpp | 388 + Src/Plugins/Library/ml_nowplaying/common.cpp | 147 + Src/Plugins/Library/ml_nowplaying/common.h | 58 + Src/Plugins/Library/ml_nowplaying/external.cpp | 110 + Src/Plugins/Library/ml_nowplaying/external.h | 44 + Src/Plugins/Library/ml_nowplaying/handler.cpp | 47 + Src/Plugins/Library/ml_nowplaying/handler.h | 19 + Src/Plugins/Library/ml_nowplaying/local_menu.cpp | 68 + Src/Plugins/Library/ml_nowplaying/local_menu.h | 15 + Src/Plugins/Library/ml_nowplaying/main.cpp | 160 + Src/Plugins/Library/ml_nowplaying/main.h | 22 + Src/Plugins/Library/ml_nowplaying/ml_nowplaying.rc | 99 + .../Library/ml_nowplaying/ml_nowplaying.sln | 30 + .../Library/ml_nowplaying/ml_nowplaying.vcxproj | 280 + .../ml_nowplaying/ml_nowplaying.vcxproj.filters | 107 + Src/Plugins/Library/ml_nowplaying/navigation.cpp | 684 ++ Src/Plugins/Library/ml_nowplaying/navigation.h | 27 + Src/Plugins/Library/ml_nowplaying/png.rc | 7 + Src/Plugins/Library/ml_nowplaying/resource.h | 23 + .../ml_nowplaying/resources/serviceIcon.png | Bin 0 -> 290 bytes Src/Plugins/Library/ml_nowplaying/service.cpp | 171 + Src/Plugins/Library/ml_nowplaying/service.h | 53 + Src/Plugins/Library/ml_nowplaying/version.rc2 | 39 + Src/Plugins/Library/ml_nowplaying/wasabi.cpp | 82 + Src/Plugins/Library/ml_nowplaying/wasabi.h | 42 + .../Library/ml_nowplaying/wasabiCallback.cpp | 90 + Src/Plugins/Library/ml_nowplaying/wasabiCallback.h | 42 + Src/Plugins/Library/ml_online/BufferCache.h | 13 + Src/Plugins/Library/ml_online/JSAPI2_Creator.cpp | 93 + Src/Plugins/Library/ml_online/JSAPI2_Creator.h | 31 + Src/Plugins/Library/ml_online/JnetCOM.cpp | 656 ++ Src/Plugins/Library/ml_online/JnetCOM.h | 91 + Src/Plugins/Library/ml_online/Main.cpp | 599 ++ Src/Plugins/Library/ml_online/Main.h | 79 + Src/Plugins/Library/ml_online/OMCOM.cpp | 821 ++ Src/Plugins/Library/ml_online/OMCOM.h | 43 + Src/Plugins/Library/ml_online/Preferences.cpp | 110 + Src/Plugins/Library/ml_online/Preferences.h | 16 + .../Library/ml_online/Setup/SetupGroupFilter.h | 91 + Src/Plugins/Library/ml_online/Setup/setup.cpp | 71 + .../Library/ml_online/Setup/setupDetails.cpp | 80 + Src/Plugins/Library/ml_online/Setup/setupDetails.h | 30 + .../Library/ml_online/Setup/setupDetailsGroup.cpp | 284 + .../ml_online/Setup/setupDetailsService.cpp | 596 ++ Src/Plugins/Library/ml_online/Setup/setupGroup.cpp | 929 ++ Src/Plugins/Library/ml_online/Setup/setupGroup.h | 131 + .../Library/ml_online/Setup/setupGroupFilter.cpp | 226 + .../Library/ml_online/Setup/setupGroupList.cpp | 190 + .../Library/ml_online/Setup/setupGroupList.h | 52 + Src/Plugins/Library/ml_online/Setup/setupImage.cpp | 282 + Src/Plugins/Library/ml_online/Setup/setupImage.h | 50 + .../Library/ml_online/Setup/setupListbox.cpp | 878 ++ Src/Plugins/Library/ml_online/Setup/setupListbox.h | 84 + .../Library/ml_online/Setup/setupListboxLabel.cpp | 228 + .../Library/ml_online/Setup/setupListboxLabel.h | 57 + Src/Plugins/Library/ml_online/Setup/setupLog.cpp | 395 + Src/Plugins/Library/ml_online/Setup/setupLog.h | 54 + Src/Plugins/Library/ml_online/Setup/setupPage.cpp | 648 ++ Src/Plugins/Library/ml_online/Setup/setupPage.h | 85 + .../Library/ml_online/Setup/setupPageWnd.cpp | 145 + .../Library/ml_online/Setup/setupRecord.cpp | 567 ++ Src/Plugins/Library/ml_online/Setup/setupRecord.h | 84 + .../Library/ml_online/Setup/setupServicePanel.cpp | 797 ++ .../Library/ml_online/Setup/setupServicePanel.h | 79 + Src/Plugins/Library/ml_online/api__ml_online.h | 62 + Src/Plugins/Library/ml_online/browserEvent.cpp | 100 + Src/Plugins/Library/ml_online/browserEvent.h | 41 + Src/Plugins/Library/ml_online/commands.cpp | 418 + Src/Plugins/Library/ml_online/commands.h | 29 + Src/Plugins/Library/ml_online/common.cpp | 374 + Src/Plugins/Library/ml_online/common.h | 81 + Src/Plugins/Library/ml_online/config.cpp | 304 + Src/Plugins/Library/ml_online/config.h | 54 + Src/Plugins/Library/ml_online/external.cpp | 273 + Src/Plugins/Library/ml_online/external.h | 49 + Src/Plugins/Library/ml_online/forceUrl.cpp | 51 + Src/Plugins/Library/ml_online/forceUrl.h | 27 + Src/Plugins/Library/ml_online/handler.cpp | 168 + Src/Plugins/Library/ml_online/handler.h | 20 + Src/Plugins/Library/ml_online/import.h | 18 + Src/Plugins/Library/ml_online/importFile.cpp | 265 + Src/Plugins/Library/ml_online/importUrl.cpp | 321 + Src/Plugins/Library/ml_online/jsapi2_omcom.cpp | 136 + Src/Plugins/Library/ml_online/jsapi2_omcom.h | 27 + Src/Plugins/Library/ml_online/local_menu.cpp | 379 + Src/Plugins/Library/ml_online/local_menu.h | 40 + Src/Plugins/Library/ml_online/messageBox.cpp | 193 + Src/Plugins/Library/ml_online/messageBox.h | 13 + Src/Plugins/Library/ml_online/ml_online.rc | 453 + Src/Plugins/Library/ml_online/ml_online.sln | 30 + Src/Plugins/Library/ml_online/ml_online.vcxproj | 395 + .../Library/ml_online/ml_online.vcxproj.filters | 301 + Src/Plugins/Library/ml_online/navigation.cpp | 1472 +++ Src/Plugins/Library/ml_online/navigation.h | 96 + Src/Plugins/Library/ml_online/png.rc | 27 + Src/Plugins/Library/ml_online/resource.h | 206 + .../Library/ml_online/resources/iconAol.png | Bin 0 -> 253 bytes .../Library/ml_online/resources/iconAolGames.png | Bin 0 -> 206 bytes .../Library/ml_online/resources/iconDefault.png | Bin 0 -> 245 bytes .../Library/ml_online/resources/iconIn2Tv.png | Bin 0 -> 247 bytes .../Library/ml_online/resources/iconMusicNow.png | Bin 0 -> 294 bytes .../ml_online/resources/iconShoutcastRadio.png | Bin 0 -> 277 bytes .../ml_online/resources/iconShoutcastTv.png | Bin 0 -> 197 bytes .../ml_online/resources/iconSingingfish.png | Bin 0 -> 249 bytes .../Library/ml_online/resources/iconWaMusic.png | Bin 0 -> 300 bytes .../Library/ml_online/resources/iconWaRemote.png | Bin 0 -> 264 bytes .../ml_online/resources/pages/serviceEditor.htm | 30 + .../Library/ml_online/resources/pages/webdev.js | 95 + .../Library/ml_online/resources/service64x64.png | Bin 0 -> 1667 bytes Src/Plugins/Library/ml_online/serviceHelper.cpp | 1232 +++ Src/Plugins/Library/ml_online/serviceHelper.h | 57 + Src/Plugins/Library/ml_online/serviceHost.cpp | 394 + Src/Plugins/Library/ml_online/serviceHost.h | 75 + Src/Plugins/Library/ml_online/testPages.rc | 7 + Src/Plugins/Library/ml_online/version.rc2 | 39 + Src/Plugins/Library/ml_online/wasabi.cpp | 144 + Src/Plugins/Library/ml_playlists/AMNWatcher.cpp | 241 + Src/Plugins/Library/ml_playlists/AddPlaylist.cpp | 124 + .../Library/ml_playlists/CurrentPlaylist.cpp | 313 + Src/Plugins/Library/ml_playlists/CurrentPlaylist.h | 12 + Src/Plugins/Library/ml_playlists/DESIGN.TXT | 7 + Src/Plugins/Library/ml_playlists/Playlist.cpp | 331 + Src/Plugins/Library/ml_playlists/Playlist.h | 66 + .../ml_playlists/PlaylistDirectoryCallback.cpp | 41 + .../ml_playlists/PlaylistDirectoryCallback.h | 15 + Src/Plugins/Library/ml_playlists/PlaylistInfo.cpp | 233 + Src/Plugins/Library/ml_playlists/PlaylistInfo.h | 58 + Src/Plugins/Library/ml_playlists/PlaylistView.cpp | 300 + Src/Plugins/Library/ml_playlists/PlaylistView.h | 32 + Src/Plugins/Library/ml_playlists/PlaylistsCB.cpp | 120 + Src/Plugins/Library/ml_playlists/PlaylistsCB.h | 16 + Src/Plugins/Library/ml_playlists/PlaylistsCOM.cpp | 178 + Src/Plugins/Library/ml_playlists/PlaylistsCOM.h | 20 + .../Library/ml_playlists/RenamePlaylist.cpp | 72 + Src/Plugins/Library/ml_playlists/SendTo.cpp | 697 ++ Src/Plugins/Library/ml_playlists/SendTo.h | 100 + .../Library/ml_playlists/api__ml_playlists.h | 27 + Src/Plugins/Library/ml_playlists/main.h | 148 + Src/Plugins/Library/ml_playlists/ml_playlists.cpp | 237 + Src/Plugins/Library/ml_playlists/ml_playlists.rc | 498 + Src/Plugins/Library/ml_playlists/ml_playlists.sln | 81 + .../Library/ml_playlists/ml_playlists.vcxproj | 359 + .../ml_playlists/ml_playlists.vcxproj.filters | 199 + Src/Plugins/Library/ml_playlists/ml_subclass.cpp | 271 + Src/Plugins/Library/ml_playlists/pe_subclass.cpp | 63 + Src/Plugins/Library/ml_playlists/playlists.cpp | 185 + Src/Plugins/Library/ml_playlists/playlists.h | 6 + Src/Plugins/Library/ml_playlists/playlistsXML.cpp | 88 + Src/Plugins/Library/ml_playlists/playlistsXML.h | 27 + Src/Plugins/Library/ml_playlists/pluginproc.cpp | 527 ++ Src/Plugins/Library/ml_playlists/resource.h | 177 + .../resources/ti_cloud_playlist_16x16x16.bmp | Bin 0 -> 578 bytes .../resources/ti_playlist_16x16x16.bmp | Bin 0 -> 568 bytes Src/Plugins/Library/ml_playlists/version.rc2 | 39 + Src/Plugins/Library/ml_playlists/view_pl.cpp | 2885 ++++++ .../Library/ml_playlists/view_playlists.cpp | 1953 ++++ Src/Plugins/Library/ml_playlists/wa_subclass.cpp | 90 + Src/Plugins/Library/ml_plg/AddPlaylist.cpp | 139 + Src/Plugins/Library/ml_plg/AlbumID.cpp | 45 + Src/Plugins/Library/ml_plg/IDScanner.cpp | 698 ++ Src/Plugins/Library/ml_plg/IDScanner.h | 97 + .../Library/ml_plg/PlaylistGeneratorAPI.cpp | 44 + Src/Plugins/Library/ml_plg/PlaylistGeneratorAPI.h | 14 + Src/Plugins/Library/ml_plg/api__ml_plg.h | 50 + .../Library/ml_plg/api_playlist_generator.h | 36 + Src/Plugins/Library/ml_plg/atltransactionmanager.h | 697 ++ Src/Plugins/Library/ml_plg/generate.cpp | 766 ++ Src/Plugins/Library/ml_plg/gn_logo_88x83.bmp | Bin 0 -> 21966 bytes Src/Plugins/Library/ml_plg/impl_playlist.cpp | 328 + Src/Plugins/Library/ml_plg/impl_playlist.h | 65 + Src/Plugins/Library/ml_plg/main.h | 87 + Src/Plugins/Library/ml_plg/ml_plg.cpp | 719 ++ Src/Plugins/Library/ml_plg/ml_plg.rc | 312 + Src/Plugins/Library/ml_plg/ml_plg.sln | 30 + Src/Plugins/Library/ml_plg/ml_plg.vcxproj | 331 + Src/Plugins/Library/ml_plg/ml_plg.vcxproj.filters | 130 + Src/Plugins/Library/ml_plg/pass1.cpp | 101 + Src/Plugins/Library/ml_plg/pass2.cpp | 72 + Src/Plugins/Library/ml_plg/playlist.cpp | 635 ++ Src/Plugins/Library/ml_plg/playlist.h | 40 + Src/Plugins/Library/ml_plg/prefs.cpp | 596 ++ Src/Plugins/Library/ml_plg/resource.h | 120 + Src/Plugins/Library/ml_plg/util.cpp | 36 + Src/Plugins/Library/ml_plg/version.rc2 | 39 + Src/Plugins/Library/ml_plg/view.cpp | 368 + Src/Plugins/Library/ml_pmp/AlbumArtListView.cpp | 848 ++ Src/Plugins/Library/ml_pmp/AlbumArtListView.h | 44 + Src/Plugins/Library/ml_pmp/ArtistAlbumLists.cpp | 1173 +++ Src/Plugins/Library/ml_pmp/ArtistAlbumLists.h | 64 + Src/Plugins/Library/ml_pmp/DeviceCommands.cpp | 192 + Src/Plugins/Library/ml_pmp/DeviceCommands.h | 65 + Src/Plugins/Library/ml_pmp/DeviceView.cpp | 2714 ++++++ Src/Plugins/Library/ml_pmp/DeviceView.h | 257 + Src/Plugins/Library/ml_pmp/Filters.cpp | 829 ++ Src/Plugins/Library/ml_pmp/Filters.h | 58 + Src/Plugins/Library/ml_pmp/IconStore.cpp | 237 + Src/Plugins/Library/ml_pmp/IconStore.h | 39 + Src/Plugins/Library/ml_pmp/LinkedQueue.cpp | 125 + Src/Plugins/Library/ml_pmp/LinkedQueue.h | 41 + Src/Plugins/Library/ml_pmp/PmpDevice.cpp | 557 ++ Src/Plugins/Library/ml_pmp/PmpDevice.h | 1 + Src/Plugins/Library/ml_pmp/SkinnedListView.cpp | 856 ++ Src/Plugins/Library/ml_pmp/SkinnedListView.h | 86 + Src/Plugins/Library/ml_pmp/api__ml_pmp.h | 56 + Src/Plugins/Library/ml_pmp/autofill.cpp | 432 + Src/Plugins/Library/ml_pmp/banner.cpp | 216 + Src/Plugins/Library/ml_pmp/banner.h | 44 + Src/Plugins/Library/ml_pmp/config.cpp | 50 + Src/Plugins/Library/ml_pmp/config.h | 21 + Src/Plugins/Library/ml_pmp/editinfo.cpp | 790 ++ Src/Plugins/Library/ml_pmp/graphics.cpp | 316 + Src/Plugins/Library/ml_pmp/graphics.h | 71 + .../Library/ml_pmp/indirectplaybackserver.cpp | 313 + Src/Plugins/Library/ml_pmp/local_menu.cpp | 189 + Src/Plugins/Library/ml_pmp/local_menu.h | 15 + Src/Plugins/Library/ml_pmp/main.cpp | 1454 +++ Src/Plugins/Library/ml_pmp/main.h | 91 + Src/Plugins/Library/ml_pmp/metadata_utils.cpp | 516 + Src/Plugins/Library/ml_pmp/metadata_utils.h | 48 + Src/Plugins/Library/ml_pmp/ml_pmp.rc | 1228 +++ Src/Plugins/Library/ml_pmp/ml_pmp.sln | 141 + Src/Plugins/Library/ml_pmp/ml_pmp.vcxproj | 444 + Src/Plugins/Library/ml_pmp/ml_pmp.vcxproj.filters | 298 + Src/Plugins/Library/ml_pmp/mt19937ar.cpp | 3 + Src/Plugins/Library/ml_pmp/mt19937ar.h | 6 + Src/Plugins/Library/ml_pmp/pluginloader.cpp | 187 + Src/Plugins/Library/ml_pmp/pluginloader.h | 14 + Src/Plugins/Library/ml_pmp/pmp.h | 321 + Src/Plugins/Library/ml_pmp/prefs.cpp | 1541 +++ Src/Plugins/Library/ml_pmp/replaceVars.cpp | 213 + Src/Plugins/Library/ml_pmp/resource1.h | 475 + Src/Plugins/Library/ml_pmp/resources/albumArt.png | Bin 0 -> 192 bytes Src/Plugins/Library/ml_pmp/resources/columns.png | Bin 0 -> 133 bytes .../Library/ml_pmp/resources/deviceIcon.png | Bin 0 -> 222 bytes Src/Plugins/Library/ml_pmp/resources/notfound.png | Bin 0 -> 3645 bytes .../Library/ml_pmp/resources/playlistIcon.png | Bin 0 -> 194 bytes .../ml_pmp/resources/sync-command-small.png | Bin 0 -> 532 bytes .../ml_pmp/resources/transfer_queue_16x16.png | Bin 0 -> 215 bytes .../ml_pmp/resources/transfer_queue_16x16_1.png | Bin 0 -> 217 bytes .../ml_pmp/resources/transfer_queue_16x16_2.png | Bin 0 -> 211 bytes .../ml_pmp/resources/transfer_queue_16x16_3.png | Bin 0 -> 216 bytes Src/Plugins/Library/ml_pmp/resources/usb.png | Bin 0 -> 441 bytes Src/Plugins/Library/ml_pmp/resources/videoIcon.png | Bin 0 -> 197 bytes Src/Plugins/Library/ml_pmp/resources/viewMode.png | Bin 0 -> 146 bytes Src/Plugins/Library/ml_pmp/syncCloudDialog.cpp | 675 ++ Src/Plugins/Library/ml_pmp/syncCloudDialog.h | 17 + Src/Plugins/Library/ml_pmp/syncDialog.cpp | 443 + Src/Plugins/Library/ml_pmp/syncDialog.h | 20 + Src/Plugins/Library/ml_pmp/transcoder.h | 38 + Src/Plugins/Library/ml_pmp/transcoder_imp.cpp | 539 ++ Src/Plugins/Library/ml_pmp/transcoder_imp.h | 122 + Src/Plugins/Library/ml_pmp/transfer_thread.cpp | 503 + Src/Plugins/Library/ml_pmp/transfer_thread.h | 94 + Src/Plugins/Library/ml_pmp/version.rc2 | 39 + Src/Plugins/Library/ml_pmp/view_pmp_devices.cpp | 764 ++ Src/Plugins/Library/ml_pmp/view_pmp_media.cpp | 3205 +++++++ Src/Plugins/Library/ml_pmp/view_pmp_queue.cpp | 1181 +++ Src/Plugins/Library/ml_rg/Process.cpp | 53 + Src/Plugins/Library/ml_rg/Process.h | 25 + Src/Plugins/Library/ml_rg/Progress.cpp | 261 + Src/Plugins/Library/ml_rg/RGFactory.cpp | 67 + Src/Plugins/Library/ml_rg/RGFactory.h | 24 + Src/Plugins/Library/ml_rg/Results.cpp | 172 + Src/Plugins/Library/ml_rg/api__ml_rg.h | 24 + Src/Plugins/Library/ml_rg/config.cpp | 52 + Src/Plugins/Library/ml_rg/main.h | 146 + Src/Plugins/Library/ml_rg/metadata.cpp | 28 + Src/Plugins/Library/ml_rg/ml_rg.cpp | 409 + Src/Plugins/Library/ml_rg/ml_rg.rc | 152 + Src/Plugins/Library/ml_rg/ml_rg.sln | 57 + Src/Plugins/Library/ml_rg/ml_rg.vcxproj | 310 + Src/Plugins/Library/ml_rg/ml_rg.vcxproj.filters | 68 + Src/Plugins/Library/ml_rg/obj_replaygain.h | 62 + Src/Plugins/Library/ml_rg/replaygain.cpp | 370 + Src/Plugins/Library/ml_rg/resource.h | 40 + Src/Plugins/Library/ml_rg/version.rc2 | 39 + Src/Plugins/Library/ml_transcode/LinkedQueue.cpp | 139 + Src/Plugins/Library/ml_transcode/LinkedQueue.h | 41 + .../Library/ml_transcode/api__ml_transcode.h | 26 + Src/Plugins/Library/ml_transcode/main.cpp | 1100 +++ Src/Plugins/Library/ml_transcode/ml_transcode.rc | 162 + Src/Plugins/Library/ml_transcode/ml_transcode.sln | 30 + .../Library/ml_transcode/ml_transcode.vcxproj | 333 + .../ml_transcode/ml_transcode.vcxproj.filters | 65 + Src/Plugins/Library/ml_transcode/replaceVars.cpp | 317 + Src/Plugins/Library/ml_transcode/resource.h | 45 + Src/Plugins/Library/ml_transcode/reversesync.h | 9 + Src/Plugins/Library/ml_transcode/version.rc2 | 39 + Src/Plugins/Library/ml_webdev/commands.cpp | 328 + Src/Plugins/Library/ml_webdev/commands.h | 29 + Src/Plugins/Library/ml_webdev/common.cpp | 238 + Src/Plugins/Library/ml_webdev/common.h | 68 + Src/Plugins/Library/ml_webdev/config.cpp | 74 + Src/Plugins/Library/ml_webdev/config.h | 18 + Src/Plugins/Library/ml_webdev/external.cpp | 272 + Src/Plugins/Library/ml_webdev/external.h | 49 + Src/Plugins/Library/ml_webdev/forceUrl.cpp | 77 + Src/Plugins/Library/ml_webdev/forceUrl.h | 15 + Src/Plugins/Library/ml_webdev/import.h | 18 + Src/Plugins/Library/ml_webdev/importFile.cpp | 266 + Src/Plugins/Library/ml_webdev/importUrl.cpp | 320 + Src/Plugins/Library/ml_webdev/local_menu.cpp | 222 + Src/Plugins/Library/ml_webdev/local_menu.h | 17 + Src/Plugins/Library/ml_webdev/main.cpp | 167 + Src/Plugins/Library/ml_webdev/main.h | 25 + Src/Plugins/Library/ml_webdev/ml_webdev.rc | 182 + Src/Plugins/Library/ml_webdev/ml_webdev.sln | 30 + Src/Plugins/Library/ml_webdev/ml_webdev.vcxproj | 382 + .../Library/ml_webdev/ml_webdev.vcxproj.filters | 187 + Src/Plugins/Library/ml_webdev/navigation.cpp | 1261 +++ Src/Plugins/Library/ml_webdev/navigation.h | 83 + Src/Plugins/Library/ml_webdev/png.rc | 9 + Src/Plugins/Library/ml_webdev/resource.h | 60 + Src/Plugins/Library/ml_webdev/resources/gear.png | Bin 0 -> 265 bytes Src/Plugins/Library/ml_webdev/resources/help.png | Bin 0 -> 288 bytes .../Library/ml_webdev/resources/pages/api2.js | 624 ++ .../ml_webdev/resources/pages/applicationApi.htm | 32 + .../resources/pages/asyncDownloaderApi.htm | 42 + .../ml_webdev/resources/pages/bookmarkApi.htm | 17 + .../ml_webdev/resources/pages/configApi.htm | 28 + .../ml_webdev/resources/pages/historyApi.htm | 18 + .../ml_webdev/resources/pages/mediaCoreApi.htm | 37 + .../ml_webdev/resources/pages/playQueueApi.htm | 74 + .../ml_webdev/resources/pages/playlistApi.htm | 118 + .../ml_webdev/resources/pages/podcastApi.htm | 15 + .../ml_webdev/resources/pages/securityApi.htm | 17 + .../ml_webdev/resources/pages/serviceEditor.htm | 31 + .../Library/ml_webdev/resources/pages/skinApi.htm | 40 + .../ml_webdev/resources/pages/transportApi.htm | 64 + .../resources/pages/transportEventsApi.htm | 25 + .../Library/ml_webdev/resources/pages/webdev.htm | 29 + .../Library/ml_webdev/resources/pages/webdev.js | 97 + .../Library/ml_webdev/resources/pages/welcome.htm | 22 + Src/Plugins/Library/ml_webdev/serviceHelper.cpp | 232 + Src/Plugins/Library/ml_webdev/serviceHelper.h | 28 + Src/Plugins/Library/ml_webdev/serviceHost.cpp | 243 + Src/Plugins/Library/ml_webdev/serviceHost.h | 55 + Src/Plugins/Library/ml_webdev/testPages.rc | 22 + Src/Plugins/Library/ml_webdev/version.rc2 | 39 + Src/Plugins/Library/ml_webdev/wasabi.cpp | 87 + Src/Plugins/Library/ml_webdev/wasabi.h | 45 + Src/Plugins/Library/ml_wire/AtomParse.h | 51 + .../Library/ml_wire/BackgroundDownloader.cpp | 338 + Src/Plugins/Library/ml_wire/BackgroundDownloader.h | 18 + Src/Plugins/Library/ml_wire/ChannelCheck.h | 14 + Src/Plugins/Library/ml_wire/ChannelRefresher.cpp | 33 + Src/Plugins/Library/ml_wire/ChannelRefresher.h | 14 + Src/Plugins/Library/ml_wire/ChannelSync.h | 31 + Src/Plugins/Library/ml_wire/Cloud.cpp | 235 + Src/Plugins/Library/ml_wire/Cloud.h | 35 + Src/Plugins/Library/ml_wire/DESIGN.txt | 12 + Src/Plugins/Library/ml_wire/Defaults.cpp | 40 + Src/Plugins/Library/ml_wire/Defaults.h | 34 + Src/Plugins/Library/ml_wire/DownloadManager.cpp | 1 + Src/Plugins/Library/ml_wire/DownloadStatus.cpp | 153 + Src/Plugins/Library/ml_wire/DownloadStatus.h | 41 + Src/Plugins/Library/ml_wire/DownloadThread.cpp | 222 + Src/Plugins/Library/ml_wire/DownloadThread.h | 28 + Src/Plugins/Library/ml_wire/Downloaded.cpp | 121 + Src/Plugins/Library/ml_wire/Downloaded.h | 79 + Src/Plugins/Library/ml_wire/DownloadsDialog.cpp | 1453 +++ Src/Plugins/Library/ml_wire/DownloadsDialog.h | 13 + Src/Plugins/Library/ml_wire/DownloadsParse.cpp | 204 + Src/Plugins/Library/ml_wire/DownloadsParse.h | 13 + Src/Plugins/Library/ml_wire/ExternalCOM.cpp | 85 + Src/Plugins/Library/ml_wire/ExternalCOM.h | 40 + Src/Plugins/Library/ml_wire/Factory.cpp | 64 + Src/Plugins/Library/ml_wire/Factory.h | 22 + Src/Plugins/Library/ml_wire/FeedParse.cpp | 38 + Src/Plugins/Library/ml_wire/FeedParse.h | 24 + Src/Plugins/Library/ml_wire/FeedUtil.cpp | 37 + Src/Plugins/Library/ml_wire/FeedUtil.h | 8 + Src/Plugins/Library/ml_wire/Feeds.cpp | 298 + Src/Plugins/Library/ml_wire/Feeds.h | 50 + Src/Plugins/Library/ml_wire/FeedsDialog.h | 5 + Src/Plugins/Library/ml_wire/Item.cpp | 174 + Src/Plugins/Library/ml_wire/Item.h | 50 + Src/Plugins/Library/ml_wire/JSAPI2_Creator.cpp | 94 + Src/Plugins/Library/ml_wire/JSAPI2_Creator.h | 31 + Src/Plugins/Library/ml_wire/JSAPI2_PodcastsAPI.cpp | 109 + Src/Plugins/Library/ml_wire/JSAPI2_PodcastsAPI.h | 30 + Src/Plugins/Library/ml_wire/Loader.cpp | 100 + Src/Plugins/Library/ml_wire/Loader.h | 6 + Src/Plugins/Library/ml_wire/Main.cpp | 469 + Src/Plugins/Library/ml_wire/Main.h | 69 + Src/Plugins/Library/ml_wire/MessageProcessor.cpp | 7 + Src/Plugins/Library/ml_wire/MessageProcessor.h | 35 + Src/Plugins/Library/ml_wire/OPMLParse.cpp | 33 + Src/Plugins/Library/ml_wire/OPMLParse.h | 28 + Src/Plugins/Library/ml_wire/PCastFactory.cpp | 60 + Src/Plugins/Library/ml_wire/PCastFactory.h | 24 + Src/Plugins/Library/ml_wire/PCastURIHandler.cpp | 258 + Src/Plugins/Library/ml_wire/PCastURIHandler.h | 21 + Src/Plugins/Library/ml_wire/ParseUtil.cpp | 43 + Src/Plugins/Library/ml_wire/ParseUtil.h | 8 + Src/Plugins/Library/ml_wire/Preferences.cpp | 160 + Src/Plugins/Library/ml_wire/Preferences.h | 6 + Src/Plugins/Library/ml_wire/RFCDate.cpp | 216 + Src/Plugins/Library/ml_wire/RFCDate.h | 7 + Src/Plugins/Library/ml_wire/RSSCOM.cpp | 232 + Src/Plugins/Library/ml_wire/RSSCOM.h | 42 + Src/Plugins/Library/ml_wire/RSSParse.cpp | 181 + Src/Plugins/Library/ml_wire/RSSParse.h | 9 + Src/Plugins/Library/ml_wire/TODO.txt | 51 + Src/Plugins/Library/ml_wire/UpdateAutoDownload.cpp | 57 + Src/Plugins/Library/ml_wire/UpdateAutoDownload.h | 28 + Src/Plugins/Library/ml_wire/UpdateTime.cpp | 62 + Src/Plugins/Library/ml_wire/UpdateTime.h | 29 + Src/Plugins/Library/ml_wire/Util.h | 69 + Src/Plugins/Library/ml_wire/WantsDownloadStatus.h | 14 + Src/Plugins/Library/ml_wire/Wire.cpp | 99 + Src/Plugins/Library/ml_wire/Wire.h | 62 + Src/Plugins/Library/ml_wire/XMLWriter.cpp | 272 + Src/Plugins/Library/ml_wire/XMLWriter.h | 8 + Src/Plugins/Library/ml_wire/api__ml_wire.h | 27 + Src/Plugins/Library/ml_wire/api_podcasts.h | 46 + Src/Plugins/Library/ml_wire/channelEditor.cpp | 687 ++ Src/Plugins/Library/ml_wire/channelEditor.h | 15 + Src/Plugins/Library/ml_wire/date.c | 1 + Src/Plugins/Library/ml_wire/date.h | 20 + Src/Plugins/Library/ml_wire/db.cpp | 171 + Src/Plugins/Library/ml_wire/errors.h | 17 + Src/Plugins/Library/ml_wire/ifc_article.h | 15 + Src/Plugins/Library/ml_wire/ifc_podcast.h | 39 + Src/Plugins/Library/ml_wire/layout.cpp | 215 + Src/Plugins/Library/ml_wire/layout.h | 43 + Src/Plugins/Library/ml_wire/ml_podcast.rc | 396 + Src/Plugins/Library/ml_wire/ml_wire.sln | 87 + Src/Plugins/Library/ml_wire/ml_wire.vcxproj | 423 + .../Library/ml_wire/ml_wire.vcxproj.filters | 345 + Src/Plugins/Library/ml_wire/navigation.cpp | 420 + Src/Plugins/Library/ml_wire/navigation.h | 24 + Src/Plugins/Library/ml_wire/png.rc | 15 + Src/Plugins/Library/ml_wire/resource.h | 220 + .../Library/ml_wire/resources/discoverIcon.png | Bin 0 -> 223 bytes .../Library/ml_wire/resources/downloadIcon.png | Bin 0 -> 220 bytes .../Library/ml_wire/resources/mediaIcon.png | Bin 0 -> 168 bytes .../Library/ml_wire/resources/subscriptionIcon.png | Bin 0 -> 244 bytes Src/Plugins/Library/ml_wire/resources/textIcon.png | Bin 0 -> 152 bytes Src/Plugins/Library/ml_wire/service.cpp | 273 + Src/Plugins/Library/ml_wire/service.h | 69 + Src/Plugins/Library/ml_wire/subscriptionView.cpp | 2691 ++++++ Src/Plugins/Library/ml_wire/subscriptionView.h | 29 + Src/Plugins/Library/ml_wire/util.cpp | 271 + Src/Plugins/Library/ml_wire/version.rc2 | 39 + Src/Plugins/Output/out_disk/main.cpp | 1203 +++ Src/Plugins/Output/out_disk/out_disk.h | 18 + Src/Plugins/Output/out_disk/out_disk.rc | 184 + Src/Plugins/Output/out_disk/out_disk.sln | 30 + Src/Plugins/Output/out_disk/out_disk.vcxproj | 301 + .../Output/out_disk/out_disk.vcxproj.filters | 35 + Src/Plugins/Output/out_disk/resource.h | 80 + Src/Plugins/Output/out_disk/version.rc2 | 39 + Src/Plugins/Output/out_ds/Config.cpp | 50 + Src/Plugins/Output/out_ds/Config.h | 70 + Src/Plugins/Output/out_ds/DevEnum.cpp | 132 + Src/Plugins/Output/out_ds/DevEnum.h | 67 + Src/Plugins/Output/out_ds/SoundBlock.cpp | 70 + Src/Plugins/Output/out_ds/SoundBlock.h | 21 + Src/Plugins/Output/out_ds/SoundBlockList.cpp | 129 + Src/Plugins/Output/out_ds/SoundBlockList.h | 32 + Src/Plugins/Output/out_ds/VolCtrl.cpp | 196 + Src/Plugins/Output/out_ds/VolCtrl.h | 44 + Src/Plugins/Output/out_ds/api.h | 16 + Src/Plugins/Output/out_ds/cnv_ds2.cpp | 798 ++ Src/Plugins/Output/out_ds/cnv_ds2.h | 85 + Src/Plugins/Output/out_ds/ds2.cpp | 1372 +++ Src/Plugins/Output/out_ds/ds2.h | 237 + Src/Plugins/Output/out_ds/ds2_devenum.cpp | 32 + Src/Plugins/Output/out_ds/ds2_misc.cpp | 4 + Src/Plugins/Output/out_ds/ds2_volctrl.cpp | 1 + Src/Plugins/Output/out_ds/ds_ipc.h | 16 + Src/Plugins/Output/out_ds/ds_main.h | 14 + Src/Plugins/Output/out_ds/out_ds.h | 29 + Src/Plugins/Output/out_ds/out_ds.sln | 30 + Src/Plugins/Output/out_ds/out_ds.vcxproj | 304 + Src/Plugins/Output/out_ds/out_ds.vcxproj.filters | 89 + Src/Plugins/Output/out_ds/out_ds_joy.cpp | 101 + Src/Plugins/Output/out_ds/res_wa2/out_ds2.rc | 304 + Src/Plugins/Output/out_ds/res_wa2/resource.h | 146 + Src/Plugins/Output/out_ds/res_wa2/version.rc2 | 39 + Src/Plugins/Output/out_ds/wa2.cpp | 1005 ++ Src/Plugins/Output/out_ds/wa2_config.cpp | 1150 +++ Src/Plugins/Output/out_wasapi/api.h | 9 + Src/Plugins/Output/out_wasapi/main.cpp | 393 + Src/Plugins/Output/out_wasapi/out_wasapi.rc | 81 + Src/Plugins/Output/out_wasapi/out_wasapi.sln | 31 + Src/Plugins/Output/out_wasapi/out_wasapi.vcxproj | 250 + .../Output/out_wasapi/out_wasapi.vcxproj.filters | 32 + Src/Plugins/Output/out_wasapi/resource.h | 18 + Src/Plugins/Output/out_wasapi/version.rc2 | 39 + Src/Plugins/Output/out_wasapi/waveformat.cpp | 69 + Src/Plugins/Output/out_wave/api.h | 16 + Src/Plugins/Output/out_wave/cnv_pcmwaveout.cpp | 248 + Src/Plugins/Output/out_wave/cnv_pcmwaveout.h | 37 + Src/Plugins/Output/out_wave/out_wave.cpp | 576 ++ Src/Plugins/Output/out_wave/out_wave.h | 7 + Src/Plugins/Output/out_wave/out_wave.rc | 158 + Src/Plugins/Output/out_wave/out_wave.sln | 30 + Src/Plugins/Output/out_wave/out_wave.vcxproj | 284 + .../Output/out_wave/out_wave.vcxproj.filters | 44 + Src/Plugins/Output/out_wave/resource.h | 60 + Src/Plugins/Output/out_wave/version.rc2 | 39 + Src/Plugins/Output/out_wave/wa2_config.cpp | 267 + Src/Plugins/Output/out_wave/waveout.cpp | 617 ++ Src/Plugins/Output/out_wave/waveout.h | 125 + Src/Plugins/Portable/pmp_activesync/ASDevice.cpp | 798 ++ Src/Plugins/Portable/pmp_activesync/ASDevice.h | 172 + .../pmp_activesync/activesync/Inc/IRAPIStream.h | 286 + .../Portable/pmp_activesync/activesync/Inc/rapi.h | 118 + .../Portable/pmp_activesync/activesync/Inc/rapi2.h | 3090 ++++++ .../pmp_activesync/activesync/Inc/rapitypes.h | 445 + .../pmp_activesync/activesync/Inc/rapitypes2.h | 248 + .../pmp_activesync/activesync/Lib/rapiuuid.lib | Bin 0 -> 4964 bytes Src/Plugins/Portable/pmp_activesync/main.cpp | 220 + .../Portable/pmp_activesync/pmp_activesync.rc | 116 + .../Portable/pmp_activesync/pmp_activesync.sln | 18 + .../Portable/pmp_activesync/pmp_activesync.vcxproj | 357 + .../pmp_activesync/pmp_activesync.vcxproj.filters | 51 + Src/Plugins/Portable/pmp_activesync/resource.h | 31 + .../pmp_activesync/resources/activeSyncIcon.png | Bin 0 -> 246 bytes Src/Plugins/Portable/pmp_activesync/version.rc2 | 39 + Src/Plugins/Portable/pmp_android/albumart.cpp | 58 + Src/Plugins/Portable/pmp_android/androiddevice.cpp | 1869 ++++ Src/Plugins/Portable/pmp_android/androiddevice.h | 240 + .../Portable/pmp_android/androidplaylist.cpp | 134 + Src/Plugins/Portable/pmp_android/androidplaylist.h | 49 + .../Portable/pmp_android/androidplaylistsaver.cpp | 74 + .../Portable/pmp_android/androidplaylistsaver.h | 33 + Src/Plugins/Portable/pmp_android/api.cpp | 64 + Src/Plugins/Portable/pmp_android/api.h | 34 + .../Portable/pmp_android/deviceprovider.cpp | 343 + Src/Plugins/Portable/pmp_android/deviceprovider.h | 50 + Src/Plugins/Portable/pmp_android/eject.cpp | 138 + Src/Plugins/Portable/pmp_android/filecopy.cpp | 67 + Src/Plugins/Portable/pmp_android/main.cpp | 601 ++ Src/Plugins/Portable/pmp_android/pmp_android.rc | 209 + Src/Plugins/Portable/pmp_android/pmp_android.sln | 130 + .../Portable/pmp_android/pmp_android.vcxproj | 332 + .../pmp_android/pmp_android.vcxproj.filters | 91 + Src/Plugins/Portable/pmp_android/resource.h | 75 + .../Portable/pmp_android/resources/androidIcon.png | Bin 0 -> 197 bytes .../pmp_android/resources/generic_android.png | Bin 0 -> 5721 bytes Src/Plugins/Portable/pmp_android/utils.cpp | 334 + Src/Plugins/Portable/pmp_android/version.rc2 | 39 + Src/Plugins/Portable/pmp_ipod/SysInfoXML.cpp | 149 + Src/Plugins/Portable/pmp_ipod/api.h | 30 + Src/Plugins/Portable/pmp_ipod/deviceprovider.cpp | 343 + Src/Plugins/Portable/pmp_ipod/deviceprovider.h | 50 + Src/Plugins/Portable/pmp_ipod/eject.cpp | 357 + Src/Plugins/Portable/pmp_ipod/filecopy.cpp | 69 + Src/Plugins/Portable/pmp_ipod/hash58.cpp | 154 + Src/Plugins/Portable/pmp_ipod/hash58.h | 1 + Src/Plugins/Portable/pmp_ipod/iPodArtworkDB.cpp | 914 ++ Src/Plugins/Portable/pmp_ipod/iPodArtworkDB.h | 232 + Src/Plugins/Portable/pmp_ipod/iPodDB.cpp | 4807 ++++++++++ Src/Plugins/Portable/pmp_ipod/iPodDB.h | 1256 +++ Src/Plugins/Portable/pmp_ipod/iPodDevice.cpp | 1795 ++++ Src/Plugins/Portable/pmp_ipod/iPodDevice.h | 142 + Src/Plugins/Portable/pmp_ipod/iPodInfo.cpp | 1040 ++ Src/Plugins/Portable/pmp_ipod/iPodInfo.h | 97 + Src/Plugins/Portable/pmp_ipod/iPodSD.cpp | 523 ++ Src/Plugins/Portable/pmp_ipod/iPodSD.h | 78 + Src/Plugins/Portable/pmp_ipod/main.cpp | 336 + Src/Plugins/Portable/pmp_ipod/pmp_ipod.rc | 194 + Src/Plugins/Portable/pmp_ipod/pmp_ipod.sln | 75 + Src/Plugins/Portable/pmp_ipod/pmp_ipod.vcxproj | 355 + .../Portable/pmp_ipod/pmp_ipod.vcxproj.filters | 113 + Src/Plugins/Portable/pmp_ipod/resource.h | 71 + .../pmp_ipod/resources/apple_ipod_classic.png | Bin 0 -> 3464 bytes .../pmp_ipod/resources/apple_ipod_classic_16.png | Bin 0 -> 199 bytes .../pmp_ipod/resources/apple_ipod_nano_1g.png | Bin 0 -> 1991 bytes .../pmp_ipod/resources/apple_ipod_nano_1g_16.png | Bin 0 -> 192 bytes .../pmp_ipod/resources/apple_ipod_nano_2g.png | Bin 0 -> 1861 bytes .../pmp_ipod/resources/apple_ipod_nano_2g_16.png | Bin 0 -> 195 bytes .../pmp_ipod/resources/apple_ipod_nano_3g.png | Bin 0 -> 2128 bytes .../pmp_ipod/resources/apple_ipod_nano_3g_16.png | Bin 0 -> 186 bytes .../pmp_ipod/resources/apple_ipod_nano_4g.png | Bin 0 -> 2513 bytes .../pmp_ipod/resources/apple_ipod_nano_4g_16.png | Bin 0 -> 193 bytes .../pmp_ipod/resources/apple_ipod_nano_5g.png | Bin 0 -> 1939 bytes .../pmp_ipod/resources/apple_ipod_nano_5g_16.png | Bin 0 -> 187 bytes .../pmp_ipod/resources/apple_ipod_shuffle_1g.png | Bin 0 -> 1781 bytes .../resources/apple_ipod_shuffle_1g_16.png | Bin 0 -> 182 bytes .../pmp_ipod/resources/apple_ipod_shuffle_2g.png | Bin 0 -> 1117 bytes .../resources/apple_ipod_shuffle_2g_16.png | Bin 0 -> 230 bytes .../pmp_ipod/resources/apple_ipod_shuffle_3g.png | Bin 0 -> 1345 bytes .../resources/apple_ipod_shuffle_3g_16.png | Bin 0 -> 119 bytes .../pmp_ipod/resources/apple_ipod_shuffle_4g.png | Bin 0 -> 1964 bytes .../resources/apple_ipod_shuffle_4g_16.png | Bin 0 -> 233 bytes Src/Plugins/Portable/pmp_ipod/sha1.c | 240 + Src/Plugins/Portable/pmp_ipod/sha1.h | 9 + Src/Plugins/Portable/pmp_ipod/version.rc2 | 39 + Src/Plugins/Portable/pmp_ipod/yail.cpp | 114 + Src/Plugins/Portable/pmp_ipod/yail.h | 25 + Src/Plugins/Portable/pmp_njb/NJBDevice.cpp | 714 ++ Src/Plugins/Portable/pmp_njb/NJBDevice.h | 168 + .../Portable/pmp_njb/NOMAD DAP PC SDK v3_5.pdf | Bin 0 -> 1469440 bytes Src/Plugins/Portable/pmp_njb/Nmsdk.h | 922 ++ Src/Plugins/Portable/pmp_njb/ctnmjb2.dll | Bin 0 -> 126976 bytes Src/Plugins/Portable/pmp_njb/main.cpp | 181 + Src/Plugins/Portable/pmp_njb/pmp_njb.rc | 109 + Src/Plugins/Portable/pmp_njb/pmp_njb.sln | 30 + Src/Plugins/Portable/pmp_njb/pmp_njb.vcxproj | 315 + .../Portable/pmp_njb/pmp_njb.vcxproj.filters | 61 + Src/Plugins/Portable/pmp_njb/resource.h | 23 + Src/Plugins/Portable/pmp_njb/resources/zenIcon.png | Bin 0 -> 238 bytes Src/Plugins/Portable/pmp_njb/version.rc2 | 39 + Src/Plugins/Portable/pmp_p4s/Cert.zip | Bin 0 -> 6499 bytes Src/Plugins/Portable/pmp_p4s/MTP Error Codes.txt | 41 + Src/Plugins/Portable/pmp_p4s/MyProgress.cpp | 95 + Src/Plugins/Portable/pmp_p4s/MyProgress.h | 35 + Src/Plugins/Portable/pmp_p4s/P4SDevice.cpp | 2185 +++++ Src/Plugins/Portable/pmp_p4s/P4SDevice.h | 243 + Src/Plugins/Portable/pmp_p4s/WMDRMDeviceApp.h | 502 + Src/Plugins/Portable/pmp_p4s/WMDRMDeviceApp_i.c | 93 + Src/Plugins/Portable/pmp_p4s/deviceprovider.cpp | 285 + Src/Plugins/Portable/pmp_p4s/deviceprovider.h | 49 + Src/Plugins/Portable/pmp_p4s/key-sub-523.c | 531 ++ Src/Plugins/Portable/pmp_p4s/main.cpp | 571 ++ Src/Plugins/Portable/pmp_p4s/mssachlp.lib | Bin 0 -> 3800538 bytes Src/Plugins/Portable/pmp_p4s/mswmdm.h | 9913 ++++++++++++++++++++ Src/Plugins/Portable/pmp_p4s/mswmdm_i.c | 269 + Src/Plugins/Portable/pmp_p4s/pmp_p4s.rc | 91 + Src/Plugins/Portable/pmp_p4s/pmp_p4s.vcxproj | 434 + .../Portable/pmp_p4s/pmp_p4s.vcxproj.filters | 93 + Src/Plugins/Portable/pmp_p4s/resource1.h | 31 + .../Portable/pmp_p4s/resources/creativeZenIcon.png | Bin 0 -> 238 bytes .../Portable/pmp_p4s/resources/nokiaIcon.png | Bin 0 -> 226 bytes Src/Plugins/Portable/pmp_p4s/sac.h | 26 + Src/Plugins/Portable/pmp_p4s/scclient.h | 73 + Src/Plugins/Portable/pmp_p4s/sehupd.lib | Bin 0 -> 37298 bytes Src/Plugins/Portable/pmp_p4s/version.rc2 | 39 + Src/Plugins/Portable/pmp_p4s/wmdm.chm | Bin 0 -> 857488 bytes Src/Plugins/Portable/pmp_usb/albumart.cpp | 58 + Src/Plugins/Portable/pmp_usb/api.cpp | 64 + Src/Plugins/Portable/pmp_usb/api.h | 34 + Src/Plugins/Portable/pmp_usb/deviceprovider.cpp | 343 + Src/Plugins/Portable/pmp_usb/deviceprovider.h | 51 + Src/Plugins/Portable/pmp_usb/eject.cpp | 135 + Src/Plugins/Portable/pmp_usb/filecopy.cpp | 67 + Src/Plugins/Portable/pmp_usb/main.cpp | 547 ++ Src/Plugins/Portable/pmp_usb/pmp_usb2.rc | 209 + Src/Plugins/Portable/pmp_usb/pmp_usb2.sln | 129 + Src/Plugins/Portable/pmp_usb/pmp_usb2.vcxproj | 340 + .../Portable/pmp_usb/pmp_usb2.vcxproj.filters | 89 + Src/Plugins/Portable/pmp_usb/resource.h | 75 + Src/Plugins/Portable/pmp_usb/resources/pspIcon.png | Bin 0 -> 216 bytes Src/Plugins/Portable/pmp_usb/resources/usbIcon.png | Bin 0 -> 200 bytes Src/Plugins/Portable/pmp_usb/usbdevice.cpp | 1913 ++++ Src/Plugins/Portable/pmp_usb/usbdevice.h | 242 + Src/Plugins/Portable/pmp_usb/usbplaylist.cpp | 33 + Src/Plugins/Portable/pmp_usb/usbplaylist.h | 35 + Src/Plugins/Portable/pmp_usb/usbplaylistsaver.cpp | 74 + Src/Plugins/Portable/pmp_usb/usbplaylistsaver.h | 31 + Src/Plugins/Portable/pmp_usb/utils.cpp | 355 + Src/Plugins/Portable/pmp_usb/version.rc2 | 39 + Src/Plugins/Portable/pmp_wifi/ConnectActivity.cpp | 53 + Src/Plugins/Portable/pmp_wifi/ConnectActivity.h | 16 + Src/Plugins/Portable/pmp_wifi/InfoDownloader.cpp | 201 + Src/Plugins/Portable/pmp_wifi/InfoDownloader.h | 51 + Src/Plugins/Portable/pmp_wifi/ListenServer.cpp | 147 + Src/Plugins/Portable/pmp_wifi/Pair.cpp | 87 + Src/Plugins/Portable/pmp_wifi/Pair.h | 25 + Src/Plugins/Portable/pmp_wifi/PlaylistSync.cpp | 150 + Src/Plugins/Portable/pmp_wifi/PlaylistSync.h | 9 + Src/Plugins/Portable/pmp_wifi/RenameDownloader.cpp | 39 + Src/Plugins/Portable/pmp_wifi/RenameDownloader.h | 3 + Src/Plugins/Portable/pmp_wifi/SongDownloader.cpp | 88 + Src/Plugins/Portable/pmp_wifi/SongDownloader.h | 24 + .../Portable/pmp_wifi/SongListDownloader.cpp | 178 + Src/Plugins/Portable/pmp_wifi/SongListDownloader.h | 58 + Src/Plugins/Portable/pmp_wifi/WifiDevice.cpp | 269 + Src/Plugins/Portable/pmp_wifi/WifiDevice.h | 64 + Src/Plugins/Portable/pmp_wifi/WifiPlaylist.cpp | 104 + Src/Plugins/Portable/pmp_wifi/WifiPlaylist.h | 36 + Src/Plugins/Portable/pmp_wifi/XMLString.cpp | 49 + Src/Plugins/Portable/pmp_wifi/XMLString.h | 28 + Src/Plugins/Portable/pmp_wifi/api.cpp | 72 + Src/Plugins/Portable/pmp_wifi/api.h | 44 + Src/Plugins/Portable/pmp_wifi/device.cpp | 797 ++ Src/Plugins/Portable/pmp_wifi/device.h | 127 + Src/Plugins/Portable/pmp_wifi/images.cpp | 72 + Src/Plugins/Portable/pmp_wifi/images.h | 5 + Src/Plugins/Portable/pmp_wifi/main.cpp | 263 + Src/Plugins/Portable/pmp_wifi/main.h | 30 + Src/Plugins/Portable/pmp_wifi/modelInfo.cpp | 168 + Src/Plugins/Portable/pmp_wifi/modelInfo.h | 22 + Src/Plugins/Portable/pmp_wifi/pmp_wifi.rc | 126 + Src/Plugins/Portable/pmp_wifi/pmp_wifi.sln | 83 + Src/Plugins/Portable/pmp_wifi/pmp_wifi.vcxproj | 341 + .../Portable/pmp_wifi/pmp_wifi.vcxproj.filters | 214 + Src/Plugins/Portable/pmp_wifi/post.cpp | 518 + Src/Plugins/Portable/pmp_wifi/resource.h | 65 + Src/Plugins/Portable/pmp_wifi/resources/attach.png | Bin 0 -> 788 bytes .../Portable/pmp_wifi/resources/attach16.png | Bin 0 -> 239 bytes .../pmp_wifi/resources/generic_android.png | Bin 0 -> 5721 bytes .../pmp_wifi/resources/generic_drive_wifi_16.png | Bin 0 -> 242 bytes .../resources/htc_desire_passion_bravo.png | Bin 0 -> 4177 bytes .../resources/htc_desire_passion_bravo_16.png | Bin 0 -> 162 bytes .../Portable/pmp_wifi/resources/htc_evo_4g.png | Bin 0 -> 2305 bytes .../Portable/pmp_wifi/resources/htc_evo_4g_16.png | Bin 0 -> 159 bytes .../Portable/pmp_wifi/resources/htc_hd2.png | Bin 0 -> 1861 bytes .../Portable/pmp_wifi/resources/htc_hd2_16.png | Bin 0 -> 162 bytes .../Portable/pmp_wifi/resources/htc_incredible.png | Bin 0 -> 2484 bytes .../pmp_wifi/resources/htc_incredible_16.png | Bin 0 -> 182 bytes .../pmp_wifi/resources/htc_nexus_one_16.png | Bin 0 -> 164 bytes .../Portable/pmp_wifi/resources/motorola_droid.png | Bin 0 -> 2662 bytes .../pmp_wifi/resources/motorola_droid_16.png | Bin 0 -> 156 bytes .../pmp_wifi/resources/motorola_droid_x.png | Bin 0 -> 2319 bytes .../pmp_wifi/resources/motorola_droid_x_16.png | Bin 0 -> 169 bytes .../Portable/pmp_wifi/resources/nexus_one.png | Bin 0 -> 4053 bytes Src/Plugins/Portable/pmp_wifi/resources/wifi.png | Bin 0 -> 599 bytes Src/Plugins/Portable/pmp_wifi/version.rc2 | 39 + Src/Plugins/SDK/burner/BurnerCommon.cpp | 25 + Src/Plugins/SDK/burner/BurnerCommon.h | 14 + Src/Plugins/SDK/burner/DataBurner.cpp | 148 + Src/Plugins/SDK/burner/DataBurner.h | 20 + Src/Plugins/SDK/burner/ISOBurner.cpp | 67 + Src/Plugins/SDK/burner/ISOBurner.h | 15 + Src/Plugins/SDK/burner/ISOCreator.cpp | 139 + Src/Plugins/SDK/burner/api.h | 5 + Src/Plugins/SDK/burner/burner.rc | 114 + Src/Plugins/SDK/burner/burner.vcxproj | 270 + Src/Plugins/SDK/burner/burner.vcxproj.filters | 100 + Src/Plugins/SDK/burner/factory_databurner.cpp | 71 + Src/Plugins/SDK/burner/factory_databurner.h | 23 + Src/Plugins/SDK/burner/factory_isoburner.cpp | 71 + Src/Plugins/SDK/burner/factory_isoburner.h | 23 + Src/Plugins/SDK/burner/factory_isocreator.cpp | 72 + Src/Plugins/SDK/burner/factory_isocreator.h | 23 + Src/Plugins/SDK/burner/ifc_burner_writecallback.h | 33 + Src/Plugins/SDK/burner/isocreator.h | 20 + Src/Plugins/SDK/burner/main.cpp | 55 + Src/Plugins/SDK/burner/obj_databurner.h | 60 + Src/Plugins/SDK/burner/obj_isoburner.h | 46 + Src/Plugins/SDK/burner/obj_isocreator.h | 70 + Src/Plugins/SDK/burner/resource.h | 14 + Src/Plugins/SDK/coverdirectory/CoverDirectory.cpp | 314 + Src/Plugins/SDK/coverdirectory/CoverDirectory.h | 36 + Src/Plugins/SDK/coverdirectory/api.h | 23 + .../SDK/coverdirectory/cover_directory.vcxproj | 235 + .../coverdirectory/cover_directory.vcxproj.filters | 41 + Src/Plugins/SDK/coverdirectory/coverdirectory.rc | 76 + Src/Plugins/SDK/coverdirectory/coverdirectory.sln | 24 + Src/Plugins/SDK/coverdirectory/main.cpp | 83 + Src/Plugins/SDK/coverdirectory/resource.h | 14 + Src/Plugins/SDK/coverdirectory/version.rc2 | 39 + Src/Plugins/SDK/dsp_test/RESOURCE.H | 18 + Src/Plugins/SDK/dsp_test/SCRIPT1.RC | 97 + Src/Plugins/SDK/dsp_test/dsp_ns.txt | 23 + Src/Plugins/SDK/dsp_test/dsp_test.c | 478 + Src/Plugins/SDK/dsp_test/dsp_test.sln | 24 + Src/Plugins/SDK/dsp_test/dsp_test.vcxproj | 227 + Src/Plugins/SDK/dsp_test/dsp_test.vcxproj.filters | 32 + Src/Plugins/SDK/gen_classicart/api.h | 24 + Src/Plugins/SDK/gen_classicart/build_lng.cmd | 3 + Src/Plugins/SDK/gen_classicart/gen_classicart.cpp | 935 ++ Src/Plugins/SDK/gen_classicart/gen_classicart.nsi | 125 + Src/Plugins/SDK/gen_classicart/gen_classicart.rc | 156 + Src/Plugins/SDK/gen_classicart/gen_classicart.sln | 30 + .../SDK/gen_classicart/gen_classicart.vcxproj | 449 + .../gen_classicart/gen_classicart.vcxproj.filters | 42 + Src/Plugins/SDK/gen_classicart/lng_generator.exe | Bin 0 -> 67072 bytes Src/Plugins/SDK/gen_classicart/notfound.png | Bin 0 -> 3792 bytes Src/Plugins/SDK/gen_classicart/resource.h | 33 + Src/Plugins/SDK/in_tone/IN_TONE.sln | 24 + Src/Plugins/SDK/in_tone/IN_TONE.vcxproj | 319 + Src/Plugins/SDK/in_tone/IN_TONE.vcxproj.filters | 29 + Src/Plugins/SDK/in_tone/MAIN.C | 240 + Src/Plugins/SDK/in_tone/in_tone.rc | 109 + Src/Plugins/SDK/in_tone/resource.h | 18 + Src/Plugins/SDK/in_tone/version.rc2 | 39 + Src/Plugins/SDK/irctell/api__irctell.h | 7 + Src/Plugins/SDK/irctell/dde.cpp | 50 + Src/Plugins/SDK/irctell/dde.h | 9 + Src/Plugins/SDK/irctell/irctell.cpp | 122 + Src/Plugins/SDK/irctell/irctell.h | 29 + Src/Plugins/SDK/irctell/irctell.rc | 74 + Src/Plugins/SDK/irctell/irctell.sln | 24 + Src/Plugins/SDK/irctell/irctell.vcxproj | 243 + Src/Plugins/SDK/irctell/irctell.vcxproj.filters | 44 + Src/Plugins/SDK/irctell/resource.h | 17 + Src/Plugins/SDK/irctell/version.rc2 | 39 + Src/Plugins/SDK/ml_iso/ToISO.cpp | 366 + Src/Plugins/SDK/ml_iso/api__ml_iso.h | 10 + Src/Plugins/SDK/ml_iso/main.cpp | 150 + Src/Plugins/SDK/ml_iso/main.h | 12 + Src/Plugins/SDK/ml_iso/ml_iso.rc | 122 + Src/Plugins/SDK/ml_iso/ml_iso.sln | 24 + Src/Plugins/SDK/ml_iso/ml_iso.vcxproj | 246 + Src/Plugins/SDK/ml_iso/ml_iso.vcxproj.filters | 41 + Src/Plugins/SDK/ml_iso/resource.h | 26 + Src/Plugins/SDK/ml_iso/version.rc2 | 39 + Src/Plugins/SDK/ml_xmlex/api.h | 38 + Src/Plugins/SDK/ml_xmlex/main.cpp | 69 + Src/Plugins/SDK/ml_xmlex/main.h | 13 + Src/Plugins/SDK/ml_xmlex/ml_xmlex.rc | 108 + Src/Plugins/SDK/ml_xmlex/ml_xmlex.sln | 24 + Src/Plugins/SDK/ml_xmlex/ml_xmlex.vcxproj | 241 + Src/Plugins/SDK/ml_xmlex/ml_xmlex.vcxproj.filters | 41 + Src/Plugins/SDK/ml_xmlex/readme.txt | 13 + Src/Plugins/SDK/ml_xmlex/resource.h | 22 + Src/Plugins/SDK/ml_xmlex/version.rc2 | 39 + Src/Plugins/SDK/ml_xmlex/xmltest.xml | 8 + Src/Plugins/SDK/ml_xmlex/xmltest2.xml | 7 + Src/Plugins/SDK/ml_xmlex/xmlview.cpp | 301 + Src/Plugins/SDK/out_null/Out_null.dsp | 108 + Src/Plugins/SDK/out_null/Out_null.dsw | 29 + Src/Plugins/SDK/out_null/Out_null.sln | 25 + Src/Plugins/SDK/out_null/Out_null.vcxproj | 296 + Src/Plugins/SDK/out_null/Out_null.vcxproj.filters | 32 + Src/Plugins/SDK/out_null/main.c | 159 + Src/Plugins/SDK/out_null/out_null.rc | 109 + Src/Plugins/SDK/out_null/resource.h | 18 + Src/Plugins/SDK/out_null/version.rc2 | 39 + Src/Plugins/SDK/plLoadEx/ExComponent.cpp | 32 + Src/Plugins/SDK/plLoadEx/ExComponent.h | 16 + Src/Plugins/SDK/plLoadEx/SimpleHandler.cpp | 45 + Src/Plugins/SDK/plLoadEx/SimpleHandler.h | 21 + Src/Plugins/SDK/plLoadEx/SimpleHandlerFactory.cpp | 56 + Src/Plugins/SDK/plLoadEx/SimpleHandlerFactory.h | 20 + Src/Plugins/SDK/plLoadEx/SimpleLoader.cpp | 26 + Src/Plugins/SDK/plLoadEx/SimpleLoader.h | 14 + Src/Plugins/SDK/plLoadEx/example.simple | Bin 0 -> 58 bytes Src/Plugins/SDK/plLoadEx/plLoadEx.rc | 74 + Src/Plugins/SDK/plLoadEx/plLoadEx.sln | 24 + Src/Plugins/SDK/plLoadEx/plLoadEx.vcxproj | 229 + Src/Plugins/SDK/plLoadEx/plLoadEx.vcxproj.filters | 56 + Src/Plugins/SDK/plLoadEx/resource.h | 17 + Src/Plugins/SDK/plLoadEx/version.rc2 | 39 + Src/Plugins/SDK/plLoadEx/w5s.cpp | 9 + Src/Plugins/Visualization/vis_avs/FRONTEND.H | 370 + Src/Plugins/Visualization/vis_avs/LICENSE.TXT | 24 + Src/Plugins/Visualization/vis_avs/TIMING.C | 69 + Src/Plugins/Visualization/vis_avs/Timing.h | 32 + Src/Plugins/Visualization/vis_avs/VIS.H | 76 + Src/Plugins/Visualization/vis_avs/ape.h | 96 + Src/Plugins/Visualization/vis_avs/apesdk/ape.rc | 80 + Src/Plugins/Visualization/vis_avs/apesdk/avs_ape.h | 110 + .../Visualization/vis_avs/apesdk/avstut00.avs | Bin 0 -> 485 bytes .../Visualization/vis_avs/apesdk/avstut00.cpp | 294 + .../Visualization/vis_avs/apesdk/avstut00.dsp | 120 + .../Visualization/vis_avs/apesdk/avstut00.dsw | 29 + .../Visualization/vis_avs/apesdk/resource.h | 19 + Src/Plugins/Visualization/vis_avs/avs-hilited.png | Bin 0 -> 1450 bytes Src/Plugins/Visualization/vis_avs/avs-normal.png | Bin 0 -> 1449 bytes Src/Plugins/Visualization/vis_avs/avs-selected.png | Bin 0 -> 1468 bytes Src/Plugins/Visualization/vis_avs/avs_eelif.cpp | 235 + Src/Plugins/Visualization/vis_avs/avs_eelif.h | 59 + Src/Plugins/Visualization/vis_avs/bpm.cpp | 711 ++ Src/Plugins/Visualization/vis_avs/bpm.h | 56 + Src/Plugins/Visualization/vis_avs/bump_lig.bin | 22 + Src/Plugins/Visualization/vis_avs/cfgwin.cpp | 1918 ++++ Src/Plugins/Visualization/vis_avs/cfgwnd.h | 47 + Src/Plugins/Visualization/vis_avs/color_mo.bin | 11 + Src/Plugins/Visualization/vis_avs/draw.cpp | 1476 +++ Src/Plugins/Visualization/vis_avs/draw.h | 42 + Src/Plugins/Visualization/vis_avs/dyn_dist.bin | 12 + Src/Plugins/Visualization/vis_avs/dyn_move.bin | 1 + Src/Plugins/Visualization/vis_avs/dyn_shift.bin | 7 + Src/Plugins/Visualization/vis_avs/effect_l.bin | 6 + .../Visualization/vis_avs/evallib/BISON.EXE | Bin 0 -> 91787 bytes .../Visualization/vis_avs/evallib/CAL_TAB.C | 576 ++ .../Visualization/vis_avs/evallib/Compiler.c | 747 ++ .../Visualization/vis_avs/evallib/Compiler.h | 47 + Src/Plugins/Visualization/vis_avs/evallib/GETTOK.C | 6 + Src/Plugins/Visualization/vis_avs/evallib/LEX.EXE | Bin 0 -> 28277 bytes Src/Plugins/Visualization/vis_avs/evallib/LEX.H | 53 + Src/Plugins/Visualization/vis_avs/evallib/LEXGET.C | 20 + Src/Plugins/Visualization/vis_avs/evallib/LEXSWI.C | 23 + Src/Plugins/Visualization/vis_avs/evallib/LEXTAB.C | 260 + Src/Plugins/Visualization/vis_avs/evallib/LLSAVE.C | 6 + Src/Plugins/Visualization/vis_avs/evallib/LMOVB.C | 29 + Src/Plugins/Visualization/vis_avs/evallib/Scan.l | 54 + Src/Plugins/Visualization/vis_avs/evallib/YYLEX.C | 143 + .../Visualization/vis_avs/evallib/bison/BISON.HAI | 334 + .../Visualization/vis_avs/evallib/bison/BISON.SIM | 577 ++ Src/Plugins/Visualization/vis_avs/evallib/cal.y | 155 + .../Visualization/vis_avs/evallib/cal_tab.h | 13 + Src/Plugins/Visualization/vis_avs/evallib/cfunc.c | 901 ++ Src/Plugins/Visualization/vis_avs/evallib/eval.c | 238 + Src/Plugins/Visualization/vis_avs/evallib/eval.h | 41 + .../Visualization/vis_avs/evallib/makel.bat | 1 + .../Visualization/vis_avs/evallib/makey.bat | 2 + .../vis_avs/evallib/new_eval_stuff.zip | Bin 0 -> 23625 bytes .../Visualization/vis_avs/evallib/readme.txt | 84 + Src/Plugins/Visualization/vis_avs/ff_ipc.h | 104 + Src/Plugins/Visualization/vis_avs/help_1.bin | 32 + Src/Plugins/Visualization/vis_avs/help_2.bin | 32 + Src/Plugins/Visualization/vis_avs/help_3.bin | 133 + Src/Plugins/Visualization/vis_avs/help_4.bin | 6 + Src/Plugins/Visualization/vis_avs/laser/LD32.H | 964 ++ Src/Plugins/Visualization/vis_avs/laser/laser.cpp | 149 + .../Visualization/vis_avs/laser/laserline.cpp | 218 + .../Visualization/vis_avs/laser/laserline.h | 55 + Src/Plugins/Visualization/vis_avs/laser/ld32.c | 187 + .../Visualization/vis_avs/laser/linelist.cpp | 107 + Src/Plugins/Visualization/vis_avs/laser/linelist.h | 57 + .../Visualization/vis_avs/laser/rl_beathold.cpp | 165 + .../Visualization/vis_avs/laser/rl_bren.cpp | 183 + .../Visualization/vis_avs/laser/rl_cones.cpp | 363 + .../Visualization/vis_avs/laser/rl_line.cpp | 338 + .../Visualization/vis_avs/laser/rl_trans.cpp | 338 + Src/Plugins/Visualization/vis_avs/linedraw.cpp | 261 + Src/Plugins/Visualization/vis_avs/main.cpp | 604 ++ Src/Plugins/Visualization/vis_avs/matrix.cpp | 74 + Src/Plugins/Visualization/vis_avs/movement.bin | 5 + Src/Plugins/Visualization/vis_avs/peices.bmp | Bin 0 -> 19844 bytes Src/Plugins/Visualization/vis_avs/r_avi.cpp | 383 + Src/Plugins/Visualization/vis_avs/r_blit.cpp | 557 ++ Src/Plugins/Visualization/vis_avs/r_blur.cpp | 883 ++ Src/Plugins/Visualization/vis_avs/r_bpm.cpp | 285 + Src/Plugins/Visualization/vis_avs/r_bright.cpp | 506 + Src/Plugins/Visualization/vis_avs/r_bspin.cpp | 315 + Src/Plugins/Visualization/vis_avs/r_bump.cpp | 562 ++ Src/Plugins/Visualization/vis_avs/r_chanshift.cpp | 362 + Src/Plugins/Visualization/vis_avs/r_clear.cpp | 231 + Src/Plugins/Visualization/vis_avs/r_colorfade.cpp | 357 + .../Visualization/vis_avs/r_colorreduction.cpp | 193 + .../Visualization/vis_avs/r_colorreplace.cpp | 159 + Src/Plugins/Visualization/vis_avs/r_comment.cpp | 116 + Src/Plugins/Visualization/vis_avs/r_contrast.cpp | 226 + Src/Plugins/Visualization/vis_avs/r_dcolormod.cpp | 359 + Src/Plugins/Visualization/vis_avs/r_ddm.cpp | 492 + Src/Plugins/Visualization/vis_avs/r_defs.h | 739 ++ Src/Plugins/Visualization/vis_avs/r_dmove.cpp | 780 ++ Src/Plugins/Visualization/vis_avs/r_dotfnt.cpp | 324 + Src/Plugins/Visualization/vis_avs/r_dotgrid.cpp | 331 + Src/Plugins/Visualization/vis_avs/r_dotpln.cpp | 313 + Src/Plugins/Visualization/vis_avs/r_fadeout.cpp | 280 + Src/Plugins/Visualization/vis_avs/r_fastbright.cpp | 279 + Src/Plugins/Visualization/vis_avs/r_grain.cpp | 406 + Src/Plugins/Visualization/vis_avs/r_interf.cpp | 525 ++ Src/Plugins/Visualization/vis_avs/r_interleave.cpp | 381 + Src/Plugins/Visualization/vis_avs/r_invert.cpp | 186 + Src/Plugins/Visualization/vis_avs/r_linemode.cpp | 190 + Src/Plugins/Visualization/vis_avs/r_list.cpp | 1401 +++ Src/Plugins/Visualization/vis_avs/r_list.h | 173 + Src/Plugins/Visualization/vis_avs/r_mirror.cpp | 385 + Src/Plugins/Visualization/vis_avs/r_mosaic.cpp | 299 + Src/Plugins/Visualization/vis_avs/r_multidelay.cpp | 440 + Src/Plugins/Visualization/vis_avs/r_multiplier.cpp | 365 + Src/Plugins/Visualization/vis_avs/r_nfclr.cpp | 233 + Src/Plugins/Visualization/vis_avs/r_onetone.cpp | 269 + Src/Plugins/Visualization/vis_avs/r_oscring.cpp | 356 + Src/Plugins/Visualization/vis_avs/r_oscstar.cpp | 365 + Src/Plugins/Visualization/vis_avs/r_parts.cpp | 311 + Src/Plugins/Visualization/vis_avs/r_picture.cpp | 372 + Src/Plugins/Visualization/vis_avs/r_rotblit.cpp | 327 + Src/Plugins/Visualization/vis_avs/r_rotstar.cpp | 271 + Src/Plugins/Visualization/vis_avs/r_scat.cpp | 161 + Src/Plugins/Visualization/vis_avs/r_shift.cpp | 391 + Src/Plugins/Visualization/vis_avs/r_simple.cpp | 427 + Src/Plugins/Visualization/vis_avs/r_sscope.cpp | 595 ++ Src/Plugins/Visualization/vis_avs/r_stack.cpp | 338 + Src/Plugins/Visualization/vis_avs/r_stack.h | 29 + Src/Plugins/Visualization/vis_avs/r_stars.cpp | 402 + Src/Plugins/Visualization/vis_avs/r_svp.cpp | 227 + Src/Plugins/Visualization/vis_avs/r_text.cpp | 1012 ++ Src/Plugins/Visualization/vis_avs/r_timescope.cpp | 289 + Src/Plugins/Visualization/vis_avs/r_trans.cpp | 982 ++ Src/Plugins/Visualization/vis_avs/r_transition.cpp | 632 ++ Src/Plugins/Visualization/vis_avs/r_transition.h | 63 + Src/Plugins/Visualization/vis_avs/r_unkn.cpp | 114 + Src/Plugins/Visualization/vis_avs/r_unkn.h | 55 + Src/Plugins/Visualization/vis_avs/r_videodelay.cpp | 365 + Src/Plugins/Visualization/vis_avs/r_water.cpp | 502 + Src/Plugins/Visualization/vis_avs/r_waterbump.cpp | 439 + Src/Plugins/Visualization/vis_avs/reaplay_avs.dsp | 641 ++ Src/Plugins/Visualization/vis_avs/render.cpp | 141 + Src/Plugins/Visualization/vis_avs/render.h | 43 + Src/Plugins/Visualization/vis_avs/res.rc | 2329 +++++ Src/Plugins/Visualization/vis_avs/resource.h | 665 ++ Src/Plugins/Visualization/vis_avs/rlib.cpp | 458 + Src/Plugins/Visualization/vis_avs/rlib.h | 74 + Src/Plugins/Visualization/vis_avs/supersco.bin | 18 + Src/Plugins/Visualization/vis_avs/svp_vis.h | 136 + Src/Plugins/Visualization/vis_avs/undo.cpp | 177 + Src/Plugins/Visualization/vis_avs/undo.h | 81 + Src/Plugins/Visualization/vis_avs/util.cpp | 536 ++ Src/Plugins/Visualization/vis_avs/version.rc2 | 40 + Src/Plugins/Visualization/vis_avs/vis_Avs.dsw | 29 + Src/Plugins/Visualization/vis_avs/vis_avs.dsp | 722 ++ Src/Plugins/Visualization/vis_avs/vis_avs.sln | 24 + Src/Plugins/Visualization/vis_avs/vis_avs.txt | 228 + Src/Plugins/Visualization/vis_avs/vis_avs.vcxproj | 814 ++ .../Visualization/vis_avs/vis_avs.vcxproj.filters | 397 + Src/Plugins/Visualization/vis_avs/wa_ipc.h | 1127 +++ Src/Plugins/Visualization/vis_avs/whatsnew.txt | 233 + Src/Plugins/Visualization/vis_avs/wnd.cpp | 1749 ++++ Src/Plugins/Visualization/vis_avs/wnd.h | 39 + .../Visualization/vis_milk2/DOCUMENTATION.TXT | 1022 ++ .../Visualization/vis_milk2/api__vis_milk2.h | 16 + Src/Plugins/Visualization/vis_milk2/config.cpp | 1589 ++++ Src/Plugins/Visualization/vis_milk2/config2.cpp | 425 + Src/Plugins/Visualization/vis_milk2/defines.h | 223 + .../Visualization/vis_milk2/desktop_mode.cpp | 1097 +++ Src/Plugins/Visualization/vis_milk2/dxcontext.cpp | 1419 +++ Src/Plugins/Visualization/vis_milk2/dxcontext.h | 151 + Src/Plugins/Visualization/vis_milk2/fft.cpp | 320 + Src/Plugins/Visualization/vis_milk2/fft.h | 60 + Src/Plugins/Visualization/vis_milk2/gstring.h | 229 + Src/Plugins/Visualization/vis_milk2/icon_t.h | 49 + Src/Plugins/Visualization/vis_milk2/manifest.xml | 1 + Src/Plugins/Visualization/vis_milk2/md_defines.h | 83 + Src/Plugins/Visualization/vis_milk2/menu.cpp | 751 ++ Src/Plugins/Visualization/vis_milk2/menu.h | 126 + Src/Plugins/Visualization/vis_milk2/milkdrop.nsi | 439 + .../Visualization/vis_milk2/milkdrop_DX9.dsw | 29 + .../Visualization/vis_milk2/milkdrop_DX9.sln | 24 + Src/Plugins/Visualization/vis_milk2/milkdropfs.cpp | 4801 ++++++++++ .../vis_milk2/ns-eel2/asm-nseel-ppc-gcc.c | 1041 ++ .../vis_milk2/ns-eel2/asm-nseel-x64-macho.o | Bin 0 -> 7505 bytes .../vis_milk2/ns-eel2/asm-nseel-x86-gcc.c | 1566 ++++ .../vis_milk2/ns-eel2/asm-nseel-x86-msvc.c | 2463 +++++ .../vis_milk2/ns-eel2/ns-eel-addfuncs.h | 74 + .../Visualization/vis_milk2/ns-eel2/ns-eel-int.h | 227 + .../Visualization/vis_milk2/ns-eel2/ns-eel.h | 155 + .../Visualization/vis_milk2/ns-eel2/nseel-caltab.c | 553 ++ .../Visualization/vis_milk2/ns-eel2/nseel-cfunc.c | 131 + .../vis_milk2/ns-eel2/nseel-compiler.c | 1791 ++++ .../Visualization/vis_milk2/ns-eel2/nseel-eval.c | 321 + .../Visualization/vis_milk2/ns-eel2/nseel-lextab.c | 277 + .../Visualization/vis_milk2/ns-eel2/nseel-ram.c | 320 + .../Visualization/vis_milk2/ns-eel2/nseel-yylex.c | 163 + Src/Plugins/Visualization/vis_milk2/plugin.cpp | 9653 +++++++++++++++++++ Src/Plugins/Visualization/vis_milk2/plugin.dsp | 304 + Src/Plugins/Visualization/vis_milk2/plugin.h | 729 ++ Src/Plugins/Visualization/vis_milk2/plugin.rc | 1501 +++ Src/Plugins/Visualization/vis_milk2/plugin.vcxproj | 419 + .../Visualization/vis_milk2/plugin.vcxproj.filters | 164 + .../Visualization/vis_milk2/plugin_icon.ico | Bin 0 -> 7398 bytes .../Visualization/vis_milk2/pluginshell.cpp | 3693 ++++++++ Src/Plugins/Visualization/vis_milk2/pluginshell.h | 371 + Src/Plugins/Visualization/vis_milk2/resource.h | 877 ++ .../Visualization/vis_milk2/shell_defines.h | 76 + Src/Plugins/Visualization/vis_milk2/state.cpp | 1961 ++++ Src/Plugins/Visualization/vis_milk2/state.h | 448 + Src/Plugins/Visualization/vis_milk2/support.cpp | 361 + Src/Plugins/Visualization/vis_milk2/support.h | 109 + Src/Plugins/Visualization/vis_milk2/temp.ico | Bin 0 -> 7398 bytes Src/Plugins/Visualization/vis_milk2/texmgr.cpp | 752 ++ Src/Plugins/Visualization/vis_milk2/texmgr.h | 120 + Src/Plugins/Visualization/vis_milk2/text1.bin | Bin 0 -> 948 bytes Src/Plugins/Visualization/vis_milk2/textmgr.cpp | 700 ++ Src/Plugins/Visualization/vis_milk2/textmgr.h | 91 + Src/Plugins/Visualization/vis_milk2/utility.cpp | 1259 +++ Src/Plugins/Visualization/vis_milk2/utility.h | 164 + Src/Plugins/Visualization/vis_milk2/version.rc2 | 39 + Src/Plugins/Visualization/vis_milk2/vis.cpp | 275 + Src/Plugins/Visualization/vis_milk2/vis.h | 1 + .../Visualization/vis_milk2/vms_desktop.lib | Bin 0 -> 2520 bytes Src/Plugins/Visualization/vis_nsfs/Svis.cpp | 892 ++ Src/Plugins/Visualization/vis_nsfs/dd.h | 28 + Src/Plugins/Visualization/vis_nsfs/ddraw.cpp | 229 + Src/Plugins/Visualization/vis_nsfs/license.txt | 9 + Src/Plugins/Visualization/vis_nsfs/linedraw.cpp | 86 + Src/Plugins/Visualization/vis_nsfs/makepal.cpp | 96 + Src/Plugins/Visualization/vis_nsfs/moveframe.cpp | 609 ++ Src/Plugins/Visualization/vis_nsfs/resource.h | 147 + Src/Plugins/Visualization/vis_nsfs/scope.cpp | 200 + Src/Plugins/Visualization/vis_nsfs/shitdrop.cpp | 318 + Src/Plugins/Visualization/vis_nsfs/tinyvis.txt | 54 + Src/Plugins/Visualization/vis_nsfs/version.rc2 | 39 + Src/Plugins/Visualization/vis_nsfs/vis_nsfs.rc | 194 + Src/Plugins/Visualization/vis_nsfs/vis_nsfs.sln | 24 + .../Visualization/vis_nsfs/vis_nsfs.vcxproj | 372 + .../vis_nsfs/vis_nsfs.vcxproj.filters | 53 + 2888 files changed, 665700 insertions(+) create mode 100644 Src/Plugins/DSP/dsp_sc/Include/c_datapump.h create mode 100644 Src/Plugins/DSP/dsp_sc/Include/c_wavein.h create mode 100644 Src/Plugins/DSP/dsp_sc/NSIS/ShellDispatch.dll create mode 100644 Src/Plugins/DSP/dsp_sc/NSIS/dsp_sc.dll create mode 100644 Src/Plugins/DSP/dsp_sc/NSIS/dsp_sc_license.txt create mode 100644 Src/Plugins/DSP/dsp_sc/NSIS/dsp_sc_v2.nsi create mode 100644 Src/Plugins/DSP/dsp_sc/NSIS/modern-install.ico create mode 100644 Src/Plugins/DSP/dsp_sc/NSIS/win.bmp create mode 100644 Src/Plugins/DSP/dsp_sc/Resource/ICY.ICO create mode 100644 Src/Plugins/DSP/dsp_sc/Resource/Script1.rc create mode 100644 Src/Plugins/DSP/dsp_sc/Resource/downarrow.ico create mode 100644 Src/Plugins/DSP/dsp_sc/Resource/kill.ico create mode 100644 Src/Plugins/DSP/dsp_sc/Resource/play.ico create mode 100644 Src/Plugins/DSP/dsp_sc/Resource/refresh.ico create mode 100644 Src/Plugins/DSP/dsp_sc/Resource/resource.h create mode 100644 Src/Plugins/DSP/dsp_sc/Resource/stop.ico create mode 100644 Src/Plugins/DSP/dsp_sc/api.h create mode 100644 Src/Plugins/DSP/dsp_sc/crossfader/c_crossfader.cpp create mode 100644 Src/Plugins/DSP/dsp_sc/crossfader/c_crossfader.h create mode 100644 Src/Plugins/DSP/dsp_sc/docs/Source_DSP_Changelog.html create mode 100644 Src/Plugins/DSP/dsp_sc/docs/Source_DSP_Plug-in.html create mode 100644 Src/Plugins/DSP/dsp_sc/docs/Source_DSP_Plug-in_Config_Examples.html create mode 100644 Src/Plugins/DSP/dsp_sc/docs/res/About_tab.png create mode 100644 Src/Plugins/DSP/dsp_sc/docs/res/Direct_Source_to_a_SHOUTcast_v1_DNAS_Server_(Legacy).png create mode 100644 Src/Plugins/DSP/dsp_sc/docs/res/Direct_Source_to_a_SHOUTcast_v2_DNAS_Server.png create mode 100644 Src/Plugins/DSP/dsp_sc/docs/res/Input_tab_soundcard_input.png create mode 100644 Src/Plugins/DSP/dsp_sc/docs/res/Input_tab_winamp_input.png create mode 100644 Src/Plugins/DSP/dsp_sc/docs/res/Output_tab_artwork_tab_v1_enabled.png create mode 100644 Src/Plugins/DSP/dsp_sc/docs/res/Output_tab_artwork_tab_v2_enabled.png create mode 100644 Src/Plugins/DSP/dsp_sc/docs/res/Output_tab_directory_tab.png create mode 100644 Src/Plugins/DSP/dsp_sc/docs/res/Output_tab_encoder_tab.png create mode 100644 Src/Plugins/DSP/dsp_sc/docs/res/Output_tab_login_tab_v1_enabled.png create mode 100644 Src/Plugins/DSP/dsp_sc/docs/res/Output_tab_login_tab_v2_enabled.png create mode 100644 Src/Plugins/DSP/dsp_sc/docs/res/Output_tab_logs_tab.png create mode 100644 Src/Plugins/DSP/dsp_sc/docs/res/Output_tab_titles_tab.png create mode 100644 Src/Plugins/DSP/dsp_sc/docs/res/Output_tag_configuration_error.png create mode 100644 Src/Plugins/DSP/dsp_sc/docs/res/Select_Source_DSP_in_Winamp.png create mode 100644 Src/Plugins/DSP/dsp_sc/docs/res/Summary_tab.png create mode 100644 Src/Plugins/DSP/dsp_sc/dsp_sc.vcxproj create mode 100644 Src/Plugins/DSP/dsp_sc/dsp_sc.vcxproj.filters create mode 100644 Src/Plugins/DSP/dsp_sc/main.cpp create mode 100644 Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/_ptrlist.h create mode 100644 Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder.cpp create mode 100644 Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder.h create mode 100644 Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_aacp.cpp create mode 100644 Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_aacp.h create mode 100644 Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_fhgaac.cpp create mode 100644 Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_fhgaac.h create mode 100644 Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_mp3dll.cpp create mode 100644 Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_mp3dll.h create mode 100644 Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_nsv.cpp create mode 100644 Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_nsv.h create mode 100644 Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_ogg.cpp create mode 100644 Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_ogg.h create mode 100644 Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/enc_if.h create mode 100644 Src/Plugins/DSP/dsp_sc/sc2srclib/Include/c_jobmanager.h create mode 100644 Src/Plugins/DSP/dsp_sc/sc2srclib/Include/c_serial_jobmanager.h create mode 100644 Src/Plugins/DSP/dsp_sc/sc2srclib/Include/c_shoutcast_2_output.h create mode 100644 Src/Plugins/DSP/dsp_sc/sc2srclib/Include/shoutcast_output.h create mode 100644 Src/Plugins/DSP/dsp_sc/sc2srclib/lame/include/lame.h create mode 100644 Src/Plugins/DSP/dsp_sc/sc2srclib/lame/libmp3lame/lame_global_flags.h create mode 100644 Src/Plugins/DSP/dsp_sc/sc2srclib/shoutcast_output.cpp create mode 100644 Src/Plugins/DSP/dsp_sc/sc2srclib/uvAuth21/uvAuth21.cpp create mode 100644 Src/Plugins/DSP/dsp_sc/sc2srclib/uvAuth21/uvAuth21.h create mode 100644 Src/Plugins/DSP/dsp_sc/utils.cpp create mode 100644 Src/Plugins/DSP/dsp_sc/utils.h create mode 100644 Src/Plugins/DSP/dsp_sps/api__dsp_sps.h create mode 100644 Src/Plugins/DSP/dsp_sps/constant.bin create mode 100644 Src/Plugins/DSP/dsp_sps/dsp_sps.cpp create mode 100644 Src/Plugins/DSP/dsp_sps/dsp_sps.dsp create mode 100644 Src/Plugins/DSP/dsp_sps/dsp_sps.dsw create mode 100644 Src/Plugins/DSP/dsp_sps/dsp_sps.vcxproj create mode 100644 Src/Plugins/DSP/dsp_sps/dsp_sps.vcxproj.filters create mode 100644 Src/Plugins/DSP/dsp_sps/dxi/AudioPlugIn.cpp create mode 100644 Src/Plugins/DSP/dsp_sps/dxi/AudioPlugIn.def create mode 100644 Src/Plugins/DSP/dsp_sps/dxi/AudioPlugIn.dep create mode 100644 Src/Plugins/DSP/dsp_sps/dxi/AudioPlugIn.dsp create mode 100644 Src/Plugins/DSP/dsp_sps/dxi/AudioPlugIn.dsw create mode 100644 Src/Plugins/DSP/dsp_sps/dxi/AudioPlugIn.h create mode 100644 Src/Plugins/DSP/dsp_sps/dxi/AudioPlugIn.mak create mode 100644 Src/Plugins/DSP/dsp_sps/dxi/AudioPlugIn.rc create mode 100644 Src/Plugins/DSP/dsp_sps/dxi/AudioPlugInPropPage.cpp create mode 100644 Src/Plugins/DSP/dsp_sps/dxi/AudioPlugInPropPage.h create mode 100644 Src/Plugins/DSP/dsp_sps/dxi/Filter.cpp create mode 100644 Src/Plugins/DSP/dsp_sps/dxi/Filter.h create mode 100644 Src/Plugins/DSP/dsp_sps/dxi/MediaParams.cpp create mode 100644 Src/Plugins/DSP/dsp_sps/dxi/MediaParams.h create mode 100644 Src/Plugins/DSP/dsp_sps/dxi/ParamEnvelope.cpp create mode 100644 Src/Plugins/DSP/dsp_sps/dxi/ParamEnvelope.h create mode 100644 Src/Plugins/DSP/dsp_sps/dxi/Parameters.h create mode 100644 Src/Plugins/DSP/dsp_sps/dxi/PlugInApp.cpp create mode 100644 Src/Plugins/DSP/dsp_sps/dxi/PlugInApp.h create mode 100644 Src/Plugins/DSP/dsp_sps/dxi/PlugInGUIDs.h create mode 100644 Src/Plugins/DSP/dsp_sps/dxi/ReadMe.txt create mode 100644 Src/Plugins/DSP/dsp_sps/dxi/StdAfx.cpp create mode 100644 Src/Plugins/DSP/dsp_sps/dxi/StdAfx.h create mode 100644 Src/Plugins/DSP/dsp_sps/dxi/dmoguids.lib create mode 100644 Src/Plugins/DSP/dsp_sps/dxi/include/CakeMedParam.h create mode 100644 Src/Plugins/DSP/dsp_sps/dxi/include/CakeMedParam_i.c create mode 100644 Src/Plugins/DSP/dsp_sps/dxi/include/DXi.h create mode 100644 Src/Plugins/DSP/dsp_sps/dxi/include/DeferZeroFill.h create mode 100644 Src/Plugins/DSP/dsp_sps/dxi/res/AudioPlugIn.rc2 create mode 100644 Src/Plugins/DSP/dsp_sps/dxi/resource.h create mode 100644 Src/Plugins/DSP/dsp_sps/function.bin create mode 100644 Src/Plugins/DSP/dsp_sps/general.bin create mode 100644 Src/Plugins/DSP/dsp_sps/operator.bin create mode 100644 Src/Plugins/DSP/dsp_sps/res.rc create mode 100644 Src/Plugins/DSP/dsp_sps/resource.h create mode 100644 Src/Plugins/DSP/dsp_sps/sps_common.cpp create mode 100644 Src/Plugins/DSP/dsp_sps/sps_common.h create mode 100644 Src/Plugins/DSP/dsp_sps/sps_configdlg.h create mode 100644 Src/Plugins/DSP/dsp_sps/version.rc2 create mode 100644 Src/Plugins/Encoder/enc_fhgaac/ADTSAACEncoder.cpp create mode 100644 Src/Plugins/Encoder/enc_fhgaac/ADTSAACEncoder.h create mode 100644 Src/Plugins/Encoder/enc_fhgaac/FhGAACEncoder.cpp create mode 100644 Src/Plugins/Encoder/enc_fhgaac/FhGAACEncoder.h create mode 100644 Src/Plugins/Encoder/enc_fhgaac/MP4Writer.cpp create mode 100644 Src/Plugins/Encoder/enc_fhgaac/MP4Writer.h create mode 100644 Src/Plugins/Encoder/enc_fhgaac/config.cpp create mode 100644 Src/Plugins/Encoder/enc_fhgaac/config.h create mode 100644 Src/Plugins/Encoder/enc_fhgaac/enc_fhgaac.rc create mode 100644 Src/Plugins/Encoder/enc_fhgaac/enc_fhgaac.sln create mode 100644 Src/Plugins/Encoder/enc_fhgaac/enc_fhgaac.vcproj create mode 100644 Src/Plugins/Encoder/enc_fhgaac/encoder_common.h create mode 100644 Src/Plugins/Encoder/enc_fhgaac/fraunhofer_wa_prefs_noalpha.bmp create mode 100644 Src/Plugins/Encoder/enc_fhgaac/libirc.lib create mode 100644 Src/Plugins/Encoder/enc_fhgaac/link_control.cpp create mode 100644 Src/Plugins/Encoder/enc_fhgaac/link_control.h create mode 100644 Src/Plugins/Encoder/enc_fhgaac/main.cpp create mode 100644 Src/Plugins/Encoder/enc_fhgaac/mp4FastAAClib.h create mode 100644 Src/Plugins/Encoder/enc_fhgaac/mp4FastAAClib.lib create mode 100644 Src/Plugins/Encoder/enc_fhgaac/preferences.cpp create mode 100644 Src/Plugins/Encoder/enc_fhgaac/preferences.h create mode 100644 Src/Plugins/Encoder/enc_fhgaac/preferences_adts.cpp create mode 100644 Src/Plugins/Encoder/enc_fhgaac/preferences_mp4.cpp create mode 100644 Src/Plugins/Encoder/enc_fhgaac/resource.h create mode 100644 Src/Plugins/Encoder/enc_fhgaac/version.rc2 create mode 100644 Src/Plugins/Encoder/enc_flac/AudioCoderFlac.cpp create mode 100644 Src/Plugins/Encoder/enc_flac/AudioCoderFlac.h create mode 100644 Src/Plugins/Encoder/enc_flac/StreamFileWin32.cpp create mode 100644 Src/Plugins/Encoder/enc_flac/StreamFileWin32.h create mode 100644 Src/Plugins/Encoder/enc_flac/api__enc_flac.h create mode 100644 Src/Plugins/Encoder/enc_flac/enc_flac2.rc create mode 100644 Src/Plugins/Encoder/enc_flac/enc_flac2.sln create mode 100644 Src/Plugins/Encoder/enc_flac/enc_flac2.vcxproj create mode 100644 Src/Plugins/Encoder/enc_flac/enc_flac2.vcxproj.filters create mode 100644 Src/Plugins/Encoder/enc_flac/main.cpp create mode 100644 Src/Plugins/Encoder/enc_flac/resource.h create mode 100644 Src/Plugins/Encoder/enc_flac/version.rc2 create mode 100644 Src/Plugins/Encoder/enc_lame/24bit.cpp create mode 100644 Src/Plugins/Encoder/enc_lame/BladeMP3EncDLL.c create mode 100644 Src/Plugins/Encoder/enc_lame/BladeMP3EncDLL.h create mode 100644 Src/Plugins/Encoder/enc_lame/MP3Coder.cpp create mode 100644 Src/Plugins/Encoder/enc_lame/MP3Coder.h create mode 100644 Src/Plugins/Encoder/enc_lame/enc_lame.rc create mode 100644 Src/Plugins/Encoder/enc_lame/enc_lame.sln create mode 100644 Src/Plugins/Encoder/enc_lame/enc_lame.vcxproj create mode 100644 Src/Plugins/Encoder/enc_lame/enc_lame.vcxproj.filters create mode 100644 Src/Plugins/Encoder/enc_lame/main.cpp create mode 100644 Src/Plugins/Encoder/enc_lame/resource.h create mode 100644 Src/Plugins/Encoder/enc_lame/version.rc2 create mode 100644 Src/Plugins/Encoder/enc_vorbis/Libs/libogg_static.lib create mode 100644 Src/Plugins/Encoder/enc_vorbis/Libs/vorbis_static.lib create mode 100644 Src/Plugins/Encoder/enc_vorbis/Libs/vorbisenc_static.lib create mode 100644 Src/Plugins/Encoder/enc_vorbis/enc_vorbis.rc create mode 100644 Src/Plugins/Encoder/enc_vorbis/enc_vorbis.sln create mode 100644 Src/Plugins/Encoder/enc_vorbis/enc_vorbis.vcxproj create mode 100644 Src/Plugins/Encoder/enc_vorbis/enc_vorbis.vcxproj.filters create mode 100644 Src/Plugins/Encoder/enc_vorbis/main.cpp create mode 100644 Src/Plugins/Encoder/enc_vorbis/ogg/include/ogg/config_types.h create mode 100644 Src/Plugins/Encoder/enc_vorbis/ogg/include/ogg/ogg.h create mode 100644 Src/Plugins/Encoder/enc_vorbis/ogg/include/ogg/os_types.h create mode 100644 Src/Plugins/Encoder/enc_vorbis/resource.h create mode 100644 Src/Plugins/Encoder/enc_vorbis/version.rc2 create mode 100644 Src/Plugins/Encoder/enc_vorbis/vorbis/include/vorbis/codec.h create mode 100644 Src/Plugins/Encoder/enc_vorbis/vorbis/include/vorbis/vorbisenc.h create mode 100644 Src/Plugins/Encoder/enc_vorbis/vorbis/include/vorbis/vorbisfile.h create mode 100644 Src/Plugins/Encoder/enc_wav/ACMEncoder.cpp create mode 100644 Src/Plugins/Encoder/enc_wav/ACMEncoder.h create mode 100644 Src/Plugins/Encoder/enc_wav/Config.cpp create mode 100644 Src/Plugins/Encoder/enc_wav/Config.h create mode 100644 Src/Plugins/Encoder/enc_wav/Finisher.h create mode 100644 Src/Plugins/Encoder/enc_wav/WAVEncoder.cpp create mode 100644 Src/Plugins/Encoder/enc_wav/WAVEncoder.h create mode 100644 Src/Plugins/Encoder/enc_wav/enc_wav.rc create mode 100644 Src/Plugins/Encoder/enc_wav/enc_wav.sln create mode 100644 Src/Plugins/Encoder/enc_wav/enc_wav.vcxproj create mode 100644 Src/Plugins/Encoder/enc_wav/enc_wav.vcxproj.filters create mode 100644 Src/Plugins/Encoder/enc_wav/main.cpp create mode 100644 Src/Plugins/Encoder/enc_wav/preferences.cpp create mode 100644 Src/Plugins/Encoder/enc_wav/resource.h create mode 100644 Src/Plugins/Encoder/enc_wav/version.rc2 create mode 100644 Src/Plugins/Encoder/enc_wma/ASFErr.h create mode 100644 Src/Plugins/Encoder/enc_wma/AudioCoderWMA.cpp create mode 100644 Src/Plugins/Encoder/enc_wma/AudioCoderWMA.h create mode 100644 Src/Plugins/Encoder/enc_wma/config.cpp create mode 100644 Src/Plugins/Encoder/enc_wma/enc_wma.rc create mode 100644 Src/Plugins/Encoder/enc_wma/enc_wma.sln create mode 100644 Src/Plugins/Encoder/enc_wma/enc_wma.vcxproj create mode 100644 Src/Plugins/Encoder/enc_wma/enc_wma.vcxproj.filters create mode 100644 Src/Plugins/Encoder/enc_wma/main.cpp create mode 100644 Src/Plugins/Encoder/enc_wma/main.h create mode 100644 Src/Plugins/Encoder/enc_wma/nserror.h create mode 100644 Src/Plugins/Encoder/enc_wma/resource.h create mode 100644 Src/Plugins/Encoder/enc_wma/version.rc2 create mode 100644 Src/Plugins/Encoder/enc_wma/wmaudiosdk.h create mode 100644 Src/Plugins/General/gen_crasher/ExceptionHandler.cpp create mode 100644 Src/Plugins/General/gen_crasher/ExceptionHandler.h create mode 100644 Src/Plugins/General/gen_crasher/GetWinVer.cpp create mode 100644 Src/Plugins/General/gen_crasher/GetWinVer.h create mode 100644 Src/Plugins/General/gen_crasher/MiniVersion.cpp create mode 100644 Src/Plugins/General/gen_crasher/MiniVersion.h create mode 100644 Src/Plugins/General/gen_crasher/ReadMe.txt create mode 100644 Src/Plugins/General/gen_crasher/api__gen_crasher.h create mode 100644 Src/Plugins/General/gen_crasher/config.cpp create mode 100644 Src/Plugins/General/gen_crasher/config.h create mode 100644 Src/Plugins/General/gen_crasher/configDlg.cpp create mode 100644 Src/Plugins/General/gen_crasher/configDlg.h create mode 100644 Src/Plugins/General/gen_crasher/crashDlg.cpp create mode 100644 Src/Plugins/General/gen_crasher/crashDlg.h create mode 100644 Src/Plugins/General/gen_crasher/feedback/email/SendEmail.cpp create mode 100644 Src/Plugins/General/gen_crasher/feedback/email/SendEmail.h create mode 100644 Src/Plugins/General/gen_crasher/feedback/feedback.rc create mode 100644 Src/Plugins/General/gen_crasher/feedback/feedback.sln create mode 100644 Src/Plugins/General/gen_crasher/feedback/feedback.vcproj create mode 100644 Src/Plugins/General/gen_crasher/feedback/feedback.vcxproj create mode 100644 Src/Plugins/General/gen_crasher/feedback/feedback.vcxproj.filters create mode 100644 Src/Plugins/General/gen_crasher/feedback/main.cpp create mode 100644 Src/Plugins/General/gen_crasher/feedback/main.h create mode 100644 Src/Plugins/General/gen_crasher/feedback/manifest.xml create mode 100644 Src/Plugins/General/gen_crasher/feedback/operations.cpp create mode 100644 Src/Plugins/General/gen_crasher/feedback/resource.h create mode 100644 Src/Plugins/General/gen_crasher/feedback/silent.cpp create mode 100644 Src/Plugins/General/gen_crasher/feedback/smtp/Base64.cpp create mode 100644 Src/Plugins/General/gen_crasher/feedback/smtp/Base64.h create mode 100644 Src/Plugins/General/gen_crasher/feedback/smtp/Smtp.cpp create mode 100644 Src/Plugins/General/gen_crasher/feedback/smtp/Smtp.h create mode 100644 Src/Plugins/General/gen_crasher/feedback/version.rc2 create mode 100644 Src/Plugins/General/gen_crasher/feedback/xzip/XZip.cpp create mode 100644 Src/Plugins/General/gen_crasher/feedback/xzip/XZip.h create mode 100644 Src/Plugins/General/gen_crasher/gen_crasher.rc create mode 100644 Src/Plugins/General/gen_crasher/gen_crasher.sln create mode 100644 Src/Plugins/General/gen_crasher/gen_crasher.vcxproj create mode 100644 Src/Plugins/General/gen_crasher/gen_crasher.vcxproj.filters create mode 100644 Src/Plugins/General/gen_crasher/main.cpp create mode 100644 Src/Plugins/General/gen_crasher/main.h create mode 100644 Src/Plugins/General/gen_crasher/minidump.h create mode 100644 Src/Plugins/General/gen_crasher/resource.h create mode 100644 Src/Plugins/General/gen_crasher/settings.cpp create mode 100644 Src/Plugins/General/gen_crasher/settings.h create mode 100644 Src/Plugins/General/gen_crasher/smtpDlg.cpp create mode 100644 Src/Plugins/General/gen_crasher/smtpDlg.h create mode 100644 Src/Plugins/General/gen_crasher/version.rc2 create mode 100644 Src/Plugins/General/gen_ff/AlbumArt.cpp create mode 100644 Src/Plugins/General/gen_ff/AlbumArt.h create mode 100644 Src/Plugins/General/gen_ff/MediaDownloader.cpp create mode 100644 Src/Plugins/General/gen_ff/README.txt create mode 100644 Src/Plugins/General/gen_ff/WinampConfigScriptObject.cpp create mode 100644 Src/Plugins/General/gen_ff/WinampConfigScriptObject.h create mode 100644 Src/Plugins/General/gen_ff/api__gen_ff.h create mode 100644 Src/Plugins/General/gen_ff/bitmaps/library-hilited.png create mode 100644 Src/Plugins/General/gen_ff/bitmaps/library-selected.png create mode 100644 Src/Plugins/General/gen_ff/bitmaps/library-unselected.png create mode 100644 Src/Plugins/General/gen_ff/bitmaps/mb-hilited.png create mode 100644 Src/Plugins/General/gen_ff/bitmaps/mb-selected.png create mode 100644 Src/Plugins/General/gen_ff/bitmaps/mb-unselected.png create mode 100644 Src/Plugins/General/gen_ff/bitmaps/pledit-hover.png create mode 100644 Src/Plugins/General/gen_ff/bitmaps/pledit-selected.png create mode 100644 Src/Plugins/General/gen_ff/bitmaps/pledit-unselected.png create mode 100644 Src/Plugins/General/gen_ff/bitmaps/video-hilited.png create mode 100644 Src/Plugins/General/gen_ff/bitmaps/video-selected.png create mode 100644 Src/Plugins/General/gen_ff/bitmaps/video-unselected.png create mode 100644 Src/Plugins/General/gen_ff/bitmaps/vis-hilited.png create mode 100644 Src/Plugins/General/gen_ff/bitmaps/vis-selected.png create mode 100644 Src/Plugins/General/gen_ff/bitmaps/vis-unselected.png create mode 100644 Src/Plugins/General/gen_ff/embedwndguid.cpp create mode 100644 Src/Plugins/General/gen_ff/embedwndguid.h create mode 100644 Src/Plugins/General/gen_ff/ff_ipc.cpp create mode 100644 Src/Plugins/General/gen_ff/ff_ipc.h create mode 100644 Src/Plugins/General/gen_ff/fsmonitor.cpp create mode 100644 Src/Plugins/General/gen_ff/fsmonitor.h create mode 100644 Src/Plugins/General/gen_ff/gen.h create mode 100644 Src/Plugins/General/gen_ff/gen_ff.rc create mode 100644 Src/Plugins/General/gen_ff/gen_ff.rc2 create mode 100644 Src/Plugins/General/gen_ff/gen_ff.sln create mode 100644 Src/Plugins/General/gen_ff/gen_ff.vcxproj create mode 100644 Src/Plugins/General/gen_ff/gen_ff.vcxproj.filters create mode 100644 Src/Plugins/General/gen_ff/gen_ff_ipc.h create mode 100644 Src/Plugins/General/gen_ff/main.cpp create mode 100644 Src/Plugins/General/gen_ff/main.h create mode 100644 Src/Plugins/General/gen_ff/menuactions.cpp create mode 100644 Src/Plugins/General/gen_ff/menuactions.h create mode 100644 Src/Plugins/General/gen_ff/minibrowserCOM.cpp create mode 100644 Src/Plugins/General/gen_ff/minibrowserCOM.h create mode 100644 Src/Plugins/General/gen_ff/precomp__gen_ff.cpp create mode 100644 Src/Plugins/General/gen_ff/precomp__gen_ff.h create mode 100644 Src/Plugins/General/gen_ff/prefs.cpp create mode 100644 Src/Plugins/General/gen_ff/prefs.h create mode 100644 Src/Plugins/General/gen_ff/prefs_about.cpp create mode 100644 Src/Plugins/General/gen_ff/prefs_alpha.cpp create mode 100644 Src/Plugins/General/gen_ff/prefs_colorthemes.cpp create mode 100644 Src/Plugins/General/gen_ff/prefs_font.cpp create mode 100644 Src/Plugins/General/gen_ff/prefs_general.cpp create mode 100644 Src/Plugins/General/gen_ff/resource.h create mode 100644 Src/Plugins/General/gen_ff/servicelink.cpp create mode 100644 Src/Plugins/General/gen_ff/skininfo.cpp create mode 100644 Src/Plugins/General/gen_ff/skininfo.h create mode 100644 Src/Plugins/General/gen_ff/wa2buckitems.cpp create mode 100644 Src/Plugins/General/gen_ff/wa2buckitems.h create mode 100644 Src/Plugins/General/gen_ff/wa2cfgitems.cpp create mode 100644 Src/Plugins/General/gen_ff/wa2cfgitems.h create mode 100644 Src/Plugins/General/gen_ff/wa2core.cpp create mode 100644 Src/Plugins/General/gen_ff/wa2core.h create mode 100644 Src/Plugins/General/gen_ff/wa2coreactions.cpp create mode 100644 Src/Plugins/General/gen_ff/wa2coreactions.h create mode 100644 Src/Plugins/General/gen_ff/wa2frontend.cpp create mode 100644 Src/Plugins/General/gen_ff/wa2frontend.h create mode 100644 Src/Plugins/General/gen_ff/wa2groupdefs.cpp create mode 100644 Src/Plugins/General/gen_ff/wa2groupdefs.h create mode 100644 Src/Plugins/General/gen_ff/wa2playlist.cpp create mode 100644 Src/Plugins/General/gen_ff/wa2playlist.h create mode 100644 Src/Plugins/General/gen_ff/wa2pldirobj.cpp create mode 100644 Src/Plugins/General/gen_ff/wa2pldirobj.h create mode 100644 Src/Plugins/General/gen_ff/wa2pledit.cpp create mode 100644 Src/Plugins/General/gen_ff/wa2pledit.h create mode 100644 Src/Plugins/General/gen_ff/wa2songticker.cpp create mode 100644 Src/Plugins/General/gen_ff/wa2songticker.h create mode 100644 Src/Plugins/General/gen_ff/wa2wndembed.cpp create mode 100644 Src/Plugins/General/gen_ff/wa2wndembed.h create mode 100644 Src/Plugins/General/gen_ff/wasabi.dsp create mode 100644 Src/Plugins/General/gen_ff/wasabi.vcproj create mode 100644 Src/Plugins/General/gen_ff/wasabicfg.h create mode 100644 Src/Plugins/General/gen_hotkeys/Configdlg.cpp create mode 100644 Src/Plugins/General/gen_hotkeys/Configdlg.h create mode 100644 Src/Plugins/General/gen_hotkeys/HOTKEY.CPP create mode 100644 Src/Plugins/General/gen_hotkeys/HOTKEY.H create mode 100644 Src/Plugins/General/gen_hotkeys/HotKeyCtl.cpp create mode 100644 Src/Plugins/General/gen_hotkeys/HotKeyCtl.h create mode 100644 Src/Plugins/General/gen_hotkeys/RESOURCE.H create mode 100644 Src/Plugins/General/gen_hotkeys/WINAMP.H create mode 100644 Src/Plugins/General/gen_hotkeys/Wacommands.cpp create mode 100644 Src/Plugins/General/gen_hotkeys/Wacommands.h create mode 100644 Src/Plugins/General/gen_hotkeys/accelBlock.cpp create mode 100644 Src/Plugins/General/gen_hotkeys/accelBlock.h create mode 100644 Src/Plugins/General/gen_hotkeys/api__gen_hotkeys.h create mode 100644 Src/Plugins/General/gen_hotkeys/gen_hotkeys.cpp create mode 100644 Src/Plugins/General/gen_hotkeys/gen_hotkeys.h create mode 100644 Src/Plugins/General/gen_hotkeys/gen_hotkeys.rc create mode 100644 Src/Plugins/General/gen_hotkeys/gen_hotkeys.sln create mode 100644 Src/Plugins/General/gen_hotkeys/gen_hotkeys.vcxproj create mode 100644 Src/Plugins/General/gen_hotkeys/gen_hotkeys.vcxproj.filters create mode 100644 Src/Plugins/General/gen_hotkeys/version.rc2 create mode 100644 Src/Plugins/General/gen_hotkeys/wa_hotkeys.h create mode 100644 Src/Plugins/General/gen_hotkeys/x.bmp create mode 100644 Src/Plugins/General/gen_ml/HeaderIconList.cpp create mode 100644 Src/Plugins/General/gen_ml/IPC.cpp create mode 100644 Src/Plugins/General/gen_ml/MediaLibraryCOM.cpp create mode 100644 Src/Plugins/General/gen_ml/MediaLibraryCOM.h create mode 100644 Src/Plugins/General/gen_ml/MusicID.cpp create mode 100644 Src/Plugins/General/gen_ml/MusicID.h create mode 100644 Src/Plugins/General/gen_ml/OnlineMediaCOM.cpp create mode 100644 Src/Plugins/General/gen_ml/OnlineMediaCOM.h create mode 100644 Src/Plugins/General/gen_ml/RatingsCOM.cpp create mode 100644 Src/Plugins/General/gen_ml/RatingsCOM.h create mode 100644 Src/Plugins/General/gen_ml/SmoothScrollList.cpp create mode 100644 Src/Plugins/General/gen_ml/api__gen_ml.h create mode 100644 Src/Plugins/General/gen_ml/banner.cpp create mode 100644 Src/Plugins/General/gen_ml/banner.h create mode 100644 Src/Plugins/General/gen_ml/childwnd.cpp create mode 100644 Src/Plugins/General/gen_ml/childwnd.h create mode 100644 Src/Plugins/General/gen_ml/colors.cpp create mode 100644 Src/Plugins/General/gen_ml/colors.h create mode 100644 Src/Plugins/General/gen_ml/comboskin.cpp create mode 100644 Src/Plugins/General/gen_ml/comboskin.h create mode 100644 Src/Plugins/General/gen_ml/config.cpp create mode 100644 Src/Plugins/General/gen_ml/config.h create mode 100644 Src/Plugins/General/gen_ml/fileview.cpp create mode 100644 Src/Plugins/General/gen_ml/fileview.h create mode 100644 Src/Plugins/General/gen_ml/fileview_columns.cpp create mode 100644 Src/Plugins/General/gen_ml/fileview_compare.cpp create mode 100644 Src/Plugins/General/gen_ml/fileview_filesystem.cpp create mode 100644 Src/Plugins/General/gen_ml/fileview_format.cpp create mode 100644 Src/Plugins/General/gen_ml/fileview_internal.h create mode 100644 Src/Plugins/General/gen_ml/fileview_menu.cpp create mode 100644 Src/Plugins/General/gen_ml/fileview_metadata.cpp create mode 100644 Src/Plugins/General/gen_ml/fileview_toolbar.cpp create mode 100644 Src/Plugins/General/gen_ml/flickerfix.cpp create mode 100644 Src/Plugins/General/gen_ml/folderborwser_listbox.cpp create mode 100644 Src/Plugins/General/gen_ml/folderbrowser.cpp create mode 100644 Src/Plugins/General/gen_ml/folderbrowser.h create mode 100644 Src/Plugins/General/gen_ml/folderbrowser_internal.h create mode 100644 Src/Plugins/General/gen_ml/gaystring.cpp create mode 100644 Src/Plugins/General/gen_ml/gaystring.h create mode 100644 Src/Plugins/General/gen_ml/gen_ml.rc create mode 100644 Src/Plugins/General/gen_ml/gen_ml.sln create mode 100644 Src/Plugins/General/gen_ml/gen_ml.vcxproj create mode 100644 Src/Plugins/General/gen_ml/gen_ml.vcxproj.filters create mode 100644 Src/Plugins/General/gen_ml/graphics.cpp create mode 100644 Src/Plugins/General/gen_ml/graphics.h create mode 100644 Src/Plugins/General/gen_ml/imagefilters.cpp create mode 100644 Src/Plugins/General/gen_ml/imagefilters.h create mode 100644 Src/Plugins/General/gen_ml/itemlist.cpp create mode 100644 Src/Plugins/General/gen_ml/itemlist.h create mode 100644 Src/Plugins/General/gen_ml/klib/khash.h create mode 100644 Src/Plugins/General/gen_ml/listheader.cpp create mode 100644 Src/Plugins/General/gen_ml/listskin.cpp create mode 100644 Src/Plugins/General/gen_ml/listskin.h create mode 100644 Src/Plugins/General/gen_ml/listview.cpp create mode 100644 Src/Plugins/General/gen_ml/listview.h create mode 100644 Src/Plugins/General/gen_ml/main.cpp create mode 100644 Src/Plugins/General/gen_ml/main.h create mode 100644 Src/Plugins/General/gen_ml/menu.cpp create mode 100644 Src/Plugins/General/gen_ml/menu.h create mode 100644 Src/Plugins/General/gen_ml/menufucker.h create mode 100644 Src/Plugins/General/gen_ml/ml.h create mode 100644 Src/Plugins/General/gen_ml/ml_cloud.cpp create mode 100644 Src/Plugins/General/gen_ml/ml_cloud.h create mode 100644 Src/Plugins/General/gen_ml/ml_cloudcolumn.cpp create mode 100644 Src/Plugins/General/gen_ml/ml_cloudcolumn.h create mode 100644 Src/Plugins/General/gen_ml/ml_ex/ex.rc create mode 100644 Src/Plugins/General/gen_ml/ml_ex/ml_ex.dsp create mode 100644 Src/Plugins/General/gen_ml/ml_ex/ml_ex.dsw create mode 100644 Src/Plugins/General/gen_ml/ml_ex/resource.h create mode 100644 Src/Plugins/General/gen_ml/ml_ex/view_ex.cpp create mode 100644 Src/Plugins/General/gen_ml/ml_imagefilter.cpp create mode 100644 Src/Plugins/General/gen_ml/ml_imagefilter.h create mode 100644 Src/Plugins/General/gen_ml/ml_imagelist.cpp create mode 100644 Src/Plugins/General/gen_ml/ml_imagelist.h create mode 100644 Src/Plugins/General/gen_ml/ml_imageloader.cpp create mode 100644 Src/Plugins/General/gen_ml/ml_imageloader.h create mode 100644 Src/Plugins/General/gen_ml/ml_ipc.h create mode 100644 Src/Plugins/General/gen_ml/ml_ipc_0313.h create mode 100644 Src/Plugins/General/gen_ml/ml_lib.cpp create mode 100644 Src/Plugins/General/gen_ml/ml_rating.cpp create mode 100644 Src/Plugins/General/gen_ml/ml_rating.h create mode 100644 Src/Plugins/General/gen_ml/ml_ratingcolumn.cpp create mode 100644 Src/Plugins/General/gen_ml/ml_ratingcolumn.h create mode 100644 Src/Plugins/General/gen_ml/mldwm.cpp create mode 100644 Src/Plugins/General/gen_ml/mldwm.h create mode 100644 Src/Plugins/General/gen_ml/navigation.cpp create mode 100644 Src/Plugins/General/gen_ml/navigation.h create mode 100644 Src/Plugins/General/gen_ml/plugin.cpp create mode 100644 Src/Plugins/General/gen_ml/png.rc create mode 100644 Src/Plugins/General/gen_ml/prefs.cpp create mode 100644 Src/Plugins/General/gen_ml/reflectmsg.cpp create mode 100644 Src/Plugins/General/gen_ml/reflectmsg.h create mode 100644 Src/Plugins/General/gen_ml/resource.h create mode 100644 Src/Plugins/General/gen_ml/resources/checkmark.png create mode 100644 Src/Plugins/General/gen_ml/resources/cloud_16_incloud.png create mode 100644 Src/Plugins/General/gen_ml/resources/cloud_16_partial.png create mode 100644 Src/Plugins/General/gen_ml/resources/cloud_16_unavail.png create mode 100644 Src/Plugins/General/gen_ml/resources/cloud_16_upload.png create mode 100644 Src/Plugins/General/gen_ml/resources/cloud_16_uploading.png create mode 100644 Src/Plugins/General/gen_ml/resources/dragdrop.cur create mode 100644 Src/Plugins/General/gen_ml/resources/filetype_audio_16.png create mode 100644 Src/Plugins/General/gen_ml/resources/filetype_audio_32.png create mode 100644 Src/Plugins/General/gen_ml/resources/filetype_playlist_16.png create mode 100644 Src/Plugins/General/gen_ml/resources/filetype_playlist_32.png create mode 100644 Src/Plugins/General/gen_ml/resources/filetype_unknown_16.png create mode 100644 Src/Plugins/General/gen_ml/resources/filetype_unknown_32.png create mode 100644 Src/Plugins/General/gen_ml/resources/filetype_video_16.png create mode 100644 Src/Plugins/General/gen_ml/resources/filetype_video_32.png create mode 100644 Src/Plugins/General/gen_ml/resources/menu_arrow.png create mode 100644 Src/Plugins/General/gen_ml/resources/menu_check.png create mode 100644 Src/Plugins/General/gen_ml/resources/menu_dot.png create mode 100644 Src/Plugins/General/gen_ml/resources/menu_scrollarrow.png create mode 100644 Src/Plugins/General/gen_ml/resources/menu_scrollarrow_disabled.png create mode 100644 Src/Plugins/General/gen_ml/resources/rating.png create mode 100644 Src/Plugins/General/gen_ml/resources/sortarrow.png create mode 100644 Src/Plugins/General/gen_ml/resources/splitarrow.png create mode 100644 Src/Plugins/General/gen_ml/resources/splitarrow_pressed.png create mode 100644 Src/Plugins/General/gen_ml/resources/stars_invert.bmp create mode 100644 Src/Plugins/General/gen_ml/resources/ti_default_16x16x16.bmp create mode 100644 Src/Plugins/General/gen_ml/resources/ti_labs_16x16x16.bmp create mode 100644 Src/Plugins/General/gen_ml/resources/tree_closed_16x16x16.bmp create mode 100644 Src/Plugins/General/gen_ml/resources/tree_closed_disabled_16x16x16.bmp create mode 100644 Src/Plugins/General/gen_ml/resources/tree_open_16x16x16.bmp create mode 100644 Src/Plugins/General/gen_ml/resources/view_mode_detail.png create mode 100644 Src/Plugins/General/gen_ml/resources/view_mode_icon.png create mode 100644 Src/Plugins/General/gen_ml/resources/view_mode_list.png create mode 100644 Src/Plugins/General/gen_ml/scrollwnd.cpp create mode 100644 Src/Plugins/General/gen_ml/scrollwnd.h create mode 100644 Src/Plugins/General/gen_ml/sendto.cpp create mode 100644 Src/Plugins/General/gen_ml/sendto.h create mode 100644 Src/Plugins/General/gen_ml/service.cpp create mode 100644 Src/Plugins/General/gen_ml/service.h create mode 100644 Src/Plugins/General/gen_ml/setup.cpp create mode 100644 Src/Plugins/General/gen_ml/skinexport.cpp create mode 100644 Src/Plugins/General/gen_ml/skinexport.h create mode 100644 Src/Plugins/General/gen_ml/skinnedbutton.cpp create mode 100644 Src/Plugins/General/gen_ml/skinnedbutton.h create mode 100644 Src/Plugins/General/gen_ml/skinnedcombo.cpp create mode 100644 Src/Plugins/General/gen_ml/skinnedcombo.h create mode 100644 Src/Plugins/General/gen_ml/skinneddivider.cpp create mode 100644 Src/Plugins/General/gen_ml/skinneddivider.h create mode 100644 Src/Plugins/General/gen_ml/skinneddlg.cpp create mode 100644 Src/Plugins/General/gen_ml/skinneddlg.h create mode 100644 Src/Plugins/General/gen_ml/skinnededit.cpp create mode 100644 Src/Plugins/General/gen_ml/skinnededit.h create mode 100644 Src/Plugins/General/gen_ml/skinnedfolder.cpp create mode 100644 Src/Plugins/General/gen_ml/skinnedfolder.h create mode 100644 Src/Plugins/General/gen_ml/skinnedheader.cpp create mode 100644 Src/Plugins/General/gen_ml/skinnedheader.h create mode 100644 Src/Plugins/General/gen_ml/skinnedlistbox.cpp create mode 100644 Src/Plugins/General/gen_ml/skinnedlistbox.h create mode 100644 Src/Plugins/General/gen_ml/skinnedlistview.cpp create mode 100644 Src/Plugins/General/gen_ml/skinnedlistview.h create mode 100644 Src/Plugins/General/gen_ml/skinnedmenu.cpp create mode 100644 Src/Plugins/General/gen_ml/skinnedmenu.h create mode 100644 Src/Plugins/General/gen_ml/skinnedmenuthreadinfo.cpp create mode 100644 Src/Plugins/General/gen_ml/skinnedmenuthreadinfo.h create mode 100644 Src/Plugins/General/gen_ml/skinnedmenuwnd.cpp create mode 100644 Src/Plugins/General/gen_ml/skinnedmenuwnd.h create mode 100644 Src/Plugins/General/gen_ml/skinnedprogressbar.cpp create mode 100644 Src/Plugins/General/gen_ml/skinnedprogressbar.h create mode 100644 Src/Plugins/General/gen_ml/skinnedscrollwnd.cpp create mode 100644 Src/Plugins/General/gen_ml/skinnedscrollwnd.h create mode 100644 Src/Plugins/General/gen_ml/skinnedstatic.cpp create mode 100644 Src/Plugins/General/gen_ml/skinnedstatic.h create mode 100644 Src/Plugins/General/gen_ml/skinnedtooltip.cpp create mode 100644 Src/Plugins/General/gen_ml/skinnedtooltip.h create mode 100644 Src/Plugins/General/gen_ml/skinnedwnd.cpp create mode 100644 Src/Plugins/General/gen_ml/skinnedwnd.h create mode 100644 Src/Plugins/General/gen_ml/skinning.cpp create mode 100644 Src/Plugins/General/gen_ml/skinning.h create mode 100644 Src/Plugins/General/gen_ml/stockobjects.cpp create mode 100644 Src/Plugins/General/gen_ml/stockobjects.h create mode 100644 Src/Plugins/General/gen_ml/stringvector.cpp create mode 100644 Src/Plugins/General/gen_ml/stringvector.h create mode 100644 Src/Plugins/General/gen_ml/unused.cpp create mode 100644 Src/Plugins/General/gen_ml/util.cpp create mode 100644 Src/Plugins/General/gen_ml/version.rc2 create mode 100644 Src/Plugins/General/gen_ml/view_mb.h create mode 100644 Src/Plugins/General/gen_ml/view_ml.cpp create mode 100644 Src/Plugins/General/gen_ml/wa_dlg.cpp create mode 100644 Src/Plugins/General/gen_ml/webinfo_dlg.cpp create mode 100644 Src/Plugins/General/gen_ml/webinfo_obj.cpp create mode 100644 Src/Plugins/General/gen_ml/webinfo_obj.h create mode 100644 Src/Plugins/General/gen_tray/GEN_TRAY.sln create mode 100644 Src/Plugins/General/gen_tray/GEN_TRAY.vcxproj create mode 100644 Src/Plugins/General/gen_tray/GEN_TRAY.vcxproj.filters create mode 100644 Src/Plugins/General/gen_tray/RESOURCE.H create mode 100644 Src/Plugins/General/gen_tray/TRAYCTL.C create mode 100644 Src/Plugins/General/gen_tray/WINAMPCMD.H create mode 100644 Src/Plugins/General/gen_tray/api__gen_tray.h create mode 100644 Src/Plugins/General/gen_tray/gen_tray.rc create mode 100644 Src/Plugins/General/gen_tray/icons/compact.bmp create mode 100644 Src/Plugins/General/gen_tray/icons/icon1.ico create mode 100644 Src/Plugins/General/gen_tray/icons/icon2.ico create mode 100644 Src/Plugins/General/gen_tray/icons/icon3.ico create mode 100644 Src/Plugins/General/gen_tray/icons/icon4.ico create mode 100644 Src/Plugins/General/gen_tray/icons/icon5.ico create mode 100644 Src/Plugins/General/gen_tray/icons/icon7.ico create mode 100644 Src/Plugins/General/gen_tray/icons/icon8.ico create mode 100644 Src/Plugins/General/gen_tray/icons/icon9.ico create mode 100644 Src/Plugins/General/gen_tray/version.rc2 create mode 100644 Src/Plugins/Input/in_avi/ExtendedFileInfo.cpp create mode 100644 Src/Plugins/Input/in_avi/InfoDialog.cpp create mode 100644 Src/Plugins/Input/in_avi/PlayThread.cpp create mode 100644 Src/Plugins/Input/in_avi/StreamSelector.h create mode 100644 Src/Plugins/Input/in_avi/VideoThread.cpp create mode 100644 Src/Plugins/Input/in_avi/VideoThread.h create mode 100644 Src/Plugins/Input/in_avi/api.cpp create mode 100644 Src/Plugins/Input/in_avi/api__in_avi.h create mode 100644 Src/Plugins/Input/in_avi/http_avi_reader.cpp create mode 100644 Src/Plugins/Input/in_avi/http_avi_reader.h create mode 100644 Src/Plugins/Input/in_avi/ifc_aviaudiodecoder.h create mode 100644 Src/Plugins/Input/in_avi/ifc_avivideodecoder.h create mode 100644 Src/Plugins/Input/in_avi/in_avi.rc create mode 100644 Src/Plugins/Input/in_avi/in_avi.sln create mode 100644 Src/Plugins/Input/in_avi/in_avi.vcxproj create mode 100644 Src/Plugins/Input/in_avi/in_avi.vcxproj.filters create mode 100644 Src/Plugins/Input/in_avi/interfaces.h create mode 100644 Src/Plugins/Input/in_avi/main.cpp create mode 100644 Src/Plugins/Input/in_avi/main.h create mode 100644 Src/Plugins/Input/in_avi/player.h create mode 100644 Src/Plugins/Input/in_avi/resource.h create mode 100644 Src/Plugins/Input/in_avi/svc_avidecoder.h create mode 100644 Src/Plugins/Input/in_avi/version.rc2 create mode 100644 Src/Plugins/Input/in_avi/win32_avi_reader.cpp create mode 100644 Src/Plugins/Input/in_avi/win32_avi_reader.h create mode 100644 Src/Plugins/Input/in_cdda/AUDIO.Cpp create mode 100644 Src/Plugins/Input/in_cdda/AUDIO.H create mode 100644 Src/Plugins/Input/in_cdda/CDDB.Cpp create mode 100644 Src/Plugins/Input/in_cdda/CDDB.H create mode 100644 Src/Plugins/Input/in_cdda/CDDBInterface.h create mode 100644 Src/Plugins/Input/in_cdda/CDPlay.cpp create mode 100644 Src/Plugins/Input/in_cdda/CDPlay.h create mode 100644 Src/Plugins/Input/in_cdda/CDText.cpp create mode 100644 Src/Plugins/Input/in_cdda/CONFIG.Cpp create mode 100644 Src/Plugins/Input/in_cdda/DAEPlay.cpp create mode 100644 Src/Plugins/Input/in_cdda/DAEPlay.h create mode 100644 Src/Plugins/Input/in_cdda/DB.Cpp create mode 100644 Src/Plugins/Input/in_cdda/EditCDInfo.cpp create mode 100644 Src/Plugins/Input/in_cdda/ExtendedFileInfo.cpp create mode 100644 Src/Plugins/Input/in_cdda/ExtendedRead.cpp create mode 100644 Src/Plugins/Input/in_cdda/FuncTypedefs.h create mode 100644 Src/Plugins/Input/in_cdda/MAIN.H create mode 100644 Src/Plugins/Input/in_cdda/MCI.Cpp create mode 100644 Src/Plugins/Input/in_cdda/MCIPlay.h create mode 100644 Src/Plugins/Input/in_cdda/Main.cpp create mode 100644 Src/Plugins/Input/in_cdda/PlayStatus.cpp create mode 100644 Src/Plugins/Input/in_cdda/PlayStatus.h create mode 100644 Src/Plugins/Input/in_cdda/VeritasPlay.cpp create mode 100644 Src/Plugins/Input/in_cdda/VeritasPlay.h create mode 100644 Src/Plugins/Input/in_cdda/WindacPlay.cpp create mode 100644 Src/Plugins/Input/in_cdda/WindacPlay.h create mode 100644 Src/Plugins/Input/in_cdda/api__in_cdda.h create mode 100644 Src/Plugins/Input/in_cdda/cddbevnt.cpp create mode 100644 Src/Plugins/Input/in_cdda/cddbevnt.h create mode 100644 Src/Plugins/Input/in_cdda/cddbui.cpp create mode 100644 Src/Plugins/Input/in_cdda/cddbui.h create mode 100644 Src/Plugins/Input/in_cdda/discid.cpp create mode 100644 Src/Plugins/Input/in_cdda/grabwnd.cpp create mode 100644 Src/Plugins/Input/in_cdda/grabwnd.h create mode 100644 Src/Plugins/Input/in_cdda/in_cdda.rc create mode 100644 Src/Plugins/Input/in_cdda/in_cdda.sln create mode 100644 Src/Plugins/Input/in_cdda/in_cdda.vcxproj create mode 100644 Src/Plugins/Input/in_cdda/in_cdda.vcxproj.filters create mode 100644 Src/Plugins/Input/in_cdda/inst.nsi create mode 100644 Src/Plugins/Input/in_cdda/nde_cd.cpp create mode 100644 Src/Plugins/Input/in_cdda/ntddcdrm.h create mode 100644 Src/Plugins/Input/in_cdda/resource.h create mode 100644 Src/Plugins/Input/in_cdda/scsi_id.cpp create mode 100644 Src/Plugins/Input/in_cdda/util.cpp create mode 100644 Src/Plugins/Input/in_cdda/version.rc2 create mode 100644 Src/Plugins/Input/in_cdda/windac/Aspi.h create mode 100644 Src/Plugins/Input/in_cdda/windac/Aspifunc.cpp create mode 100644 Src/Plugins/Input/in_cdda/windac/Aspifunc.h create mode 100644 Src/Plugins/Input/in_cdda/windac/Dac32.cpp create mode 100644 Src/Plugins/Input/in_cdda/windac/Dac32.dsp create mode 100644 Src/Plugins/Input/in_cdda/windac/Dac32.h create mode 100644 Src/Plugins/Input/in_cdda/windac/Dac32.mak create mode 100644 Src/Plugins/Input/in_cdda/windac/Dac32.rc create mode 100644 Src/Plugins/Input/in_cdda/windac/NTScsi.cpp create mode 100644 Src/Plugins/Input/in_cdda/windac/NTScsi.h create mode 100644 Src/Plugins/Input/in_cdda/windac/RESOURCE.H create mode 100644 Src/Plugins/Input/in_cdda/windac/SCSIDEFS.H create mode 100644 Src/Plugins/Input/in_cdda/windac/Winaspi.h create mode 100644 Src/Plugins/Input/in_cdda/workorder.cpp create mode 100644 Src/Plugins/Input/in_cdda/workorder.h create mode 100644 Src/Plugins/Input/in_dshow/CSampleCB.h create mode 100644 Src/Plugins/Input/in_dshow/CWAAudioRenderer.cpp create mode 100644 Src/Plugins/Input/in_dshow/CWAAudioRenderer.h create mode 100644 Src/Plugins/Input/in_dshow/CWAVideoRenderer.cpp create mode 100644 Src/Plugins/Input/in_dshow/CWAVideoRenderer.h create mode 100644 Src/Plugins/Input/in_dshow/DSTrackSelector.cpp create mode 100644 Src/Plugins/Input/in_dshow/DSTrackSelector.h create mode 100644 Src/Plugins/Input/in_dshow/Header.cpp create mode 100644 Src/Plugins/Input/in_dshow/Header.h create mode 100644 Src/Plugins/Input/in_dshow/IN2.H create mode 100644 Src/Plugins/Input/in_dshow/Main.cpp create mode 100644 Src/Plugins/Input/in_dshow/Main.h create mode 100644 Src/Plugins/Input/in_dshow/audioswitch.cpp create mode 100644 Src/Plugins/Input/in_dshow/audioswitch.h create mode 100644 Src/Plugins/Input/in_dshow/base/amextra.cpp create mode 100644 Src/Plugins/Input/in_dshow/base/amextra.h create mode 100644 Src/Plugins/Input/in_dshow/base/amfilter.cpp create mode 100644 Src/Plugins/Input/in_dshow/base/amfilter.h create mode 100644 Src/Plugins/Input/in_dshow/base/amvideo.cpp create mode 100644 Src/Plugins/Input/in_dshow/base/arithutil.cpp create mode 100644 Src/Plugins/Input/in_dshow/base/cache.h create mode 100644 Src/Plugins/Input/in_dshow/base/checkbmi.h create mode 100644 Src/Plugins/Input/in_dshow/base/combase.cpp create mode 100644 Src/Plugins/Input/in_dshow/base/combase.h create mode 100644 Src/Plugins/Input/in_dshow/base/cprop.cpp create mode 100644 Src/Plugins/Input/in_dshow/base/cprop.h create mode 100644 Src/Plugins/Input/in_dshow/base/ctlutil.cpp create mode 100644 Src/Plugins/Input/in_dshow/base/ctlutil.h create mode 100644 Src/Plugins/Input/in_dshow/base/ddmm.cpp create mode 100644 Src/Plugins/Input/in_dshow/base/ddmm.h create mode 100644 Src/Plugins/Input/in_dshow/base/dllentry.cpp create mode 100644 Src/Plugins/Input/in_dshow/base/dllsetup.cpp create mode 100644 Src/Plugins/Input/in_dshow/base/dllsetup.h create mode 100644 Src/Plugins/Input/in_dshow/base/dxmperf.h create mode 100644 Src/Plugins/Input/in_dshow/base/fourcc.h create mode 100644 Src/Plugins/Input/in_dshow/base/measure.h create mode 100644 Src/Plugins/Input/in_dshow/base/msgthrd.h create mode 100644 Src/Plugins/Input/in_dshow/base/mtype.cpp create mode 100644 Src/Plugins/Input/in_dshow/base/mtype.h create mode 100644 Src/Plugins/Input/in_dshow/base/outputq.cpp create mode 100644 Src/Plugins/Input/in_dshow/base/outputq.h create mode 100644 Src/Plugins/Input/in_dshow/base/perflog.cpp create mode 100644 Src/Plugins/Input/in_dshow/base/perflog.h create mode 100644 Src/Plugins/Input/in_dshow/base/perfstruct.h create mode 100644 Src/Plugins/Input/in_dshow/base/pstream.cpp create mode 100644 Src/Plugins/Input/in_dshow/base/pstream.h create mode 100644 Src/Plugins/Input/in_dshow/base/pullpin.cpp create mode 100644 Src/Plugins/Input/in_dshow/base/pullpin.h create mode 100644 Src/Plugins/Input/in_dshow/base/refclock.cpp create mode 100644 Src/Plugins/Input/in_dshow/base/refclock.h create mode 100644 Src/Plugins/Input/in_dshow/base/reftime.h create mode 100644 Src/Plugins/Input/in_dshow/base/renbase.cpp create mode 100644 Src/Plugins/Input/in_dshow/base/renbase.h create mode 100644 Src/Plugins/Input/in_dshow/base/schedule.cpp create mode 100644 Src/Plugins/Input/in_dshow/base/schedule.h create mode 100644 Src/Plugins/Input/in_dshow/base/seekpt.cpp create mode 100644 Src/Plugins/Input/in_dshow/base/seekpt.h create mode 100644 Src/Plugins/Input/in_dshow/base/source.cpp create mode 100644 Src/Plugins/Input/in_dshow/base/source.h create mode 100644 Src/Plugins/Input/in_dshow/base/streams.h create mode 100644 Src/Plugins/Input/in_dshow/base/strmctl.cpp create mode 100644 Src/Plugins/Input/in_dshow/base/strmctl.h create mode 100644 Src/Plugins/Input/in_dshow/base/sysclock.cpp create mode 100644 Src/Plugins/Input/in_dshow/base/sysclock.h create mode 100644 Src/Plugins/Input/in_dshow/base/transfrm.cpp create mode 100644 Src/Plugins/Input/in_dshow/base/transfrm.h create mode 100644 Src/Plugins/Input/in_dshow/base/transip.cpp create mode 100644 Src/Plugins/Input/in_dshow/base/transip.h create mode 100644 Src/Plugins/Input/in_dshow/base/videoctl.cpp create mode 100644 Src/Plugins/Input/in_dshow/base/videoctl.h create mode 100644 Src/Plugins/Input/in_dshow/base/vtrans.cpp create mode 100644 Src/Plugins/Input/in_dshow/base/vtrans.h create mode 100644 Src/Plugins/Input/in_dshow/base/winctrl.cpp create mode 100644 Src/Plugins/Input/in_dshow/base/winctrl.h create mode 100644 Src/Plugins/Input/in_dshow/base/winutil.cpp create mode 100644 Src/Plugins/Input/in_dshow/base/winutil.h create mode 100644 Src/Plugins/Input/in_dshow/base/wxdebug.cpp create mode 100644 Src/Plugins/Input/in_dshow/base/wxdebug.h create mode 100644 Src/Plugins/Input/in_dshow/base/wxlist.cpp create mode 100644 Src/Plugins/Input/in_dshow/base/wxlist.h create mode 100644 Src/Plugins/Input/in_dshow/base/wxutil.cpp create mode 100644 Src/Plugins/Input/in_dshow/base/wxutil.h create mode 100644 Src/Plugins/Input/in_dshow/config.cpp create mode 100644 Src/Plugins/Input/in_dshow/dshowrender.cpp create mode 100644 Src/Plugins/Input/in_dshow/header_asf.cpp create mode 100644 Src/Plugins/Input/in_dshow/header_asf.h create mode 100644 Src/Plugins/Input/in_dshow/header_avi.cpp create mode 100644 Src/Plugins/Input/in_dshow/header_avi.h create mode 100644 Src/Plugins/Input/in_dshow/header_mpg.cpp create mode 100644 Src/Plugins/Input/in_dshow/header_mpg.h create mode 100644 Src/Plugins/Input/in_dshow/header_wav.cpp create mode 100644 Src/Plugins/Input/in_dshow/header_wav.h create mode 100644 Src/Plugins/Input/in_dshow/in_dshow.dsp create mode 100644 Src/Plugins/Input/in_dshow/in_dshow.dsw create mode 100644 Src/Plugins/Input/in_dshow/in_dshow.rc create mode 100644 Src/Plugins/Input/in_dshow/in_dshow.sln create mode 100644 Src/Plugins/Input/in_dshow/in_dshow.vcxproj create mode 100644 Src/Plugins/Input/in_dshow/in_dshow.vcxproj.filters create mode 100644 Src/Plugins/Input/in_dshow/info.cpp create mode 100644 Src/Plugins/Input/in_dshow/resource.h create mode 100644 Src/Plugins/Input/in_dshow/version.rc2 create mode 100644 Src/Plugins/Input/in_flac/About.cpp create mode 100644 Src/Plugins/Input/in_flac/AlbumArt.cpp create mode 100644 Src/Plugins/Input/in_flac/AlbumArt.h create mode 100644 Src/Plugins/Input/in_flac/DecodeThread.cpp create mode 100644 Src/Plugins/Input/in_flac/ExtendedFileInfo.cpp create mode 100644 Src/Plugins/Input/in_flac/ExtendedRead.cpp create mode 100644 Src/Plugins/Input/in_flac/FLACFileCallbacks.cpp create mode 100644 Src/Plugins/Input/in_flac/FLACFileCallbacks.h create mode 100644 Src/Plugins/Input/in_flac/FileInfo.cpp create mode 100644 Src/Plugins/Input/in_flac/Metadata.cpp create mode 100644 Src/Plugins/Input/in_flac/Metadata.h create mode 100644 Src/Plugins/Input/in_flac/Preferences.cpp create mode 100644 Src/Plugins/Input/in_flac/QuickBuf.h create mode 100644 Src/Plugins/Input/in_flac/RawReader.cpp create mode 100644 Src/Plugins/Input/in_flac/RawReader.h create mode 100644 Src/Plugins/Input/in_flac/Stopper.cpp create mode 100644 Src/Plugins/Input/in_flac/Stopper.h create mode 100644 Src/Plugins/Input/in_flac/StreamFileWin32.cpp create mode 100644 Src/Plugins/Input/in_flac/StreamFileWin32.h create mode 100644 Src/Plugins/Input/in_flac/api__in_flv.h create mode 100644 Src/Plugins/Input/in_flac/in_flac.rc create mode 100644 Src/Plugins/Input/in_flac/in_flac.sln create mode 100644 Src/Plugins/Input/in_flac/in_flac.vcxproj create mode 100644 Src/Plugins/Input/in_flac/in_flac.vcxproj.filters create mode 100644 Src/Plugins/Input/in_flac/main.cpp create mode 100644 Src/Plugins/Input/in_flac/main.h create mode 100644 Src/Plugins/Input/in_flac/mkv_flac_decoder.cpp create mode 100644 Src/Plugins/Input/in_flac/mkv_flac_decoder.h create mode 100644 Src/Plugins/Input/in_flac/resource.h create mode 100644 Src/Plugins/Input/in_flac/version.rc2 create mode 100644 Src/Plugins/Input/in_flv/AMFDispatch.cpp create mode 100644 Src/Plugins/Input/in_flv/AMFDispatch.h create mode 100644 Src/Plugins/Input/in_flv/AMFObject.cpp create mode 100644 Src/Plugins/Input/in_flv/AMFObject.h create mode 100644 Src/Plugins/Input/in_flv/BackgroundDownloader.cpp create mode 100644 Src/Plugins/Input/in_flv/BackgroundDownloader.h create mode 100644 Src/Plugins/Input/in_flv/ExtendedInfo.cpp create mode 100644 Src/Plugins/Input/in_flv/FLVAudioHeader.cpp create mode 100644 Src/Plugins/Input/in_flv/FLVAudioHeader.h create mode 100644 Src/Plugins/Input/in_flv/FLVCOM.cpp create mode 100644 Src/Plugins/Input/in_flv/FLVCOM.h create mode 100644 Src/Plugins/Input/in_flv/FLVHeader.cpp create mode 100644 Src/Plugins/Input/in_flv/FLVHeader.h create mode 100644 Src/Plugins/Input/in_flv/FLVMetadata.cpp create mode 100644 Src/Plugins/Input/in_flv/FLVMetadata.h create mode 100644 Src/Plugins/Input/in_flv/FLVProcessor.cpp create mode 100644 Src/Plugins/Input/in_flv/FLVProcessor.h create mode 100644 Src/Plugins/Input/in_flv/FLVReader.cpp create mode 100644 Src/Plugins/Input/in_flv/FLVReader.h create mode 100644 Src/Plugins/Input/in_flv/FLVStreamHeader.cpp create mode 100644 Src/Plugins/Input/in_flv/FLVStreamHeader.h create mode 100644 Src/Plugins/Input/in_flv/FLVUtil.h create mode 100644 Src/Plugins/Input/in_flv/FLVVideoHeader.cpp create mode 100644 Src/Plugins/Input/in_flv/FLVVideoHeader.h create mode 100644 Src/Plugins/Input/in_flv/FileProcessor.cpp create mode 100644 Src/Plugins/Input/in_flv/FileProcessor.h create mode 100644 Src/Plugins/Input/in_flv/PlayThread.cpp create mode 100644 Src/Plugins/Input/in_flv/ProgressiveProcessor.cpp create mode 100644 Src/Plugins/Input/in_flv/ProgressiveProcessor.h create mode 100644 Src/Plugins/Input/in_flv/StreamProcessor.cpp create mode 100644 Src/Plugins/Input/in_flv/StreamProcessor.h create mode 100644 Src/Plugins/Input/in_flv/VideoThread.cpp create mode 100644 Src/Plugins/Input/in_flv/VideoThread.h create mode 100644 Src/Plugins/Input/in_flv/api__in_flv.h create mode 100644 Src/Plugins/Input/in_flv/ifc_flvaudiodecoder.h create mode 100644 Src/Plugins/Input/in_flv/ifc_flvvideodecoder.h create mode 100644 Src/Plugins/Input/in_flv/in_flv.rc create mode 100644 Src/Plugins/Input/in_flv/in_flv.sln create mode 100644 Src/Plugins/Input/in_flv/in_flv.vcxproj create mode 100644 Src/Plugins/Input/in_flv/in_flv.vcxproj.filters create mode 100644 Src/Plugins/Input/in_flv/main.cpp create mode 100644 Src/Plugins/Input/in_flv/main.h create mode 100644 Src/Plugins/Input/in_flv/resource.h create mode 100644 Src/Plugins/Input/in_flv/svc_flvdecoder.h create mode 100644 Src/Plugins/Input/in_flv/version.rc2 create mode 100644 Src/Plugins/Input/in_linein/LineIn.cpp create mode 100644 Src/Plugins/Input/in_linein/LineIn.h create mode 100644 Src/Plugins/Input/in_linein/audio.cpp create mode 100644 Src/Plugins/Input/in_linein/audio.h create mode 100644 Src/Plugins/Input/in_linein/in_linein.cpp create mode 100644 Src/Plugins/Input/in_linein/in_linein.rc create mode 100644 Src/Plugins/Input/in_linein/in_linein.sln create mode 100644 Src/Plugins/Input/in_linein/in_linein.vcxproj create mode 100644 Src/Plugins/Input/in_linein/in_linein.vcxproj.filters create mode 100644 Src/Plugins/Input/in_linein/main.h create mode 100644 Src/Plugins/Input/in_linein/resource.h create mode 100644 Src/Plugins/Input/in_linein/version.rc2 create mode 100644 Src/Plugins/Input/in_midi/CompressionUtility.cpp create mode 100644 Src/Plugins/Input/in_midi/CompressionUtility.h create mode 100644 Src/Plugins/Input/in_midi/In2.h create mode 100644 Src/Plugins/Input/in_midi/cleaner.cpp create mode 100644 Src/Plugins/Input/in_midi/cmf.cpp create mode 100644 Src/Plugins/Input/in_midi/config.cpp create mode 100644 Src/Plugins/Input/in_midi/core_api.h create mode 100644 Src/Plugins/Input/in_midi/cvt.h create mode 100644 Src/Plugins/Input/in_midi/dmplugin.h create mode 100644 Src/Plugins/Input/in_midi/dmusicc.h create mode 100644 Src/Plugins/Input/in_midi/dmusicf.h create mode 100644 Src/Plugins/Input/in_midi/dmusici.h create mode 100644 Src/Plugins/Input/in_midi/fakedsound.cpp create mode 100644 Src/Plugins/Input/in_midi/fakedsound.h create mode 100644 Src/Plugins/Input/in_midi/genres.c create mode 100644 Src/Plugins/Input/in_midi/genres.h create mode 100644 Src/Plugins/Input/in_midi/gmf.cpp create mode 100644 Src/Plugins/Input/in_midi/guids.cpp create mode 100644 Src/Plugins/Input/in_midi/hmi.cpp create mode 100644 Src/Plugins/Input/in_midi/hmp.cpp create mode 100644 Src/Plugins/Input/in_midi/in_midi.rc create mode 100644 Src/Plugins/Input/in_midi/in_midi.sln create mode 100644 Src/Plugins/Input/in_midi/in_midi.vcxproj create mode 100644 Src/Plugins/Input/in_midi/in_midi.vcxproj.filters create mode 100644 Src/Plugins/Input/in_midi/info.cpp create mode 100644 Src/Plugins/Input/in_midi/main.cpp create mode 100644 Src/Plugins/Input/in_midi/main.h create mode 100644 Src/Plugins/Input/in_midi/midi_driver.cpp create mode 100644 Src/Plugins/Input/in_midi/midifile.cpp create mode 100644 Src/Plugins/Input/in_midi/midifile.h create mode 100644 Src/Plugins/Input/in_midi/midiinfo.cpp create mode 100644 Src/Plugins/Input/in_midi/midiout_helper.cpp create mode 100644 Src/Plugins/Input/in_midi/mids.cpp create mode 100644 Src/Plugins/Input/in_midi/mus.cpp create mode 100644 Src/Plugins/Input/in_midi/out_dmusic.cpp create mode 100644 Src/Plugins/Input/in_midi/out_midi.cpp create mode 100644 Src/Plugins/Input/in_midi/resource.h create mode 100644 Src/Plugins/Input/in_midi/sampling.cpp create mode 100644 Src/Plugins/Input/in_midi/seq.cpp create mode 100644 Src/Plugins/Input/in_midi/seq.h create mode 100644 Src/Plugins/Input/in_midi/utils.cpp create mode 100644 Src/Plugins/Input/in_midi/utils.h create mode 100644 Src/Plugins/Input/in_midi/version.rc2 create mode 100644 Src/Plugins/Input/in_midi/wa2.cpp create mode 100644 Src/Plugins/Input/in_midi/wa3.cpp create mode 100644 Src/Plugins/Input/in_midi/xmi.cpp create mode 100644 Src/Plugins/Input/in_mkv/ExtendedFileInfo.cpp create mode 100644 Src/Plugins/Input/in_mkv/InfoDialog.cpp create mode 100644 Src/Plugins/Input/in_mkv/MKVDuration.cpp create mode 100644 Src/Plugins/Input/in_mkv/MKVDuration.h create mode 100644 Src/Plugins/Input/in_mkv/MKVInfo.cpp create mode 100644 Src/Plugins/Input/in_mkv/MKVInfo.h create mode 100644 Src/Plugins/Input/in_mkv/MKVPlayer.h create mode 100644 Src/Plugins/Input/in_mkv/PlayThread.cpp create mode 100644 Src/Plugins/Input/in_mkv/api__in_mkv.cpp create mode 100644 Src/Plugins/Input/in_mkv/api__in_mkv.h create mode 100644 Src/Plugins/Input/in_mkv/ifc_mkvaudiodecoder.h create mode 100644 Src/Plugins/Input/in_mkv/ifc_mkvvideodecoder.h create mode 100644 Src/Plugins/Input/in_mkv/in_mkv.rc create mode 100644 Src/Plugins/Input/in_mkv/in_mkv.sln create mode 100644 Src/Plugins/Input/in_mkv/in_mkv.vcxproj create mode 100644 Src/Plugins/Input/in_mkv/in_mkv.vcxproj.filters create mode 100644 Src/Plugins/Input/in_mkv/main.cpp create mode 100644 Src/Plugins/Input/in_mkv/main.h create mode 100644 Src/Plugins/Input/in_mkv/resource.h create mode 100644 Src/Plugins/Input/in_mkv/svc_mkvdecoder.h create mode 100644 Src/Plugins/Input/in_mkv/version.rc2 create mode 100644 Src/Plugins/Input/in_mod-openmpt/ExtendedInfo.cpp create mode 100644 Src/Plugins/Input/in_mod-openmpt/ExtendedRead.cpp create mode 100644 Src/Plugins/Input/in_mod-openmpt/MODPlayer.h create mode 100644 Src/Plugins/Input/in_mod-openmpt/MODThread.cpp create mode 100644 Src/Plugins/Input/in_mod-openmpt/api__in_mod.h create mode 100644 Src/Plugins/Input/in_mod-openmpt/in_mod-openmpt.rc create mode 100644 Src/Plugins/Input/in_mod-openmpt/in_mod-openmpt.sln create mode 100644 Src/Plugins/Input/in_mod-openmpt/in_mod-openmpt.vcxproj create mode 100644 Src/Plugins/Input/in_mod-openmpt/in_mod-openmpt.vcxproj.filters create mode 100644 Src/Plugins/Input/in_mod-openmpt/main.cpp create mode 100644 Src/Plugins/Input/in_mod-openmpt/nxfile-callbacks.cpp create mode 100644 Src/Plugins/Input/in_mod-openmpt/resource.h create mode 100644 Src/Plugins/Input/in_mod-openmpt/version.rc2 create mode 100644 Src/Plugins/Input/in_mod/fir_proc.cpp create mode 100644 Src/Plugins/Input/in_mod/fir_table.h create mode 100644 Src/Plugins/Input/in_mod/mikamp/in_mod.rc create mode 100644 Src/Plugins/Input/in_mod/mikamp/include/Main.h create mode 100644 Src/Plugins/Input/in_mod/mikamp/include/in2.h create mode 100644 Src/Plugins/Input/in_mod/mikamp/int64.lib create mode 100644 Src/Plugins/Input/in_mod/mikamp/mikamp.dsp create mode 100644 Src/Plugins/Input/in_mod/mikamp/mikamp.sln create mode 100644 Src/Plugins/Input/in_mod/mikamp/mikamp.vcproj create mode 100644 Src/Plugins/Input/in_mod/mikamp/playbackconfig.cpp create mode 100644 Src/Plugins/Input/in_mod/mikamp/resource.h create mode 100644 Src/Plugins/Input/in_mod/mikamp/src/Config.c create mode 100644 Src/Plugins/Input/in_mod/mikamp/src/ExtendedRead.cpp create mode 100644 Src/Plugins/Input/in_mod/mikamp/src/Info.c create mode 100644 Src/Plugins/Input/in_mod/mikamp/src/Main.c create mode 100644 Src/Plugins/Input/in_mod/mikamp/src/api.h create mode 100644 Src/Plugins/Input/in_mod/mikamp/src/drv_amp.c create mode 100644 Src/Plugins/Input/in_mod/mikamp/src/drv_buffer.cpp create mode 100644 Src/Plugins/Input/in_mod/mikamp/src/drv_buffer.h create mode 100644 Src/Plugins/Input/in_mod/mikamp/src/rf_wrapper.c create mode 100644 Src/Plugins/Input/in_mod/mikamp/version.rc2 create mode 100644 Src/Plugins/Input/in_mp3/AACFrame.cpp create mode 100644 Src/Plugins/Input/in_mp3/AACFrame.h create mode 100644 Src/Plugins/Input/in_mp3/AlbumArt.cpp create mode 100644 Src/Plugins/Input/in_mp3/AlbumArt.h create mode 100644 Src/Plugins/Input/in_mp3/CVbriHeader.cpp create mode 100644 Src/Plugins/Input/in_mp3/CVbriHeader.h create mode 100644 Src/Plugins/Input/in_mp3/CreateFile.cpp create mode 100644 Src/Plugins/Input/in_mp3/CreateFile.h create mode 100644 Src/Plugins/Input/in_mp3/DXHEAD.C create mode 100644 Src/Plugins/Input/in_mp3/DXHEAD.H create mode 100644 Src/Plugins/Input/in_mp3/DecodeThread.cpp create mode 100644 Src/Plugins/Input/in_mp3/DecodeThread.h create mode 100644 Src/Plugins/Input/in_mp3/ExtendedInfo.cpp create mode 100644 Src/Plugins/Input/in_mp3/ExtendedRead.cpp create mode 100644 Src/Plugins/Input/in_mp3/FactoryHelper.h create mode 100644 Src/Plugins/Input/in_mp3/ID3Info.cpp create mode 100644 Src/Plugins/Input/in_mp3/ID3v1.cpp create mode 100644 Src/Plugins/Input/in_mp3/ID3v1.h create mode 100644 Src/Plugins/Input/in_mp3/ID3v2.cpp create mode 100644 Src/Plugins/Input/in_mp3/ID3v2.h create mode 100644 Src/Plugins/Input/in_mp3/IN2.H create mode 100644 Src/Plugins/Input/in_mp3/LAMEinfo.cpp create mode 100644 Src/Plugins/Input/in_mp3/LAMEinfo.h create mode 100644 Src/Plugins/Input/in_mp3/Lyrics3.cpp create mode 100644 Src/Plugins/Input/in_mp3/Lyrics3.h create mode 100644 Src/Plugins/Input/in_mp3/MP3Info.cpp create mode 100644 Src/Plugins/Input/in_mp3/MP3Info.h create mode 100644 Src/Plugins/Input/in_mp3/Metadata.cpp create mode 100644 Src/Plugins/Input/in_mp3/Metadata.h create mode 100644 Src/Plugins/Input/in_mp3/MetadataFactory.cpp create mode 100644 Src/Plugins/Input/in_mp3/MetadataFactory.h create mode 100644 Src/Plugins/Input/in_mp3/OFL.cpp create mode 100644 Src/Plugins/Input/in_mp3/OFL.h create mode 100644 Src/Plugins/Input/in_mp3/RawMediaReader.cpp create mode 100644 Src/Plugins/Input/in_mp3/RawMediaReader.h create mode 100644 Src/Plugins/Input/in_mp3/Stopper.cpp create mode 100644 Src/Plugins/Input/in_mp3/Stopper.h create mode 100644 Src/Plugins/Input/in_mp3/StreamInfo.cpp create mode 100644 Src/Plugins/Input/in_mp3/WasabiMetadata.cpp create mode 100644 Src/Plugins/Input/in_mp3/WasabiMetadata.h create mode 100644 Src/Plugins/Input/in_mp3/about.cpp create mode 100644 Src/Plugins/Input/in_mp3/adts.h create mode 100644 Src/Plugins/Input/in_mp3/adts_mp2.cpp create mode 100644 Src/Plugins/Input/in_mp3/adts_mp2.h create mode 100644 Src/Plugins/Input/in_mp3/adts_vlb.cpp create mode 100644 Src/Plugins/Input/in_mp3/adts_vlb.h create mode 100644 Src/Plugins/Input/in_mp3/apev2.cpp create mode 100644 Src/Plugins/Input/in_mp3/apev2.h create mode 100644 Src/Plugins/Input/in_mp3/api__in_mp3.h create mode 100644 Src/Plugins/Input/in_mp3/config.cpp create mode 100644 Src/Plugins/Input/in_mp3/config.h create mode 100644 Src/Plugins/Input/in_mp3/giofile.cpp create mode 100644 Src/Plugins/Input/in_mp3/giofile.h create mode 100644 Src/Plugins/Input/in_mp3/graphics/filterWater.cpp create mode 100644 Src/Plugins/Input/in_mp3/graphics/filterWater.h create mode 100644 Src/Plugins/Input/in_mp3/graphics/image.cpp create mode 100644 Src/Plugins/Input/in_mp3/graphics/image.h create mode 100644 Src/Plugins/Input/in_mp3/graphics/imageFilters.cpp create mode 100644 Src/Plugins/Input/in_mp3/graphics/imageFilters.h create mode 100644 Src/Plugins/Input/in_mp3/id3.cpp create mode 100644 Src/Plugins/Input/in_mp3/id3.h create mode 100644 Src/Plugins/Input/in_mp3/id3dlg.cpp create mode 100644 Src/Plugins/Input/in_mp3/ifc_mpeg_stream_reader.h create mode 100644 Src/Plugins/Input/in_mp3/in_mp3.rc create mode 100644 Src/Plugins/Input/in_mp3/in_mp3.sln create mode 100644 Src/Plugins/Input/in_mp3/in_mp3.vcxproj create mode 100644 Src/Plugins/Input/in_mp3/in_mp3.vcxproj.filters create mode 100644 Src/Plugins/Input/in_mp3/main.cpp create mode 100644 Src/Plugins/Input/in_mp3/main.h create mode 100644 Src/Plugins/Input/in_mp3/mp4.cpp create mode 100644 Src/Plugins/Input/in_mp3/mpegutil.cpp create mode 100644 Src/Plugins/Input/in_mp3/mpegutil.h create mode 100644 Src/Plugins/Input/in_mp3/pdtimer.cpp create mode 100644 Src/Plugins/Input/in_mp3/pdtimer.h create mode 100644 Src/Plugins/Input/in_mp3/proxydt.h create mode 100644 Src/Plugins/Input/in_mp3/resource.h create mode 100644 Src/Plugins/Input/in_mp3/resources/aboutMP3_256.bmp create mode 100644 Src/Plugins/Input/in_mp3/resources/hand.cur create mode 100644 Src/Plugins/Input/in_mp3/resources/llama.bmp create mode 100644 Src/Plugins/Input/in_mp3/resources/logoCodTech.90x42.bmp create mode 100644 Src/Plugins/Input/in_mp3/resources/logoIIS_127x42.bmp create mode 100644 Src/Plugins/Input/in_mp3/resources/logoId3v2_44x42.bmp create mode 100644 Src/Plugins/Input/in_mp3/resources/logoMp3surround_101x42.bmp create mode 100644 Src/Plugins/Input/in_mp3/resources/logoWA.bmp create mode 100644 Src/Plugins/Input/in_mp3/tagz.cpp create mode 100644 Src/Plugins/Input/in_mp3/tagz.h create mode 100644 Src/Plugins/Input/in_mp3/titlelist.cpp create mode 100644 Src/Plugins/Input/in_mp3/todo.txt create mode 100644 Src/Plugins/Input/in_mp3/uvox_3901.cpp create mode 100644 Src/Plugins/Input/in_mp3/uvox_3901.h create mode 100644 Src/Plugins/Input/in_mp3/uvox_3902.cpp create mode 100644 Src/Plugins/Input/in_mp3/uvox_3902.h create mode 100644 Src/Plugins/Input/in_mp3/version.rc2 create mode 100644 Src/Plugins/Input/in_mp4/AlbumArt.cpp create mode 100644 Src/Plugins/Input/in_mp4/AlbumArt.h create mode 100644 Src/Plugins/Input/in_mp4/AudioSample.h create mode 100644 Src/Plugins/Input/in_mp4/ExtendedInfo.cpp create mode 100644 Src/Plugins/Input/in_mp4/ExtendedRead.cpp create mode 100644 Src/Plugins/Input/in_mp4/Main.cpp create mode 100644 Src/Plugins/Input/in_mp4/Main.h create mode 100644 Src/Plugins/Input/in_mp4/PlayThread.cpp create mode 100644 Src/Plugins/Input/in_mp4/RawMediaReader.cpp create mode 100644 Src/Plugins/Input/in_mp4/RawMediaReader.h create mode 100644 Src/Plugins/Input/in_mp4/Stopper.cpp create mode 100644 Src/Plugins/Input/in_mp4/Stopper.h create mode 100644 Src/Plugins/Input/in_mp4/VideoThread.cpp create mode 100644 Src/Plugins/Input/in_mp4/VideoThread.h create mode 100644 Src/Plugins/Input/in_mp4/VirtualIO.cpp create mode 100644 Src/Plugins/Input/in_mp4/VirtualIO.h create mode 100644 Src/Plugins/Input/in_mp4/api.cpp create mode 100644 Src/Plugins/Input/in_mp4/api__in_mp4.h create mode 100644 Src/Plugins/Input/in_mp4/config.cpp create mode 100644 Src/Plugins/Input/in_mp4/in_mp4.rc create mode 100644 Src/Plugins/Input/in_mp4/in_mp4.sln create mode 100644 Src/Plugins/Input/in_mp4/in_mp4.vcxproj create mode 100644 Src/Plugins/Input/in_mp4/in_mp4.vcxproj.filters create mode 100644 Src/Plugins/Input/in_mp4/infobox.cpp create mode 100644 Src/Plugins/Input/in_mp4/mp4.cpp create mode 100644 Src/Plugins/Input/in_mp4/mpeg4audio.cpp create mode 100644 Src/Plugins/Input/in_mp4/mpeg4audio.h create mode 100644 Src/Plugins/Input/in_mp4/mpeg4video.cpp create mode 100644 Src/Plugins/Input/in_mp4/mpeg4video.h create mode 100644 Src/Plugins/Input/in_mp4/resource.h create mode 100644 Src/Plugins/Input/in_mp4/version.rc2 create mode 100644 Src/Plugins/Input/in_nsv/BigLib.cpp create mode 100644 Src/Plugins/Input/in_nsv/Main.cpp create mode 100644 Src/Plugins/Input/in_nsv/api.h create mode 100644 Src/Plugins/Input/in_nsv/config.cpp create mode 100644 Src/Plugins/Input/in_nsv/in_nsv.rc create mode 100644 Src/Plugins/Input/in_nsv/in_nsv.sln create mode 100644 Src/Plugins/Input/in_nsv/in_nsv.vcxproj create mode 100644 Src/Plugins/Input/in_nsv/in_nsv.vcxproj.filters create mode 100644 Src/Plugins/Input/in_nsv/infodlg.cpp create mode 100644 Src/Plugins/Input/in_nsv/nsv_logo.bmp create mode 100644 Src/Plugins/Input/in_nsv/proxydt.h create mode 100644 Src/Plugins/Input/in_nsv/resource.h create mode 100644 Src/Plugins/Input/in_nsv/version.rc2 create mode 100644 Src/Plugins/Input/in_swf/ExtendedFileInfo.cpp create mode 100644 Src/Plugins/Input/in_swf/FLVExternalInterface.cpp create mode 100644 Src/Plugins/Input/in_swf/FLVExternalInterface.h create mode 100644 Src/Plugins/Input/in_swf/FlashDispInterface.h create mode 100644 Src/Plugins/Input/in_swf/SWFContainer.cpp create mode 100644 Src/Plugins/Input/in_swf/SWFContainer.h create mode 100644 Src/Plugins/Input/in_swf/SWFParameters.cpp create mode 100644 Src/Plugins/Input/in_swf/SWFParameters.h create mode 100644 Src/Plugins/Input/in_swf/SWFThread.cpp create mode 100644 Src/Plugins/Input/in_swf/XMLString.cpp create mode 100644 Src/Plugins/Input/in_swf/XMLString.h create mode 100644 Src/Plugins/Input/in_swf/api.h create mode 100644 Src/Plugins/Input/in_swf/flash9e.tlh create mode 100644 Src/Plugins/Input/in_swf/in_swf.rc create mode 100644 Src/Plugins/Input/in_swf/in_swf.sln create mode 100644 Src/Plugins/Input/in_swf/in_swf.vcxproj create mode 100644 Src/Plugins/Input/in_swf/in_swf.vcxproj.filters create mode 100644 Src/Plugins/Input/in_swf/main.cpp create mode 100644 Src/Plugins/Input/in_swf/main.h create mode 100644 Src/Plugins/Input/in_swf/resource.h create mode 100644 Src/Plugins/Input/in_swf/version.rc2 create mode 100644 Src/Plugins/Input/in_swf/winampFLV.fla create mode 100644 Src/Plugins/Input/in_vorbis/DlgBase.cpp create mode 100644 Src/Plugins/Input/in_vorbis/DlgBase.h create mode 100644 Src/Plugins/Input/in_vorbis/ExtendedRead.cpp create mode 100644 Src/Plugins/Input/in_vorbis/about.cpp create mode 100644 Src/Plugins/Input/in_vorbis/api__in_vorbis.h create mode 100644 Src/Plugins/Input/in_vorbis/c_string.cpp create mode 100644 Src/Plugins/Input/in_vorbis/c_string.h create mode 100644 Src/Plugins/Input/in_vorbis/chainedstream_parse.cpp create mode 100644 Src/Plugins/Input/in_vorbis/config.cpp create mode 100644 Src/Plugins/Input/in_vorbis/core_api.h create mode 100644 Src/Plugins/Input/in_vorbis/decoder.cpp create mode 100644 Src/Plugins/Input/in_vorbis/decoder.h create mode 100644 Src/Plugins/Input/in_vorbis/genres.c create mode 100644 Src/Plugins/Input/in_vorbis/genres.h create mode 100644 Src/Plugins/Input/in_vorbis/http.cpp create mode 100644 Src/Plugins/Input/in_vorbis/in_vorbis.rc create mode 100644 Src/Plugins/Input/in_vorbis/in_vorbis.sln create mode 100644 Src/Plugins/Input/in_vorbis/in_vorbis.vcxproj create mode 100644 Src/Plugins/Input/in_vorbis/in_vorbis.vcxproj.filters create mode 100644 Src/Plugins/Input/in_vorbis/info_.cpp create mode 100644 Src/Plugins/Input/in_vorbis/infobox.cpp create mode 100644 Src/Plugins/Input/in_vorbis/localfile.cpp create mode 100644 Src/Plugins/Input/in_vorbis/main.h create mode 100644 Src/Plugins/Input/in_vorbis/mkv_vorbis_decoder.cpp create mode 100644 Src/Plugins/Input/in_vorbis/mkv_vorbis_decoder.h create mode 100644 Src/Plugins/Input/in_vorbis/oggdrop/112.png create mode 100644 Src/Plugins/Input/in_vorbis/oggdrop/113.png create mode 100644 Src/Plugins/Input/in_vorbis/oggdrop/114.png create mode 100644 Src/Plugins/Input/in_vorbis/oggdrop/115.png create mode 100644 Src/Plugins/Input/in_vorbis/oggdrop/116.png create mode 100644 Src/Plugins/Input/in_vorbis/oggdrop/117.png create mode 100644 Src/Plugins/Input/in_vorbis/oggdrop/118.png create mode 100644 Src/Plugins/Input/in_vorbis/oggdrop/119.png create mode 100644 Src/Plugins/Input/in_vorbis/oggdrop/120.png create mode 100644 Src/Plugins/Input/in_vorbis/oggdrop/121.png create mode 100644 Src/Plugins/Input/in_vorbis/oggdrop/122.png create mode 100644 Src/Plugins/Input/in_vorbis/oggdrop/123.png create mode 100644 Src/Plugins/Input/in_vorbis/resource.h create mode 100644 Src/Plugins/Input/in_vorbis/rf.h create mode 100644 Src/Plugins/Input/in_vorbis/shaper.cpp create mode 100644 Src/Plugins/Input/in_vorbis/shaper.h create mode 100644 Src/Plugins/Input/in_vorbis/unihack.cpp create mode 100644 Src/Plugins/Input/in_vorbis/vcedit.c create mode 100644 Src/Plugins/Input/in_vorbis/vcedit.h create mode 100644 Src/Plugins/Input/in_vorbis/version.rc2 create mode 100644 Src/Plugins/Input/in_vorbis/wa2.cpp create mode 100644 Src/Plugins/Input/in_vorbis/winnt_helper.cpp create mode 100644 Src/Plugins/Input/in_wave/AudioThread.cpp create mode 100644 Src/Plugins/Input/in_wave/AudioThread.h create mode 100644 Src/Plugins/Input/in_wave/ExtendedRead.cpp create mode 100644 Src/Plugins/Input/in_wave/RawReader.cpp create mode 100644 Src/Plugins/Input/in_wave/RawReader.h create mode 100644 Src/Plugins/Input/in_wave/VirtualIO.cpp create mode 100644 Src/Plugins/Input/in_wave/VirtualIO.h create mode 100644 Src/Plugins/Input/in_wave/api__in_wave.h create mode 100644 Src/Plugins/Input/in_wave/config.cpp create mode 100644 Src/Plugins/Input/in_wave/config.h create mode 100644 Src/Plugins/Input/in_wave/extensions.cpp create mode 100644 Src/Plugins/Input/in_wave/in_wave.rc create mode 100644 Src/Plugins/Input/in_wave/in_wave.sln create mode 100644 Src/Plugins/Input/in_wave/in_wave.vcxproj create mode 100644 Src/Plugins/Input/in_wave/in_wave.vcxproj.filters create mode 100644 Src/Plugins/Input/in_wave/main.cpp create mode 100644 Src/Plugins/Input/in_wave/main.h create mode 100644 Src/Plugins/Input/in_wave/resource.h create mode 100644 Src/Plugins/Input/in_wave/version.rc2 create mode 100644 Src/Plugins/Input/in_wmvdrm/ASXLoader.cpp create mode 100644 Src/Plugins/Input/in_wmvdrm/ASXLoader.h create mode 100644 Src/Plugins/Input/in_wmvdrm/AlbumArt.cpp create mode 100644 Src/Plugins/Input/in_wmvdrm/AlbumArt.h create mode 100644 Src/Plugins/Input/in_wmvdrm/AllocLayer.cpp create mode 100644 Src/Plugins/Input/in_wmvdrm/AllocLayer.h create mode 100644 Src/Plugins/Input/in_wmvdrm/AudioFormat.cpp create mode 100644 Src/Plugins/Input/in_wmvdrm/AudioFormat.h create mode 100644 Src/Plugins/Input/in_wmvdrm/AudioLayer.cpp create mode 100644 Src/Plugins/Input/in_wmvdrm/AudioLayer.h create mode 100644 Src/Plugins/Input/in_wmvdrm/AudioThread.cpp create mode 100644 Src/Plugins/Input/in_wmvdrm/AudioThread.h create mode 100644 Src/Plugins/Input/in_wmvdrm/AutoChar.h create mode 100644 Src/Plugins/Input/in_wmvdrm/AutoWide.h create mode 100644 Src/Plugins/Input/in_wmvdrm/BufferLayer.cpp create mode 100644 Src/Plugins/Input/in_wmvdrm/BufferLayer.h create mode 100644 Src/Plugins/Input/in_wmvdrm/BufferPool.h create mode 100644 Src/Plugins/Input/in_wmvdrm/CachedData.h create mode 100644 Src/Plugins/Input/in_wmvdrm/ClockLayer.cpp create mode 100644 Src/Plugins/Input/in_wmvdrm/ClockLayer.h create mode 100644 Src/Plugins/Input/in_wmvdrm/ConfigDialog.cpp create mode 100644 Src/Plugins/Input/in_wmvdrm/ConfigDialog.h create mode 100644 Src/Plugins/Input/in_wmvdrm/DESIGN.txt create mode 100644 Src/Plugins/Input/in_wmvdrm/ExtendedFileInfo.cpp create mode 100644 Src/Plugins/Input/in_wmvdrm/ExtendedRead.cpp create mode 100644 Src/Plugins/Input/in_wmvdrm/ExtendedRead.h create mode 100644 Src/Plugins/Input/in_wmvdrm/FactoryHelper.h create mode 100644 Src/Plugins/Input/in_wmvdrm/FileInfoDialog.cpp create mode 100644 Src/Plugins/Input/in_wmvdrm/FileInfoDialog.h create mode 100644 Src/Plugins/Input/in_wmvdrm/FileTypes.cpp create mode 100644 Src/Plugins/Input/in_wmvdrm/FileTypes.h create mode 100644 Src/Plugins/Input/in_wmvdrm/GainLayer.cpp create mode 100644 Src/Plugins/Input/in_wmvdrm/GainLayer.h create mode 100644 Src/Plugins/Input/in_wmvdrm/Main.h create mode 100644 Src/Plugins/Input/in_wmvdrm/MediaThread.cpp create mode 100644 Src/Plugins/Input/in_wmvdrm/MediaThread.h create mode 100644 Src/Plugins/Input/in_wmvdrm/MetaTag.cpp create mode 100644 Src/Plugins/Input/in_wmvdrm/MetaTag.h create mode 100644 Src/Plugins/Input/in_wmvdrm/MetaTagFactory.cpp create mode 100644 Src/Plugins/Input/in_wmvdrm/MetaTagFactory.h create mode 100644 Src/Plugins/Input/in_wmvdrm/OutputStream.h create mode 100644 Src/Plugins/Input/in_wmvdrm/PlaylistHandler.cpp create mode 100644 Src/Plugins/Input/in_wmvdrm/PlaylistHandler.h create mode 100644 Src/Plugins/Input/in_wmvdrm/RawReader.cpp create mode 100644 Src/Plugins/Input/in_wmvdrm/RawReader.h create mode 100644 Src/Plugins/Input/in_wmvdrm/Remaining.h create mode 100644 Src/Plugins/Input/in_wmvdrm/SeekLayer.cpp create mode 100644 Src/Plugins/Input/in_wmvdrm/SeekLayer.h create mode 100644 Src/Plugins/Input/in_wmvdrm/StatusHook.cpp create mode 100644 Src/Plugins/Input/in_wmvdrm/StatusHook.h create mode 100644 Src/Plugins/Input/in_wmvdrm/TODO.txt create mode 100644 Src/Plugins/Input/in_wmvdrm/TagAlias.cpp create mode 100644 Src/Plugins/Input/in_wmvdrm/TagAlias.h create mode 100644 Src/Plugins/Input/in_wmvdrm/VideoDataConverter.cpp create mode 100644 Src/Plugins/Input/in_wmvdrm/VideoDataConverter.h create mode 100644 Src/Plugins/Input/in_wmvdrm/VideoLayer.cpp create mode 100644 Src/Plugins/Input/in_wmvdrm/VideoLayer.h create mode 100644 Src/Plugins/Input/in_wmvdrm/VideoOutputChildDDraw.cpp create mode 100644 Src/Plugins/Input/in_wmvdrm/VideoOutputChildDDraw.h create mode 100644 Src/Plugins/Input/in_wmvdrm/VideoThread.cpp create mode 100644 Src/Plugins/Input/in_wmvdrm/VideoThread.h create mode 100644 Src/Plugins/Input/in_wmvdrm/WMCallback.cpp create mode 100644 Src/Plugins/Input/in_wmvdrm/WMCallback.h create mode 100644 Src/Plugins/Input/in_wmvdrm/WMDRMModule.cpp create mode 100644 Src/Plugins/Input/in_wmvdrm/WMDRMModule.h create mode 100644 Src/Plugins/Input/in_wmvdrm/WMHandler.cpp create mode 100644 Src/Plugins/Input/in_wmvdrm/WMHandler.h create mode 100644 Src/Plugins/Input/in_wmvdrm/WMInformation.cpp create mode 100644 Src/Plugins/Input/in_wmvdrm/WMInformation.h create mode 100644 Src/Plugins/Input/in_wmvdrm/WMPlaylist.cpp create mode 100644 Src/Plugins/Input/in_wmvdrm/WMPlaylist.h create mode 100644 Src/Plugins/Input/in_wmvdrm/WPLLoader.cpp create mode 100644 Src/Plugins/Input/in_wmvdrm/WPLLoader.h create mode 100644 Src/Plugins/Input/in_wmvdrm/WaitLayer.cpp create mode 100644 Src/Plugins/Input/in_wmvdrm/WaitLayer.h create mode 100644 Src/Plugins/Input/in_wmvdrm/WinampInterface.cpp create mode 100644 Src/Plugins/Input/in_wmvdrm/WinampInterface.h create mode 100644 Src/Plugins/Input/in_wmvdrm/XMLString.cpp create mode 100644 Src/Plugins/Input/in_wmvdrm/XMLString.h create mode 100644 Src/Plugins/Input/in_wmvdrm/api.cpp create mode 100644 Src/Plugins/Input/in_wmvdrm/api.h create mode 100644 Src/Plugins/Input/in_wmvdrm/config.cpp create mode 100644 Src/Plugins/Input/in_wmvdrm/config.h create mode 100644 Src/Plugins/Input/in_wmvdrm/directdraw.cpp create mode 100644 Src/Plugins/Input/in_wmvdrm/directdraw.h create mode 100644 Src/Plugins/Input/in_wmvdrm/factory_Handler.cpp create mode 100644 Src/Plugins/Input/in_wmvdrm/factory_Handler.h create mode 100644 Src/Plugins/Input/in_wmvdrm/in_wm.rc create mode 100644 Src/Plugins/Input/in_wmvdrm/in_wmvdrm.sln create mode 100644 Src/Plugins/Input/in_wmvdrm/in_wmvdrm.vcxproj create mode 100644 Src/Plugins/Input/in_wmvdrm/in_wmvdrm.vcxproj.filters create mode 100644 Src/Plugins/Input/in_wmvdrm/loadini.cpp create mode 100644 Src/Plugins/Input/in_wmvdrm/loadini.h create mode 100644 Src/Plugins/Input/in_wmvdrm/main.cpp create mode 100644 Src/Plugins/Input/in_wmvdrm/output/AudioOut.h create mode 100644 Src/Plugins/Input/in_wmvdrm/output/OutPlugin.cpp create mode 100644 Src/Plugins/Input/in_wmvdrm/output/OutPlugin.h create mode 100644 Src/Plugins/Input/in_wmvdrm/res_wav/resource.h create mode 100644 Src/Plugins/Input/in_wmvdrm/resource.h create mode 100644 Src/Plugins/Input/in_wmvdrm/util.cpp create mode 100644 Src/Plugins/Input/in_wmvdrm/util.h create mode 100644 Src/Plugins/Input/in_wmvdrm/version.rc2 create mode 100644 Src/Plugins/Input/in_wmvdrm/vid_ddraw.cpp create mode 100644 Src/Plugins/Input/in_wmvdrm/vid_ddraw.h create mode 100644 Src/Plugins/Input/in_wmvdrm/vid_overlay.cpp create mode 100644 Src/Plugins/Input/in_wmvdrm/vid_overlay.h create mode 100644 Src/Plugins/Input/in_wmvdrm/vidutils.cpp create mode 100644 Src/Plugins/Input/in_wmvdrm/vidutils.h create mode 100644 Src/Plugins/Input/in_wv/changes.txt create mode 100644 Src/Plugins/Input/in_wv/in2.h create mode 100644 Src/Plugins/Input/in_wv/in_wv.cpp create mode 100644 Src/Plugins/Input/in_wv/resource.h create mode 100644 Src/Plugins/Input/in_wv/wasabi/Agave/AlbumArt/svc_albumArtProvider.h create mode 100644 Src/Plugins/Input/in_wv/wasabi/Agave/Config/api_config.h create mode 100644 Src/Plugins/Input/in_wv/wasabi/Agave/Config/ifc_configgroup.h create mode 100644 Src/Plugins/Input/in_wv/wasabi/Agave/Config/ifc_configitem.h create mode 100644 Src/Plugins/Input/in_wv/wasabi/Agave/Language/api_language.h create mode 100644 Src/Plugins/Input/in_wv/wasabi/Agave/Language/lang.h create mode 100644 Src/Plugins/Input/in_wv/wasabi/Wasabi.cpp create mode 100644 Src/Plugins/Input/in_wv/wasabi/Wasabi.h create mode 100644 Src/Plugins/Input/in_wv/wasabi/Winamp/DSP.H create mode 100644 Src/Plugins/Input/in_wv/wasabi/Winamp/GEN.H create mode 100644 Src/Plugins/Input/in_wv/wasabi/Winamp/IN2.H create mode 100644 Src/Plugins/Input/in_wv/wasabi/Winamp/api_audiostream.h create mode 100644 Src/Plugins/Input/in_wv/wasabi/Winamp/api_decodefile.h create mode 100644 Src/Plugins/Input/in_wv/wasabi/Winamp/api_random.h create mode 100644 Src/Plugins/Input/in_wv/wasabi/Winamp/api_wa5component.h create mode 100644 Src/Plugins/Input/in_wv/wasabi/Winamp/ipc_pe.h create mode 100644 Src/Plugins/Input/in_wv/wasabi/Winamp/wa_dlg.h create mode 100644 Src/Plugins/Input/in_wv/wasabi/Winamp/wa_hotkeys.h create mode 100644 Src/Plugins/Input/in_wv/wasabi/Winamp/wa_ipc.h create mode 100644 Src/Plugins/Input/in_wv/wasabi/api/memmgr/api_memmgr.h create mode 100644 Src/Plugins/Input/in_wv/wasabi/api/service/api_service.h create mode 100644 Src/Plugins/Input/in_wv/wasabi/api/service/services.h create mode 100644 Src/Plugins/Input/in_wv/wasabi/api/service/waservicefactory.h create mode 100644 Src/Plugins/Input/in_wv/wasabi/bfc/dispatch.h create mode 100644 Src/Plugins/Input/in_wv/wasabi/bfc/nsguid.h create mode 100644 Src/Plugins/Input/in_wv/wasabi/bfc/platform/guid.h create mode 100644 Src/Plugins/Input/in_wv/wasabi/bfc/platform/platform.h create mode 100644 Src/Plugins/Input/in_wv/wasabi/bfc/platform/types.h create mode 100644 Src/Plugins/Input/in_wv/wasabi/bfc/platform/win32.h create mode 100644 Src/Plugins/Input/in_wv/wasabi/bfc/std_mkncc.h create mode 100644 Src/Plugins/Input/in_wv/wasabi/nu/AutoChar.h create mode 100644 Src/Plugins/Input/in_wv/wasabi/nu/AutoWide.h create mode 100644 Src/Plugins/Input/in_wv/wavpack.rc create mode 100644 Src/Plugins/Input/in_wv/winamp.vcproj create mode 100644 Src/Plugins/Library/ml_autotag/main.cpp create mode 100644 Src/Plugins/Library/ml_autotag/main.h create mode 100644 Src/Plugins/Library/ml_autotag/metadata.cpp create mode 100644 Src/Plugins/Library/ml_autotag/ml_autotag.rc create mode 100644 Src/Plugins/Library/ml_autotag/ml_autotag.sln create mode 100644 Src/Plugins/Library/ml_autotag/ml_autotag.vcxproj create mode 100644 Src/Plugins/Library/ml_autotag/ml_autotag.vcxproj.filters create mode 100644 Src/Plugins/Library/ml_autotag/resource.h create mode 100644 Src/Plugins/Library/ml_autotag/tagger.cpp create mode 100644 Src/Plugins/Library/ml_autotag/tagger.h create mode 100644 Src/Plugins/Library/ml_autotag/version.rc2 create mode 100644 Src/Plugins/Library/ml_bookmarks/api__ml_bookmarks.h create mode 100644 Src/Plugins/Library/ml_bookmarks/bookmark.cpp create mode 100644 Src/Plugins/Library/ml_bookmarks/bookmark.h create mode 100644 Src/Plugins/Library/ml_bookmarks/listview.cpp create mode 100644 Src/Plugins/Library/ml_bookmarks/listview.h create mode 100644 Src/Plugins/Library/ml_bookmarks/main.cpp create mode 100644 Src/Plugins/Library/ml_bookmarks/main.h create mode 100644 Src/Plugins/Library/ml_bookmarks/ml_bookmarks.rc create mode 100644 Src/Plugins/Library/ml_bookmarks/ml_bookmarks.sln create mode 100644 Src/Plugins/Library/ml_bookmarks/ml_bookmarks.vcxproj create mode 100644 Src/Plugins/Library/ml_bookmarks/ml_bookmarks.vcxproj.filters create mode 100644 Src/Plugins/Library/ml_bookmarks/resource.h create mode 100644 Src/Plugins/Library/ml_bookmarks/resources/ti_bookmarks_16x16x16.bmp create mode 100644 Src/Plugins/Library/ml_bookmarks/version.rc2 create mode 100644 Src/Plugins/Library/ml_bookmarks/view.cpp create mode 100644 Src/Plugins/Library/ml_devices/backBuffer.cpp create mode 100644 Src/Plugins/Library/ml_devices/backBuffer.h create mode 100644 Src/Plugins/Library/ml_devices/common.h create mode 100644 Src/Plugins/Library/ml_devices/config.cpp create mode 100644 Src/Plugins/Library/ml_devices/config.h create mode 100644 Src/Plugins/Library/ml_devices/deviceCommands.cpp create mode 100644 Src/Plugins/Library/ml_devices/deviceCommands.h create mode 100644 Src/Plugins/Library/ml_devices/deviceHandler.cpp create mode 100644 Src/Plugins/Library/ml_devices/deviceHandler.h create mode 100644 Src/Plugins/Library/ml_devices/deviceManagerHandler.cpp create mode 100644 Src/Plugins/Library/ml_devices/deviceManagerHandler.h create mode 100644 Src/Plugins/Library/ml_devices/embeddedEditor.cpp create mode 100644 Src/Plugins/Library/ml_devices/embeddedEditor.h create mode 100644 Src/Plugins/Library/ml_devices/eventRelay.cpp create mode 100644 Src/Plugins/Library/ml_devices/eventRelay.h create mode 100644 Src/Plugins/Library/ml_devices/fillRegion.cpp create mode 100644 Src/Plugins/Library/ml_devices/fillRegion.h create mode 100644 Src/Plugins/Library/ml_devices/gen_deviceprovider/common.h create mode 100644 Src/Plugins/Library/ml_devices/gen_deviceprovider/device.cpp create mode 100644 Src/Plugins/Library/ml_devices/gen_deviceprovider/device.h create mode 100644 Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceActivity.cpp create mode 100644 Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceActivity.h create mode 100644 Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceCommandNodeParser.cpp create mode 100644 Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceCommandNodeParser.h create mode 100644 Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceCommandParser.cpp create mode 100644 Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceCommandParser.h create mode 100644 Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceConnectionNodeParser.cpp create mode 100644 Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceConnectionNodeParser.h create mode 100644 Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceConnectionParser.cpp create mode 100644 Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceConnectionParser.h create mode 100644 Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceIconEditor.cpp create mode 100644 Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceIconEditor.h create mode 100644 Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceNodeParser.cpp create mode 100644 Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceNodeParser.h create mode 100644 Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceParser.cpp create mode 100644 Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceParser.h create mode 100644 Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceTypeNodeParser.cpp create mode 100644 Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceTypeNodeParser.h create mode 100644 Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceTypeParser.cpp create mode 100644 Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceTypeParser.h create mode 100644 Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceView.cpp create mode 100644 Src/Plugins/Library/ml_devices/gen_deviceprovider/deviceView.h create mode 100644 Src/Plugins/Library/ml_devices/gen_deviceprovider/gen_deviceprovider.sln create mode 100644 Src/Plugins/Library/ml_devices/gen_deviceprovider/gen_deviceprovider.vcproj create mode 100644 Src/Plugins/Library/ml_devices/gen_deviceprovider/gen_deviceprovider.vcxproj create mode 100644 Src/Plugins/Library/ml_devices/gen_deviceprovider/gen_deviceprovider.vcxproj.filters create mode 100644 Src/Plugins/Library/ml_devices/gen_deviceprovider/iconStore.cpp create mode 100644 Src/Plugins/Library/ml_devices/gen_deviceprovider/iconStore.h create mode 100644 Src/Plugins/Library/ml_devices/gen_deviceprovider/main.cpp create mode 100644 Src/Plugins/Library/ml_devices/gen_deviceprovider/main.h create mode 100644 Src/Plugins/Library/ml_devices/gen_deviceprovider/plugin.cpp create mode 100644 Src/Plugins/Library/ml_devices/gen_deviceprovider/plugin.h create mode 100644 Src/Plugins/Library/ml_devices/gen_deviceprovider/provider.cpp create mode 100644 Src/Plugins/Library/ml_devices/gen_deviceprovider/provider.h create mode 100644 Src/Plugins/Library/ml_devices/gen_deviceprovider/resource.h create mode 100644 Src/Plugins/Library/ml_devices/gen_deviceprovider/resources.rc create mode 100644 Src/Plugins/Library/ml_devices/gen_deviceprovider/stringBuilder.cpp create mode 100644 Src/Plugins/Library/ml_devices/gen_deviceprovider/stringBuilder.h create mode 100644 Src/Plugins/Library/ml_devices/gen_deviceprovider/strings.cpp create mode 100644 Src/Plugins/Library/ml_devices/gen_deviceprovider/strings.h create mode 100644 Src/Plugins/Library/ml_devices/gen_deviceprovider/supportedCommand.cpp create mode 100644 Src/Plugins/Library/ml_devices/gen_deviceprovider/supportedCommand.h create mode 100644 Src/Plugins/Library/ml_devices/gen_deviceprovider/testSuite.cpp create mode 100644 Src/Plugins/Library/ml_devices/gen_deviceprovider/testSuite.h create mode 100644 Src/Plugins/Library/ml_devices/gen_deviceprovider/testSuiteLoader.cpp create mode 100644 Src/Plugins/Library/ml_devices/gen_deviceprovider/testSuiteLoader.h create mode 100644 Src/Plugins/Library/ml_devices/gen_deviceprovider/testprovider.xml create mode 100644 Src/Plugins/Library/ml_devices/gen_deviceprovider/wasabi.cpp create mode 100644 Src/Plugins/Library/ml_devices/gen_deviceprovider/wasabi.h create mode 100644 Src/Plugins/Library/ml_devices/graphics.cpp create mode 100644 Src/Plugins/Library/ml_devices/graphics.h create mode 100644 Src/Plugins/Library/ml_devices/image.cpp create mode 100644 Src/Plugins/Library/ml_devices/image.h create mode 100644 Src/Plugins/Library/ml_devices/imageCache.cpp create mode 100644 Src/Plugins/Library/ml_devices/imageCache.h create mode 100644 Src/Plugins/Library/ml_devices/infoWidget.cpp create mode 100644 Src/Plugins/Library/ml_devices/infoWidget.h create mode 100644 Src/Plugins/Library/ml_devices/listWidget.cpp create mode 100644 Src/Plugins/Library/ml_devices/listWidget.h create mode 100644 Src/Plugins/Library/ml_devices/listWidgetCategory.cpp create mode 100644 Src/Plugins/Library/ml_devices/listWidgetCommand.cpp create mode 100644 Src/Plugins/Library/ml_devices/listWidgetConnection.cpp create mode 100644 Src/Plugins/Library/ml_devices/listWidgetGroup.cpp create mode 100644 Src/Plugins/Library/ml_devices/listWidgetInternal.h create mode 100644 Src/Plugins/Library/ml_devices/listWidgetItem.cpp create mode 100644 Src/Plugins/Library/ml_devices/listWidgetPaint.cpp create mode 100644 Src/Plugins/Library/ml_devices/listWidgetTooltip.cpp create mode 100644 Src/Plugins/Library/ml_devices/local_menu.cpp create mode 100644 Src/Plugins/Library/ml_devices/local_menu.h create mode 100644 Src/Plugins/Library/ml_devices/main.cpp create mode 100644 Src/Plugins/Library/ml_devices/main.h create mode 100644 Src/Plugins/Library/ml_devices/managerView.cpp create mode 100644 Src/Plugins/Library/ml_devices/managerView.h create mode 100644 Src/Plugins/Library/ml_devices/ml_devices.rc create mode 100644 Src/Plugins/Library/ml_devices/ml_devices.sln create mode 100644 Src/Plugins/Library/ml_devices/ml_devices.vcxproj create mode 100644 Src/Plugins/Library/ml_devices/ml_devices.vcxproj.filters create mode 100644 Src/Plugins/Library/ml_devices/navigation.cpp create mode 100644 Src/Plugins/Library/ml_devices/navigation.h create mode 100644 Src/Plugins/Library/ml_devices/navigationIcons.cpp create mode 100644 Src/Plugins/Library/ml_devices/navigationIcons.h create mode 100644 Src/Plugins/Library/ml_devices/plugin.cpp create mode 100644 Src/Plugins/Library/ml_devices/plugin.h create mode 100644 Src/Plugins/Library/ml_devices/png.rc create mode 100644 Src/Plugins/Library/ml_devices/resource.h create mode 100644 Src/Plugins/Library/ml_devices/resources/action-bg.png create mode 100644 Src/Plugins/Library/ml_devices/resources/arrows.png create mode 100644 Src/Plugins/Library/ml_devices/resources/attach-command-large.png create mode 100644 Src/Plugins/Library/ml_devices/resources/attach-command-small.png create mode 100644 Src/Plugins/Library/ml_devices/resources/cancel-sync-command-small.png create mode 100644 Src/Plugins/Library/ml_devices/resources/command-bg.png create mode 100644 Src/Plugins/Library/ml_devices/resources/command-secondary-bg.png create mode 100644 Src/Plugins/Library/ml_devices/resources/commands/detach.png create mode 100644 Src/Plugins/Library/ml_devices/resources/commands/eject.png create mode 100644 Src/Plugins/Library/ml_devices/resources/commands/settings.png create mode 100644 Src/Plugins/Library/ml_devices/resources/commands/sync.png create mode 100644 Src/Plugins/Library/ml_devices/resources/connections/bluetooth.png create mode 100644 Src/Plugins/Library/ml_devices/resources/connections/cloud.png create mode 100644 Src/Plugins/Library/ml_devices/resources/connections/usb.png create mode 100644 Src/Plugins/Library/ml_devices/resources/connections/wifi.png create mode 100644 Src/Plugins/Library/ml_devices/resources/detach-command-large.png create mode 100644 Src/Plugins/Library/ml_devices/resources/detach-command-small.png create mode 100644 Src/Plugins/Library/ml_devices/resources/devices-title-en.png create mode 100644 Src/Plugins/Library/ml_devices/resources/devices.png create mode 100644 Src/Plugins/Library/ml_devices/resources/eject-command-small.png create mode 100644 Src/Plugins/Library/ml_devices/resources/food/apple.png create mode 100644 Src/Plugins/Library/ml_devices/resources/food/banana.png create mode 100644 Src/Plugins/Library/ml_devices/resources/food/bread.png create mode 100644 Src/Plugins/Library/ml_devices/resources/food/broccoli.png create mode 100644 Src/Plugins/Library/ml_devices/resources/food/carrot.png create mode 100644 Src/Plugins/Library/ml_devices/resources/food/cereal.png create mode 100644 Src/Plugins/Library/ml_devices/resources/food/cucumber.png create mode 100644 Src/Plugins/Library/ml_devices/resources/food/eggplant.png create mode 100644 Src/Plugins/Library/ml_devices/resources/food/grape.png create mode 100644 Src/Plugins/Library/ml_devices/resources/food/lemon.png create mode 100644 Src/Plugins/Library/ml_devices/resources/food/muffin.png create mode 100644 Src/Plugins/Library/ml_devices/resources/food/orange.png create mode 100644 Src/Plugins/Library/ml_devices/resources/food/pasta.png create mode 100644 Src/Plugins/Library/ml_devices/resources/food/peach.png create mode 100644 Src/Plugins/Library/ml_devices/resources/food/pear.png create mode 100644 Src/Plugins/Library/ml_devices/resources/food/pepper.png create mode 100644 Src/Plugins/Library/ml_devices/resources/food/pretzel.png create mode 100644 Src/Plugins/Library/ml_devices/resources/food/rice.png create mode 100644 Src/Plugins/Library/ml_devices/resources/generic-device-160x160.png create mode 100644 Src/Plugins/Library/ml_devices/resources/generic-device-16x16.png create mode 100644 Src/Plugins/Library/ml_devices/resources/item-hover.png create mode 100644 Src/Plugins/Library/ml_devices/resources/item-select.png create mode 100644 Src/Plugins/Library/ml_devices/resources/progress-large.png create mode 100644 Src/Plugins/Library/ml_devices/resources/progress-small.png create mode 100644 Src/Plugins/Library/ml_devices/resources/spacebar.png create mode 100644 Src/Plugins/Library/ml_devices/resources/sync-command-large.png create mode 100644 Src/Plugins/Library/ml_devices/resources/sync-command-small.png create mode 100644 Src/Plugins/Library/ml_devices/resources/unknown-command-large.png create mode 100644 Src/Plugins/Library/ml_devices/resources/zoom/192x256.png create mode 100644 Src/Plugins/Library/ml_devices/resources/zoom/24x32.png create mode 100644 Src/Plugins/Library/ml_devices/resources/zoom/48x64.png create mode 100644 Src/Plugins/Library/ml_devices/resources/zoom/96x128.png create mode 100644 Src/Plugins/Library/ml_devices/statusBar.cpp create mode 100644 Src/Plugins/Library/ml_devices/statusBar.h create mode 100644 Src/Plugins/Library/ml_devices/strings.cpp create mode 100644 Src/Plugins/Library/ml_devices/strings.h create mode 100644 Src/Plugins/Library/ml_devices/version.rc2 create mode 100644 Src/Plugins/Library/ml_devices/wasabi.cpp create mode 100644 Src/Plugins/Library/ml_devices/wasabi.h create mode 100644 Src/Plugins/Library/ml_devices/welcomeWidget.cpp create mode 100644 Src/Plugins/Library/ml_devices/welcomeWidget.h create mode 100644 Src/Plugins/Library/ml_devices/widget.cpp create mode 100644 Src/Plugins/Library/ml_devices/widget.h create mode 100644 Src/Plugins/Library/ml_devices/widgetHost.cpp create mode 100644 Src/Plugins/Library/ml_devices/widgetHost.h create mode 100644 Src/Plugins/Library/ml_devices/widgetStyle.cpp create mode 100644 Src/Plugins/Library/ml_devices/widgetStyle.h create mode 100644 Src/Plugins/Library/ml_disc/M3UWriter.cpp create mode 100644 Src/Plugins/Library/ml_disc/M3UWriter.h create mode 100644 Src/Plugins/Library/ml_disc/PLSWriter.cpp create mode 100644 Src/Plugins/Library/ml_disc/PLSWriter.h create mode 100644 Src/Plugins/Library/ml_disc/ReplayGain.cpp create mode 100644 Src/Plugins/Library/ml_disc/ReplayGain.h create mode 100644 Src/Plugins/Library/ml_disc/api__ml_disc.h create mode 100644 Src/Plugins/Library/ml_disc/banner.cpp create mode 100644 Src/Plugins/Library/ml_disc/banner.h create mode 100644 Src/Plugins/Library/ml_disc/cdburn.cpp create mode 100644 Src/Plugins/Library/ml_disc/cdrip.cpp create mode 100644 Src/Plugins/Library/ml_disc/cmdbar_data.cpp create mode 100644 Src/Plugins/Library/ml_disc/commandbar.cpp create mode 100644 Src/Plugins/Library/ml_disc/commandbar.h create mode 100644 Src/Plugins/Library/ml_disc/config.cpp create mode 100644 Src/Plugins/Library/ml_disc/config.h create mode 100644 Src/Plugins/Library/ml_disc/copyfiles.cpp create mode 100644 Src/Plugins/Library/ml_disc/copyfiles.h create mode 100644 Src/Plugins/Library/ml_disc/copyfiles_post.cpp create mode 100644 Src/Plugins/Library/ml_disc/copyinternal.h create mode 100644 Src/Plugins/Library/ml_disc/copyprep.cpp create mode 100644 Src/Plugins/Library/ml_disc/copyprogress.cpp create mode 100644 Src/Plugins/Library/ml_disc/discInfo.cpp create mode 100644 Src/Plugins/Library/ml_disc/discInfo.h create mode 100644 Src/Plugins/Library/ml_disc/drive.cpp create mode 100644 Src/Plugins/Library/ml_disc/drive.h create mode 100644 Src/Plugins/Library/ml_disc/driveListBox.cpp create mode 100644 Src/Plugins/Library/ml_disc/driveListBox.h create mode 100644 Src/Plugins/Library/ml_disc/drivemngr.cpp create mode 100644 Src/Plugins/Library/ml_disc/drivemngr.h create mode 100644 Src/Plugins/Library/ml_disc/drives.cpp create mode 100644 Src/Plugins/Library/ml_disc/drives.h create mode 100644 Src/Plugins/Library/ml_disc/formatfilename.cpp create mode 100644 Src/Plugins/Library/ml_disc/helpwnd.cpp create mode 100644 Src/Plugins/Library/ml_disc/infoBox.cpp create mode 100644 Src/Plugins/Library/ml_disc/infoBox.h create mode 100644 Src/Plugins/Library/ml_disc/main.cpp create mode 100644 Src/Plugins/Library/ml_disc/main.h create mode 100644 Src/Plugins/Library/ml_disc/medium.cpp create mode 100644 Src/Plugins/Library/ml_disc/medium.h create mode 100644 Src/Plugins/Library/ml_disc/menu.cpp create mode 100644 Src/Plugins/Library/ml_disc/menu.h create mode 100644 Src/Plugins/Library/ml_disc/ml_disc.rc create mode 100644 Src/Plugins/Library/ml_disc/ml_disc.sln create mode 100644 Src/Plugins/Library/ml_disc/ml_disc.vcxproj create mode 100644 Src/Plugins/Library/ml_disc/ml_disc.vcxproj.filters create mode 100644 Src/Plugins/Library/ml_disc/png.rc create mode 100644 Src/Plugins/Library/ml_disc/prefs.cpp create mode 100644 Src/Plugins/Library/ml_disc/primosdk_helper.cpp create mode 100644 Src/Plugins/Library/ml_disc/primosdk_helper.h create mode 100644 Src/Plugins/Library/ml_disc/questionwnd.cpp create mode 100644 Src/Plugins/Library/ml_disc/resource.h create mode 100644 Src/Plugins/Library/ml_disc/resources/cdrom.png create mode 100644 Src/Plugins/Library/ml_disc/resources/cdrom_32x32_24.bmp create mode 100644 Src/Plugins/Library/ml_disc/resources/eject1.png create mode 100644 Src/Plugins/Library/ml_disc/resources/eject2.png create mode 100644 Src/Plugins/Library/ml_disc/resources/eject3.png create mode 100644 Src/Plugins/Library/ml_disc/resources/eject4.png create mode 100644 Src/Plugins/Library/ml_disc/resources/eject_d.png create mode 100644 Src/Plugins/Library/ml_disc/resources/eject_n.png create mode 100644 Src/Plugins/Library/ml_disc/resources/eject_p.png create mode 100644 Src/Plugins/Library/ml_disc/resources/enqueue_d.png create mode 100644 Src/Plugins/Library/ml_disc/resources/enqueue_menu_16x16.png create mode 100644 Src/Plugins/Library/ml_disc/resources/enqueue_n.png create mode 100644 Src/Plugins/Library/ml_disc/resources/enqueue_p.png create mode 100644 Src/Plugins/Library/ml_disc/resources/enqueuem_d.png create mode 100644 Src/Plugins/Library/ml_disc/resources/enqueuem_n.png create mode 100644 Src/Plugins/Library/ml_disc/resources/enqueuem_p.png create mode 100644 Src/Plugins/Library/ml_disc/resources/filecopy.png create mode 100644 Src/Plugins/Library/ml_disc/resources/listbox_back_2x68x24.bmp create mode 100644 Src/Plugins/Library/ml_disc/resources/play_d.png create mode 100644 Src/Plugins/Library/ml_disc/resources/play_menu_16x16.png create mode 100644 Src/Plugins/Library/ml_disc/resources/play_n.png create mode 100644 Src/Plugins/Library/ml_disc/resources/play_p.png create mode 100644 Src/Plugins/Library/ml_disc/resources/playm_d.png create mode 100644 Src/Plugins/Library/ml_disc/resources/playm_n.png create mode 100644 Src/Plugins/Library/ml_disc/resources/playm_p.png create mode 100644 Src/Plugins/Library/ml_disc/resources/rip&burn_logo_228x25x16.bmp create mode 100644 Src/Plugins/Library/ml_disc/resources/sonic_powered_44x22.bmp create mode 100644 Src/Plugins/Library/ml_disc/settings.cpp create mode 100644 Src/Plugins/Library/ml_disc/settings.h create mode 100644 Src/Plugins/Library/ml_disc/spti.cpp create mode 100644 Src/Plugins/Library/ml_disc/spti.h create mode 100644 Src/Plugins/Library/ml_disc/version.rc2 create mode 100644 Src/Plugins/Library/ml_disc/view_cdrom.cpp create mode 100644 Src/Plugins/Library/ml_disc/view_container.cpp create mode 100644 Src/Plugins/Library/ml_disc/view_data.cpp create mode 100644 Src/Plugins/Library/ml_disc/view_info.cpp create mode 100644 Src/Plugins/Library/ml_disc/view_ripburn.cpp create mode 100644 Src/Plugins/Library/ml_disc/view_wait.cpp create mode 100644 Src/Plugins/Library/ml_downloads/AtomParse.h create mode 100644 Src/Plugins/Library/ml_downloads/DESIGN.txt create mode 100644 Src/Plugins/Library/ml_downloads/Defaults.cpp create mode 100644 Src/Plugins/Library/ml_downloads/Defaults.h create mode 100644 Src/Plugins/Library/ml_downloads/DownloadManager.cpp create mode 100644 Src/Plugins/Library/ml_downloads/DownloadStatus.cpp create mode 100644 Src/Plugins/Library/ml_downloads/DownloadStatus.h create mode 100644 Src/Plugins/Library/ml_downloads/DownloadThread.cpp create mode 100644 Src/Plugins/Library/ml_downloads/DownloadThread.h create mode 100644 Src/Plugins/Library/ml_downloads/DownloadViewCallback.cpp create mode 100644 Src/Plugins/Library/ml_downloads/DownloadViewCallback.h create mode 100644 Src/Plugins/Library/ml_downloads/Downloaded.cpp create mode 100644 Src/Plugins/Library/ml_downloads/Downloaded.h create mode 100644 Src/Plugins/Library/ml_downloads/DownloadsDialog.cpp create mode 100644 Src/Plugins/Library/ml_downloads/DownloadsDialog.h create mode 100644 Src/Plugins/Library/ml_downloads/DownloadsParse.cpp create mode 100644 Src/Plugins/Library/ml_downloads/DownloadsParse.h create mode 100644 Src/Plugins/Library/ml_downloads/Main.cpp create mode 100644 Src/Plugins/Library/ml_downloads/Main.h create mode 100644 Src/Plugins/Library/ml_downloads/MessageProcessor.cpp create mode 100644 Src/Plugins/Library/ml_downloads/MessageProcessor.h create mode 100644 Src/Plugins/Library/ml_downloads/ParseUtil.cpp create mode 100644 Src/Plugins/Library/ml_downloads/ParseUtil.h create mode 100644 Src/Plugins/Library/ml_downloads/Preferences.cpp create mode 100644 Src/Plugins/Library/ml_downloads/Preferences.h create mode 100644 Src/Plugins/Library/ml_downloads/RFCDate.cpp create mode 100644 Src/Plugins/Library/ml_downloads/RFCDate.h create mode 100644 Src/Plugins/Library/ml_downloads/TODO.txt create mode 100644 Src/Plugins/Library/ml_downloads/Util.h create mode 100644 Src/Plugins/Library/ml_downloads/XMLWriter.cpp create mode 100644 Src/Plugins/Library/ml_downloads/XMLWriter.h create mode 100644 Src/Plugins/Library/ml_downloads/api__ml_downloads.h create mode 100644 Src/Plugins/Library/ml_downloads/date.c create mode 100644 Src/Plugins/Library/ml_downloads/date.h create mode 100644 Src/Plugins/Library/ml_downloads/db.cpp create mode 100644 Src/Plugins/Library/ml_downloads/errors.h create mode 100644 Src/Plugins/Library/ml_downloads/layout.cpp create mode 100644 Src/Plugins/Library/ml_downloads/layout.h create mode 100644 Src/Plugins/Library/ml_downloads/ml_downloads.rc create mode 100644 Src/Plugins/Library/ml_downloads/ml_downloads.sln create mode 100644 Src/Plugins/Library/ml_downloads/ml_downloads.vcxproj create mode 100644 Src/Plugins/Library/ml_downloads/ml_downloads.vcxproj.filters create mode 100644 Src/Plugins/Library/ml_downloads/png.rc create mode 100644 Src/Plugins/Library/ml_downloads/resource.h create mode 100644 Src/Plugins/Library/ml_downloads/resources/downloadIcon.bmp create mode 100644 Src/Plugins/Library/ml_downloads/resources/downloadIcon.png create mode 100644 Src/Plugins/Library/ml_downloads/resources/downloadIcon1.bmp create mode 100644 Src/Plugins/Library/ml_downloads/resources/downloadIcon2.bmp create mode 100644 Src/Plugins/Library/ml_downloads/resources/downloadIcon3.bmp create mode 100644 Src/Plugins/Library/ml_downloads/util.cpp create mode 100644 Src/Plugins/Library/ml_downloads/version.rc2 create mode 100644 Src/Plugins/Library/ml_fanzone.7z create mode 100644 Src/Plugins/Library/ml_fanzone/CMakeLists.txt create mode 100644 Src/Plugins/Library/ml_fanzone/api__ml_fanzone.h create mode 100644 Src/Plugins/Library/ml_fanzone/compatibility.manifest create mode 100644 Src/Plugins/Library/ml_fanzone/listview.cpp create mode 100644 Src/Plugins/Library/ml_fanzone/listview.h create mode 100644 Src/Plugins/Library/ml_fanzone/main.cpp create mode 100644 Src/Plugins/Library/ml_fanzone/main.h create mode 100644 Src/Plugins/Library/ml_fanzone/ml_fanzone.dll.manifest create mode 100644 Src/Plugins/Library/ml_fanzone/ml_fanzone.rc create mode 100644 Src/Plugins/Library/ml_fanzone/ml_fanzone.vcxproj create mode 100644 Src/Plugins/Library/ml_fanzone/ml_fanzone.vcxproj.filters create mode 100644 Src/Plugins/Library/ml_fanzone/myCefApp.cpp create mode 100644 Src/Plugins/Library/ml_fanzone/resource.h create mode 100644 Src/Plugins/Library/ml_fanzone/resources/FANZONE_16x16.bmp create mode 100644 Src/Plugins/Library/ml_fanzone/resources/cefsimple.ico create mode 100644 Src/Plugins/Library/ml_fanzone/resources/small.ico create mode 100644 Src/Plugins/Library/ml_fanzone/version.rc2 create mode 100644 Src/Plugins/Library/ml_fanzone/view.cpp create mode 100644 Src/Plugins/Library/ml_history/HistoryAPI.cpp create mode 100644 Src/Plugins/Library/ml_history/HistoryAPI.h create mode 100644 Src/Plugins/Library/ml_history/HistoryAPIFactory.cpp create mode 100644 Src/Plugins/Library/ml_history/HistoryAPIFactory.h create mode 100644 Src/Plugins/Library/ml_history/JSAPI2_Creator.cpp create mode 100644 Src/Plugins/Library/ml_history/JSAPI2_Creator.h create mode 100644 Src/Plugins/Library/ml_history/JSAPI2_HistoryAPI.cpp create mode 100644 Src/Plugins/Library/ml_history/JSAPI2_HistoryAPI.h create mode 100644 Src/Plugins/Library/ml_history/JSAPI2_HistoryRecord.cpp create mode 100644 Src/Plugins/Library/ml_history/JSAPI2_HistoryRecord.h create mode 100644 Src/Plugins/Library/ml_history/JSAPI2_HistoryRecordList.cpp create mode 100644 Src/Plugins/Library/ml_history/JSAPI2_HistoryRecordList.h create mode 100644 Src/Plugins/Library/ml_history/Main.cpp create mode 100644 Src/Plugins/Library/ml_history/Main.h create mode 100644 Src/Plugins/Library/ml_history/api__ml_history.h create mode 100644 Src/Plugins/Library/ml_history/api_history.cpp create mode 100644 Src/Plugins/Library/ml_history/api_history.h create mode 100644 Src/Plugins/Library/ml_history/db_error.txt create mode 100644 Src/Plugins/Library/ml_history/history.h create mode 100644 Src/Plugins/Library/ml_history/ml_history.cpp create mode 100644 Src/Plugins/Library/ml_history/ml_history.h create mode 100644 Src/Plugins/Library/ml_history/ml_history.rc create mode 100644 Src/Plugins/Library/ml_history/ml_history.sln create mode 100644 Src/Plugins/Library/ml_history/ml_history.vcxproj create mode 100644 Src/Plugins/Library/ml_history/ml_history.vcxproj.filters create mode 100644 Src/Plugins/Library/ml_history/prefs.cpp create mode 100644 Src/Plugins/Library/ml_history/resource.h create mode 100644 Src/Plugins/Library/ml_history/resources/ti_history_items_16x16x16.bmp create mode 100644 Src/Plugins/Library/ml_history/version.rc2 create mode 100644 Src/Plugins/Library/ml_history/view_history.cpp create mode 100644 Src/Plugins/Library/ml_hotmixradio/api__ml_hotmixradio.h create mode 100644 Src/Plugins/Library/ml_hotmixradio/listview.cpp create mode 100644 Src/Plugins/Library/ml_hotmixradio/listview.h create mode 100644 Src/Plugins/Library/ml_hotmixradio/main.cpp create mode 100644 Src/Plugins/Library/ml_hotmixradio/main.h create mode 100644 Src/Plugins/Library/ml_hotmixradio/ml_hotmixradio.rc create mode 100644 Src/Plugins/Library/ml_hotmixradio/ml_hotmixradio.vcxproj create mode 100644 Src/Plugins/Library/ml_hotmixradio/ml_hotmixradio.vcxproj.filters create mode 100644 Src/Plugins/Library/ml_hotmixradio/resource.h create mode 100644 Src/Plugins/Library/ml_hotmixradio/resources/HOTMIXRADIO_16x16.bmp create mode 100644 Src/Plugins/Library/ml_hotmixradio/version.rc2 create mode 100644 Src/Plugins/Library/ml_hotmixradio/view.cpp create mode 100644 Src/Plugins/Library/ml_impex/ImportPlaylists.cpp create mode 100644 Src/Plugins/Library/ml_impex/ImporterAPI.cpp create mode 100644 Src/Plugins/Library/ml_impex/ImporterAPI.h create mode 100644 Src/Plugins/Library/ml_impex/api__ml_impex.h create mode 100644 Src/Plugins/Library/ml_impex/api_importer.h create mode 100644 Src/Plugins/Library/ml_impex/impex.cpp create mode 100644 Src/Plugins/Library/ml_impex/importer.cpp create mode 100644 Src/Plugins/Library/ml_impex/importer.h create mode 100644 Src/Plugins/Library/ml_impex/itunesxmlwrite.cpp create mode 100644 Src/Plugins/Library/ml_impex/itunesxmlwrite.h create mode 100644 Src/Plugins/Library/ml_impex/ml_impex.rc create mode 100644 Src/Plugins/Library/ml_impex/ml_impex.sln create mode 100644 Src/Plugins/Library/ml_impex/ml_impex.vcxproj create mode 100644 Src/Plugins/Library/ml_impex/ml_impex.vcxproj.filters create mode 100644 Src/Plugins/Library/ml_impex/resource.h create mode 100644 Src/Plugins/Library/ml_impex/version.rc2 create mode 100644 Src/Plugins/Library/ml_local/AlbumArtCache.cpp create mode 100644 Src/Plugins/Library/ml_local/AlbumArtCache.h create mode 100644 Src/Plugins/Library/ml_local/AlbumArtContainer.cpp create mode 100644 Src/Plugins/Library/ml_local/AlbumArtContainer.h create mode 100644 Src/Plugins/Library/ml_local/AlbumArtFilter.cpp create mode 100644 Src/Plugins/Library/ml_local/AlbumArtFilter.h create mode 100644 Src/Plugins/Library/ml_local/AlbumFilter.cpp create mode 100644 Src/Plugins/Library/ml_local/AlbumFilter.h create mode 100644 Src/Plugins/Library/ml_local/DBitemrecord.cpp create mode 100644 Src/Plugins/Library/ml_local/FolderBrowseEx.cpp create mode 100644 Src/Plugins/Library/ml_local/FolderBrowseEx.h create mode 100644 Src/Plugins/Library/ml_local/LocalMediaCOM.cpp create mode 100644 Src/Plugins/Library/ml_local/LocalMediaCOM.h create mode 100644 Src/Plugins/Library/ml_local/MD5.cpp create mode 100644 Src/Plugins/Library/ml_local/MD5.h create mode 100644 Src/Plugins/Library/ml_local/MLDBCallback.h create mode 100644 Src/Plugins/Library/ml_local/MLString.cpp create mode 100644 Src/Plugins/Library/ml_local/MLString.h create mode 100644 Src/Plugins/Library/ml_local/Main.cpp create mode 100644 Src/Plugins/Library/ml_local/Main.h create mode 100644 Src/Plugins/Library/ml_local/ReIndexUI.cpp create mode 100644 Src/Plugins/Library/ml_local/SaveQuery.cpp create mode 100644 Src/Plugins/Library/ml_local/ScanFolderBrowser.cpp create mode 100644 Src/Plugins/Library/ml_local/ScanFolderBrowser.h create mode 100644 Src/Plugins/Library/ml_local/SimpleFilter.cpp create mode 100644 Src/Plugins/Library/ml_local/SimpleFilter.h create mode 100644 Src/Plugins/Library/ml_local/TitleInfo.cpp create mode 100644 Src/Plugins/Library/ml_local/ViewFilter.cpp create mode 100644 Src/Plugins/Library/ml_local/ViewFilter.h create mode 100644 Src/Plugins/Library/ml_local/add.cpp create mode 100644 Src/Plugins/Library/ml_local/api__ml_local.h create mode 100644 Src/Plugins/Library/ml_local/api_mldb.cpp create mode 100644 Src/Plugins/Library/ml_local/api_mldb.h create mode 100644 Src/Plugins/Library/ml_local/bgscan.cpp create mode 100644 Src/Plugins/Library/ml_local/contnr.cpp create mode 100644 Src/Plugins/Library/ml_local/contnr.h create mode 100644 Src/Plugins/Library/ml_local/db.h create mode 100644 Src/Plugins/Library/ml_local/db_error.txt create mode 100644 Src/Plugins/Library/ml_local/editinfo.cpp create mode 100644 Src/Plugins/Library/ml_local/editquery.cpp create mode 100644 Src/Plugins/Library/ml_local/editquery.h create mode 100644 Src/Plugins/Library/ml_local/evntsink.cpp create mode 100644 Src/Plugins/Library/ml_local/evntsink.h create mode 100644 Src/Plugins/Library/ml_local/guess.cpp create mode 100644 Src/Plugins/Library/ml_local/handleMessage.cpp create mode 100644 Src/Plugins/Library/ml_local/local_menu.cpp create mode 100644 Src/Plugins/Library/ml_local/local_menu.h create mode 100644 Src/Plugins/Library/ml_local/metaRecord.h create mode 100644 Src/Plugins/Library/ml_local/ml_local.cpp create mode 100644 Src/Plugins/Library/ml_local/ml_local.h create mode 100644 Src/Plugins/Library/ml_local/ml_local.rc create mode 100644 Src/Plugins/Library/ml_local/ml_local.sln create mode 100644 Src/Plugins/Library/ml_local/ml_local.vcxproj create mode 100644 Src/Plugins/Library/ml_local/ml_local.vcxproj.filters create mode 100644 Src/Plugins/Library/ml_local/ml_subclass.cpp create mode 100644 Src/Plugins/Library/ml_local/mldbApi.cpp create mode 100644 Src/Plugins/Library/ml_local/mldbApi.h create mode 100644 Src/Plugins/Library/ml_local/mldbApiFactory.cpp create mode 100644 Src/Plugins/Library/ml_local/mldbApiFactory.h create mode 100644 Src/Plugins/Library/ml_local/nde_error.txt create mode 100644 Src/Plugins/Library/ml_local/nde_itemRecord.cpp create mode 100644 Src/Plugins/Library/ml_local/pe_subclass.cpp create mode 100644 Src/Plugins/Library/ml_local/prefs.cpp create mode 100644 Src/Plugins/Library/ml_local/queries.cpp create mode 100644 Src/Plugins/Library/ml_local/queries.txt create mode 100644 Src/Plugins/Library/ml_local/remove.cpp create mode 100644 Src/Plugins/Library/ml_local/resource.h create mode 100644 Src/Plugins/Library/ml_local/resources/icn_alb_art.bmp create mode 100644 Src/Plugins/Library/ml_local/resources/icn_columns.bmp create mode 100644 Src/Plugins/Library/ml_local/resources/icn_view_mode.bmp create mode 100644 Src/Plugins/Library/ml_local/resources/nf_simple.bmp create mode 100644 Src/Plugins/Library/ml_local/resources/nf_simplealbum.bmp create mode 100644 Src/Plugins/Library/ml_local/resources/nf_threefilters.bmp create mode 100644 Src/Plugins/Library/ml_local/resources/nf_twofilters.bmp create mode 100644 Src/Plugins/Library/ml_local/resources/notfound.png create mode 100644 Src/Plugins/Library/ml_local/resources/ti_audio_16x16x16.bmp create mode 100644 Src/Plugins/Library/ml_local/resources/ti_most_played_16x16x16.bmp create mode 100644 Src/Plugins/Library/ml_local/resources/ti_never_played_16x16x16.bmp create mode 100644 Src/Plugins/Library/ml_local/resources/ti_podcasts_16x16x16.bmp create mode 100644 Src/Plugins/Library/ml_local/resources/ti_recently_added_16x16x16.bmp create mode 100644 Src/Plugins/Library/ml_local/resources/ti_recently_modified_16x16x16.bmp create mode 100644 Src/Plugins/Library/ml_local/resources/ti_recently_played_16x16x16.bmp create mode 100644 Src/Plugins/Library/ml_local/resources/ti_top_rated_16x16x16.bmp create mode 100644 Src/Plugins/Library/ml_local/resources/ti_video_16x16x16.bmp create mode 100644 Src/Plugins/Library/ml_local/util.cpp create mode 100644 Src/Plugins/Library/ml_local/version.rc2 create mode 100644 Src/Plugins/Library/ml_local/view_audio.cpp create mode 100644 Src/Plugins/Library/ml_local/view_errorinfo.cpp create mode 100644 Src/Plugins/Library/ml_local/view_media.cpp create mode 100644 Src/Plugins/Library/ml_local/view_miniinfo.cpp create mode 100644 Src/Plugins/Library/ml_local/wa_subclass.cpp create mode 100644 Src/Plugins/Library/ml_nft/api__ml_nft.h create mode 100644 Src/Plugins/Library/ml_nft/listview.cpp create mode 100644 Src/Plugins/Library/ml_nft/listview.h create mode 100644 Src/Plugins/Library/ml_nft/main.cpp create mode 100644 Src/Plugins/Library/ml_nft/main.h create mode 100644 Src/Plugins/Library/ml_nft/ml_nft.rc create mode 100644 Src/Plugins/Library/ml_nft/ml_nft.vcxproj create mode 100644 Src/Plugins/Library/ml_nft/ml_nft.vcxproj.filters create mode 100644 Src/Plugins/Library/ml_nft/resource.h create mode 100644 Src/Plugins/Library/ml_nft/resources/NFT_16x16.bmp create mode 100644 Src/Plugins/Library/ml_nft/resources/_NFT_16x16.png create mode 100644 Src/Plugins/Library/ml_nft/version.rc2 create mode 100644 Src/Plugins/Library/ml_nft/view.cpp create mode 100644 Src/Plugins/Library/ml_nowplaying/common.cpp create mode 100644 Src/Plugins/Library/ml_nowplaying/common.h create mode 100644 Src/Plugins/Library/ml_nowplaying/external.cpp create mode 100644 Src/Plugins/Library/ml_nowplaying/external.h create mode 100644 Src/Plugins/Library/ml_nowplaying/handler.cpp create mode 100644 Src/Plugins/Library/ml_nowplaying/handler.h create mode 100644 Src/Plugins/Library/ml_nowplaying/local_menu.cpp create mode 100644 Src/Plugins/Library/ml_nowplaying/local_menu.h create mode 100644 Src/Plugins/Library/ml_nowplaying/main.cpp create mode 100644 Src/Plugins/Library/ml_nowplaying/main.h create mode 100644 Src/Plugins/Library/ml_nowplaying/ml_nowplaying.rc create mode 100644 Src/Plugins/Library/ml_nowplaying/ml_nowplaying.sln create mode 100644 Src/Plugins/Library/ml_nowplaying/ml_nowplaying.vcxproj create mode 100644 Src/Plugins/Library/ml_nowplaying/ml_nowplaying.vcxproj.filters create mode 100644 Src/Plugins/Library/ml_nowplaying/navigation.cpp create mode 100644 Src/Plugins/Library/ml_nowplaying/navigation.h create mode 100644 Src/Plugins/Library/ml_nowplaying/png.rc create mode 100644 Src/Plugins/Library/ml_nowplaying/resource.h create mode 100644 Src/Plugins/Library/ml_nowplaying/resources/serviceIcon.png create mode 100644 Src/Plugins/Library/ml_nowplaying/service.cpp create mode 100644 Src/Plugins/Library/ml_nowplaying/service.h create mode 100644 Src/Plugins/Library/ml_nowplaying/version.rc2 create mode 100644 Src/Plugins/Library/ml_nowplaying/wasabi.cpp create mode 100644 Src/Plugins/Library/ml_nowplaying/wasabi.h create mode 100644 Src/Plugins/Library/ml_nowplaying/wasabiCallback.cpp create mode 100644 Src/Plugins/Library/ml_nowplaying/wasabiCallback.h create mode 100644 Src/Plugins/Library/ml_online/BufferCache.h create mode 100644 Src/Plugins/Library/ml_online/JSAPI2_Creator.cpp create mode 100644 Src/Plugins/Library/ml_online/JSAPI2_Creator.h create mode 100644 Src/Plugins/Library/ml_online/JnetCOM.cpp create mode 100644 Src/Plugins/Library/ml_online/JnetCOM.h create mode 100644 Src/Plugins/Library/ml_online/Main.cpp create mode 100644 Src/Plugins/Library/ml_online/Main.h create mode 100644 Src/Plugins/Library/ml_online/OMCOM.cpp create mode 100644 Src/Plugins/Library/ml_online/OMCOM.h create mode 100644 Src/Plugins/Library/ml_online/Preferences.cpp create mode 100644 Src/Plugins/Library/ml_online/Preferences.h create mode 100644 Src/Plugins/Library/ml_online/Setup/SetupGroupFilter.h create mode 100644 Src/Plugins/Library/ml_online/Setup/setup.cpp create mode 100644 Src/Plugins/Library/ml_online/Setup/setupDetails.cpp create mode 100644 Src/Plugins/Library/ml_online/Setup/setupDetails.h create mode 100644 Src/Plugins/Library/ml_online/Setup/setupDetailsGroup.cpp create mode 100644 Src/Plugins/Library/ml_online/Setup/setupDetailsService.cpp create mode 100644 Src/Plugins/Library/ml_online/Setup/setupGroup.cpp create mode 100644 Src/Plugins/Library/ml_online/Setup/setupGroup.h create mode 100644 Src/Plugins/Library/ml_online/Setup/setupGroupFilter.cpp create mode 100644 Src/Plugins/Library/ml_online/Setup/setupGroupList.cpp create mode 100644 Src/Plugins/Library/ml_online/Setup/setupGroupList.h create mode 100644 Src/Plugins/Library/ml_online/Setup/setupImage.cpp create mode 100644 Src/Plugins/Library/ml_online/Setup/setupImage.h create mode 100644 Src/Plugins/Library/ml_online/Setup/setupListbox.cpp create mode 100644 Src/Plugins/Library/ml_online/Setup/setupListbox.h create mode 100644 Src/Plugins/Library/ml_online/Setup/setupListboxLabel.cpp create mode 100644 Src/Plugins/Library/ml_online/Setup/setupListboxLabel.h create mode 100644 Src/Plugins/Library/ml_online/Setup/setupLog.cpp create mode 100644 Src/Plugins/Library/ml_online/Setup/setupLog.h create mode 100644 Src/Plugins/Library/ml_online/Setup/setupPage.cpp create mode 100644 Src/Plugins/Library/ml_online/Setup/setupPage.h create mode 100644 Src/Plugins/Library/ml_online/Setup/setupPageWnd.cpp create mode 100644 Src/Plugins/Library/ml_online/Setup/setupRecord.cpp create mode 100644 Src/Plugins/Library/ml_online/Setup/setupRecord.h create mode 100644 Src/Plugins/Library/ml_online/Setup/setupServicePanel.cpp create mode 100644 Src/Plugins/Library/ml_online/Setup/setupServicePanel.h create mode 100644 Src/Plugins/Library/ml_online/api__ml_online.h create mode 100644 Src/Plugins/Library/ml_online/browserEvent.cpp create mode 100644 Src/Plugins/Library/ml_online/browserEvent.h create mode 100644 Src/Plugins/Library/ml_online/commands.cpp create mode 100644 Src/Plugins/Library/ml_online/commands.h create mode 100644 Src/Plugins/Library/ml_online/common.cpp create mode 100644 Src/Plugins/Library/ml_online/common.h create mode 100644 Src/Plugins/Library/ml_online/config.cpp create mode 100644 Src/Plugins/Library/ml_online/config.h create mode 100644 Src/Plugins/Library/ml_online/external.cpp create mode 100644 Src/Plugins/Library/ml_online/external.h create mode 100644 Src/Plugins/Library/ml_online/forceUrl.cpp create mode 100644 Src/Plugins/Library/ml_online/forceUrl.h create mode 100644 Src/Plugins/Library/ml_online/handler.cpp create mode 100644 Src/Plugins/Library/ml_online/handler.h create mode 100644 Src/Plugins/Library/ml_online/import.h create mode 100644 Src/Plugins/Library/ml_online/importFile.cpp create mode 100644 Src/Plugins/Library/ml_online/importUrl.cpp create mode 100644 Src/Plugins/Library/ml_online/jsapi2_omcom.cpp create mode 100644 Src/Plugins/Library/ml_online/jsapi2_omcom.h create mode 100644 Src/Plugins/Library/ml_online/local_menu.cpp create mode 100644 Src/Plugins/Library/ml_online/local_menu.h create mode 100644 Src/Plugins/Library/ml_online/messageBox.cpp create mode 100644 Src/Plugins/Library/ml_online/messageBox.h create mode 100644 Src/Plugins/Library/ml_online/ml_online.rc create mode 100644 Src/Plugins/Library/ml_online/ml_online.sln create mode 100644 Src/Plugins/Library/ml_online/ml_online.vcxproj create mode 100644 Src/Plugins/Library/ml_online/ml_online.vcxproj.filters create mode 100644 Src/Plugins/Library/ml_online/navigation.cpp create mode 100644 Src/Plugins/Library/ml_online/navigation.h create mode 100644 Src/Plugins/Library/ml_online/png.rc create mode 100644 Src/Plugins/Library/ml_online/resource.h create mode 100644 Src/Plugins/Library/ml_online/resources/iconAol.png create mode 100644 Src/Plugins/Library/ml_online/resources/iconAolGames.png create mode 100644 Src/Plugins/Library/ml_online/resources/iconDefault.png create mode 100644 Src/Plugins/Library/ml_online/resources/iconIn2Tv.png create mode 100644 Src/Plugins/Library/ml_online/resources/iconMusicNow.png create mode 100644 Src/Plugins/Library/ml_online/resources/iconShoutcastRadio.png create mode 100644 Src/Plugins/Library/ml_online/resources/iconShoutcastTv.png create mode 100644 Src/Plugins/Library/ml_online/resources/iconSingingfish.png create mode 100644 Src/Plugins/Library/ml_online/resources/iconWaMusic.png create mode 100644 Src/Plugins/Library/ml_online/resources/iconWaRemote.png create mode 100644 Src/Plugins/Library/ml_online/resources/pages/serviceEditor.htm create mode 100644 Src/Plugins/Library/ml_online/resources/pages/webdev.js create mode 100644 Src/Plugins/Library/ml_online/resources/service64x64.png create mode 100644 Src/Plugins/Library/ml_online/serviceHelper.cpp create mode 100644 Src/Plugins/Library/ml_online/serviceHelper.h create mode 100644 Src/Plugins/Library/ml_online/serviceHost.cpp create mode 100644 Src/Plugins/Library/ml_online/serviceHost.h create mode 100644 Src/Plugins/Library/ml_online/testPages.rc create mode 100644 Src/Plugins/Library/ml_online/version.rc2 create mode 100644 Src/Plugins/Library/ml_online/wasabi.cpp create mode 100644 Src/Plugins/Library/ml_playlists/AMNWatcher.cpp create mode 100644 Src/Plugins/Library/ml_playlists/AddPlaylist.cpp create mode 100644 Src/Plugins/Library/ml_playlists/CurrentPlaylist.cpp create mode 100644 Src/Plugins/Library/ml_playlists/CurrentPlaylist.h create mode 100644 Src/Plugins/Library/ml_playlists/DESIGN.TXT create mode 100644 Src/Plugins/Library/ml_playlists/Playlist.cpp create mode 100644 Src/Plugins/Library/ml_playlists/Playlist.h create mode 100644 Src/Plugins/Library/ml_playlists/PlaylistDirectoryCallback.cpp create mode 100644 Src/Plugins/Library/ml_playlists/PlaylistDirectoryCallback.h create mode 100644 Src/Plugins/Library/ml_playlists/PlaylistInfo.cpp create mode 100644 Src/Plugins/Library/ml_playlists/PlaylistInfo.h create mode 100644 Src/Plugins/Library/ml_playlists/PlaylistView.cpp create mode 100644 Src/Plugins/Library/ml_playlists/PlaylistView.h create mode 100644 Src/Plugins/Library/ml_playlists/PlaylistsCB.cpp create mode 100644 Src/Plugins/Library/ml_playlists/PlaylistsCB.h create mode 100644 Src/Plugins/Library/ml_playlists/PlaylistsCOM.cpp create mode 100644 Src/Plugins/Library/ml_playlists/PlaylistsCOM.h create mode 100644 Src/Plugins/Library/ml_playlists/RenamePlaylist.cpp create mode 100644 Src/Plugins/Library/ml_playlists/SendTo.cpp create mode 100644 Src/Plugins/Library/ml_playlists/SendTo.h create mode 100644 Src/Plugins/Library/ml_playlists/api__ml_playlists.h create mode 100644 Src/Plugins/Library/ml_playlists/main.h create mode 100644 Src/Plugins/Library/ml_playlists/ml_playlists.cpp create mode 100644 Src/Plugins/Library/ml_playlists/ml_playlists.rc create mode 100644 Src/Plugins/Library/ml_playlists/ml_playlists.sln create mode 100644 Src/Plugins/Library/ml_playlists/ml_playlists.vcxproj create mode 100644 Src/Plugins/Library/ml_playlists/ml_playlists.vcxproj.filters create mode 100644 Src/Plugins/Library/ml_playlists/ml_subclass.cpp create mode 100644 Src/Plugins/Library/ml_playlists/pe_subclass.cpp create mode 100644 Src/Plugins/Library/ml_playlists/playlists.cpp create mode 100644 Src/Plugins/Library/ml_playlists/playlists.h create mode 100644 Src/Plugins/Library/ml_playlists/playlistsXML.cpp create mode 100644 Src/Plugins/Library/ml_playlists/playlistsXML.h create mode 100644 Src/Plugins/Library/ml_playlists/pluginproc.cpp create mode 100644 Src/Plugins/Library/ml_playlists/resource.h create mode 100644 Src/Plugins/Library/ml_playlists/resources/ti_cloud_playlist_16x16x16.bmp create mode 100644 Src/Plugins/Library/ml_playlists/resources/ti_playlist_16x16x16.bmp create mode 100644 Src/Plugins/Library/ml_playlists/version.rc2 create mode 100644 Src/Plugins/Library/ml_playlists/view_pl.cpp create mode 100644 Src/Plugins/Library/ml_playlists/view_playlists.cpp create mode 100644 Src/Plugins/Library/ml_playlists/wa_subclass.cpp create mode 100644 Src/Plugins/Library/ml_plg/AddPlaylist.cpp create mode 100644 Src/Plugins/Library/ml_plg/AlbumID.cpp create mode 100644 Src/Plugins/Library/ml_plg/IDScanner.cpp create mode 100644 Src/Plugins/Library/ml_plg/IDScanner.h create mode 100644 Src/Plugins/Library/ml_plg/PlaylistGeneratorAPI.cpp create mode 100644 Src/Plugins/Library/ml_plg/PlaylistGeneratorAPI.h create mode 100644 Src/Plugins/Library/ml_plg/api__ml_plg.h create mode 100644 Src/Plugins/Library/ml_plg/api_playlist_generator.h create mode 100644 Src/Plugins/Library/ml_plg/atltransactionmanager.h create mode 100644 Src/Plugins/Library/ml_plg/generate.cpp create mode 100644 Src/Plugins/Library/ml_plg/gn_logo_88x83.bmp create mode 100644 Src/Plugins/Library/ml_plg/impl_playlist.cpp create mode 100644 Src/Plugins/Library/ml_plg/impl_playlist.h create mode 100644 Src/Plugins/Library/ml_plg/main.h create mode 100644 Src/Plugins/Library/ml_plg/ml_plg.cpp create mode 100644 Src/Plugins/Library/ml_plg/ml_plg.rc create mode 100644 Src/Plugins/Library/ml_plg/ml_plg.sln create mode 100644 Src/Plugins/Library/ml_plg/ml_plg.vcxproj create mode 100644 Src/Plugins/Library/ml_plg/ml_plg.vcxproj.filters create mode 100644 Src/Plugins/Library/ml_plg/pass1.cpp create mode 100644 Src/Plugins/Library/ml_plg/pass2.cpp create mode 100644 Src/Plugins/Library/ml_plg/playlist.cpp create mode 100644 Src/Plugins/Library/ml_plg/playlist.h create mode 100644 Src/Plugins/Library/ml_plg/prefs.cpp create mode 100644 Src/Plugins/Library/ml_plg/resource.h create mode 100644 Src/Plugins/Library/ml_plg/util.cpp create mode 100644 Src/Plugins/Library/ml_plg/version.rc2 create mode 100644 Src/Plugins/Library/ml_plg/view.cpp create mode 100644 Src/Plugins/Library/ml_pmp/AlbumArtListView.cpp create mode 100644 Src/Plugins/Library/ml_pmp/AlbumArtListView.h create mode 100644 Src/Plugins/Library/ml_pmp/ArtistAlbumLists.cpp create mode 100644 Src/Plugins/Library/ml_pmp/ArtistAlbumLists.h create mode 100644 Src/Plugins/Library/ml_pmp/DeviceCommands.cpp create mode 100644 Src/Plugins/Library/ml_pmp/DeviceCommands.h create mode 100644 Src/Plugins/Library/ml_pmp/DeviceView.cpp create mode 100644 Src/Plugins/Library/ml_pmp/DeviceView.h create mode 100644 Src/Plugins/Library/ml_pmp/Filters.cpp create mode 100644 Src/Plugins/Library/ml_pmp/Filters.h create mode 100644 Src/Plugins/Library/ml_pmp/IconStore.cpp create mode 100644 Src/Plugins/Library/ml_pmp/IconStore.h create mode 100644 Src/Plugins/Library/ml_pmp/LinkedQueue.cpp create mode 100644 Src/Plugins/Library/ml_pmp/LinkedQueue.h create mode 100644 Src/Plugins/Library/ml_pmp/PmpDevice.cpp create mode 100644 Src/Plugins/Library/ml_pmp/PmpDevice.h create mode 100644 Src/Plugins/Library/ml_pmp/SkinnedListView.cpp create mode 100644 Src/Plugins/Library/ml_pmp/SkinnedListView.h create mode 100644 Src/Plugins/Library/ml_pmp/api__ml_pmp.h create mode 100644 Src/Plugins/Library/ml_pmp/autofill.cpp create mode 100644 Src/Plugins/Library/ml_pmp/banner.cpp create mode 100644 Src/Plugins/Library/ml_pmp/banner.h create mode 100644 Src/Plugins/Library/ml_pmp/config.cpp create mode 100644 Src/Plugins/Library/ml_pmp/config.h create mode 100644 Src/Plugins/Library/ml_pmp/editinfo.cpp create mode 100644 Src/Plugins/Library/ml_pmp/graphics.cpp create mode 100644 Src/Plugins/Library/ml_pmp/graphics.h create mode 100644 Src/Plugins/Library/ml_pmp/indirectplaybackserver.cpp create mode 100644 Src/Plugins/Library/ml_pmp/local_menu.cpp create mode 100644 Src/Plugins/Library/ml_pmp/local_menu.h create mode 100644 Src/Plugins/Library/ml_pmp/main.cpp create mode 100644 Src/Plugins/Library/ml_pmp/main.h create mode 100644 Src/Plugins/Library/ml_pmp/metadata_utils.cpp create mode 100644 Src/Plugins/Library/ml_pmp/metadata_utils.h create mode 100644 Src/Plugins/Library/ml_pmp/ml_pmp.rc create mode 100644 Src/Plugins/Library/ml_pmp/ml_pmp.sln create mode 100644 Src/Plugins/Library/ml_pmp/ml_pmp.vcxproj create mode 100644 Src/Plugins/Library/ml_pmp/ml_pmp.vcxproj.filters create mode 100644 Src/Plugins/Library/ml_pmp/mt19937ar.cpp create mode 100644 Src/Plugins/Library/ml_pmp/mt19937ar.h create mode 100644 Src/Plugins/Library/ml_pmp/pluginloader.cpp create mode 100644 Src/Plugins/Library/ml_pmp/pluginloader.h create mode 100644 Src/Plugins/Library/ml_pmp/pmp.h create mode 100644 Src/Plugins/Library/ml_pmp/prefs.cpp create mode 100644 Src/Plugins/Library/ml_pmp/replaceVars.cpp create mode 100644 Src/Plugins/Library/ml_pmp/resource1.h create mode 100644 Src/Plugins/Library/ml_pmp/resources/albumArt.png create mode 100644 Src/Plugins/Library/ml_pmp/resources/columns.png create mode 100644 Src/Plugins/Library/ml_pmp/resources/deviceIcon.png create mode 100644 Src/Plugins/Library/ml_pmp/resources/notfound.png create mode 100644 Src/Plugins/Library/ml_pmp/resources/playlistIcon.png create mode 100644 Src/Plugins/Library/ml_pmp/resources/sync-command-small.png create mode 100644 Src/Plugins/Library/ml_pmp/resources/transfer_queue_16x16.png create mode 100644 Src/Plugins/Library/ml_pmp/resources/transfer_queue_16x16_1.png create mode 100644 Src/Plugins/Library/ml_pmp/resources/transfer_queue_16x16_2.png create mode 100644 Src/Plugins/Library/ml_pmp/resources/transfer_queue_16x16_3.png create mode 100644 Src/Plugins/Library/ml_pmp/resources/usb.png create mode 100644 Src/Plugins/Library/ml_pmp/resources/videoIcon.png create mode 100644 Src/Plugins/Library/ml_pmp/resources/viewMode.png create mode 100644 Src/Plugins/Library/ml_pmp/syncCloudDialog.cpp create mode 100644 Src/Plugins/Library/ml_pmp/syncCloudDialog.h create mode 100644 Src/Plugins/Library/ml_pmp/syncDialog.cpp create mode 100644 Src/Plugins/Library/ml_pmp/syncDialog.h create mode 100644 Src/Plugins/Library/ml_pmp/transcoder.h create mode 100644 Src/Plugins/Library/ml_pmp/transcoder_imp.cpp create mode 100644 Src/Plugins/Library/ml_pmp/transcoder_imp.h create mode 100644 Src/Plugins/Library/ml_pmp/transfer_thread.cpp create mode 100644 Src/Plugins/Library/ml_pmp/transfer_thread.h create mode 100644 Src/Plugins/Library/ml_pmp/version.rc2 create mode 100644 Src/Plugins/Library/ml_pmp/view_pmp_devices.cpp create mode 100644 Src/Plugins/Library/ml_pmp/view_pmp_media.cpp create mode 100644 Src/Plugins/Library/ml_pmp/view_pmp_queue.cpp create mode 100644 Src/Plugins/Library/ml_rg/Process.cpp create mode 100644 Src/Plugins/Library/ml_rg/Process.h create mode 100644 Src/Plugins/Library/ml_rg/Progress.cpp create mode 100644 Src/Plugins/Library/ml_rg/RGFactory.cpp create mode 100644 Src/Plugins/Library/ml_rg/RGFactory.h create mode 100644 Src/Plugins/Library/ml_rg/Results.cpp create mode 100644 Src/Plugins/Library/ml_rg/api__ml_rg.h create mode 100644 Src/Plugins/Library/ml_rg/config.cpp create mode 100644 Src/Plugins/Library/ml_rg/main.h create mode 100644 Src/Plugins/Library/ml_rg/metadata.cpp create mode 100644 Src/Plugins/Library/ml_rg/ml_rg.cpp create mode 100644 Src/Plugins/Library/ml_rg/ml_rg.rc create mode 100644 Src/Plugins/Library/ml_rg/ml_rg.sln create mode 100644 Src/Plugins/Library/ml_rg/ml_rg.vcxproj create mode 100644 Src/Plugins/Library/ml_rg/ml_rg.vcxproj.filters create mode 100644 Src/Plugins/Library/ml_rg/obj_replaygain.h create mode 100644 Src/Plugins/Library/ml_rg/replaygain.cpp create mode 100644 Src/Plugins/Library/ml_rg/resource.h create mode 100644 Src/Plugins/Library/ml_rg/version.rc2 create mode 100644 Src/Plugins/Library/ml_transcode/LinkedQueue.cpp create mode 100644 Src/Plugins/Library/ml_transcode/LinkedQueue.h create mode 100644 Src/Plugins/Library/ml_transcode/api__ml_transcode.h create mode 100644 Src/Plugins/Library/ml_transcode/main.cpp create mode 100644 Src/Plugins/Library/ml_transcode/ml_transcode.rc create mode 100644 Src/Plugins/Library/ml_transcode/ml_transcode.sln create mode 100644 Src/Plugins/Library/ml_transcode/ml_transcode.vcxproj create mode 100644 Src/Plugins/Library/ml_transcode/ml_transcode.vcxproj.filters create mode 100644 Src/Plugins/Library/ml_transcode/replaceVars.cpp create mode 100644 Src/Plugins/Library/ml_transcode/resource.h create mode 100644 Src/Plugins/Library/ml_transcode/reversesync.h create mode 100644 Src/Plugins/Library/ml_transcode/version.rc2 create mode 100644 Src/Plugins/Library/ml_webdev/commands.cpp create mode 100644 Src/Plugins/Library/ml_webdev/commands.h create mode 100644 Src/Plugins/Library/ml_webdev/common.cpp create mode 100644 Src/Plugins/Library/ml_webdev/common.h create mode 100644 Src/Plugins/Library/ml_webdev/config.cpp create mode 100644 Src/Plugins/Library/ml_webdev/config.h create mode 100644 Src/Plugins/Library/ml_webdev/external.cpp create mode 100644 Src/Plugins/Library/ml_webdev/external.h create mode 100644 Src/Plugins/Library/ml_webdev/forceUrl.cpp create mode 100644 Src/Plugins/Library/ml_webdev/forceUrl.h create mode 100644 Src/Plugins/Library/ml_webdev/import.h create mode 100644 Src/Plugins/Library/ml_webdev/importFile.cpp create mode 100644 Src/Plugins/Library/ml_webdev/importUrl.cpp create mode 100644 Src/Plugins/Library/ml_webdev/local_menu.cpp create mode 100644 Src/Plugins/Library/ml_webdev/local_menu.h create mode 100644 Src/Plugins/Library/ml_webdev/main.cpp create mode 100644 Src/Plugins/Library/ml_webdev/main.h create mode 100644 Src/Plugins/Library/ml_webdev/ml_webdev.rc create mode 100644 Src/Plugins/Library/ml_webdev/ml_webdev.sln create mode 100644 Src/Plugins/Library/ml_webdev/ml_webdev.vcxproj create mode 100644 Src/Plugins/Library/ml_webdev/ml_webdev.vcxproj.filters create mode 100644 Src/Plugins/Library/ml_webdev/navigation.cpp create mode 100644 Src/Plugins/Library/ml_webdev/navigation.h create mode 100644 Src/Plugins/Library/ml_webdev/png.rc create mode 100644 Src/Plugins/Library/ml_webdev/resource.h create mode 100644 Src/Plugins/Library/ml_webdev/resources/gear.png create mode 100644 Src/Plugins/Library/ml_webdev/resources/help.png create mode 100644 Src/Plugins/Library/ml_webdev/resources/pages/api2.js create mode 100644 Src/Plugins/Library/ml_webdev/resources/pages/applicationApi.htm create mode 100644 Src/Plugins/Library/ml_webdev/resources/pages/asyncDownloaderApi.htm create mode 100644 Src/Plugins/Library/ml_webdev/resources/pages/bookmarkApi.htm create mode 100644 Src/Plugins/Library/ml_webdev/resources/pages/configApi.htm create mode 100644 Src/Plugins/Library/ml_webdev/resources/pages/historyApi.htm create mode 100644 Src/Plugins/Library/ml_webdev/resources/pages/mediaCoreApi.htm create mode 100644 Src/Plugins/Library/ml_webdev/resources/pages/playQueueApi.htm create mode 100644 Src/Plugins/Library/ml_webdev/resources/pages/playlistApi.htm create mode 100644 Src/Plugins/Library/ml_webdev/resources/pages/podcastApi.htm create mode 100644 Src/Plugins/Library/ml_webdev/resources/pages/securityApi.htm create mode 100644 Src/Plugins/Library/ml_webdev/resources/pages/serviceEditor.htm create mode 100644 Src/Plugins/Library/ml_webdev/resources/pages/skinApi.htm create mode 100644 Src/Plugins/Library/ml_webdev/resources/pages/transportApi.htm create mode 100644 Src/Plugins/Library/ml_webdev/resources/pages/transportEventsApi.htm create mode 100644 Src/Plugins/Library/ml_webdev/resources/pages/webdev.htm create mode 100644 Src/Plugins/Library/ml_webdev/resources/pages/webdev.js create mode 100644 Src/Plugins/Library/ml_webdev/resources/pages/welcome.htm create mode 100644 Src/Plugins/Library/ml_webdev/serviceHelper.cpp create mode 100644 Src/Plugins/Library/ml_webdev/serviceHelper.h create mode 100644 Src/Plugins/Library/ml_webdev/serviceHost.cpp create mode 100644 Src/Plugins/Library/ml_webdev/serviceHost.h create mode 100644 Src/Plugins/Library/ml_webdev/testPages.rc create mode 100644 Src/Plugins/Library/ml_webdev/version.rc2 create mode 100644 Src/Plugins/Library/ml_webdev/wasabi.cpp create mode 100644 Src/Plugins/Library/ml_webdev/wasabi.h create mode 100644 Src/Plugins/Library/ml_wire/AtomParse.h create mode 100644 Src/Plugins/Library/ml_wire/BackgroundDownloader.cpp create mode 100644 Src/Plugins/Library/ml_wire/BackgroundDownloader.h create mode 100644 Src/Plugins/Library/ml_wire/ChannelCheck.h create mode 100644 Src/Plugins/Library/ml_wire/ChannelRefresher.cpp create mode 100644 Src/Plugins/Library/ml_wire/ChannelRefresher.h create mode 100644 Src/Plugins/Library/ml_wire/ChannelSync.h create mode 100644 Src/Plugins/Library/ml_wire/Cloud.cpp create mode 100644 Src/Plugins/Library/ml_wire/Cloud.h create mode 100644 Src/Plugins/Library/ml_wire/DESIGN.txt create mode 100644 Src/Plugins/Library/ml_wire/Defaults.cpp create mode 100644 Src/Plugins/Library/ml_wire/Defaults.h create mode 100644 Src/Plugins/Library/ml_wire/DownloadManager.cpp create mode 100644 Src/Plugins/Library/ml_wire/DownloadStatus.cpp create mode 100644 Src/Plugins/Library/ml_wire/DownloadStatus.h create mode 100644 Src/Plugins/Library/ml_wire/DownloadThread.cpp create mode 100644 Src/Plugins/Library/ml_wire/DownloadThread.h create mode 100644 Src/Plugins/Library/ml_wire/Downloaded.cpp create mode 100644 Src/Plugins/Library/ml_wire/Downloaded.h create mode 100644 Src/Plugins/Library/ml_wire/DownloadsDialog.cpp create mode 100644 Src/Plugins/Library/ml_wire/DownloadsDialog.h create mode 100644 Src/Plugins/Library/ml_wire/DownloadsParse.cpp create mode 100644 Src/Plugins/Library/ml_wire/DownloadsParse.h create mode 100644 Src/Plugins/Library/ml_wire/ExternalCOM.cpp create mode 100644 Src/Plugins/Library/ml_wire/ExternalCOM.h create mode 100644 Src/Plugins/Library/ml_wire/Factory.cpp create mode 100644 Src/Plugins/Library/ml_wire/Factory.h create mode 100644 Src/Plugins/Library/ml_wire/FeedParse.cpp create mode 100644 Src/Plugins/Library/ml_wire/FeedParse.h create mode 100644 Src/Plugins/Library/ml_wire/FeedUtil.cpp create mode 100644 Src/Plugins/Library/ml_wire/FeedUtil.h create mode 100644 Src/Plugins/Library/ml_wire/Feeds.cpp create mode 100644 Src/Plugins/Library/ml_wire/Feeds.h create mode 100644 Src/Plugins/Library/ml_wire/FeedsDialog.h create mode 100644 Src/Plugins/Library/ml_wire/Item.cpp create mode 100644 Src/Plugins/Library/ml_wire/Item.h create mode 100644 Src/Plugins/Library/ml_wire/JSAPI2_Creator.cpp create mode 100644 Src/Plugins/Library/ml_wire/JSAPI2_Creator.h create mode 100644 Src/Plugins/Library/ml_wire/JSAPI2_PodcastsAPI.cpp create mode 100644 Src/Plugins/Library/ml_wire/JSAPI2_PodcastsAPI.h create mode 100644 Src/Plugins/Library/ml_wire/Loader.cpp create mode 100644 Src/Plugins/Library/ml_wire/Loader.h create mode 100644 Src/Plugins/Library/ml_wire/Main.cpp create mode 100644 Src/Plugins/Library/ml_wire/Main.h create mode 100644 Src/Plugins/Library/ml_wire/MessageProcessor.cpp create mode 100644 Src/Plugins/Library/ml_wire/MessageProcessor.h create mode 100644 Src/Plugins/Library/ml_wire/OPMLParse.cpp create mode 100644 Src/Plugins/Library/ml_wire/OPMLParse.h create mode 100644 Src/Plugins/Library/ml_wire/PCastFactory.cpp create mode 100644 Src/Plugins/Library/ml_wire/PCastFactory.h create mode 100644 Src/Plugins/Library/ml_wire/PCastURIHandler.cpp create mode 100644 Src/Plugins/Library/ml_wire/PCastURIHandler.h create mode 100644 Src/Plugins/Library/ml_wire/ParseUtil.cpp create mode 100644 Src/Plugins/Library/ml_wire/ParseUtil.h create mode 100644 Src/Plugins/Library/ml_wire/Preferences.cpp create mode 100644 Src/Plugins/Library/ml_wire/Preferences.h create mode 100644 Src/Plugins/Library/ml_wire/RFCDate.cpp create mode 100644 Src/Plugins/Library/ml_wire/RFCDate.h create mode 100644 Src/Plugins/Library/ml_wire/RSSCOM.cpp create mode 100644 Src/Plugins/Library/ml_wire/RSSCOM.h create mode 100644 Src/Plugins/Library/ml_wire/RSSParse.cpp create mode 100644 Src/Plugins/Library/ml_wire/RSSParse.h create mode 100644 Src/Plugins/Library/ml_wire/TODO.txt create mode 100644 Src/Plugins/Library/ml_wire/UpdateAutoDownload.cpp create mode 100644 Src/Plugins/Library/ml_wire/UpdateAutoDownload.h create mode 100644 Src/Plugins/Library/ml_wire/UpdateTime.cpp create mode 100644 Src/Plugins/Library/ml_wire/UpdateTime.h create mode 100644 Src/Plugins/Library/ml_wire/Util.h create mode 100644 Src/Plugins/Library/ml_wire/WantsDownloadStatus.h create mode 100644 Src/Plugins/Library/ml_wire/Wire.cpp create mode 100644 Src/Plugins/Library/ml_wire/Wire.h create mode 100644 Src/Plugins/Library/ml_wire/XMLWriter.cpp create mode 100644 Src/Plugins/Library/ml_wire/XMLWriter.h create mode 100644 Src/Plugins/Library/ml_wire/api__ml_wire.h create mode 100644 Src/Plugins/Library/ml_wire/api_podcasts.h create mode 100644 Src/Plugins/Library/ml_wire/channelEditor.cpp create mode 100644 Src/Plugins/Library/ml_wire/channelEditor.h create mode 100644 Src/Plugins/Library/ml_wire/date.c create mode 100644 Src/Plugins/Library/ml_wire/date.h create mode 100644 Src/Plugins/Library/ml_wire/db.cpp create mode 100644 Src/Plugins/Library/ml_wire/errors.h create mode 100644 Src/Plugins/Library/ml_wire/ifc_article.h create mode 100644 Src/Plugins/Library/ml_wire/ifc_podcast.h create mode 100644 Src/Plugins/Library/ml_wire/layout.cpp create mode 100644 Src/Plugins/Library/ml_wire/layout.h create mode 100644 Src/Plugins/Library/ml_wire/ml_podcast.rc create mode 100644 Src/Plugins/Library/ml_wire/ml_wire.sln create mode 100644 Src/Plugins/Library/ml_wire/ml_wire.vcxproj create mode 100644 Src/Plugins/Library/ml_wire/ml_wire.vcxproj.filters create mode 100644 Src/Plugins/Library/ml_wire/navigation.cpp create mode 100644 Src/Plugins/Library/ml_wire/navigation.h create mode 100644 Src/Plugins/Library/ml_wire/png.rc create mode 100644 Src/Plugins/Library/ml_wire/resource.h create mode 100644 Src/Plugins/Library/ml_wire/resources/discoverIcon.png create mode 100644 Src/Plugins/Library/ml_wire/resources/downloadIcon.png create mode 100644 Src/Plugins/Library/ml_wire/resources/mediaIcon.png create mode 100644 Src/Plugins/Library/ml_wire/resources/subscriptionIcon.png create mode 100644 Src/Plugins/Library/ml_wire/resources/textIcon.png create mode 100644 Src/Plugins/Library/ml_wire/service.cpp create mode 100644 Src/Plugins/Library/ml_wire/service.h create mode 100644 Src/Plugins/Library/ml_wire/subscriptionView.cpp create mode 100644 Src/Plugins/Library/ml_wire/subscriptionView.h create mode 100644 Src/Plugins/Library/ml_wire/util.cpp create mode 100644 Src/Plugins/Library/ml_wire/version.rc2 create mode 100644 Src/Plugins/Output/out_disk/main.cpp create mode 100644 Src/Plugins/Output/out_disk/out_disk.h create mode 100644 Src/Plugins/Output/out_disk/out_disk.rc create mode 100644 Src/Plugins/Output/out_disk/out_disk.sln create mode 100644 Src/Plugins/Output/out_disk/out_disk.vcxproj create mode 100644 Src/Plugins/Output/out_disk/out_disk.vcxproj.filters create mode 100644 Src/Plugins/Output/out_disk/resource.h create mode 100644 Src/Plugins/Output/out_disk/version.rc2 create mode 100644 Src/Plugins/Output/out_ds/Config.cpp create mode 100644 Src/Plugins/Output/out_ds/Config.h create mode 100644 Src/Plugins/Output/out_ds/DevEnum.cpp create mode 100644 Src/Plugins/Output/out_ds/DevEnum.h create mode 100644 Src/Plugins/Output/out_ds/SoundBlock.cpp create mode 100644 Src/Plugins/Output/out_ds/SoundBlock.h create mode 100644 Src/Plugins/Output/out_ds/SoundBlockList.cpp create mode 100644 Src/Plugins/Output/out_ds/SoundBlockList.h create mode 100644 Src/Plugins/Output/out_ds/VolCtrl.cpp create mode 100644 Src/Plugins/Output/out_ds/VolCtrl.h create mode 100644 Src/Plugins/Output/out_ds/api.h create mode 100644 Src/Plugins/Output/out_ds/cnv_ds2.cpp create mode 100644 Src/Plugins/Output/out_ds/cnv_ds2.h create mode 100644 Src/Plugins/Output/out_ds/ds2.cpp create mode 100644 Src/Plugins/Output/out_ds/ds2.h create mode 100644 Src/Plugins/Output/out_ds/ds2_devenum.cpp create mode 100644 Src/Plugins/Output/out_ds/ds2_misc.cpp create mode 100644 Src/Plugins/Output/out_ds/ds2_volctrl.cpp create mode 100644 Src/Plugins/Output/out_ds/ds_ipc.h create mode 100644 Src/Plugins/Output/out_ds/ds_main.h create mode 100644 Src/Plugins/Output/out_ds/out_ds.h create mode 100644 Src/Plugins/Output/out_ds/out_ds.sln create mode 100644 Src/Plugins/Output/out_ds/out_ds.vcxproj create mode 100644 Src/Plugins/Output/out_ds/out_ds.vcxproj.filters create mode 100644 Src/Plugins/Output/out_ds/out_ds_joy.cpp create mode 100644 Src/Plugins/Output/out_ds/res_wa2/out_ds2.rc create mode 100644 Src/Plugins/Output/out_ds/res_wa2/resource.h create mode 100644 Src/Plugins/Output/out_ds/res_wa2/version.rc2 create mode 100644 Src/Plugins/Output/out_ds/wa2.cpp create mode 100644 Src/Plugins/Output/out_ds/wa2_config.cpp create mode 100644 Src/Plugins/Output/out_wasapi/api.h create mode 100644 Src/Plugins/Output/out_wasapi/main.cpp create mode 100644 Src/Plugins/Output/out_wasapi/out_wasapi.rc create mode 100644 Src/Plugins/Output/out_wasapi/out_wasapi.sln create mode 100644 Src/Plugins/Output/out_wasapi/out_wasapi.vcxproj create mode 100644 Src/Plugins/Output/out_wasapi/out_wasapi.vcxproj.filters create mode 100644 Src/Plugins/Output/out_wasapi/resource.h create mode 100644 Src/Plugins/Output/out_wasapi/version.rc2 create mode 100644 Src/Plugins/Output/out_wasapi/waveformat.cpp create mode 100644 Src/Plugins/Output/out_wave/api.h create mode 100644 Src/Plugins/Output/out_wave/cnv_pcmwaveout.cpp create mode 100644 Src/Plugins/Output/out_wave/cnv_pcmwaveout.h create mode 100644 Src/Plugins/Output/out_wave/out_wave.cpp create mode 100644 Src/Plugins/Output/out_wave/out_wave.h create mode 100644 Src/Plugins/Output/out_wave/out_wave.rc create mode 100644 Src/Plugins/Output/out_wave/out_wave.sln create mode 100644 Src/Plugins/Output/out_wave/out_wave.vcxproj create mode 100644 Src/Plugins/Output/out_wave/out_wave.vcxproj.filters create mode 100644 Src/Plugins/Output/out_wave/resource.h create mode 100644 Src/Plugins/Output/out_wave/version.rc2 create mode 100644 Src/Plugins/Output/out_wave/wa2_config.cpp create mode 100644 Src/Plugins/Output/out_wave/waveout.cpp create mode 100644 Src/Plugins/Output/out_wave/waveout.h create mode 100644 Src/Plugins/Portable/pmp_activesync/ASDevice.cpp create mode 100644 Src/Plugins/Portable/pmp_activesync/ASDevice.h create mode 100644 Src/Plugins/Portable/pmp_activesync/activesync/Inc/IRAPIStream.h create mode 100644 Src/Plugins/Portable/pmp_activesync/activesync/Inc/rapi.h create mode 100644 Src/Plugins/Portable/pmp_activesync/activesync/Inc/rapi2.h create mode 100644 Src/Plugins/Portable/pmp_activesync/activesync/Inc/rapitypes.h create mode 100644 Src/Plugins/Portable/pmp_activesync/activesync/Inc/rapitypes2.h create mode 100644 Src/Plugins/Portable/pmp_activesync/activesync/Lib/rapiuuid.lib create mode 100644 Src/Plugins/Portable/pmp_activesync/main.cpp create mode 100644 Src/Plugins/Portable/pmp_activesync/pmp_activesync.rc create mode 100644 Src/Plugins/Portable/pmp_activesync/pmp_activesync.sln create mode 100644 Src/Plugins/Portable/pmp_activesync/pmp_activesync.vcxproj create mode 100644 Src/Plugins/Portable/pmp_activesync/pmp_activesync.vcxproj.filters create mode 100644 Src/Plugins/Portable/pmp_activesync/resource.h create mode 100644 Src/Plugins/Portable/pmp_activesync/resources/activeSyncIcon.png create mode 100644 Src/Plugins/Portable/pmp_activesync/version.rc2 create mode 100644 Src/Plugins/Portable/pmp_android/albumart.cpp create mode 100644 Src/Plugins/Portable/pmp_android/androiddevice.cpp create mode 100644 Src/Plugins/Portable/pmp_android/androiddevice.h create mode 100644 Src/Plugins/Portable/pmp_android/androidplaylist.cpp create mode 100644 Src/Plugins/Portable/pmp_android/androidplaylist.h create mode 100644 Src/Plugins/Portable/pmp_android/androidplaylistsaver.cpp create mode 100644 Src/Plugins/Portable/pmp_android/androidplaylistsaver.h create mode 100644 Src/Plugins/Portable/pmp_android/api.cpp create mode 100644 Src/Plugins/Portable/pmp_android/api.h create mode 100644 Src/Plugins/Portable/pmp_android/deviceprovider.cpp create mode 100644 Src/Plugins/Portable/pmp_android/deviceprovider.h create mode 100644 Src/Plugins/Portable/pmp_android/eject.cpp create mode 100644 Src/Plugins/Portable/pmp_android/filecopy.cpp create mode 100644 Src/Plugins/Portable/pmp_android/main.cpp create mode 100644 Src/Plugins/Portable/pmp_android/pmp_android.rc create mode 100644 Src/Plugins/Portable/pmp_android/pmp_android.sln create mode 100644 Src/Plugins/Portable/pmp_android/pmp_android.vcxproj create mode 100644 Src/Plugins/Portable/pmp_android/pmp_android.vcxproj.filters create mode 100644 Src/Plugins/Portable/pmp_android/resource.h create mode 100644 Src/Plugins/Portable/pmp_android/resources/androidIcon.png create mode 100644 Src/Plugins/Portable/pmp_android/resources/generic_android.png create mode 100644 Src/Plugins/Portable/pmp_android/utils.cpp create mode 100644 Src/Plugins/Portable/pmp_android/version.rc2 create mode 100644 Src/Plugins/Portable/pmp_ipod/SysInfoXML.cpp create mode 100644 Src/Plugins/Portable/pmp_ipod/api.h create mode 100644 Src/Plugins/Portable/pmp_ipod/deviceprovider.cpp create mode 100644 Src/Plugins/Portable/pmp_ipod/deviceprovider.h create mode 100644 Src/Plugins/Portable/pmp_ipod/eject.cpp create mode 100644 Src/Plugins/Portable/pmp_ipod/filecopy.cpp create mode 100644 Src/Plugins/Portable/pmp_ipod/hash58.cpp create mode 100644 Src/Plugins/Portable/pmp_ipod/hash58.h create mode 100644 Src/Plugins/Portable/pmp_ipod/iPodArtworkDB.cpp create mode 100644 Src/Plugins/Portable/pmp_ipod/iPodArtworkDB.h create mode 100644 Src/Plugins/Portable/pmp_ipod/iPodDB.cpp create mode 100644 Src/Plugins/Portable/pmp_ipod/iPodDB.h create mode 100644 Src/Plugins/Portable/pmp_ipod/iPodDevice.cpp create mode 100644 Src/Plugins/Portable/pmp_ipod/iPodDevice.h create mode 100644 Src/Plugins/Portable/pmp_ipod/iPodInfo.cpp create mode 100644 Src/Plugins/Portable/pmp_ipod/iPodInfo.h create mode 100644 Src/Plugins/Portable/pmp_ipod/iPodSD.cpp create mode 100644 Src/Plugins/Portable/pmp_ipod/iPodSD.h create mode 100644 Src/Plugins/Portable/pmp_ipod/main.cpp create mode 100644 Src/Plugins/Portable/pmp_ipod/pmp_ipod.rc create mode 100644 Src/Plugins/Portable/pmp_ipod/pmp_ipod.sln create mode 100644 Src/Plugins/Portable/pmp_ipod/pmp_ipod.vcxproj create mode 100644 Src/Plugins/Portable/pmp_ipod/pmp_ipod.vcxproj.filters create mode 100644 Src/Plugins/Portable/pmp_ipod/resource.h create mode 100644 Src/Plugins/Portable/pmp_ipod/resources/apple_ipod_classic.png create mode 100644 Src/Plugins/Portable/pmp_ipod/resources/apple_ipod_classic_16.png create mode 100644 Src/Plugins/Portable/pmp_ipod/resources/apple_ipod_nano_1g.png create mode 100644 Src/Plugins/Portable/pmp_ipod/resources/apple_ipod_nano_1g_16.png create mode 100644 Src/Plugins/Portable/pmp_ipod/resources/apple_ipod_nano_2g.png create mode 100644 Src/Plugins/Portable/pmp_ipod/resources/apple_ipod_nano_2g_16.png create mode 100644 Src/Plugins/Portable/pmp_ipod/resources/apple_ipod_nano_3g.png create mode 100644 Src/Plugins/Portable/pmp_ipod/resources/apple_ipod_nano_3g_16.png create mode 100644 Src/Plugins/Portable/pmp_ipod/resources/apple_ipod_nano_4g.png create mode 100644 Src/Plugins/Portable/pmp_ipod/resources/apple_ipod_nano_4g_16.png create mode 100644 Src/Plugins/Portable/pmp_ipod/resources/apple_ipod_nano_5g.png create mode 100644 Src/Plugins/Portable/pmp_ipod/resources/apple_ipod_nano_5g_16.png create mode 100644 Src/Plugins/Portable/pmp_ipod/resources/apple_ipod_shuffle_1g.png create mode 100644 Src/Plugins/Portable/pmp_ipod/resources/apple_ipod_shuffle_1g_16.png create mode 100644 Src/Plugins/Portable/pmp_ipod/resources/apple_ipod_shuffle_2g.png create mode 100644 Src/Plugins/Portable/pmp_ipod/resources/apple_ipod_shuffle_2g_16.png create mode 100644 Src/Plugins/Portable/pmp_ipod/resources/apple_ipod_shuffle_3g.png create mode 100644 Src/Plugins/Portable/pmp_ipod/resources/apple_ipod_shuffle_3g_16.png create mode 100644 Src/Plugins/Portable/pmp_ipod/resources/apple_ipod_shuffle_4g.png create mode 100644 Src/Plugins/Portable/pmp_ipod/resources/apple_ipod_shuffle_4g_16.png create mode 100644 Src/Plugins/Portable/pmp_ipod/sha1.c create mode 100644 Src/Plugins/Portable/pmp_ipod/sha1.h create mode 100644 Src/Plugins/Portable/pmp_ipod/version.rc2 create mode 100644 Src/Plugins/Portable/pmp_ipod/yail.cpp create mode 100644 Src/Plugins/Portable/pmp_ipod/yail.h create mode 100644 Src/Plugins/Portable/pmp_njb/NJBDevice.cpp create mode 100644 Src/Plugins/Portable/pmp_njb/NJBDevice.h create mode 100644 Src/Plugins/Portable/pmp_njb/NOMAD DAP PC SDK v3_5.pdf create mode 100644 Src/Plugins/Portable/pmp_njb/Nmsdk.h create mode 100644 Src/Plugins/Portable/pmp_njb/ctnmjb2.dll create mode 100644 Src/Plugins/Portable/pmp_njb/main.cpp create mode 100644 Src/Plugins/Portable/pmp_njb/pmp_njb.rc create mode 100644 Src/Plugins/Portable/pmp_njb/pmp_njb.sln create mode 100644 Src/Plugins/Portable/pmp_njb/pmp_njb.vcxproj create mode 100644 Src/Plugins/Portable/pmp_njb/pmp_njb.vcxproj.filters create mode 100644 Src/Plugins/Portable/pmp_njb/resource.h create mode 100644 Src/Plugins/Portable/pmp_njb/resources/zenIcon.png create mode 100644 Src/Plugins/Portable/pmp_njb/version.rc2 create mode 100644 Src/Plugins/Portable/pmp_p4s/Cert.zip create mode 100644 Src/Plugins/Portable/pmp_p4s/MTP Error Codes.txt create mode 100644 Src/Plugins/Portable/pmp_p4s/MyProgress.cpp create mode 100644 Src/Plugins/Portable/pmp_p4s/MyProgress.h create mode 100644 Src/Plugins/Portable/pmp_p4s/P4SDevice.cpp create mode 100644 Src/Plugins/Portable/pmp_p4s/P4SDevice.h create mode 100644 Src/Plugins/Portable/pmp_p4s/WMDRMDeviceApp.h create mode 100644 Src/Plugins/Portable/pmp_p4s/WMDRMDeviceApp_i.c create mode 100644 Src/Plugins/Portable/pmp_p4s/deviceprovider.cpp create mode 100644 Src/Plugins/Portable/pmp_p4s/deviceprovider.h create mode 100644 Src/Plugins/Portable/pmp_p4s/key-sub-523.c create mode 100644 Src/Plugins/Portable/pmp_p4s/main.cpp create mode 100644 Src/Plugins/Portable/pmp_p4s/mssachlp.lib create mode 100644 Src/Plugins/Portable/pmp_p4s/mswmdm.h create mode 100644 Src/Plugins/Portable/pmp_p4s/mswmdm_i.c create mode 100644 Src/Plugins/Portable/pmp_p4s/pmp_p4s.rc create mode 100644 Src/Plugins/Portable/pmp_p4s/pmp_p4s.vcxproj create mode 100644 Src/Plugins/Portable/pmp_p4s/pmp_p4s.vcxproj.filters create mode 100644 Src/Plugins/Portable/pmp_p4s/resource1.h create mode 100644 Src/Plugins/Portable/pmp_p4s/resources/creativeZenIcon.png create mode 100644 Src/Plugins/Portable/pmp_p4s/resources/nokiaIcon.png create mode 100644 Src/Plugins/Portable/pmp_p4s/sac.h create mode 100644 Src/Plugins/Portable/pmp_p4s/scclient.h create mode 100644 Src/Plugins/Portable/pmp_p4s/sehupd.lib create mode 100644 Src/Plugins/Portable/pmp_p4s/version.rc2 create mode 100644 Src/Plugins/Portable/pmp_p4s/wmdm.chm create mode 100644 Src/Plugins/Portable/pmp_usb/albumart.cpp create mode 100644 Src/Plugins/Portable/pmp_usb/api.cpp create mode 100644 Src/Plugins/Portable/pmp_usb/api.h create mode 100644 Src/Plugins/Portable/pmp_usb/deviceprovider.cpp create mode 100644 Src/Plugins/Portable/pmp_usb/deviceprovider.h create mode 100644 Src/Plugins/Portable/pmp_usb/eject.cpp create mode 100644 Src/Plugins/Portable/pmp_usb/filecopy.cpp create mode 100644 Src/Plugins/Portable/pmp_usb/main.cpp create mode 100644 Src/Plugins/Portable/pmp_usb/pmp_usb2.rc create mode 100644 Src/Plugins/Portable/pmp_usb/pmp_usb2.sln create mode 100644 Src/Plugins/Portable/pmp_usb/pmp_usb2.vcxproj create mode 100644 Src/Plugins/Portable/pmp_usb/pmp_usb2.vcxproj.filters create mode 100644 Src/Plugins/Portable/pmp_usb/resource.h create mode 100644 Src/Plugins/Portable/pmp_usb/resources/pspIcon.png create mode 100644 Src/Plugins/Portable/pmp_usb/resources/usbIcon.png create mode 100644 Src/Plugins/Portable/pmp_usb/usbdevice.cpp create mode 100644 Src/Plugins/Portable/pmp_usb/usbdevice.h create mode 100644 Src/Plugins/Portable/pmp_usb/usbplaylist.cpp create mode 100644 Src/Plugins/Portable/pmp_usb/usbplaylist.h create mode 100644 Src/Plugins/Portable/pmp_usb/usbplaylistsaver.cpp create mode 100644 Src/Plugins/Portable/pmp_usb/usbplaylistsaver.h create mode 100644 Src/Plugins/Portable/pmp_usb/utils.cpp create mode 100644 Src/Plugins/Portable/pmp_usb/version.rc2 create mode 100644 Src/Plugins/Portable/pmp_wifi/ConnectActivity.cpp create mode 100644 Src/Plugins/Portable/pmp_wifi/ConnectActivity.h create mode 100644 Src/Plugins/Portable/pmp_wifi/InfoDownloader.cpp create mode 100644 Src/Plugins/Portable/pmp_wifi/InfoDownloader.h create mode 100644 Src/Plugins/Portable/pmp_wifi/ListenServer.cpp create mode 100644 Src/Plugins/Portable/pmp_wifi/Pair.cpp create mode 100644 Src/Plugins/Portable/pmp_wifi/Pair.h create mode 100644 Src/Plugins/Portable/pmp_wifi/PlaylistSync.cpp create mode 100644 Src/Plugins/Portable/pmp_wifi/PlaylistSync.h create mode 100644 Src/Plugins/Portable/pmp_wifi/RenameDownloader.cpp create mode 100644 Src/Plugins/Portable/pmp_wifi/RenameDownloader.h create mode 100644 Src/Plugins/Portable/pmp_wifi/SongDownloader.cpp create mode 100644 Src/Plugins/Portable/pmp_wifi/SongDownloader.h create mode 100644 Src/Plugins/Portable/pmp_wifi/SongListDownloader.cpp create mode 100644 Src/Plugins/Portable/pmp_wifi/SongListDownloader.h create mode 100644 Src/Plugins/Portable/pmp_wifi/WifiDevice.cpp create mode 100644 Src/Plugins/Portable/pmp_wifi/WifiDevice.h create mode 100644 Src/Plugins/Portable/pmp_wifi/WifiPlaylist.cpp create mode 100644 Src/Plugins/Portable/pmp_wifi/WifiPlaylist.h create mode 100644 Src/Plugins/Portable/pmp_wifi/XMLString.cpp create mode 100644 Src/Plugins/Portable/pmp_wifi/XMLString.h create mode 100644 Src/Plugins/Portable/pmp_wifi/api.cpp create mode 100644 Src/Plugins/Portable/pmp_wifi/api.h create mode 100644 Src/Plugins/Portable/pmp_wifi/device.cpp create mode 100644 Src/Plugins/Portable/pmp_wifi/device.h create mode 100644 Src/Plugins/Portable/pmp_wifi/images.cpp create mode 100644 Src/Plugins/Portable/pmp_wifi/images.h create mode 100644 Src/Plugins/Portable/pmp_wifi/main.cpp create mode 100644 Src/Plugins/Portable/pmp_wifi/main.h create mode 100644 Src/Plugins/Portable/pmp_wifi/modelInfo.cpp create mode 100644 Src/Plugins/Portable/pmp_wifi/modelInfo.h create mode 100644 Src/Plugins/Portable/pmp_wifi/pmp_wifi.rc create mode 100644 Src/Plugins/Portable/pmp_wifi/pmp_wifi.sln create mode 100644 Src/Plugins/Portable/pmp_wifi/pmp_wifi.vcxproj create mode 100644 Src/Plugins/Portable/pmp_wifi/pmp_wifi.vcxproj.filters create mode 100644 Src/Plugins/Portable/pmp_wifi/post.cpp create mode 100644 Src/Plugins/Portable/pmp_wifi/resource.h create mode 100644 Src/Plugins/Portable/pmp_wifi/resources/attach.png create mode 100644 Src/Plugins/Portable/pmp_wifi/resources/attach16.png create mode 100644 Src/Plugins/Portable/pmp_wifi/resources/generic_android.png create mode 100644 Src/Plugins/Portable/pmp_wifi/resources/generic_drive_wifi_16.png create mode 100644 Src/Plugins/Portable/pmp_wifi/resources/htc_desire_passion_bravo.png create mode 100644 Src/Plugins/Portable/pmp_wifi/resources/htc_desire_passion_bravo_16.png create mode 100644 Src/Plugins/Portable/pmp_wifi/resources/htc_evo_4g.png create mode 100644 Src/Plugins/Portable/pmp_wifi/resources/htc_evo_4g_16.png create mode 100644 Src/Plugins/Portable/pmp_wifi/resources/htc_hd2.png create mode 100644 Src/Plugins/Portable/pmp_wifi/resources/htc_hd2_16.png create mode 100644 Src/Plugins/Portable/pmp_wifi/resources/htc_incredible.png create mode 100644 Src/Plugins/Portable/pmp_wifi/resources/htc_incredible_16.png create mode 100644 Src/Plugins/Portable/pmp_wifi/resources/htc_nexus_one_16.png create mode 100644 Src/Plugins/Portable/pmp_wifi/resources/motorola_droid.png create mode 100644 Src/Plugins/Portable/pmp_wifi/resources/motorola_droid_16.png create mode 100644 Src/Plugins/Portable/pmp_wifi/resources/motorola_droid_x.png create mode 100644 Src/Plugins/Portable/pmp_wifi/resources/motorola_droid_x_16.png create mode 100644 Src/Plugins/Portable/pmp_wifi/resources/nexus_one.png create mode 100644 Src/Plugins/Portable/pmp_wifi/resources/wifi.png create mode 100644 Src/Plugins/Portable/pmp_wifi/version.rc2 create mode 100644 Src/Plugins/SDK/burner/BurnerCommon.cpp create mode 100644 Src/Plugins/SDK/burner/BurnerCommon.h create mode 100644 Src/Plugins/SDK/burner/DataBurner.cpp create mode 100644 Src/Plugins/SDK/burner/DataBurner.h create mode 100644 Src/Plugins/SDK/burner/ISOBurner.cpp create mode 100644 Src/Plugins/SDK/burner/ISOBurner.h create mode 100644 Src/Plugins/SDK/burner/ISOCreator.cpp create mode 100644 Src/Plugins/SDK/burner/api.h create mode 100644 Src/Plugins/SDK/burner/burner.rc create mode 100644 Src/Plugins/SDK/burner/burner.vcxproj create mode 100644 Src/Plugins/SDK/burner/burner.vcxproj.filters create mode 100644 Src/Plugins/SDK/burner/factory_databurner.cpp create mode 100644 Src/Plugins/SDK/burner/factory_databurner.h create mode 100644 Src/Plugins/SDK/burner/factory_isoburner.cpp create mode 100644 Src/Plugins/SDK/burner/factory_isoburner.h create mode 100644 Src/Plugins/SDK/burner/factory_isocreator.cpp create mode 100644 Src/Plugins/SDK/burner/factory_isocreator.h create mode 100644 Src/Plugins/SDK/burner/ifc_burner_writecallback.h create mode 100644 Src/Plugins/SDK/burner/isocreator.h create mode 100644 Src/Plugins/SDK/burner/main.cpp create mode 100644 Src/Plugins/SDK/burner/obj_databurner.h create mode 100644 Src/Plugins/SDK/burner/obj_isoburner.h create mode 100644 Src/Plugins/SDK/burner/obj_isocreator.h create mode 100644 Src/Plugins/SDK/burner/resource.h create mode 100644 Src/Plugins/SDK/coverdirectory/CoverDirectory.cpp create mode 100644 Src/Plugins/SDK/coverdirectory/CoverDirectory.h create mode 100644 Src/Plugins/SDK/coverdirectory/api.h create mode 100644 Src/Plugins/SDK/coverdirectory/cover_directory.vcxproj create mode 100644 Src/Plugins/SDK/coverdirectory/cover_directory.vcxproj.filters create mode 100644 Src/Plugins/SDK/coverdirectory/coverdirectory.rc create mode 100644 Src/Plugins/SDK/coverdirectory/coverdirectory.sln create mode 100644 Src/Plugins/SDK/coverdirectory/main.cpp create mode 100644 Src/Plugins/SDK/coverdirectory/resource.h create mode 100644 Src/Plugins/SDK/coverdirectory/version.rc2 create mode 100644 Src/Plugins/SDK/dsp_test/RESOURCE.H create mode 100644 Src/Plugins/SDK/dsp_test/SCRIPT1.RC create mode 100644 Src/Plugins/SDK/dsp_test/dsp_ns.txt create mode 100644 Src/Plugins/SDK/dsp_test/dsp_test.c create mode 100644 Src/Plugins/SDK/dsp_test/dsp_test.sln create mode 100644 Src/Plugins/SDK/dsp_test/dsp_test.vcxproj create mode 100644 Src/Plugins/SDK/dsp_test/dsp_test.vcxproj.filters create mode 100644 Src/Plugins/SDK/gen_classicart/api.h create mode 100644 Src/Plugins/SDK/gen_classicart/build_lng.cmd create mode 100644 Src/Plugins/SDK/gen_classicart/gen_classicart.cpp create mode 100644 Src/Plugins/SDK/gen_classicart/gen_classicart.nsi create mode 100644 Src/Plugins/SDK/gen_classicart/gen_classicart.rc create mode 100644 Src/Plugins/SDK/gen_classicart/gen_classicart.sln create mode 100644 Src/Plugins/SDK/gen_classicart/gen_classicart.vcxproj create mode 100644 Src/Plugins/SDK/gen_classicart/gen_classicart.vcxproj.filters create mode 100644 Src/Plugins/SDK/gen_classicart/lng_generator.exe create mode 100644 Src/Plugins/SDK/gen_classicart/notfound.png create mode 100644 Src/Plugins/SDK/gen_classicart/resource.h create mode 100644 Src/Plugins/SDK/in_tone/IN_TONE.sln create mode 100644 Src/Plugins/SDK/in_tone/IN_TONE.vcxproj create mode 100644 Src/Plugins/SDK/in_tone/IN_TONE.vcxproj.filters create mode 100644 Src/Plugins/SDK/in_tone/MAIN.C create mode 100644 Src/Plugins/SDK/in_tone/in_tone.rc create mode 100644 Src/Plugins/SDK/in_tone/resource.h create mode 100644 Src/Plugins/SDK/in_tone/version.rc2 create mode 100644 Src/Plugins/SDK/irctell/api__irctell.h create mode 100644 Src/Plugins/SDK/irctell/dde.cpp create mode 100644 Src/Plugins/SDK/irctell/dde.h create mode 100644 Src/Plugins/SDK/irctell/irctell.cpp create mode 100644 Src/Plugins/SDK/irctell/irctell.h create mode 100644 Src/Plugins/SDK/irctell/irctell.rc create mode 100644 Src/Plugins/SDK/irctell/irctell.sln create mode 100644 Src/Plugins/SDK/irctell/irctell.vcxproj create mode 100644 Src/Plugins/SDK/irctell/irctell.vcxproj.filters create mode 100644 Src/Plugins/SDK/irctell/resource.h create mode 100644 Src/Plugins/SDK/irctell/version.rc2 create mode 100644 Src/Plugins/SDK/ml_iso/ToISO.cpp create mode 100644 Src/Plugins/SDK/ml_iso/api__ml_iso.h create mode 100644 Src/Plugins/SDK/ml_iso/main.cpp create mode 100644 Src/Plugins/SDK/ml_iso/main.h create mode 100644 Src/Plugins/SDK/ml_iso/ml_iso.rc create mode 100644 Src/Plugins/SDK/ml_iso/ml_iso.sln create mode 100644 Src/Plugins/SDK/ml_iso/ml_iso.vcxproj create mode 100644 Src/Plugins/SDK/ml_iso/ml_iso.vcxproj.filters create mode 100644 Src/Plugins/SDK/ml_iso/resource.h create mode 100644 Src/Plugins/SDK/ml_iso/version.rc2 create mode 100644 Src/Plugins/SDK/ml_xmlex/api.h create mode 100644 Src/Plugins/SDK/ml_xmlex/main.cpp create mode 100644 Src/Plugins/SDK/ml_xmlex/main.h create mode 100644 Src/Plugins/SDK/ml_xmlex/ml_xmlex.rc create mode 100644 Src/Plugins/SDK/ml_xmlex/ml_xmlex.sln create mode 100644 Src/Plugins/SDK/ml_xmlex/ml_xmlex.vcxproj create mode 100644 Src/Plugins/SDK/ml_xmlex/ml_xmlex.vcxproj.filters create mode 100644 Src/Plugins/SDK/ml_xmlex/readme.txt create mode 100644 Src/Plugins/SDK/ml_xmlex/resource.h create mode 100644 Src/Plugins/SDK/ml_xmlex/version.rc2 create mode 100644 Src/Plugins/SDK/ml_xmlex/xmltest.xml create mode 100644 Src/Plugins/SDK/ml_xmlex/xmltest2.xml create mode 100644 Src/Plugins/SDK/ml_xmlex/xmlview.cpp create mode 100644 Src/Plugins/SDK/out_null/Out_null.dsp create mode 100644 Src/Plugins/SDK/out_null/Out_null.dsw create mode 100644 Src/Plugins/SDK/out_null/Out_null.sln create mode 100644 Src/Plugins/SDK/out_null/Out_null.vcxproj create mode 100644 Src/Plugins/SDK/out_null/Out_null.vcxproj.filters create mode 100644 Src/Plugins/SDK/out_null/main.c create mode 100644 Src/Plugins/SDK/out_null/out_null.rc create mode 100644 Src/Plugins/SDK/out_null/resource.h create mode 100644 Src/Plugins/SDK/out_null/version.rc2 create mode 100644 Src/Plugins/SDK/plLoadEx/ExComponent.cpp create mode 100644 Src/Plugins/SDK/plLoadEx/ExComponent.h create mode 100644 Src/Plugins/SDK/plLoadEx/SimpleHandler.cpp create mode 100644 Src/Plugins/SDK/plLoadEx/SimpleHandler.h create mode 100644 Src/Plugins/SDK/plLoadEx/SimpleHandlerFactory.cpp create mode 100644 Src/Plugins/SDK/plLoadEx/SimpleHandlerFactory.h create mode 100644 Src/Plugins/SDK/plLoadEx/SimpleLoader.cpp create mode 100644 Src/Plugins/SDK/plLoadEx/SimpleLoader.h create mode 100644 Src/Plugins/SDK/plLoadEx/example.simple create mode 100644 Src/Plugins/SDK/plLoadEx/plLoadEx.rc create mode 100644 Src/Plugins/SDK/plLoadEx/plLoadEx.sln create mode 100644 Src/Plugins/SDK/plLoadEx/plLoadEx.vcxproj create mode 100644 Src/Plugins/SDK/plLoadEx/plLoadEx.vcxproj.filters create mode 100644 Src/Plugins/SDK/plLoadEx/resource.h create mode 100644 Src/Plugins/SDK/plLoadEx/version.rc2 create mode 100644 Src/Plugins/SDK/plLoadEx/w5s.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/FRONTEND.H create mode 100644 Src/Plugins/Visualization/vis_avs/LICENSE.TXT create mode 100644 Src/Plugins/Visualization/vis_avs/TIMING.C create mode 100644 Src/Plugins/Visualization/vis_avs/Timing.h create mode 100644 Src/Plugins/Visualization/vis_avs/VIS.H create mode 100644 Src/Plugins/Visualization/vis_avs/ape.h create mode 100644 Src/Plugins/Visualization/vis_avs/apesdk/ape.rc create mode 100644 Src/Plugins/Visualization/vis_avs/apesdk/avs_ape.h create mode 100644 Src/Plugins/Visualization/vis_avs/apesdk/avstut00.avs create mode 100644 Src/Plugins/Visualization/vis_avs/apesdk/avstut00.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/apesdk/avstut00.dsp create mode 100644 Src/Plugins/Visualization/vis_avs/apesdk/avstut00.dsw create mode 100644 Src/Plugins/Visualization/vis_avs/apesdk/resource.h create mode 100644 Src/Plugins/Visualization/vis_avs/avs-hilited.png create mode 100644 Src/Plugins/Visualization/vis_avs/avs-normal.png create mode 100644 Src/Plugins/Visualization/vis_avs/avs-selected.png create mode 100644 Src/Plugins/Visualization/vis_avs/avs_eelif.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/avs_eelif.h create mode 100644 Src/Plugins/Visualization/vis_avs/bpm.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/bpm.h create mode 100644 Src/Plugins/Visualization/vis_avs/bump_lig.bin create mode 100644 Src/Plugins/Visualization/vis_avs/cfgwin.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/cfgwnd.h create mode 100644 Src/Plugins/Visualization/vis_avs/color_mo.bin create mode 100644 Src/Plugins/Visualization/vis_avs/draw.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/draw.h create mode 100644 Src/Plugins/Visualization/vis_avs/dyn_dist.bin create mode 100644 Src/Plugins/Visualization/vis_avs/dyn_move.bin create mode 100644 Src/Plugins/Visualization/vis_avs/dyn_shift.bin create mode 100644 Src/Plugins/Visualization/vis_avs/effect_l.bin create mode 100644 Src/Plugins/Visualization/vis_avs/evallib/BISON.EXE create mode 100644 Src/Plugins/Visualization/vis_avs/evallib/CAL_TAB.C create mode 100644 Src/Plugins/Visualization/vis_avs/evallib/Compiler.c create mode 100644 Src/Plugins/Visualization/vis_avs/evallib/Compiler.h create mode 100644 Src/Plugins/Visualization/vis_avs/evallib/GETTOK.C create mode 100644 Src/Plugins/Visualization/vis_avs/evallib/LEX.EXE create mode 100644 Src/Plugins/Visualization/vis_avs/evallib/LEX.H create mode 100644 Src/Plugins/Visualization/vis_avs/evallib/LEXGET.C create mode 100644 Src/Plugins/Visualization/vis_avs/evallib/LEXSWI.C create mode 100644 Src/Plugins/Visualization/vis_avs/evallib/LEXTAB.C create mode 100644 Src/Plugins/Visualization/vis_avs/evallib/LLSAVE.C create mode 100644 Src/Plugins/Visualization/vis_avs/evallib/LMOVB.C create mode 100644 Src/Plugins/Visualization/vis_avs/evallib/Scan.l create mode 100644 Src/Plugins/Visualization/vis_avs/evallib/YYLEX.C create mode 100644 Src/Plugins/Visualization/vis_avs/evallib/bison/BISON.HAI create mode 100644 Src/Plugins/Visualization/vis_avs/evallib/bison/BISON.SIM create mode 100644 Src/Plugins/Visualization/vis_avs/evallib/cal.y create mode 100644 Src/Plugins/Visualization/vis_avs/evallib/cal_tab.h create mode 100644 Src/Plugins/Visualization/vis_avs/evallib/cfunc.c create mode 100644 Src/Plugins/Visualization/vis_avs/evallib/eval.c create mode 100644 Src/Plugins/Visualization/vis_avs/evallib/eval.h create mode 100644 Src/Plugins/Visualization/vis_avs/evallib/makel.bat create mode 100644 Src/Plugins/Visualization/vis_avs/evallib/makey.bat create mode 100644 Src/Plugins/Visualization/vis_avs/evallib/new_eval_stuff.zip create mode 100644 Src/Plugins/Visualization/vis_avs/evallib/readme.txt create mode 100644 Src/Plugins/Visualization/vis_avs/ff_ipc.h create mode 100644 Src/Plugins/Visualization/vis_avs/help_1.bin create mode 100644 Src/Plugins/Visualization/vis_avs/help_2.bin create mode 100644 Src/Plugins/Visualization/vis_avs/help_3.bin create mode 100644 Src/Plugins/Visualization/vis_avs/help_4.bin create mode 100644 Src/Plugins/Visualization/vis_avs/laser/LD32.H create mode 100644 Src/Plugins/Visualization/vis_avs/laser/laser.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/laser/laserline.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/laser/laserline.h create mode 100644 Src/Plugins/Visualization/vis_avs/laser/ld32.c create mode 100644 Src/Plugins/Visualization/vis_avs/laser/linelist.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/laser/linelist.h create mode 100644 Src/Plugins/Visualization/vis_avs/laser/rl_beathold.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/laser/rl_bren.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/laser/rl_cones.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/laser/rl_line.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/laser/rl_trans.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/linedraw.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/main.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/matrix.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/movement.bin create mode 100644 Src/Plugins/Visualization/vis_avs/peices.bmp create mode 100644 Src/Plugins/Visualization/vis_avs/r_avi.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/r_blit.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/r_blur.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/r_bpm.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/r_bright.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/r_bspin.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/r_bump.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/r_chanshift.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/r_clear.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/r_colorfade.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/r_colorreduction.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/r_colorreplace.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/r_comment.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/r_contrast.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/r_dcolormod.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/r_ddm.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/r_defs.h create mode 100644 Src/Plugins/Visualization/vis_avs/r_dmove.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/r_dotfnt.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/r_dotgrid.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/r_dotpln.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/r_fadeout.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/r_fastbright.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/r_grain.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/r_interf.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/r_interleave.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/r_invert.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/r_linemode.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/r_list.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/r_list.h create mode 100644 Src/Plugins/Visualization/vis_avs/r_mirror.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/r_mosaic.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/r_multidelay.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/r_multiplier.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/r_nfclr.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/r_onetone.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/r_oscring.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/r_oscstar.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/r_parts.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/r_picture.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/r_rotblit.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/r_rotstar.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/r_scat.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/r_shift.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/r_simple.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/r_sscope.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/r_stack.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/r_stack.h create mode 100644 Src/Plugins/Visualization/vis_avs/r_stars.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/r_svp.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/r_text.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/r_timescope.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/r_trans.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/r_transition.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/r_transition.h create mode 100644 Src/Plugins/Visualization/vis_avs/r_unkn.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/r_unkn.h create mode 100644 Src/Plugins/Visualization/vis_avs/r_videodelay.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/r_water.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/r_waterbump.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/reaplay_avs.dsp create mode 100644 Src/Plugins/Visualization/vis_avs/render.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/render.h create mode 100644 Src/Plugins/Visualization/vis_avs/res.rc create mode 100644 Src/Plugins/Visualization/vis_avs/resource.h create mode 100644 Src/Plugins/Visualization/vis_avs/rlib.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/rlib.h create mode 100644 Src/Plugins/Visualization/vis_avs/supersco.bin create mode 100644 Src/Plugins/Visualization/vis_avs/svp_vis.h create mode 100644 Src/Plugins/Visualization/vis_avs/undo.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/undo.h create mode 100644 Src/Plugins/Visualization/vis_avs/util.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/version.rc2 create mode 100644 Src/Plugins/Visualization/vis_avs/vis_Avs.dsw create mode 100644 Src/Plugins/Visualization/vis_avs/vis_avs.dsp create mode 100644 Src/Plugins/Visualization/vis_avs/vis_avs.sln create mode 100644 Src/Plugins/Visualization/vis_avs/vis_avs.txt create mode 100644 Src/Plugins/Visualization/vis_avs/vis_avs.vcxproj create mode 100644 Src/Plugins/Visualization/vis_avs/vis_avs.vcxproj.filters create mode 100644 Src/Plugins/Visualization/vis_avs/wa_ipc.h create mode 100644 Src/Plugins/Visualization/vis_avs/whatsnew.txt create mode 100644 Src/Plugins/Visualization/vis_avs/wnd.cpp create mode 100644 Src/Plugins/Visualization/vis_avs/wnd.h create mode 100644 Src/Plugins/Visualization/vis_milk2/DOCUMENTATION.TXT create mode 100644 Src/Plugins/Visualization/vis_milk2/api__vis_milk2.h create mode 100644 Src/Plugins/Visualization/vis_milk2/config.cpp create mode 100644 Src/Plugins/Visualization/vis_milk2/config2.cpp create mode 100644 Src/Plugins/Visualization/vis_milk2/defines.h create mode 100644 Src/Plugins/Visualization/vis_milk2/desktop_mode.cpp create mode 100644 Src/Plugins/Visualization/vis_milk2/dxcontext.cpp create mode 100644 Src/Plugins/Visualization/vis_milk2/dxcontext.h create mode 100644 Src/Plugins/Visualization/vis_milk2/fft.cpp create mode 100644 Src/Plugins/Visualization/vis_milk2/fft.h create mode 100644 Src/Plugins/Visualization/vis_milk2/gstring.h create mode 100644 Src/Plugins/Visualization/vis_milk2/icon_t.h create mode 100644 Src/Plugins/Visualization/vis_milk2/manifest.xml create mode 100644 Src/Plugins/Visualization/vis_milk2/md_defines.h create mode 100644 Src/Plugins/Visualization/vis_milk2/menu.cpp create mode 100644 Src/Plugins/Visualization/vis_milk2/menu.h create mode 100644 Src/Plugins/Visualization/vis_milk2/milkdrop.nsi create mode 100644 Src/Plugins/Visualization/vis_milk2/milkdrop_DX9.dsw create mode 100644 Src/Plugins/Visualization/vis_milk2/milkdrop_DX9.sln create mode 100644 Src/Plugins/Visualization/vis_milk2/milkdropfs.cpp create mode 100644 Src/Plugins/Visualization/vis_milk2/ns-eel2/asm-nseel-ppc-gcc.c create mode 100644 Src/Plugins/Visualization/vis_milk2/ns-eel2/asm-nseel-x64-macho.o create mode 100644 Src/Plugins/Visualization/vis_milk2/ns-eel2/asm-nseel-x86-gcc.c create mode 100644 Src/Plugins/Visualization/vis_milk2/ns-eel2/asm-nseel-x86-msvc.c create mode 100644 Src/Plugins/Visualization/vis_milk2/ns-eel2/ns-eel-addfuncs.h create mode 100644 Src/Plugins/Visualization/vis_milk2/ns-eel2/ns-eel-int.h create mode 100644 Src/Plugins/Visualization/vis_milk2/ns-eel2/ns-eel.h create mode 100644 Src/Plugins/Visualization/vis_milk2/ns-eel2/nseel-caltab.c create mode 100644 Src/Plugins/Visualization/vis_milk2/ns-eel2/nseel-cfunc.c create mode 100644 Src/Plugins/Visualization/vis_milk2/ns-eel2/nseel-compiler.c create mode 100644 Src/Plugins/Visualization/vis_milk2/ns-eel2/nseel-eval.c create mode 100644 Src/Plugins/Visualization/vis_milk2/ns-eel2/nseel-lextab.c create mode 100644 Src/Plugins/Visualization/vis_milk2/ns-eel2/nseel-ram.c create mode 100644 Src/Plugins/Visualization/vis_milk2/ns-eel2/nseel-yylex.c create mode 100644 Src/Plugins/Visualization/vis_milk2/plugin.cpp create mode 100644 Src/Plugins/Visualization/vis_milk2/plugin.dsp create mode 100644 Src/Plugins/Visualization/vis_milk2/plugin.h create mode 100644 Src/Plugins/Visualization/vis_milk2/plugin.rc create mode 100644 Src/Plugins/Visualization/vis_milk2/plugin.vcxproj create mode 100644 Src/Plugins/Visualization/vis_milk2/plugin.vcxproj.filters create mode 100644 Src/Plugins/Visualization/vis_milk2/plugin_icon.ico create mode 100644 Src/Plugins/Visualization/vis_milk2/pluginshell.cpp create mode 100644 Src/Plugins/Visualization/vis_milk2/pluginshell.h create mode 100644 Src/Plugins/Visualization/vis_milk2/resource.h create mode 100644 Src/Plugins/Visualization/vis_milk2/shell_defines.h create mode 100644 Src/Plugins/Visualization/vis_milk2/state.cpp create mode 100644 Src/Plugins/Visualization/vis_milk2/state.h create mode 100644 Src/Plugins/Visualization/vis_milk2/support.cpp create mode 100644 Src/Plugins/Visualization/vis_milk2/support.h create mode 100644 Src/Plugins/Visualization/vis_milk2/temp.ico create mode 100644 Src/Plugins/Visualization/vis_milk2/texmgr.cpp create mode 100644 Src/Plugins/Visualization/vis_milk2/texmgr.h create mode 100644 Src/Plugins/Visualization/vis_milk2/text1.bin create mode 100644 Src/Plugins/Visualization/vis_milk2/textmgr.cpp create mode 100644 Src/Plugins/Visualization/vis_milk2/textmgr.h create mode 100644 Src/Plugins/Visualization/vis_milk2/utility.cpp create mode 100644 Src/Plugins/Visualization/vis_milk2/utility.h create mode 100644 Src/Plugins/Visualization/vis_milk2/version.rc2 create mode 100644 Src/Plugins/Visualization/vis_milk2/vis.cpp create mode 100644 Src/Plugins/Visualization/vis_milk2/vis.h create mode 100644 Src/Plugins/Visualization/vis_milk2/vms_desktop.lib create mode 100644 Src/Plugins/Visualization/vis_nsfs/Svis.cpp create mode 100644 Src/Plugins/Visualization/vis_nsfs/dd.h create mode 100644 Src/Plugins/Visualization/vis_nsfs/ddraw.cpp create mode 100644 Src/Plugins/Visualization/vis_nsfs/license.txt create mode 100644 Src/Plugins/Visualization/vis_nsfs/linedraw.cpp create mode 100644 Src/Plugins/Visualization/vis_nsfs/makepal.cpp create mode 100644 Src/Plugins/Visualization/vis_nsfs/moveframe.cpp create mode 100644 Src/Plugins/Visualization/vis_nsfs/resource.h create mode 100644 Src/Plugins/Visualization/vis_nsfs/scope.cpp create mode 100644 Src/Plugins/Visualization/vis_nsfs/shitdrop.cpp create mode 100644 Src/Plugins/Visualization/vis_nsfs/tinyvis.txt create mode 100644 Src/Plugins/Visualization/vis_nsfs/version.rc2 create mode 100644 Src/Plugins/Visualization/vis_nsfs/vis_nsfs.rc create mode 100644 Src/Plugins/Visualization/vis_nsfs/vis_nsfs.sln create mode 100644 Src/Plugins/Visualization/vis_nsfs/vis_nsfs.vcxproj create mode 100644 Src/Plugins/Visualization/vis_nsfs/vis_nsfs.vcxproj.filters (limited to 'Src/Plugins') diff --git a/Src/Plugins/DSP/dsp_sc/Include/c_datapump.h b/Src/Plugins/DSP/dsp_sc/Include/c_datapump.h new file mode 100644 index 00000000..bcf3863b --- /dev/null +++ b/Src/Plugins/DSP/dsp_sc/Include/c_datapump.h @@ -0,0 +1,174 @@ +#ifndef __C_DATAPUMP_H__ +#define __C_DATAPUMP_H__ + +#include +#include +#include +#pragma intrinsic(memcpy,memset) + +template class C_DATAPUMP { +private: +protected: + T *BufferBottom; // bottom of the physical buffer + T *BufferTop; // top of the physical buffer + T *BufferStart; // start of the logical buffer + T *BufferEnd; // end of the logical buffer + + virtual void addItems(T *inputBuffer, size_t inputSize) { // inputSize = number of records inputBuffer contains + if(inputBuffer && inputSize) { + memcpy(BufferEnd,inputBuffer,inputSize*sizeof(T)); // copy our records in + BufferEnd += inputSize; + if(BufferEnd >= BufferTop) BufferEnd = BufferBottom + (BufferEnd-BufferTop); + } + } + + virtual void delItems(int where, size_t numItems) { // where: 0 = start, 1 = end + if(numItems > 0) { + if(numItems > size()) { // just void everything + BufferEnd = BufferStart; + } else { + if(where == 0) { // start + BufferStart += numItems; + if(BufferStart >= BufferTop) BufferStart = BufferBottom + (BufferTop-BufferStart); + } else if(where == 1) { // end + BufferEnd -= numItems; + if(BufferEnd < BufferBottom) BufferEnd = BufferTop - (BufferBottom-BufferEnd); + } + } + } + } + + virtual void getItems(T *outputBuffer, size_t outputSize) { // outputSize = number of records outputBuffer needs + if(outputBuffer && outputSize) { + memcpy(outputBuffer,BufferStart,outputSize*sizeof(T)); + } + } + +public: + C_DATAPUMP(int bufferSize) { // bufferSize = number of records + BufferBottom = NULL; + BufferTop = NULL; + BufferStart = NULL; + BufferEnd = NULL; + resizeBuffer(bufferSize); + } + + virtual ~C_DATAPUMP() { + if(getBufferSize() && BufferBottom) { + free(BufferBottom); + BufferBottom = NULL; + } + } + + virtual void resizeBuffer(size_t bufferSize) { // bufferSize = number of records + // this will invalidate any data in the buffer, so be careful when calling this function + if(bufferSize) { + if(getBufferSize() != bufferSize) { + if(BufferBottom && BufferTop && getBufferSize()) { // buffer is valid + if(getBufferSize() > bufferSize) { // buffer is getting smaller (will invalidate buffer) + BufferTop -= getBufferSize()-bufferSize; + invalidate(); + } else { // buffer is getting larger (will _NOT_ invalidate buffer... nicely moves the data over =) + T *newBuffer = (T *)malloc(bufferSize * sizeof(T)); + // new + BufferEnd = newBuffer + get(newBuffer,bufferSize); + free(BufferBottom); + BufferBottom = newBuffer; + BufferTop = BufferBottom + bufferSize; + BufferStart = BufferBottom; + /* old + T *bufptr = newBuffer; + int top = BufferEnd >= BufferStart ? BufferEnd-BufferStart : BufferTop-BufferStart; // number of records at top of physical buffer + int bottom = BufferEnd >= BufferStart ? 0 : BufferEnd-BufferBottom; // number of records at bottom of physical buffer + if(top > 0) { + memcpy(bufptr,BufferStart,top*sizeof(T)); + bufptr += top; + } + if(bottom > 0) { + memcpy(bufptr,BufferBottom,bottom*sizeof(T)); + bufptr += bottom; + } + free(BufferBottom); + BufferBottom = newBuffer; + BufferTop = BufferBottom + bufferSize; + BufferStart = BufferBottom; + BufferEnd = bufptr; + */ + } + } else { // no buffer, create (invalidates the buffer... duh) + BufferBottom = (T *)malloc(bufferSize * sizeof(T)); + BufferTop = BufferBottom + bufferSize; + invalidate(); + } + } + } + } + + virtual size_t size() { // will get the number of records the logical buffer contains + return BufferEnd >= BufferStart ? BufferEnd-BufferStart : (BufferTop-BufferStart)+(BufferEnd-BufferBottom); + } + + virtual size_t put(T *inputBuffer, size_t inputSize) { // inputSize = number of records inputBuffer contains + // returns number of records added to logical buffer + size_t retval = 0; + if(inputBuffer && inputSize) { + size_t fitting = ((BufferTop-BufferBottom)-1) - size(); // can't go over our logical boundary.... blah + if(fitting > inputSize) fitting = inputSize; // the entire thing can fit. yeay! + retval = fitting; + if(fitting > 0) { + T *bufptr = inputBuffer; + size_t top = BufferEnd >= BufferStart ? BufferTop-BufferEnd : 0; // number of records free at top of physical buffer + size_t bottom = BufferEnd >= BufferStart ? BufferStart-BufferBottom : (BufferStart-BufferEnd); // number of records free at bottom of physical buffer + if(top > 0) { + if(top > fitting) top = fitting; + addItems(bufptr,top); + fitting -= top; + bufptr += top; + } + if(bottom > 0 && fitting > 0) { + if(bottom > fitting) bottom = fitting; + addItems(bufptr,bottom); + } + } + } + return retval; + } + + virtual size_t get(T *outputBuffer, size_t outputSize) { // outputSize = number of records outputBuffer needs + // returns number of records pulled from the logical buffer + size_t retval = 0; + if(outputBuffer && outputSize) { + size_t fitting = size(); + if(fitting > outputSize) fitting = outputSize; + retval = fitting; + if(fitting > 0) { + T *bufptr = outputBuffer; + size_t top = BufferEnd >= BufferStart ? BufferEnd-BufferStart : BufferTop-BufferStart; // number of records at top of physical buffer + size_t bottom = BufferEnd >= BufferStart ? 0 : BufferEnd-BufferBottom; // number of records at bottom of physical buffer + if(top > 0) { + if(top > fitting) top = fitting; + getItems(bufptr,top); + delItems(0,top); + fitting -= top; + bufptr += top; + } + if(bottom > 0 && fitting > 0) { + if(bottom > fitting) bottom = fitting; + getItems(bufptr,bottom); + delItems(0,bottom); + } + } + } + return retval; + } + + virtual size_t getBufferSize() { // returns the size of the physical buffer in items + return BufferTop-BufferBottom; + } + + virtual void invalidate() { // calling this will wipe all data in the buffer and reset the logical pointers + BufferStart = BufferEnd = BufferBottom; + } +}; + +#endif // !__C_DATAPUMP_H__ \ No newline at end of file diff --git a/Src/Plugins/DSP/dsp_sc/Include/c_wavein.h b/Src/Plugins/DSP/dsp_sc/Include/c_wavein.h new file mode 100644 index 00000000..7bcac3cb --- /dev/null +++ b/Src/Plugins/DSP/dsp_sc/Include/c_wavein.h @@ -0,0 +1,156 @@ +#ifndef __C_WAVEIN_H__ +#define __C_WAVEIN_H__ + +#include +#include +#define EXIT_ON_ERROR(hr) \ + if (FAILED(hr)) { goto Exit; } +#define SAFE_RELEASE(what) \ + if ((what) != NULL) \ +{ (what)->Release(); (what) = NULL; } + +template class C_WAVEIN { +private: + short Samples[numbuffers][buffersize]; + WAVEFORMATEX wfx; + WAVEHDR wvhdr[numbuffers]; + HWAVEIN hwi; + WAVEINCAPS wic; + unsigned long iNumDevs, iy; + HRESULT hr; + IMMDeviceEnumerator *pEnumerate; + IMMDevice *pDevice; + IMMDeviceCollection *ppDevices; + IPropertyStore *pProps; + BOOL useXpSound; + PROPVARIANT varName; + char buf[1024]; +public: + C_WAVEIN() { + hwi = NULL; + memset(Samples, 0, sizeof(Samples)); + memset(wvhdr, 0, sizeof(wvhdr)); + iNumDevs = iy = 0; + hr = S_OK; + pEnumerate = NULL; + pDevice = NULL; + ppDevices = NULL; + pProps = NULL; + useXpSound = false; + memset(buf, 0, sizeof(buf)); + } + + virtual ~C_WAVEIN() { + Close(); + } + + char * getDeviceName(unsigned int devid=-1) { + hr = S_OK; + pEnumerate = NULL; + pDevice = NULL; + ppDevices = NULL; + pProps = NULL; + useXpSound = false; + PROPVARIANT varName; + PropVariantInit(&varName); + // Get enumerator for audio endpoint devices. + CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), + NULL, CLSCTX_INPROC_SERVER, + __uuidof(IMMDeviceEnumerator), + (void**)&pEnumerate); + EXIT_ON_ERROR(hr) + + hr = pEnumerate->GetDefaultAudioEndpoint(eCapture,eConsole,&pDevice); + EXIT_ON_ERROR(hr) +Exit: + if (FAILED(hr)) { + useXpSound = true; + } else + useXpSound = false; + + memset(buf, 0, sizeof(buf)); + if (useXpSound) { + if (!waveInGetDevCaps(devid, &wic, sizeof(WAVEINCAPS))) { + lstrcpyn(buf, wic.szPname, ARRAYSIZE(buf)); + goto Fin; + } + } else { + pDevice->OpenPropertyStore(STGM_READ, &pProps); + pProps->GetValue(PKEY_Device_FriendlyName, &varName); + WideCharToMultiByte(CP_ACP, 0, (LPWSTR)varName.pwszVal, -1, buf, ARRAYSIZE(buf), NULL, NULL); + goto Fin; + } +Fin: + PropVariantClear(&varName); + SAFE_RELEASE(pProps) + SAFE_RELEASE(pEnumerate) + SAFE_RELEASE(pDevice) + SAFE_RELEASE(ppDevices) + CoUninitialize(); + return buf; + } + + void Create(int sRate, int nCh,int devid=-1) { + if (hwi == NULL) { + wfx.wFormatTag = WAVE_FORMAT_PCM; + wfx.wBitsPerSample = 16; + wfx.nSamplesPerSec = sRate; + wfx.nChannels = (WORD)nCh; + wfx.nBlockAlign = (wfx.nChannels * wfx.wBitsPerSample) / 8; + wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign; + wfx.cbSize = 0; + waveInOpen(&hwi,devid,&wfx,0,0,CALLBACK_NULL); + waveInStop(hwi); + waveInReset(hwi); + for(int i = 0; i < numbuffers; i++) { + memset(&wvhdr[i],0,sizeof(wvhdr[i])); + wvhdr[i].lpData = (char *)&Samples[i]; + wvhdr[i].dwBufferLength = buffersize * sizeof(short); + waveInPrepareHeader(hwi,&wvhdr[i],sizeof(WAVEHDR)); + waveInAddBuffer(hwi,&wvhdr[i],sizeof(WAVEHDR)); + } + waveInStart(hwi); + } + } + + void Close() { + if (hwi != NULL) { + waveInStop(hwi); + waveInReset(hwi); + for(int i = 0; i < numbuffers; i++) { + if (wvhdr[i].dwFlags & WHDR_PREPARED) { + waveInUnprepareHeader(hwi,&wvhdr[i],sizeof(WAVEHDR)); + } + } + waveInClose(hwi); + hwi = NULL; + } + } + + short *operator[](int buffernum) { + return (short *)&Samples[buffernum]; + } + + int getNumSamples(int buffernum) { + return wvhdr[buffernum].dwBytesRecorded / (wfx.nChannels * sizeof(short)); + } + + int isOpen() { + return hwi != NULL; + } + + int isFilled(int buffernum) { + return wvhdr[buffernum].dwFlags & WHDR_DONE && wvhdr[buffernum].dwBytesRecorded <= buffersize * sizeof(short); + } + + void cycleBuffer(int buffernum) { + if (hwi != NULL) { + wvhdr[buffernum].dwFlags = WHDR_PREPARED; + wvhdr[buffernum].dwBytesRecorded = 0; + waveInAddBuffer(hwi,&wvhdr[buffernum],sizeof(WAVEHDR)); + } + } +}; + +#endif // !__C_WAVEIN_H__ \ No newline at end of file diff --git a/Src/Plugins/DSP/dsp_sc/NSIS/ShellDispatch.dll b/Src/Plugins/DSP/dsp_sc/NSIS/ShellDispatch.dll new file mode 100644 index 00000000..1968ffed Binary files /dev/null and b/Src/Plugins/DSP/dsp_sc/NSIS/ShellDispatch.dll differ diff --git a/Src/Plugins/DSP/dsp_sc/NSIS/dsp_sc.dll b/Src/Plugins/DSP/dsp_sc/NSIS/dsp_sc.dll new file mode 100644 index 00000000..9196f328 Binary files /dev/null and b/Src/Plugins/DSP/dsp_sc/NSIS/dsp_sc.dll differ diff --git a/Src/Plugins/DSP/dsp_sc/NSIS/dsp_sc_license.txt b/Src/Plugins/DSP/dsp_sc/NSIS/dsp_sc_license.txt new file mode 100644 index 00000000..0682d8d1 --- /dev/null +++ b/Src/Plugins/DSP/dsp_sc/NSIS/dsp_sc_license.txt @@ -0,0 +1,186 @@ +RADIONOMY Shoutcast Terms / Disclaimer / Contact Us + + +Shoutcast Terms of Service + + +Welcome to Shoutcast! You may use Shoutcast’s website, applications, software, widgets updates and services of Shoutcast.com and its affiliated sites (collectively, "Shoutcast") on the condition that agree to the following terms. + +BY ACCESSING, USING OR INTERACTING WITH THE SERVICE, YOU SIGNIFY ELECTRONICALLY THAT YOU AGREE TO THESE TERMS OF SERVICE ("TERMS") AND THE TERMS OF OUR PRIVACY POLICY (http://www.radionomy.com/en/static/privacy). + +If you do not agree, you may not use Shoutcast. + + +1. About these Terms. + +Shoutcast is provided by RADIONOMY SA, and its affiliates, successors, parents, subsidiaries, assigns and licensee, who provide Shoutcast (collectively, "we" or "us"). You understand and agree that as Shoutcast evolves, we may in our sole discretion change or update these Terms at any time. You can review the most current version of these terms by clicking on the "Terms of Service" hypertext link located at the bottom of our homepage at http://www.shoutcast.com. Your ongoing use of Shoutcast after we post or notify you about changes to the Terms signifies your acceptance of the new terms. If you do not agree to the changes, your sole recourse is to stop using Shoutcast. + + +2. About the Shoutcast. + +a.Shoutcast is intended for general audiences and for personal and information use only. We may add, change or discontinue any aspect of Shoutcast at any time, in our sole discretion and without notice to you. We may offer certain applications, services or features for a fee, which will be subject to separate payment terms that will be binding on you. + +b.Shoutcast is controlled and operated within Belgium. Although Shoutcast is accessible from outside of Belgium, you understand and agree that Shoutcast (i) is not designed or customized for distribution for any specific country or jurisdiction (Territory), (b) is not intended for distribution to, or use by, any person or entity in any Territory where such distribution or use would be contrary to local law or regulation, (c) may not be appropriate or available for access or use in any particular Territory, and (d) is provided without any Content filtering or rating mechanism. We have no obligation to assure that Shoutcast complies with applicable local laws and regulations within the Territories in which you elect to use Shoutcast. Your use of Shoutcast within any specific Territory is entirely at Your own risk. You are solely responsible for complying with any local laws in which you access or use Shoutcast. + + +3. Content + +a. Shoutcast offers information, directories and search functionality to enable users to access, view and listen to content such as music ("Content") and provides links to other sites and third party providers of music, Internet music stations and other content ("Content Providers"). As a directory and search service, Shoutcast does not host and is not responsible for the Content accessed through its directory or search functionality. Such Content is hosted and served by the Content Providers. The Content Providers are solely responsible for their Content offered through Shoutcast, including without limitation, obtaining all rights, licenses and royalties pertaining to their Content. + +b. You understand that Shoutcast does not pre-screen Content and you agree that Shoutcast has no obligation to pre-screen Content, although Shoutcast reserves the right to do so in its sole discretion. Some Content may contain materials that are objectionable, unlawful, or inaccurate. Shoutcast’s offerings of directories, search results, links and/or access to Content does not mean that we endorse the Content or the Content Providers. You acknowledge and agree that we are not responsible or liable to you for any Content or other materials hosted and served from these third party Content Providers. + + +4. Registration + +Registration is not required; however, you will need to register with us and obtain a user ID if you want to use certain interactive features on Shoutcast. If you register, you must provide accurate identification, contact, and other information required as part of the registration process. You agree to keep your information current. You may not create any script or other automated tool that attempts to create multiple developer accounts. We may in our sole discretion reject any registration for any reason. By signing up with Shoutcast, you represent and warrant that your information is accurate. Our affiliates may give you the ability to use your Shoutcast user ID to access other affiliate services. Your use of services provided by our affiliates may be subject to additional terms, conditions and privacy policies, which will apply to you if you elect to use those services. + + +5. Additional Terms. + +Certain Shoutcast features may be subject to supplemental usage rules, guidelines and terms, which you will have an opportunity to review and which will be binding on you if you elect to use those features. + + +6. Electronic Delivery Policy and Your Consent. + +You acknowledge that you are agreeing to these terms online and electronically and that these Terms have the same effective as an agreement on paper. You authorize us to provide you with required notices, agreements and information concerning the Shoutcast electronically. Your affirmative act of submitting and clicking to register for Shoutcast constitutes your electronic signature to these Terms. We will provide you our notices either by sending them to the e-mail address that you give to us or by posting the notices on the home page of the Shoutcast or on the relevant web page of the applicable service. If you want to withdraw your consent to receive notices electronically, your only recourse is to discontinue your use of the Shoutcast. + + +7. Privacy Policy. + +The Shoutcast Privacy Policy located at http://www.radionomy.com/en/static/privacy explains the practices that apply to your information when you use the Shoutcast. Your ongoing use of the Shoutcast signifies your consent to the Privacy Policy. You can review the Privacy Policy by clicking on the Privacy Policy link located on the home page of the Shoutcast. + + +8. Access. + +You must provide at your own expense the equipment and Internet connections that you will need to access and use the Shoutcast, whether you access Shoutcast through broadband, wifi, wireless or other type of connection. If you access the Shoutcast through a telephone line, please call your local phone company to determine if the access numbers you select are subject to long distance or other toll charges at your location. Also, wireless, data or text messaging charges apply if you access the Shoutcast through wireless applications (e.g., cell phones). Check with your carrier to verify whether there are any such fees that may apply to you. + + +9. Your Responsibilities. + +You may use Shoutcast for lawful purposes only. You are responsible for all activities under your account, including all legal liability incurred from the use of your account by you or others. + + +10. Restrictions. + +You agree that you will not access or use Shoutcast or its Content, or otherwise engage in any conduct that: + +1. violates or infringes the rights of others including, without limitation, patent, trademark, trade secret, copyright, publicity or other proprietary rights is unlawful; + +2. uses technology or other means to access Shoutcast or Content that is not authorized by us; + +3. uses any automated system, including without limitation, "robots," "spiders," or "offline readers," to access Shoutcast or Content; + +4. attempts to introduce viruses or any other computer code, files or programs that interrupt, destroy or limit the functionality of any computer software or hardware or telecommunications equipment; + +5. attempts to gain unauthorized access to our computer network or user accounts; + +6. encourages conduct that would constitute a criminal offense, or that gives rise to civil liability; offers, promotes or encourages betting or wagering prohibited by law; + +7. violates these Terms, guidelines or any policy posted on Shoutcast; + +8. attempts to damage, disable, overburden, or impair our servers or networks; or + +9. interferes with any other party's use and enjoyment of Shoutcast. + +10. You agree that we may take any legal and technical remedies to enforce these Terms, including without limitation, immediate termination of your account or access to Shoutcast if we believe in our discretion that you are violating these Terms. + + +11. No Spam. + +You may not use Shoutcast or any of our communication tools to transmit, directly or indirectly, any unsolicited bulk communications (including e-mails and instant messages). You may not harvest information about our users for the purpose of sending, or to facilitate the sending, of unsolicited bulk communications. You may not induce or allow others to use Shoutcast to violate the terms of this section. We may terminate your access or use of Shoutcast immediately and take any other legal action if you, or anyone using your access to Shoutcast, violates these provisions. We may take any technical remedies to prevent unsolicited bulk communications from entering, utilizing, or remaining within our computer or communications networks. + + +12. Proprietary Rights. + +We, our suppliers, and our users who lawfully post Content on the Shoutcast own the property rights to that Content. Further, your agree that Shoutcast, its application, software and database information, and those techniques, algorithms, and processes contained therein which have been developed, acquired, or licensed by us are proprietary to RADIONOMY. and our affiliates. + +Shoutcast and the Content is protected by international treaties, and by copyright, trademark, patent, and trade secret laws and other proprietary rights and also may have security components that protect digital information. You agree that you will not violate these rights and access and use Shoutcast and the Content only as authorized by the owners of these rights. + + +13. License To Use the Shoutcast. + +We grant you a personal, non-exclusive, non-transferable, limited and revocable license to use Shoutcast subject to these Terms. You may not use Shoutcast in a manner that exceeds the rights granted for your use of Shoutcast and its Content. Without limitation of the foregoing, you may not frame any portion of Shoutcast or Content, or reproduce, record, reprint, copy, store, publicly display, broadcast, transmit, modify, translate, port, publish, sublicense, assign, transfer, sell, loan, make derivative works or otherwise distribute the Content without our prior written consent. You may not circumvent any mechanisms for preventing the unauthorized reproduction or distribution of the Content or Shoutcasts applications, software or services. Your license terminates immediately upon cancellation or termination of your account or if we believe you are in violation of these Terms. + + +14. Content You Post To Public Areas. + +Certain areas of Shoutcast may allow you to post Content (such as comments) that can be accessed and viewed by others, including the public in general. You may only post Content to public areas on Shoutcast that you created or that you have permission to post. You many not publicly post defamatory Content or someone else’s image or personal information without the express authorization of that person. You may not post Content that violates these Terms. We do not claim ownership of any Content that you may post. However, by submitting Content to public areas of Shoutcast, you grant us, our parent, affiliates, and distributors the right to use, copy, display, perform, distribute, adapt and promote this Content in any medium. You agree that we have no duty to pre-screen Content, but we have the right to refuse to post or to edit submitted Content. We reserve the right to remove Content for any reason, but we are not responsible for any failure or delay in removing such material. + + +15. Procedure For Making Claims Of Copyright Infringement. + +If you believe that your copyrighted work has been copied and is accessible on the RADIONOMY, Winamp or Shoutcast in a way that constitutes copyright infringement, you may notify RADIONOMY by providing our copyright agent the following information: + +1. an electronic or physical signature of the owner of the copyright or the person authorized to act on the owner's behalf. + +2. a description of the copyrighted work that you claim has been infringed and a description of the infringing activity. + +3. identification of the location where the original or an authorized copy of the copyrighted work exists, for example the URL (i.e., web page address) where it is posted or the name of the book in which it has been published. + +4. identification of the URL or other specific location on the Shoutcast site where the material that you claim is infringing is located, including enough information to allow us to locate the material. + +5. your name, address, telephone number, and email address. + +6. a statement by you that you have a good faith belief that the disputed use is not authorized by the copyright owner, its agent, or the law. + +7. a statement by you that the above information in your Notice is accurate and that you are the copyright owner or authorized to act on the copyright owner's behalf. + + +RADIONOMY's agent for notice of claims of copyright infringement on this site can be reached as follows: + +By mail: +Radionomy SA +55K Boulevard International +1070 Brussels +Belgium + +Email: contact@radionomy.com + + +16. Advertisers. + +You agree that the Shoutcast may be supported by advertising. Any dealings that you have with advertisers found on the Shoutcast are between you and the advertiser and you acknowledge and agree that we are not liable for any loss or claim you may have against an advertiser. + + +17. Use Of Software. + +We may make software available for you to download or use. Such software will be subject to the terms of the license agreement that accompanies it. If there is no license agreement presented to you with the software, then the following license, in addition to the other provisions of these Terms, govern your use of such software. We grant you a personal, non-exclusive, non-transferable, limited license to install the software on any single computer or authorized device. The software is protected by copyright and other intellectual property laws and treaties and is owned by us or our suppliers. You may not sell or redistribute the software. You may not incorporate it or any portion of it into another product. You may not reverse engineer, decompile, or disassemble the software or otherwise attempt to derive the source code (except where expressly permitted by law). You may not modify, adapt, or create derivative works from the software in any way or remove proprietary notices in the software. You agree to abide by all laws and regulations in effect regarding your use of the software. You may not authorize or assist any third party to do any of the things prohibited in this paragraph. + +We may automatically check your version of the software and update it to improve its performance and capabilities. If you shut down the software during an automatic update or otherwise interfere with the installation of the update, the software may be damaged and/or cease to operate. + +18. Export laws. + +You agree to fully comply with all import and export laws, regulations, rules and orders of the European Union, or any foreign government agency or authority, and that you will not directly or indirectly export, re-export, transfer and/or release the software, related technology, or any product thereof, for any proscribed end-use, or to any proscribed country, entity or person (wherever located), without proper authorization from the European Union or other applicable agencies. You are responsible for and assume all expenses relating to your compliance with the described laws, regulations, rules and orders, and for obtaining all necessary authorizations and clearances. You further agree to assume responsibility for and bear all expenses relating to your compliance with the described laws, regulations, rules and orders, and obtaining all necessary authorizations and clearances. + + +19. DISCLAIMER. + +WE PROVIDE SHOUTCAST "AS IS" AND WITH ALL FAULTS. YOU ARE USING SHOUTCAST AT YOUR OWN RISK. WE, OUR LICENSORS AND DISTRIBUTORS DISCLAIM ALL WARRANTIES, WHETHER EXPRESS OR IMPLIED, INCLUDING ANY WARRANTIES THAT SHOUTCAST IS FREE OF DEFECTS AND ABLE TO OPERATE ON AN UNINTERRUPTED BASIS OR THAT IT WILL MEET YOUR REQUIREMENTS. WE DISCLAIM THE IMPLIED WARRANTIES THAT SHOUTCAST IS MERCHANTABLE, OF SATISFACTORY QUALITY, RELIABLE, ACCURATE, FIT FOR A PARTICULAR PURPOSE OR NEED, OR NON-INFRINGING, UNLESS SUCH IMPLIED WARRANTIES ARE LEGALLY INCAPABLE OF EXCLUSION. YOU HEREBY ACKNOWLEDGE A) CONTENT PROVIDERS AND NOT US ARE RESPONSIBLE FOR OBTAINING ALL NECESSARY RIGHTS, PERMISSIONS, LICENSES, APPLICABLE TAXES, CERTIFICATIONS AND CLEARANCES FOR THE STATIONS AND CONTENT THAT THEY PROIVIDE, B) CONTENT PROVIDERS AND NOT US ARE SOLELY RESPONSIBLE FOR ENSURING THAT THE CONTENT COMPLIES WITH ALL APPLICABLE LAWS AND REGULATIONS, C) CONTENT PROVIDERS AND NOT US ARE SOLELY RESPONSIBLE FOR ALL APPLICABLE ROYALTIES TO THE COPYRIGHT OWNERS WITH RESPECT TO THE CONTENT CONTAINED IN THEIR INDIVIDUAL STATION(S), AND D) WE SHALL HAVE NO RESPONSIBILITY OR LIABILITY TO YOU OR ANY THIRD PARTY TO OBTAIN SUCH RIGHTS, PERMISSIONS, CLEARANCES OR ROYALTY PAYMENTS AND SHALL HAVE NO RESPONSIBILITY OR LIABILITY TO YOU OR ANY THIRD PARTY FOR THE FAILURE OF SUCH CONTENT TO OBTAIN SUCH RIGHTS, PERMISSIONS, CLEARANCES OR PAY ANY APPLICABLE ROYALTIES. + + +20. LIMITATION OF LIABILITY. + +WE, OUR LICENSORS AND DISTRIBUTORS HAVE NO LIABILITY WITH RESPECT TO YOUR USE OF SHOUTCAST. TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT SHALL WE OR OUR PARENT, AFFILIATES, DIRECTORS, EMPLOYEES, DISTRIBUTORS, LICENSORS, SUPPLIERS, PARTNERS, AGENTS, DISTRIBUTORS OR RESELLERS (RADIONOMY) BE LIABLE FOR ANY INDIRECT, SPECIAL, INCIDENTAL, CONSEQUENTIAL, OR EXEMPLARY DAMAGES ARISING OUT OF OR IN ANY WAY RELATING TO THIS AGREEMENT OR THE USE OF OR INABILITY TO USE SHOUTCAST, INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, WORK STOPPAGE, LOST PROFITS, LOSS OF DATA, CORRUPTION OF DATA, COMPUTER FAILURE OR MALFUNCTION. YOUR SOLE REMEDY WITH RESPECT TO ANY DISPUTE WITH US OR SHOUTCAST IS TO CANCEL YOUR USE OF THE SERVICE. IN ADDITION, THE MAXIMUM AGGREGATE LIABILITY OF THE RADIONOMY GROUP FOR ANY CLAIMS ARISING IN CONNECTION WITH THIS AGREEMENT WILL NOT EXCEED ONE HUNDRED DOLLARS (USD $100). SHOUTCAST IS PROVIDED WITHOUT CHARGE AND YOU AGREE THAT THE FOREGOING LIMITATIONS REPRESENT A REASONABLE ALLOCATION OF RISK UNDER THESE TERMS. + + +21. Indemnification. + +Upon a request by us, you agree to defend, indemnify, and hold harmless us and our parent and other affiliated companies, and our respective employees, contractors, officers, directors, and agents from all liabilities, claims, and expenses, including attorney's fees that arise from your use or misuse of Shoutcast. We reserve the right, at our own expense, to assume the exclusive defense and control of any matter otherwise subject to indemnification by you, in which event you will cooperate with us in asserting any available defenses. + + +22. Choice of Law and Location for Resolving Disputes. + +YOU EXPRESSLY AGREE THAT EXCLUSIVE JURISDICTION FOR ANY CLAIM OR DISPUTE WITH RADIONOMY, RADIONOMY’S AFFILIATES, SHOUTCAST, THIS AGREEMENT, OR RELATING IN ANY WAY TO YOUR USE OF THIS SITE AND/OR SHOUTCAST (OR ANY FEATURES THEREOF) RESIDES IN THE FEDERAL OR STATE COURTS LOCATED IN BELGIUM AND EXPRESSLY CONSENT TO THE EXERCISE OF PERSONAL JURISDICTION IN SUCH COURTS IN CONNECTION WITH ANY SUCH DISPUTE INCLUDING ANY CLAIM INVOLVING RADIONOMY, THIS AGREEMENT, OF THIS SITE AND/OR SHOUTCAST (OR ANY FEATURES THEREOF). PLEASE NOTE THAT BY AGREEING TO THESE TERMS OF USE, YOU ARE WAIVING CLAIMS THAT YOU MIGHT OTHERWISE HAVE AGAINST US BASED ON THE LAWS OF OTHER JURISDICTIONS, INCLUDING YOUR OWN. + + +23. Severability and Integration. + +These Terms and any supplemental terms, policies, rules and guidelines posted on Shoutcast constitute the entire agreement between you and us and supersede all previous written or oral agreements. If any part of these Terms is held invalid or unenforceable, that portion shall be construed in a manner consistent with applicable law to reflect, as nearly as possible, the original intentions of the parties, and the remaining portions shall remain in full force and effect. + + +24. Termination. + +Your right to use Shoutcast automatically terminates if you violate these Terms or any rules or guidelines posted in connection with Shoutcast. We also reserve the right, in our sole discretion, to terminate your access to all or part of Shoutcast, for any reason, with or without notice. + + +Last updated 7-15-2014 diff --git a/Src/Plugins/DSP/dsp_sc/NSIS/dsp_sc_v2.nsi b/Src/Plugins/DSP/dsp_sc/NSIS/dsp_sc_v2.nsi new file mode 100644 index 00000000..9a9adeac --- /dev/null +++ b/Src/Plugins/DSP/dsp_sc/NSIS/dsp_sc_v2.nsi @@ -0,0 +1,438 @@ +;-------------------------------- +;Include Modern UI + +!include "MUI2.nsh" +!include "LogicLib.nsh" +!include "FileFunc.nsh" +!include "WordFunc.nsh" +!include "WinVer.nsh" +;-------------------------------- +; this is the version for Winamp 5.9.1 +!define MINIMAL_VERSION "5.9.1.10021" + +; The name of the installer +!define NAME "Shoutcast Source DSP Plug-in" +!define VERSION "2.4.2" +!define BUILD "449" +!define UNINSTALL "Shoutcast Source DSP" +!define UNINSTALLER "uninstall_shoutcast-source-dsp-v2.exe" +Name "${NAME}" + +BrandingText "${NAME} v${VERSION} Build ${BUILD}" + +; detect winamp path from uninstall string if available +InstallDirRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Winamp" "UninstallString" + +; The file to write +OutFile "shoutcast-dsp-2-4-2-windows.exe" + +; The default installation directory +InstallDir "$PROGRAMFILES32\Winamp" + +; The text to prompt the user to enter a directory +DirText "Please select your Winamp path below (you will be able to proceed when Winamp is detected):" +# currently doesn't work - DirShow hide + +; Request application privileges for Windows Vista+ +RequestExecutionLevel admin + +; Set the compressor (get installer as small as possible) +SetCompressor /SOLID lzma + +; Set the install types available +InstType "Full Install" +InstType "Base Install" + +; global variables +Var /GLOBAL WINAMP_INI_DIR +Var GetInstalledSize.total + +;-------------------------------- +;Interface Settings + +!define MUI_ABORTWARNING + +;-------------------------------- +;Pages + !define MUI_ICON "modern-install.ico" + !define MUI_UNICON "modern-install.ico" + + !define MUI_WELCOMEPAGE_TITLE_3LINES + !define MUI_WELCOMEPAGE_TEXT "This wizard will guide you through the installation of the ${NAME}.$\n$\nIt is recommended that you close all instances of Winamp before starting Setup. This will make it possible to install relevant files within your Winamp installation without issues.$\n$\nClick Next to continue." + !define MUI_WELCOMEFINISHPAGE_BITMAP "win.bmp" + !insertmacro MUI_PAGE_WELCOME + !insertmacro MUI_PAGE_LICENSE "dsp_sc_license.txt" +; is best to call the version check when leaving the directory page so it will be working against correct path + !define MUI_PAGE_CUSTOMFUNCTION_LEAVE CheckWinampVersion + !insertmacro MUI_PAGE_DIRECTORY + !insertmacro MUI_PAGE_COMPONENTS + !insertmacro MUI_PAGE_INSTFILES + + !define MUI_FINISHPAGE_RUN + !define MUI_FINISHPAGE_RUN_FUNCTION SetAsCurrentDSP + !define MUI_FINISHPAGE_RUN_TEXT "Set as the current DSP plug-in" + + !define MUI_FINISHPAGE_SHOWREADME ; "$INSTDIR\winamp.exe" + !define MUI_FINISHPAGE_SHOWREADME_FUNCTION RunWinamp + !define MUI_FINISHPAGE_SHOWREADME_TEXT "Run Winamp" + + !define MUI_FINISHPAGE_TEXT_LARGE + !define MUI_FINISHPAGE_TITLE_3LINES + !define MUI_FINISHPAGE_TEXT "${NAME} has been installed.$\n$\nTo enable the plug-in if it is not your current DSP plug-in, goto Winamp Preferences -> Plug-ins -> DSP/Effect and select the '${NAME}' entry.$\n$\nClick Finish to close this wizard." + !define MUI_PAGE_CUSTOMFUNCTION_SHOW RestoreCheckedStates + !define MUI_PAGE_CUSTOMFUNCTION_LEAVE SaveCheckedStates + !insertmacro MUI_PAGE_FINISH + + !define MUI_UNWELCOMEFINISHPAGE_BITMAP "win.bmp" + !define MUI_UNWELCOMEPAGE_TEXT "This wizard will guide you through the uninstallation of the ${NAME}.$\n$\nBefore starting the uninstalltion, make sure Winamp and the ${NAME} are not running.$\n$\nClick Next to continue." + !define MUI_UNFINISHPAGE_TEXT "${NAME} has been uninstalled from your Winamp install.$\n$\nClick Finish to close this wizard." + !insertmacro MUI_UNPAGE_WELCOME + !insertmacro MUI_UNPAGE_CONFIRM + !insertmacro MUI_UNPAGE_INSTFILES + !insertmacro MUI_UNPAGE_FINISH + +;-------------------------------- +;Languages + + !insertmacro MUI_LANGUAGE "English" ;first language is the default language + +;-------------------------------- +;Reserve Files + + ;If you are using solid compression, files that are required before + ;the actual installation should be stored first in the data block, + ;because this will make your installer start faster. + + !insertmacro MUI_RESERVEFILE_LANGDLL +;--------------------------------- + +Section "Shoutcast Source DSP" Core + SectionIn 1 2 RO + + SetOutPath "$INSTDIR\Plugins" + + File "dsp_sc.dll" + + ; look for lamedll.dll and remove as we now use lame_enc.dll + IfFileExists "$INSTDIR\Plugins\lamedll.dll" 0 +2 + Delete "lamedll.dll" + + ; Let's upgrade to Lame 3.100.1 + SetOutPath "$INSTDIR\Shared" + File "..\..\..\..\resources\libraries\lame_enc.dll" + +; VC142 runtimes are required for Win7 & 8 installations +; Chances are these will already exist, but let's make sure anyway... + ${If} ${AtLeastWin7} + ${AndIf} ${AtMostWin8.1} + SetOutPath "$INSTDIR\Microsoft.VC142.CRT" + File ..\..\..\..\resources\libraries\msvcp140.dll + File ..\..\..\..\resources\libraries\vcruntime140.dll + File ..\..\..\..\resources\libraries\msvcp140_1.dll + File ..\..\..\..\resources\libraries\msvcp140_2.dll + File ..\..\..\..\resources\libraries\msvcp140_atomic_wait.dll + File ..\..\..\..\resources\libraries\msvcp140_codecvt_ids.dll + File ..\..\..\..\resources\libraries\vccorlib140.dll + File ..\..\..\..\resources\libraries\concrt140.dll + ${EndIf} + + ; Write the uninstall keys for Windows + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINSTALL}" "DisplayName" "Shoutcast Source DSP Plug-in v2" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINSTALL}" "UninstallString" "$\"$INSTDIR\${UNINSTALLER}$\"" + WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINSTALL}" "NoModify" 1 + WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINSTALL}" "NoRepair" 1 + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINSTALL}" "Publisher" "Radionomy SA" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINSTALL}" "HelpLink" "http://forums.shoutcast.com/forumdisplay.php?f=140" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINSTALL}" "InstallLocation" "$INSTDIR\Plugins" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINSTALL}" "DisplayVersion" "${VERSION}.${BUILD}" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINSTALL}" "URLInfoAbout" "http://wiki.shoutcast.com/wiki/Source_DSP_Plug-in" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINSTALL}" "URLUpdateInfo" "https://www.shoutcast.com" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINSTALL}" "DisplayIcon" "$\"$INSTDIR\${UNINSTALLER}$\"" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINSTALL}" "QuietUninstallString" "$\"$INSTDIR\${UNINSTALLER}$\" /S" + Call GetInstalledSize + WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINSTALL}" "EstimatedSize" "$GetInstalledSize.total" + + IfFileExists "$INSTDIR\Uninstallers\${UNINSTALLER}" 0 +2 + Delete "$INSTDIR\Uninstallers\${UNINSTALLER}" + + SetOutPath "$INSTDIR\" + + WriteUninstaller "$INSTDIR\${UNINSTALLER}" + +SectionEnd + +Section "Documentation" Docs + SectionIn 1 + +SetOutPath "$INSTDIR\Plugins\Shoutcast Source DSP" + File ..\docs\Source_DSP_Plug-in.html + File ..\docs\Source_DSP_Plug-in_Config_Examples.html + File ..\docs\Source_DSP_Changelog.html +SetOutPath "$INSTDIR\Plugins\Shoutcast Source DSP\res" + File ..\docs\res\*.png +SectionEnd + +;-------------------------------- +;Section description text + +!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN + !insertmacro MUI_DESCRIPTION_TEXT ${Core} "The ${NAME} file.$\n$\n(This is always required)" + !insertmacro MUI_DESCRIPTION_TEXT ${Docs} "This will be put in 'Plugins\Shoutcast Source DSP' in the selected destination.$\n$\nThese will show you the features available in the plug-in as well as how to make it connect with the Shoutcast 2 tools." +!insertmacro MUI_FUNCTION_DESCRIPTION_END + +;-------------------------------- +;Uninstaller Section + +Section "Uninstall" + DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${UNINSTALL}" + Delete "$INSTDIR\Plugins\dsp_sc.dll" + Delete "$INSTDIR\Plugins\lamedll.dll" ; we don't actually install this any more + ; Delete "$INSTDIR\Shared\lame_enc.dll" + Delete "$INSTDIR\Plugins\Shoutcast Source DSP\dsp_sc.txt" + Delete "$INSTDIR\Plugins\Shoutcast Source DSP\dsp_sc_config.txt" + Delete "$INSTDIR\Plugins\Shoutcast Source DSP\Source_DSP_Plug-in.html" + Delete "$INSTDIR\Plugins\Shoutcast Source DSP\Source_DSP_Changelog.html" + Delete "$INSTDIR\Plugins\Shoutcast Source DSP\Source_DSP_Plug-in_Config_Examples.html" + Delete "$INSTDIR\Plugins\Shoutcast Source DSP\res\*.png" + Delete "$INSTDIR\Plugins\Shoutcast Source DSP\res\docs.css" + RMDir "$INSTDIR\Plugins\Shoutcast Source DSP\res" + RMDir "$INSTDIR\Plugins\Shoutcast Source DSP" + ; Delete "$INSTDIR\Microsoft.VC142.CRT\*.dll" ; not wise, because then Winamp won't run on Win7-Win8.1 - - - why did this line exist for the VC90 Runtime? + Delete "$INSTDIR\${UNINSTALLER}" + +SectionEnd + +Function .onInit + + !insertmacro MUI_LANGDLL_DISPLAY + + ;Detect running Winamp instances and close them + !define WINAMP_FILE_EXIT 40001 + + FindWindow $R0 "Winamp v1.x" + IntCmp $R0 0 ok + MessageBox MB_YESNO|MB_ICONEXCLAMATION "Please close all instances of Winamp before installing$\n${NAME}.$\n$\nAttempt to close Winamp now?" IDYES checkagain IDNO no + checkagain: + FindWindow $R0 "Winamp v1.x" + IntCmp $R0 0 ok + SendMessage $R0 ${WM_COMMAND} ${WINAMP_FILE_EXIT} 0 + Goto checkagain + no: + Abort + ok: + +FunctionEnd + +Function .onVerifyInstDir + + ;Check for Winamp installation + + IfFileExists $INSTDIR\Winamp.exe Good + Abort + Good: + +FunctionEnd + +;Uninstaller Functions +/* Function un.GetParent + + Exch $R0 + Push $R1 + Push $R2 + Push $R3 + + StrCpy $R1 0 + StrLen $R2 $R0 + + loop: + IntOp $R1 $R1 + 1 + IntCmp $R1 $R2 get 0 get + StrCpy $R3 $R0 1 -$R1 + StrCmp $R3 "\" get + Goto loop + + get: + StrCpy $R0 $R0 -$R1 + + Pop $R3 + Pop $R2 + Pop $R1 + Exch $R0 + +FunctionEnd + +Function un.onInit + + !insertmacro MUI_UNGETLANGUAGE + + Push "$INSTDIR" + Call un.GetParent + Pop $R0 + StrCpy $INSTDIR $R0 + +FunctionEnd */ + +Function GetWinampIniPath + StrCpy $WINAMP_INI_DIR $INSTDIR + ${If} $0 == "" + StrCpy $WINAMP_INI_DIR "$PROGRAMFILES\Winamp" + ${EndIf} + ClearErrors + + ${If} ${FileExists} "$WINAMP_INI_DIR\paths.ini" + ReadINIStr $0 "$WINAMP_INI_DIR\paths.ini" "Winamp" "inidir" + ${If} $0 != "" + ${WordFind2X} $0 "{" "}" "E+1" $2 + ${If} ${Errors} + ${IfNot} ${FileExists} "$0\*.*" + ${WordFind2X} $0 "%" "%" "E+1" $2 + + ${If} $2 == "WINAMP_ROOT_DIR" + ClearErrors + ${GetRoot} "$WINAMP_INI_DIR" $3 + ${WordReplace} "$0" "%$2%" "$3" "E+1" $R0 + ${If} ${Errors} + Return + ${Else} + StrCpy $WINAMP_INI_DIR $R0 + ${EndIf} + ${ElseIf} $2 == "WINAMP_PROGRAM_DIR" + ClearErrors + ${WordReplace} "$0" "%$2%" "$WINAMP_INI_DIR" "E+1" $R0 + ${If} ${Errors} + Return + ${Else} + StrCpy $WINAMP_INI_DIR $R0 + ${EndIf} + ${Else} + ClearErrors + ReadEnvStr $R0 "$2" + ${If} $R0 != "" + ${WordReplace} "$0" "%$2%" "$R0" "E+1" $R0 + ${If} ${Errors} + Return + ${Else} + StrCpy $WINAMP_INI_DIR $R0 + ${EndIf} + ${Else} + Return + ${EndIf} + ${EndIf} + ${Else} + StrCpy $WINAMP_INI_DIR $0 + ${EndIf} + ${Else} + System::Call "shell32::SHGetSpecialFolderPath(i $HWNDPARENT, t .r4, i $2, i0) i .r3" + ClearErrors + ${WordReplace} "$0" "{$2}" "$4" "E+1" $R0 + ${If} ${Errors} + Return + ${Else} + StrCpy $WINAMP_INI_DIR $R0 + ${EndIf} + ${EndIf} + ${Else} + Return + ${EndIf} + ${Else} + Return + ${EndIf} +FunctionEnd + +; set as the current DSP +Function SetAsCurrentDSP + WriteINIStr "$WINAMP_INI_DIR\winamp.ini" "winamp" "dspplugin_name" "dsp_sc.dll" + WriteINIStr "$WINAMP_INI_DIR\winamp.ini" "winamp" "dspplugin_num" "0" +FunctionEnd + +Function RunWinamp + StrCpy $1 1 + File "/oname=$PLUGINSDIR\ShellDispatch.dll" "ShellDispatch.dll" + ${If} ${FileExists} "$PLUGINSDIR\ShellDispatch.dll" + ${AndIf} ${FileExists} "$INSTDIR\winamp.exe" + Push $0 + StrCpy $0 "" + ClearErrors + GetFullPathName /SHORT $0 "$PLUGINSDIR\ShellDispatch.dll" + ${IfNot} ${Errors} + ${AndIf} $0 != "" + ExecWait 'rundll32.exe $0,RunDll_ShellExecute "open" "$INSTDIR\winamp.exe"' $1 + ${If} ${Errors} + StrCpy $1 1 + ${EndIf} + ${EndIf} + Pop $0 + ${EndIf} + + ${If} $1 != 0 + Exec "$INSTDIR\winamp.exe" + ${EndIf} +FunctionEnd + +; restore the last checked states on the finish page +Function RestoreCheckedStates + Call GetWinampIniPath + + ReadINIStr $0 "$WINAMP_INI_DIR\plugins\dsp_sc.ini" "installer" "cur" + ${If} $0 == "0" + ${NSD_Uncheck} $mui.FinishPage.Run + ${EndIf} + + ReadINIStr $0 "$WINAMP_INI_DIR\plugins\dsp_sc.ini" "installer" "run" + ${If} $0 == "0" + ${NSD_Uncheck} $mui.FinishPage.ShowReadme + ${EndIf} +FunctionEnd + +; save the last checked states on the finish page +Function SaveCheckedStates + + ${NSD_GetState} $mui.FinishPage.Run $0 + WriteINIStr "$WINAMP_INI_DIR\plugins\dsp_sc.ini" "installer" "cur" $0 + + ${NSD_GetState} $mui.FinishPage.ShowReadme $0 + WriteINIStr "$WINAMP_INI_DIR\plugins\dsp_sc.ini" "installer" "run" $0 + +FunctionEnd + +; the check version function +; only issue is that the strings won't be localised as i see the installer in the zip supports it +Function CheckWinampVersion + ${GetFileVersion} "$INSTDIR\winamp.exe" $R0 ; Get Winamp.exe version information, $R0 = Actual Version + ${if} $R0 != "" ; check if Version info is not empty + ${VersionCompare} $R0 ${MINIMAL_VERSION} $R1 ; $R1 = Result $R1=0 Versions are equal, $R1=1 Version1 is newer, $R1=2 Version2 is newer + ${if} $R1 == "2" + MessageBox MB_OK "Warning: This plug-in requires at least Winamp v${MINIMAL_VERSION} or higher.$\nThe detected version of your Winamp install is: $R0$\n$\nThe Shoutcast Source plug-in may not function correctly with the$\n version of winamp detected.Please update your Winamp client!!$\n" + Abort + ${EndIf} + ${Else} + MessageBox MB_OK "Warning: A valid Winamp install was not detected in the specified path.$\n$\nPlease check the Winamp directory and either install the latest version$\nfrom Winamp.com or choose another directory with a valid Winamp install$\nbefore you can install the Shoutcast Source on your machine." + Abort + ${EndIf} +FunctionEnd + +; Return on top of stack the total size of the selected (installed) sections, formated as DWORD +; Assumes no more than 256 sections are defined +Function GetInstalledSize + Push $0 + Push $1 + StrCpy $GetInstalledSize.total 0 + ${ForEach} $1 0 256 + 1 + StrCpy $0 0 + ${if} ${SectionIsSelected} $1 + SectionGetSize $1 $0 + IntOp $GetInstalledSize.total $GetInstalledSize.total + $0 + ${Endif} + + ; Error flag is set when an out-of-bound section is referenced + ${if} ${errors} + ${break} + ${Endif} + ${Next} + + ClearErrors + Pop $1 + Pop $0 + IntFmt $GetInstalledSize.total "0x%08X" $GetInstalledSize.total + Push $GetInstalledSize.total +FunctionEnd \ No newline at end of file diff --git a/Src/Plugins/DSP/dsp_sc/NSIS/modern-install.ico b/Src/Plugins/DSP/dsp_sc/NSIS/modern-install.ico new file mode 100644 index 00000000..ba0bb6d7 Binary files /dev/null and b/Src/Plugins/DSP/dsp_sc/NSIS/modern-install.ico differ diff --git a/Src/Plugins/DSP/dsp_sc/NSIS/win.bmp b/Src/Plugins/DSP/dsp_sc/NSIS/win.bmp new file mode 100644 index 00000000..63250f65 Binary files /dev/null and b/Src/Plugins/DSP/dsp_sc/NSIS/win.bmp differ diff --git a/Src/Plugins/DSP/dsp_sc/Resource/ICY.ICO b/Src/Plugins/DSP/dsp_sc/Resource/ICY.ICO new file mode 100644 index 00000000..9f9119a4 Binary files /dev/null and b/Src/Plugins/DSP/dsp_sc/Resource/ICY.ICO differ diff --git a/Src/Plugins/DSP/dsp_sc/Resource/Script1.rc b/Src/Plugins/DSP/dsp_sc/Resource/Script1.rc new file mode 100644 index 00000000..493b987d --- /dev/null +++ b/Src/Plugins/DSP/dsp_sc/Resource/Script1.rc @@ -0,0 +1,679 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_DIALOG DIALOGEX 0, 0, 213, 301 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU +EXSTYLE WS_EX_CONTROLPARENT +CAPTION "Shoutcast Source" +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + CONTROL "Tab1",IDC_TAB,"SysTabControl32",WS_TABSTOP,4,4,206,293 + GROUPBOX "",IDC_RECT,10,22,194,267,NOT WS_VISIBLE +END + +IDD_ENCODER DIALOGEX 0, 0, 180, 139 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + GROUPBOX "Encoder Configuration",IDC_INFO_FRAME4,0,0,179,56 + GROUPBOX "Encoder Configuration",IDC_INFO_FRAME5,0,0,180,73 + LTEXT "Encoder Type",IDC_ENCODER_HEADER,5,12,45,8 + COMBOBOX IDC_ENCTYPE,5,24,169,52,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + LTEXT "Encoder Settings",IDC_ENCSETTINGS_LABEL,5,41,95,8 + RTEXT "",IDC_ENCSETTINGS_LAME_VER,104,41,70,8,NOT WS_VISIBLE + PUSHBUTTON "Encoder Settings...",IDC_ENCSETTINGS_BUTTON,5,52,169,14,NOT WS_VISIBLE + COMBOBOX IDC_ENCSETTINGS,5,53,169,105,CBS_DROPDOWNLIST | CBS_AUTOHSCROLL | WS_DISABLED | WS_VSCROLL | WS_TABSTOP + GROUPBOX "Save Encoded Output",IDC_INFO_FRAME3,0,77,180,55 + CONTROL "Save a copy of the encoded stream audio\nNote: Extension is updated on encoder change",IDC_SAVE_ENCODED_AUDIO, + "Button",BS_AUTOCHECKBOX | BS_TOP | BS_MULTILINE | WS_TABSTOP,5,89,169,16 + EDITTEXT IDC_SAVE_ENCODED_AUDIO_EDIT,15,110,142,14,ES_AUTOHSCROLL | ES_READONLY + PUSHBUTTON "...",IDC_SAVE_ENCODED_AUDIO_BROWSE,158,110,16,14 +END + +IDD_MAIN DIALOGEX 0, 0, 194, 271 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + GROUPBOX "Status / Info (Double-Click To Configure)",IDC_STATIC,0,0,193,164,BS_LEFT + CONTROL "List1",IDC_OUTPUTSTATUS,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_NOSCROLL | LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,6,12,182,70,WS_EX_CLIENTEDGE + LTEXT "",IDC_SUMMARY,8,86,180,72 + GROUPBOX "Active input device: ",IDC_INPUT_METERS,0,168,193,102 + CTEXT "Input Levels\n(Current / Peak)",IDC_STATIC,7,178,60,16 + CTEXT "-inf dB",IDC_VOLUMETEXT_L,8,195,21,8 + CTEXT "-inf dB",IDC_VOLUMETEXT_R,46,195,21,8 + CTEXT "(-inf dB)",IDC_VOLUMETEXT_LP,5,204,27,8 + CTEXT "(-inf dB)",IDC_VOLUMETEXT_RP,43,204,27,8 + CONTROL "Progress1",IDC_VOLUMEGRAPH_L,"msctls_progress32",PBS_SMOOTH | PBS_VERTICAL,15,215,9,47 + RTEXT "0 dB",IDC_STATIC,28,215,21,8 + RTEXT "-22 dB",IDC_STATIC,28,224,21,8 + RTEXT "-45 dB",IDC_STATIC,28,234,21,8 + RTEXT "-67 dB",IDC_STATIC,28,243,21,8 + RTEXT "-inf dB",IDC_STATIC,28,253,21,8 + CONTROL "Progress1",IDC_VOLUMEGRAPH_R,"msctls_progress32",PBS_SMOOTH | PBS_VERTICAL,53,215,9,47 + CONTROL "&Winamp",IDC_INPUT_WINAMP,"Button",BS_AUTORADIOBUTTON,73,168,40,8 + CONTROL "&Soundcard",IDC_INPUT_SOUNDCARD,"Button",BS_AUTORADIOBUTTON,114,168,48,8 +END + +IDD_INPUT DIALOGEX 0, 0, 194, 271 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + GROUPBOX "Input Configuration",IDC_STATIC,0,0,193,105 + LTEXT "Input Device",IDC_INPUTDEVICESTATIC,6,12,42,8,WS_DISABLED + COMBOBOX IDC_INPUTDEVICE,6,24,100,70,CBS_DROPDOWNLIST | WS_DISABLED | WS_VSCROLL | WS_TABSTOP + LTEXT "Input Settings",IDC_INPUTSETUPSTATIC,6,42,46,8 + COMBOBOX IDC_INPUTSETUP,6,54,100,70,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + CTEXT "",IDC_CURDEVICE,6,77,116,20 + CTEXT "Input Levels\n(Current / Peak)",IDC_STATIC,120,12,60,16 + CTEXT "-inf dB",IDC_VOLUMETEXT_L,120,31,21,8 + CTEXT "-inf dB",IDC_VOLUMETEXT_R,158,31,21,8 + CTEXT "(-inf dB)",IDC_VOLUMETEXT_LP,117,40,27,8 + CTEXT "(-inf dB)",IDC_VOLUMETEXT_RP,155,40,27,8 + CONTROL "Progress1",IDC_VOLUMEGRAPH_L,"msctls_progress32",PBS_SMOOTH | PBS_VERTICAL,127,50,9,47 + RTEXT "0 dB",IDC_STATIC,140,50,21,8 + RTEXT "-22 dB",IDC_STATIC,140,59,21,8 + RTEXT "-45 dB",IDC_STATIC,140,68,21,8 + RTEXT "-67 dB",IDC_STATIC,140,78,21,8 + RTEXT "-inf dB",IDC_STATIC,140,88,21,8 + CONTROL "Progress1",IDC_VOLUMEGRAPH_R,"msctls_progress32",PBS_SMOOTH | PBS_VERTICAL,165,50,9,47 + GROUPBOX "",IDC_PANEL_RECT,0,109,193,138,NOT WS_VISIBLE +END + +IDD_PANEL_WINAMP DIALOGEX 0, 0, 194, 160 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + GROUPBOX "Metadata (Double-click to explore item folder)",IDC_SNDCARDSTATIC,0,0,193,84 + CONTROL "",IDC_METALIST,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | LVS_NOCOLUMNHEADER | LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,5,11,183,67 + GROUPBOX "Playing Artwork",IDC_SNDCARDSTATIC2,0,87,193,72 + CONTROL "",IDC_ARTWORK,"Static",SS_BITMAP | SS_CENTERIMAGE | SS_REALSIZEIMAGE,5,99,102,54 + CONTROL "",IDC_ARTWORK3,"Static",SS_LEFTNOWORDWRAP | WS_GROUP,115,99,72,54 + CTEXT "No artwork available for the playing item",IDC_ARTWORK2,63,116,65,18 +END + +IDD_CONNECTION DIALOGEX 0, 0, 194, 271 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + GROUPBOX "Output",IDC_STATIC,0,0,193,67 + LISTBOX IDC_OUTPUTLIST,6,12,50,46,LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_HSCROLL | WS_TABSTOP + GROUPBOX "Status",IDC_STATIC,62,8,126,33 + LTEXT "",IDC_STATUS,68,18,113,18 + CONTROL "Auto Connect",IDC_AUTOCONNECT,"Button",BS_AUTOCHECKBOX | BS_TOP | BS_MULTILINE | WS_TABSTOP,62,45,60,16 + PUSHBUTTON "Connect",IDC_CONNECT,127,45,61,16 + CONTROL "Tab1",IDC_CONTAB,"SysTabControl32",WS_TABSTOP,0,71,194,198 + GROUPBOX "",IDC_PANELRECT_C,6,90,180,171,NOT WS_VISIBLE | WS_DISABLED +END + +IDD_PANEL_DIRECTORY DIALOGEX 0, 0, 180, 175 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + GROUPBOX "Directory Configuration",IDC_STATIC,0,0,180,105 + CONTROL "Make this stream public (Recommended)",IDC_PUBLIC, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,5,12,144,10 + LTEXT "Name",IDC_NAME_HEADER,5,25,19,8 + EDITTEXT IDC_DESCRIPTION,5,33,169,14,ES_AUTOHSCROLL + LTEXT "URL",IDC_STATIC,5,50,16,8 + EDITTEXT IDC_SERVERURL,5,58,84,14,ES_AUTOHSCROLL + LTEXT "Genre",IDC_STATIC,93,50,20,8 + EDITTEXT IDC_GENRE,93,58,70,14,ES_AUTOHSCROLL | ES_READONLY + PUSHBUTTON "",IDC_GENRES,164,57,11,15,BS_ICON + LTEXT "AIM",IDC_STATIC,5,77,14,8 + EDITTEXT IDC_AIM,5,85,54,14,ES_AUTOHSCROLL + LTEXT "ICQ",IDC_STATIC,63,77,13,8 + EDITTEXT IDC_ICQ,63,85,54,14,ES_AUTOHSCROLL + LTEXT "IRC",IDC_STATIC,121,77,13,8 + EDITTEXT IDC_IRC,121,85,53,14,ES_AUTOHSCROLL + GROUPBOX "",IDC_INFO_FRAME,0,107,179,52 + CTEXT "Making the stream 'public' will instruct the server to list the stream in the Shoutcast Radio Directory. Uncheck this if this is not wanted e.g. for a internal company stream.",IDC_INFO_TEXT,4,118,172,34 +END + +IDD_PANEL_LOGIN DIALOGEX 0, 0, 180, 175 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + GROUPBOX "Output Configuration",IDC_STATIC,0,0,180,114 + LTEXT "Server Address",IDC_ADDRESS_HEADER,5,12,50,8 + EDITTEXT IDC_ADDRESS,5,22,80,14,ES_AUTOHSCROLL + LTEXT "Port",IDC_STATIC,89,12,14,8 + EDITTEXT IDC_PORT,89,22,40,14,ES_AUTOHSCROLL + LTEXT "Stream ID",IDC_STATIC,133,12,32,8 + EDITTEXT IDC_STATIONID,133,22,40,14,ES_AUTOHSCROLL + LTEXT "DJ / User ID",IDC_STATIC,5,40,40,8 + EDITTEXT IDC_USERID,5,50,60,14,ES_AUTOHSCROLL + LTEXT "Password",IDC_PASSWORD_HEADER,69,40,32,8 + EDITTEXT IDC_PASSWORD,69,50,104,14,ES_PASSWORD | ES_AUTOHSCROLL + CONTROL "Automatic reconnection on connection failure",IDC_RECONNECT, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,5,68,160,10 + LTEXT "Reconnection timeout",IDC_STATIC,5,83,73,8 + EDITTEXT IDC_TIMEOUT,80,81,28,12,ES_AUTOHSCROLL | ES_NUMBER + LTEXT "seconds",IDC_STATIC,110,83,29,8 + LTEXT "Connect using:",IDC_STATIC,5,98,50,8 + COMBOBOX IDC_PROTOCOL,59,96,114,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + GROUPBOX "",IDC_INFO_FRAME2,0,115,179,59 + CTEXT "Connecting to a v1.x server in v2.x mode will show the ""Unable To Connect To The Server"" error. To fix this error you will need to select either ""Automatic mode"" or ""v1.x mode"".",IDC_INFO_TEXT2,4,126,172,42,NOT WS_VISIBLE + CTEXT "When the DJ password is formatted as : e.g. dj_1:noise\n\nEnter in 'DJ / User ID' e.g. dj_1\nEnter in 'Password' e.g. noise",IDC_INFO_TEXT3,4,126,172,42,NOT WS_VISIBLE + CTEXT """Automatic mode"" attempts to pick the most appropriate protocol mode to connect to the server. If this does not work correctly, you can select a specific protocol mode to use.",IDC_INFO_TEXT4,4,126,172,42,NOT WS_VISIBLE +END + +IDD_NSVCONFIG DIALOGEX 0, 0, 269, 198 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "AAC+ Encoder Options" +FONT 8, "MS Shell Dlg", 0, 0, 0x0 +BEGIN + DEFPUSHBUTTON "OK",IDCANCEL,214,177,50,14 + GROUPBOX "",IDC_GO_HERE,7,7,255,167,NOT WS_VISIBLE +END + +IDD_ABOUT DIALOGEX 0, 0, 194, 271 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + GROUPBOX "About",IDC_STATIC,0,0,193,100 + ICON IDI_ICY,IDC_ABOUT_ICON,86,11,20,20,SS_NOTIFY | SS_REALSIZEIMAGE + CTEXT "",IDC_PROGRAMNAME,27,35,140,61 + RTEXT "Alternatively visit",IDC_STATIC,10,256,58,8 + CONTROL "www.shoutcast.com",IDC_ABOUTLINK,"Button",BS_OWNERDRAW | BS_CENTER | BS_VCENTER | WS_TABSTOP,71,254,68,11 + LTEXT "for updates.",IDC_STATIC,141,256,42,8 + GROUPBOX "Documentation and Support",IDC_STATIC,0,104,193,62 + LTEXT "For more information about the features available in the plug-in please look at the provided plug-in",IDC_STATIC,5,116,182,18 + CONTROL "documentation",IDC_HELPLINK,"Button",BS_OWNERDRAW | BS_CENTER | BS_TOP | WS_TABSTOP,139,123,51,11 + LTEXT "For issues with the plug-in not covered by the online documentation please visit the",IDC_STATIC,5,142,182,18 + CONTROL "Shoutcast support forum",IDC_FORUMLINK,"Button",BS_OWNERDRAW | BS_CENTER | BS_TOP | WS_TABSTOP,103,149,86,11 + GROUPBOX "Updates",IDC_UPDATE_HEADER,0,170,193,100 + LTEXT "We recommend keeping the plug-in up-to-date for bug fixes, perfomance improvements and new features.",IDC_STATIC,5,181,182,18 + PUSHBUTTON "Check for updates",IDC_GET_UPDATE,47,205,100,14 + CTEXT "A new version of the Source DSP is now available.\n\t to download and install the new version.",IDC_STATIC_UPDATE,5,226,182,18,NOT WS_VISIBLE + CONTROL "Click here",IDC_UPDATELINK,"Button",BS_OWNERDRAW | BS_CENTER | BS_TOP | NOT WS_VISIBLE | WS_TABSTOP,13,233,33,11 +END + +IDD_LOGGING DIALOGEX 0, 0, 180, 160 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + GROUPBOX "Connection Logging",IDC_STATIC,0,0,180,60 + CONTROL "Enable logging of connection status messages",IDC_LOGGING, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,5,12,163,10 + CONTROL "Clear log file on logging startup",IDC_CLEAR_ON_STARTUP, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,15,26,115,10 + PUSHBUTTON "Open log file...",IDC_VIEW_LOG,5,40,82,14 + PUSHBUTTON "Clear log file",IDC_CLEAR_LOG,91,40,82,14 + GROUPBOX "Next Track Logging",IDC_STATIC,0,64,180,60 + CONTROL "Enable next track logging",IDC_NEXT_TRACK_LOG,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,5,77,97,10 + EDITTEXT IDC_NEXT_TRACK_EDIT,15,91,142,14,ES_AUTOHSCROLL | ES_READONLY + PUSHBUTTON "...",IDC_NEXT_TRACK_BROWSE,158,91,16,14 + CONTROL "Save report as xml instead of plain text",IDC_NEXT_TRACK_XML, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,15,109,142,10 +END + +IDD_ARTWORK DIALOGEX 0, 0, 180, 175 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + GROUPBOX "Artwork",IDC_ARTWORK_V1_FRAME,0,0,179,70 + CTEXT "Stream is setup for a Shoutcast v1.x server which does not support in-stream artwork.\n\nTo send in-stream artwork, select either ""Automatic mode"" or ""v2.x mode"" and ensure you are connecting to a v2.x server.",IDC_ART_V1_NOTE,5,12,169,54,NOT WS_VISIBLE + GROUPBOX "Artwork",IDC_ARTWORK_V2_FRAME,0,0,179,136 + CONTROL "Send in-stream artwork",IDC_USE_ART,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,5,12,91,10 + CONTROL "Send artwork from the playing file (if available)\nNote: This is sent in the loaded image format",IDC_USE_ART_PLAYING, + "Button",BS_AUTOCHECKBOX | BS_TOP | BS_MULTILINE | WS_TABSTOP,15,26,160,18 + CONTROL "Send artwork for stream branding",IDC_USE_ART_STREAM, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,15,47,124,10 + EDITTEXT IDC_ART_EDIT,25,61,132,14,ES_AUTOHSCROLL | ES_READONLY + PUSHBUTTON "...",IDC_ART_BROWSE,158,61,16,14 + CTEXT "",IDC_ART_V2_NOTE,5,84,168,44 +END + +#if defined(APSTUDIO_INVOKED) || !defined(FULL) +IDD_PANEL_TITLE DIALOGEX 0, 0, 180, 175 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + GROUPBOX "Title Update Configuration",IDC_STATIC,0,0,180,109 + CONTROL "Disable title updates",IDC_NOTITLES,"Button",BS_AUTORADIOBUTTON,5,13,81,10 + CONTROL "Follow Winamp's title updates",IDC_AUTOTITLE,"Button",BS_AUTORADIOBUTTON,5,27,110,10 + CONTROL "Send next track title to the server (if available)",IDC_SENDNEXTTITLES, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,13,41,166,10 + CONTROL "Manual title updates",IDC_MANUALTITLE,"Button",BS_AUTORADIOBUTTON,5,55,81,10 + RTEXT "Now",IDC_STATIC,13,72,15,8 + EDITTEXT IDC_TITLE,33,69,98,14,ES_AUTOHSCROLL | WS_DISABLED + RTEXT "Next",IDC_STATIC,13,89,16,8 + EDITTEXT IDC_NEXT,34,88,97,14,ES_AUTOHSCROLL | WS_DISABLED + PUSHBUTTON "Send\nUpdate",IDC_SEND,135,68,40,35,BS_MULTILINE +END +#endif + +#if defined(APSTUDIO_INVOKED) || defined(FULL) +#if defined(APSTUDIO_INVOKED) +IDD_PANEL_TITLE$(FULL) DIALOGEX 0, 0, 180, 175 +#else +IDD_PANEL_TITLE DIALOGEX 0, 0, 180, 175 +#endif +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + GROUPBOX "Title Update Configuration",IDC_STATIC,0,0,180,94 + CONTROL "Follow Winamp updates",IDC_AUTOTITLE,"Button",BS_AUTORADIOBUTTON,5,13,91,10 + CONTROL "Send next track title to the server (if available)",IDC_SENDNEXTTITLES, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,13,27,166,10 + CONTROL "Manually update",IDC_MANUALTITLE,"Button",BS_AUTORADIOBUTTON,5,41,69,10 + RTEXT "Now",IDC_STATIC,13,58,15,8 + EDITTEXT IDC_TITLE,33,55,98,14,ES_AUTOHSCROLL | WS_DISABLED + RTEXT "Next",IDC_STATIC,13,75,16,8 + EDITTEXT IDC_NEXT,34,74,97,14,ES_AUTOHSCROLL | WS_DISABLED + PUSHBUTTON "Send\nUpdate",IDC_SEND,135,55,40,32,BS_MULTILINE + CONTROL "Use external file",IDC_EXTERNALTITLE,"Button",BS_AUTORADIOBUTTON | NOT WS_VISIBLE,5,92,68,10 + EDITTEXT IDC_NEXT_TRACK_EDIT,15,106,142,14,ES_AUTOHSCROLL | ES_READONLY | NOT WS_VISIBLE + CONTROL "Send when file content changes",IDC_AUTOTITLE4,"Button",BS_AUTORADIOBUTTON | NOT WS_VISIBLE,15,124,118,10 + CONTROL "Send every",IDC_AUTOTITLE5,"Button",BS_AUTORADIOBUTTON | NOT WS_VISIBLE,15,138,53,10 + LTEXT "Delay sending title update by",IDC_STATIC,15,157,94,8,NOT WS_VISIBLE + EDITTEXT IDC_TIMEOUT,68,137,32,12,ES_AUTOHSCROLL | ES_NUMBER | NOT WS_VISIBLE + CONTROL "",IDC_SPIN1,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS | UDS_NOTHOUSANDS | NOT WS_VISIBLE,98,135,11,16 + LTEXT "seconds (if changed)",IDC_STATIC,104,139,68,8,NOT WS_VISIBLE + PUSHBUTTON "...",IDC_NEXT_TRACK_BROWSE,158,106,16,14,NOT WS_VISIBLE + EDITTEXT IDC_TIMEOUT2,112,155,32,12,ES_AUTOHSCROLL | ES_NUMBER | NOT WS_VISIBLE + CONTROL "",IDC_SPIN2,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS | UDS_NOTHOUSANDS | NOT WS_VISIBLE,143,152,11,16 + LTEXT "seconds",IDC_STATIC,148,157,27,8,NOT WS_VISIBLE +END +#endif + +IDD_PANEL_LINEIN DIALOGEX 0, 0, 194, 160 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + GROUPBOX "Soundcard Mixer Control",IDC_SNDCARDSTATIC,0,0,193,156 + LTEXT "Choose Capture Device",IDC_STATIC,6,12,76,8 + COMBOBOX IDC_DEVBOX,6,24,163,30,CBS_DROPDOWNLIST | WS_TABSTOP + PUSHBUTTON "",IDC_REFRESH_DEVICES,170,23,16,15,BS_ICON + PUSHBUTTON "Open Mixer",IDC_MIXER,5,41,181,15 + RTEXT "Music Level",IDC_MUSSTATIC,10,61,46,8,SS_CENTERIMAGE + CONTROL "Slider1",IDC_MUSSLIDER,"msctls_trackbar32",TBS_AUTOTICKS | WS_TABSTOP,59,61,96,12 + LTEXT "0 dB",IDC_MUSLEV1_TEXT,159,61,24,8,SS_CENTERIMAGE + RTEXT "BG Level",IDC_BGMUSSTATIC,10,74,46,8,SS_CENTERIMAGE + CONTROL "Slider1",IDC_MUS2SLIDER,"msctls_trackbar32",TBS_AUTOTICKS | WS_TABSTOP,59,74,96,12 + LTEXT "0 dB",IDC_MUSLEV2_TEXT,159,74,25,8,SS_CENTERIMAGE + RTEXT "Mic Level",IDC_MICSTATIC,10,87,46,8,SS_CENTERIMAGE + CONTROL "Slider1",IDC_MICSLIDER,"msctls_trackbar32",TBS_AUTOTICKS | WS_TABSTOP,59,87,96,12 + LTEXT "0 dB",IDC_MICLEV_TEXT,159,87,26,8,SS_CENTERIMAGE + RTEXT "Fade Time",IDC_FADESTATIC,10,100,46,8,SS_CENTERIMAGE + CONTROL "Slider1",IDC_FADESLIDER,"msctls_trackbar32",TBS_AUTOTICKS | WS_TABSTOP,59,100,96,12 + LTEXT "0ms",IDC_FADETIME_TEXT,159,100,28,8,SS_CENTERIMAGE + RTEXT "Capture Device Fade Time",IDC_MICFADESTATIC,7,113,49,18 + CONTROL "",IDC_MICFADESLIDER,"msctls_trackbar32",TBS_AUTOTICKS | WS_TABSTOP,59,114,96,12 + LTEXT "0ms",IDC_MICFADETIME_TEXT,159,114,28,8,SS_CENTERIMAGE + PUSHBUTTON "Push to Talk",IDC_PTT,5,133,130,17 + CONTROL "Lock",IDC_LOCK,"Button",BS_AUTOCHECKBOX | BS_PUSHLIKE | WS_TABSTOP,139,133,38,17 + PUSHBUTTON "",IDC_LOCK_MODE,177,133,11,17,BS_ICON +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_DIALOG, DIALOG + BEGIN + LEFTMARGIN, 4 + END + + IDD_ENCODER, DIALOG + BEGIN + LEFTMARGIN, 5 + RIGHTMARGIN, 174 + END + + IDD_MAIN, DIALOG + BEGIN + LEFTMARGIN, 6 + RIGHTMARGIN, 188 + END + + IDD_INPUT, DIALOG + BEGIN + BOTTOMMARGIN, 256 + END + + IDD_CONNECTION, DIALOG + BEGIN + LEFTMARGIN, 6 + RIGHTMARGIN, 188 + END + + IDD_NSVCONFIG, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 262 + TOPMARGIN, 7 + BOTTOMMARGIN, 191 + END + + IDD_ABOUT, DIALOG + BEGIN + BOTTOMMARGIN, 258 + END + + IDD_LOGGING, DIALOG + BEGIN + HORZGUIDE, 74 + END +END +#endif // APSTUDIO_INVOKED + + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_ICY ICON "ICY.ICO" +IDI_DOWNARROW ICON "downarrow.ico" +IDI_REFRESH ICON "refresh.ico" +IDI_PLAY ICON "play.ico" +IDI_STOP ICON "stop.ico" +IDI_KILL ICON "kill.ico" + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 2,4,2,449 + PRODUCTVERSION 2,4,2,449 + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Radionomy SA" + VALUE "FileDescription", "Winamp DSP Plug-in" + VALUE "FileVersion", "2, 4, 2, 449" + VALUE "InternalName", "dsp_sc.dll" + VALUE "LegalCopyright", "Copyright © 2023 Radionomy SA" + VALUE "LegalTrademarks", "Shoutcast is a trademark of Radionomy SA" + VALUE "OriginalFilename", "dsp_sc.dll" + VALUE "ProductName", "Shoutcast Source DSP" + VALUE "ProductVersion", "2, 4, 2, 449" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + 65535 "{88380E65-4068-49BA-8EA4-3F2AF12D0A4F}" +END + +STRINGTABLE +BEGIN + IDS_PLUGIN_NAME "Shoutcast Source DSP v%s" + IDS_MODULE_NAME "Shoutcast Source DSP" + IDS_LAMEDLL_ISSUE "There was an error while trying to load the encoder dll file (lamedll.dll).\nPlease make sure you have the correct version required for this plug-in.\n\nThe Source DSP will now abort loading until resolved." + IDS_CURRENT_BITRATE "Current bitrate: %d kbps" + IDS_ENCODER_SETTINGS "Encoder Settings" + IDS_OUTPUT_X "Output %u" + IDS_NONE "None" + IDS_MP3_ENCODER "MP3 Encoder" + IDS_AACP_ENCODER "AAC+ Encoder" + IDS_NO_ENCODER_SELECTED "No encoder selected" + IDS_PANEL_LOGIN "Login" + IDS_PANEL_DIRECTORY "Directory" + IDS_PANEL_ENCODERS "Encoder" +END + +STRINGTABLE +BEGIN + IDS_TAB_MAIN "Summary" + IDS_TAB_OUTPUT "Output" + IDS_TAB_INPUT "Input" + IDS_COL_OUTPUT_NAME "Stream" + IDS_COL_STATUS "Status" + IDS_INPUT_WINAMP "Winamp (Recommended)" + IDS_INPUT_SOUNDCARD "Soundcard Input" + IDS_X_DB "%d dB" + IDS_INF_DB "-Inf dB" + IDS_NOT_CONNECTED "Not Connected" + IDS_ERROR "Error!" + IDS_CONNECTING "Connecting..." + IDS_SEND_CIPHER_REQUEST "Sending Cipher Request" + IDS_CIPHER_RESPONSE_RECEIVED "Cipher Response Received" + IDS_SENDING_AUTH "Sending Authorization" +END + +STRINGTABLE +BEGIN + IDS_RECEIVING_AUTH_RESPONSE "Receiving Authorization Response" + IDS_SENDING_CONTENT_TYPE "Sending Content Type" + IDS_RESPONSE_RECEIVED "Response Received" + IDS_SENDING_BITRATE "Sending Bitrate" + IDS_SEND_BUF_SIZE "Sending Buffer Size" + IDS_SEND_MAX_PAYLOAD_SIZE "Sending Maximum Payload Size" + IDS_SEND_YP_INFO "Sending YP Information" + IDS_SEND_FLUSH "Sending Flush" + IDS_SEND_STANDBY "Sending Standby" + IDS_SEND_INTRO_FILE "Sending Intro File" + IDS_SEND_BACKUP_FILE "Sending Backup File" + IDS_DISCONNECTING "Disconnecting" + IDS_RECONNECTING_X "Reconnecting [%u]" + IDS_SEND_TITLE_UPDATE "Sending Title Update" +END + +STRINGTABLE +BEGIN + IDS_CONNECT "Connect" + IDS_KILL "Kill" + IDS_ABORT "Abort" + IDS_DISCONNECT "Disconnect" + IDS_X_HZ_X "%u Hz, %s" + IDS_MONO "Mono" + IDS_STEREO "Stereo" + IDS_X_MS "%u ms" + IDS_PLUGIN_UNINSTALL "Do you also want to remove the saved settings for this plug-in?" + IDS_PASS_ERROR "Authentication Error:\nInvalid DJ / User ID or Password" +END + +STRINGTABLE +BEGIN + IDS_DEVICE_STRING "Device: %s" + IDS_MIC_LEGACY_MODE "Microphone (Legacy mode)" + IDS_LINEIN_LEGACY_MODE "Line In (Legacy mode)" + IDS_PTT_ON_STARTUP "Enable 'Push to Talk' on startup" + IDS_PANEL_LOGGING "Logs" + IDS_CIPHER_ERROR "Authentication Error:\nCipher Does Not Match" + IDS_NO_DEVICES_FOUND "No input devices found or currently available" + IDS_NO_CAPTURE_DEVICES "No available capture devices found" + IDS_ABOUT_MESSAGE "%s\nv%hs [Build %hs]\n\nBuild date: %hs\n\nCopyright © 2014 Radionomy SA\nAll Rights Reserved." + IDS_PANEL_ART "Artwork" + IDS_JPEG_FILE "JPEG File" + IDS_PNG_FILE "PNG File" + IDS_BMP_FILE "BMP File" + IDS_GIF_FILE "GIF File" +END + +STRINGTABLE +BEGIN + IDS_FHGAAC_ENCODER "ADTS-AAC Encoder" + IDS_PANEL_TITLES "Titles" + IDS_PUBLIC "Public" + IDS_PRIVATE "Private" + IDS_FOLLOW_WA "Follow Winamp" + IDS_MANUAL "Manual" + IDS_DISABLED "Disabled" + IDS_NOT_SET "Not set" + IDS_SUMMARY "Shoutcast mode: v%d\nDirectory mode: %s\nOutput server: %hs:%d\nTitle updates: %s\nEncoder mode: %s%s\nLogging enabled: %s\nAuto Connect: %s\nSave Encoded Output: %s" + IDS_LAME_ENCODER_VER "Encoder v%s" + IDS_SUMMARY_KBPS " at %d kbps" + IDS_X_DB_PEAK "(%d dB)" + IDS_CHANGE_NAME "Change Name" + IDS_SET_PASSWORD "Set Password" + IDS_SET_ENCODER "Set Encoder" + IDS_STREAMID_ERROR "Authentication Error:\nEntered Stream ID Not Allowed" +END + +STRINGTABLE +BEGIN + IDS_PARSE_ERROR "Connection Error:\nSent or Received Invalid Request" + IDS_VERSION_ERROR "Authentication Error:\nUltravox Version Not Supported" + IDS_IN_USE_ERROR "Connection Not Allowed By Server Due To Existing Source Connection" + IDS_YES "Yes" + IDS_NO "No" + IDS_NOT_CONFIGURED "Stream Configuration Not Complete" + IDS_ALL_FILES "All Files (*.*)|*.*||" + IDS_MPEG_AUDIO_FILES "MPEG Audio Files (*.%s)|*.%s||" + IDS_BITRATE_ERROR "Connection Not Allowed By Server Due To Configured Output Bitrate" + IDS_SET_SERVER "Set Server" + IDS_NOT_SET_SUMMARY "" + IDS_SENT_X "[%u:%0.2u:%0.2u] Sent %s" + IDS_B "bytes" + IDS_KIB "KiB" + IDS_MIB "MiB" + IDS_GIB "GiB" +END + +STRINGTABLE +BEGIN + IDS_TIB "TiB" + IDS_BLOCKED_ERROR "Connection Not Allowed By Server" + IDS_V2_ARTWORK "Stream artwork: %s\nPlaying artwork: %s\n\nArtwork which is over 511 KiB in size will\nnot be sent to the Shoutcast v2 server." + IDS_ENABLED_SIZE "Loaded (%s)" + IDS_EMPTY_ART "Oversized or empty" + IDS_STREAM_MOVED_ERROR "Connection Not Allowed By Server Due To Stream Having Been Moved" + IDS_ARTWORK_SIZES "Width: %dpx\nHeight: %dpx\n\nRaw size: %s\nType: %s" + IDS_FILEPATH "Filepath" + IDS_TITLE "Title" + IDS_ARTIST "Artist" + IDS_ALBUM "Album" + IDS_GENRE "Genre" + IDS_YEAR "Year" + IDS_COMMENT "Comment" +END + +STRINGTABLE +BEGIN + IDS_FAILED_LOAD_LAMEDLL "Unable to find the MP3 encoder dll (lame_enc.dll).\nPlease make sure it is installed in the following location:\n\n""%s""\n\n\nThe Shoutcast Source DSP will load in a reduced mode." + IDS_MP3_ENCODING_NOT_AVAILABLE "MP3 encoding not available" + IDS_SWITCHING_PROTOCOL "Automatically Switching Shoutcast Protocol Mode From v%d.x To v%d.x" + IDS_AUTOMATIC "Automatic mode" + IDS_V2_MODE "v2.x mode" + IDS_V1_MODE "v1.x mode (legacy servers)" + IDS_ENABLE_OTHER_MODE "Unable To Connect To The Server.\nEnable 'Automatic' or 'v%d.x' mode." + IDS_24BIT_MODE_DETECTED "Winamp appears to be configured to provide 24-bit audio via the 'Playback' preferences.\n\nThis is not supported by this plug-in and it will now abort loading.\n\nDisabling the 24-bit audio option on the 'Playback' preferences page will allow this plug-in to be used." + IDS_UPDATE_HEADER "Update: v%hs is now available" + IDS_UPDATE_TITLE "<> Plug-in Update Available <>" + IDS_TAB_ABOUT "About | Support | Updates" + IDS_UPDATE "Updates" + IDS_UPDATE_CHECK_ERROR "The Winamp install is not able to run the update check\nat this time. If this warning persists you will need to\nmanually check for an updated version of the plug-in.\n\nChoose 'Yes' to go to the Shoutcast download page." + IDS_CHECK_FOR_UPDATES "Check for updates" + IDS_CHECKING_FOR_UPDATES "Checking for updates..." + IDS_NO_NEW_UPDATE "This is the current version of the Source DSP plug-in." +END + +STRINGTABLE +BEGIN + IDS_HAS_NEW_UPDATE "A new version of the Source DSP is now available.\n\t to download and install the new version." + IDS_CHECK_UPDATE_FAIL "Unable to check for version updates at this time." +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/Src/Plugins/DSP/dsp_sc/Resource/downarrow.ico b/Src/Plugins/DSP/dsp_sc/Resource/downarrow.ico new file mode 100644 index 00000000..b3acb3f6 Binary files /dev/null and b/Src/Plugins/DSP/dsp_sc/Resource/downarrow.ico differ diff --git a/Src/Plugins/DSP/dsp_sc/Resource/kill.ico b/Src/Plugins/DSP/dsp_sc/Resource/kill.ico new file mode 100644 index 00000000..232f7d1b Binary files /dev/null and b/Src/Plugins/DSP/dsp_sc/Resource/kill.ico differ diff --git a/Src/Plugins/DSP/dsp_sc/Resource/play.ico b/Src/Plugins/DSP/dsp_sc/Resource/play.ico new file mode 100644 index 00000000..91ad0c01 Binary files /dev/null and b/Src/Plugins/DSP/dsp_sc/Resource/play.ico differ diff --git a/Src/Plugins/DSP/dsp_sc/Resource/refresh.ico b/Src/Plugins/DSP/dsp_sc/Resource/refresh.ico new file mode 100644 index 00000000..c31d7ea3 Binary files /dev/null and b/Src/Plugins/DSP/dsp_sc/Resource/refresh.ico differ diff --git a/Src/Plugins/DSP/dsp_sc/Resource/resource.h b/Src/Plugins/DSP/dsp_sc/Resource/resource.h new file mode 100644 index 00000000..0c1328cc --- /dev/null +++ b/Src/Plugins/DSP/dsp_sc/Resource/resource.h @@ -0,0 +1,327 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Script1.rc +// +#define IDS_PLUGIN_NAME 1 +#define IDS_MODULE_NAME 2 +#define IDS_LAMEDLL_ISSUE 4 +#define IDS_CURRENT_BITRATE 5 +#define IDS_ENCODER_SETTINGS 6 +#define IDS_OUTPUT_X 7 +#define IDS_NONE 8 +#define IDS_MP3_ENCODER 9 +#define IDS_AACP_ENCODER 10 +#define IDS_AAC_LC_ENCODER 11 +#define IDS_NO_ENCODER_SELECTED 12 +#define IDS_PANEL_CONNECTION 13 +#define IDS_PANEL_LOGIN 13 +#define IDS_PANEL_YELLOWPAGES 14 +#define IDS_PANEL_DIRECTORY 14 +#define IDS_PANEL_ENCODERS 15 +#define IDS_TAB_MAIN 16 +#define IDS_TAB_OUTPUT 17 +#define IDS_TAB_INPUT 18 +#define IDS_COL_OUTPUT_NAME 20 +#define IDS_COL_STATUS 21 +#define IDS_INPUT_WINAMP 22 +#define IDS_INPUT_SOUNDCARD 23 +#define IDS_X_DB 24 +#define IDS_INF_DB 25 +#define IDS_NOT_CONNECTED 26 +#define IDS_ERROR 27 +#define IDS_CONNECTING 28 +#define IDS_SEND_CIPHER_REQUEST 29 +#define IDS_CIPHER_RESPONSE_RECEIVED 30 +#define IDS_SENDING_AUTH 31 +#define IDS_RECEIVING_AUTH_RESPONSE 32 +#define IDS_SENDING_CONTENT_TYPE 33 +#define IDS_RESPONSE_RECEIVED 34 +#define IDS_SENDING_BITRATE 35 +#define IDS_SEND_BUF_SIZE 37 +#define IDS_SEND_MAX_PAYLOAD_SIZE 38 +#define IDS_SEND_YP_INFO 39 +#define IDS_SEND_FLUSH 40 +#define IDS_SEND_STANDBY 41 +#define IDS_SEND_INTRO_FILE 42 +#define IDS_SEND_BACKUP_FILE 43 +#define IDS_DISCONNECTING 44 +#define IDS_SENT_X_BYTES 45 +#define IDS_RECONNECTING_X 46 +#define IDS_SEND_TITLE_UPDATE 47 +#define IDS_CONNECT 48 +#define IDS_KILL 49 +#define IDS_ABORT 50 +#define IDS_DISCONNECT 51 +#define IDS_X_HZ_X 53 +#define IDS_MONO 54 +#define IDS_STEREO 55 +#define IDS_X_MS 56 +#define IDS_AAC_LC_CONFIG_TITLE 58 +#define IDS_STRING59 59 +#define IDS_PLUGIN_UNINSTALL 59 +#define IDS_PASS_ERROR 62 +#define IDS_OGG_ENCODER 63 +#define IDS_OGG_CONFIG_TITLE 64 +#define IDS_XP_SOUND 65 +#define IDS_STRING65 65 +#define IDS_DEVICE_STRING 65 +#define IDS_MIC_LEGACY_MODE 66 +#define IDS_LINEIN_LEGACY_MODE 67 +#define IDS_PTT_ON_STARTUP 68 +#define IDS_PANEL_LOGGING 69 +#define IDS_CIPHER_ERROR 70 +#define IDS_STRING36 71 +#define IDS_NO_DEVICES_FOUND 71 +#define IDS_NO_CAPTURE_DEVICES 72 +#define IDS_ABOUT_MESSAGE 73 +#define IDS_CIPHER_ERROR_ENABLE_V1_MODE 74 +#define IDS_PANEL_ART 75 +#define IDS_JPEG_FILE 76 +#define IDS_PNG_FILE 77 +#define IDS_BMP_FILE 78 +#define IDS_GIF_FILE 79 +#define IDS_FHGAAC_ENCODER 80 +#define IDS_PANEL_TITLES 81 +#define IDS_PUBLIC 82 +#define IDS_PRIVATE 83 +#define IDS_FOLLOW_WA 84 +#define IDS_MANUAL 85 +#define IDS_DISABLED 86 +#define IDS_NOT_SET 87 +#define IDS_SUMMARY 88 +#define IDS_LAME_ENCODER_VER 89 +#define IDS_SUMMARY_KBPS 90 +#define IDS_X_DB_PEAK 91 +#define IDS_CHANGE_NAME 92 +#define IDS_SET_PASSWORD 93 +#define IDS_SET_ENCODER 94 +#define IDS_STREAMID_ERROR 95 +#define IDS_PARSE_ERROR 96 +#define IDS_VERSION_ERROR 97 +#define IDS_IN_USE_ERROR 98 +#define IDS_YES 99 +#define IDS_YES2 100 +#define IDS_NO 100 +#define IDD_DIALOG 101 +#define IDS_NOT_CONFIGURED 101 +#define IDS_ALL_FILES 102 +#define IDD_MAIN 103 +#define IDS_MPEG_AUDIO_FILES 103 +#define IDD_CONNECTION 104 +#define IDS_BITRATE_ERROR 104 +#define IDD_ENCODER 105 +#define IDS_SET_SERVER 105 +#define IDD_INPUT 106 +#define IDS_NOT_SET_SUMMARY 106 +#define IDD_PANEL_LINEIN 107 +#define IDS_SENT_X 107 +#define IDI_ICY 108 +#define IDS_B 108 +#define IDS_KIB 109 +#define IDD_PANEL_DIRECTORY 110 +#define IDS_MiB 110 +#define IDS_MIB 110 +#define IDS_GiB 111 +#define IDS_GIB 111 +#define IDS_TIB 112 +#define IDD_NSVCONFIG 113 +#define IDS_BLOCKED_ERROR 113 +#define IDS_V2_ARTWORK 114 +#define IDB_BITMAP1 115 +#define IDS_ENABLED_SIZE 115 +#define IDS_EMPTY_ART 116 +#define IDS_STREAM_MOVED_ERROR 117 +#define IDS_ARTWORK_SIZES 120 +#define IDS_FILEPATH 121 +#define IDI_DOWNARROW 122 +#define IDS_TITLE 122 +#define IDD_LOGGING 123 +#define IDS_ARTIST 123 +#define IDI_REFRESH 124 +#define IDD_ARTWORK 124 +#define IDS_ALBUM 124 +#define IDD_PANEL_TITLE 125 +#define IDS_GENRE 125 +#define IDS_YEAR 126 +#define IDI_PLAY 127 +#define IDS_COMMENT 127 +#define IDI_STOP 128 +#define IDS_FAILED_LOAD_LAMEDLL 128 +#define IDI_KILL 129 +#define IDS_MP3_ENCODING_NOT_AVAILABLE 129 +#define IDS_SWITCHING_PROTOCOL 130 +#define IDS_AUTOMATIC 131 +#define IDD_PANEL_WINAMP 132 +#define IDS_V2_MODE 132 +#define IDD_PANEL_LOGIN 133 +#define IDS_V1_MODE 133 +#define IDD_ABOUT 134 +#define IDS_ENABLE_OTHER_MODE 134 +#define IDS_24BIT_MODE_DETECTED 135 +#define IDS_UPDATE_HEADER 136 +#define IDS_UPDATE_TITLE 137 +#define IDS_TAB_ABOUT 138 +#define IDS_UPDATE 139 +#define IDS_UPDATE_CHECK_ERROR 140 +#define IDS_CHECK_FOR_UPDATES 141 +#define IDS_CHECKING_FOR_UPDATES 142 +#define IDS_NO_NEW_UPDATE 143 +#define IDS_STRING144 144 +#define IDS_HAS_NEW_UPDATE 144 +#define IDS_CHECK_UPDATE_FAIL 145 +#define IDC_ABOUT_ICON 666 +#define IDC_TAB 1000 +#define IDC_RECT 1001 +#define IDC_OUTPUTSTATUS 1002 +#define IDC_ADDRESS 1003 +#define IDC_PORT 1004 +#define IDC_PASSWORD 1005 +#define IDC_CONNECT 1006 +#define IDC_STATUS 1007 +#define IDC_OUTPUTLIST 1008 +#define IDC_ENCODERLIST 1009 +#define IDC_ENCSETTINGS 1010 +#define IDC_ENCTYPE 1012 +#define IDC_STATIONID 1014 +#define IDC_AUTOTITLE4 1015 +#define IDC_AUTOTITLE5 1016 +#define IDC_DESCRIPTION 1017 +#define IDC_PUBLIC 1019 +#define IDC_URL 1020 +#define IDC_GENRE 1021 +#define IDC_AIM 1022 +#define IDC_ICQ 1023 +#define IDC_IRC 1024 +#define IDC_SENDNEXTTITLES 1025 +#define IDC_TITLE 1027 +#define IDC_NEXT 1028 +#define IDC_INPUTDEVICE 1031 +#define IDC_MUS2SLIDER 1032 +#define IDC_VOLUMETEXT_L 1033 +#define IDC_VOLUMEGRAPH_L 1034 +#define IDC_VOLUMETEXT_R 1035 +#define IDC_VOLUMEGRAPH_R 1036 +#define IDC_VOLUMETEXT_LP 1037 +#define IDC_VOLUMETEXT_RP 1038 +#define IDC_MUSSLIDER 1039 +#define IDC_MICSLIDER 1040 +#define IDC_MICLEV_TEXT 1041 +#define IDC_FADESLIDER 1042 +#define IDC_FADETIME_TEXT 1043 +#define IDC_MIXER 1044 +#define IDC_PTT 1045 +#define IDC_LOCK 1046 +#define IDC_MICFADESLIDER 1047 +#define IDC_MICFADETIME_TEXT 1048 +#define IDC_INPUTDEVICESTATIC 1049 +#define IDC_INPUTSETUP 1050 +#define IDC_SNDCARDSTATIC 1051 +#define IDC_MICINPUTSTATIC 1052 +#define IDC_SNDCARDSTATIC2 1052 +#define IDC_MUSSTATIC 1053 +#define IDC_BGMUSSTATIC 1054 +#define IDC_MICSTATIC 1055 +#define IDC_FADESTATIC 1056 +#define IDC_INPUTSETUPSTATIC 1057 +#define IDC_MICFADESTATIC 1057 +#define IDC_PANEL_RECT 1058 +#define IDC_RECONNECT 1060 +#define IDC_TIMEOUT 1061 +#define IDC_PANELRECT_C 1062 +#define IDC_CONTAB 1063 +#define IDC_CHECK_MINTOSYSTRAY 1064 +#define IDC_MINTOSYSTRAY 1065 +#define IDC_ENCSETTINGS_BUTTON 1066 +#define IDC_ENCSETTINGS_LABEL 1067 +#define IDC_ENCSETTINGS_LAME_VER 1068 +#define IDC_USERID 1069 +#define IDC_LOCK_MODE 1070 +#define IDC_REFRESH_DEVICES 1071 +#define IDC_ABOUTLINK 1072 +#define IDC_SEND 1073 +#define IDC_INPUTMODE 1074 +#define IDC_SUMMARY 1075 +#define IDC_DEVBOX 1076 +#define IDC_CURDEVICE 1077 +#define IDC_INFO_FRAME 1079 +#define IDC_INFO_TEXT 1080 +#define IDC_INFO_FRAME2 1081 +#define IDC_LOGGING 1081 +#define IDC_INFO_TEXT2 1082 +#define IDC_CLEAR_LOG 1082 +#define IDC_VIEW_LOG 1083 +#define IDC_INFO_TEXT3 1083 +#define IDC_CLEAR_ON_STARTUP 1084 +#define IDC_INFO_TEXT4 1084 +#define IDC_IGNORE_SENT_MESSAGE 1085 +#define IDC_NEXT_TRACK_LOG 1086 +#define IDC_NEXT_TRACK_EDIT 1087 +#define IDC_NEXT_TRACK_XML 1088 +#define IDC_NEXT_TRACK_BROWSE 1089 +#define IDC_USE_ART 1090 +#define IDC_USE_ART_PLAYING 1091 +#define IDC_USE_ART_STREAM 1092 +#define IDC_ART_BROWSE 1093 +#define IDC_ART_EDIT 1094 +#define IDC_ART_V1_NOTE 1095 +#define IDC_ART_V1_NOTE2 1096 +#define IDC_SPIN1 1097 +#define IDC_ART_V2_NOTE 1097 +#define IDC_SPIN2 1098 +#define IDC_GENRES 1099 +#define IDC_NOTITLES 1100 +#define IDC_AUTOTITLE 1101 +#define IDC_EXTERNALTITLE 1102 +#define IDC_MANUALTITLE 1103 +#define IDC_INPUT_METERS 1104 +#define IDC_STREAM_1 1105 +#define IDC_STREAM_2 1106 +#define IDC_STREAM_3 1107 +#define IDC_STREAM_4 1108 +#define IDC_STREAM_5 1109 +#define IDC_FORUMLINK 1110 +#define IDC_HELPLINK 1111 +#define IDC_GO_HERE 1112 +#define IDC_FORUMLINK2 1112 +#define IDC_GET_UPDATE 1112 +#define IDC_TIMEOUT2 1113 +#define IDC_UPDATELINK 1113 +#define IDC_AUTOCONNECT 1114 +#define IDC_AUTOURL 1115 +#define IDC_PROGRAMNAME 1116 +#define IDC_SERVERURL 1117 +#define IDC_MUSLEV1_TEXT 1118 +#define IDC_MUSLEV2_TEXT 1119 +#define IDC_SAVE_ENCODED_AUDIO 1120 +#define IDC_SAVE_ENCODED_AUDIO_EDIT 1121 +#define IDC_SAVE_ENCODED_AUDIO_BROWSE 1122 +#define IDC_INFO_FRAME3 1123 +#define IDC_INFO_FRAME4 1124 +#define IDC_INFO_FRAME5 1125 +#define IDC_ADDRESS_HEADER 1126 +#define IDC_PASSWORD_HEADER 1127 +#define IDC_NAME_HEADER 1128 +#define IDC_ENCODER_HEADER 1129 +#define IDC_INPUT_WINAMP 1130 +#define IDC_INPUT_SOUNDCARD 1131 +#define IDC_ARTWORK_V1_FRAME 1132 +#define IDC_ARTWORK_V1_FRAME2 1133 +#define IDC_ARTWORK_V2_FRAME 1133 +#define IDC_ARTWORK 1133 +#define IDC_ARTWORK2 1134 +#define IDC_ARTWORK3 1135 +#define IDC_METALIST 1136 +#define IDC_PROTOCOL 1137 +#define IDC_STATIC_UPDATE 1138 +#define IDC_UPDATE_HEADER 1139 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 135 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1140 +#define _APS_NEXT_SYMED_VALUE 110 +#endif +#endif diff --git a/Src/Plugins/DSP/dsp_sc/Resource/stop.ico b/Src/Plugins/DSP/dsp_sc/Resource/stop.ico new file mode 100644 index 00000000..6a8f1b7d Binary files /dev/null and b/Src/Plugins/DSP/dsp_sc/Resource/stop.ico differ diff --git a/Src/Plugins/DSP/dsp_sc/api.h b/Src/Plugins/DSP/dsp_sc/api.h new file mode 100644 index 00000000..d2b78cfc --- /dev/null +++ b/Src/Plugins/DSP/dsp_sc/api.h @@ -0,0 +1,36 @@ +#ifndef NULLSOFT_APIH +#define NULLSOFT_APIH + +#include +extern api_service *serviceManager; +#define WASABI_API_SVC serviceManager + +#include + +#include "../Agave/language/api_language.h" + +#include "../Agave/queue/api_queue.h" + +#include +extern api_memmgr *memmgrApi; +#define WASABI_API_MEMMGR memmgrApi + +#include +#include + +#include "../Agave/AlbumArt/api_albumart.h" + +#include "../Agave/ExplorerFindFile/api_explorerfindfile.h" + +// {B6CB4A7C-A8D0-4c55-8E60-9F7A7A23DA0F} +static const GUID playbackConfigGroupGUID = +{ 0xb6cb4a7c, 0xa8d0, 0x4c55, { 0x8e, 0x60, 0x9f, 0x7a, 0x7a, 0x23, 0xda, 0xf } }; + +#include "..\Agave/Config/api_config.h" +extern api_config *configApi; +#define AGAVE_API_CONFIG configApi + +#include "..\..\..\Components\wac_network\wac_network_http_receiver_api.h" +#include "..\..\..\Components\wac_downloadManager\wac_downloadManager_api.h" + +#endif \ No newline at end of file diff --git a/Src/Plugins/DSP/dsp_sc/crossfader/c_crossfader.cpp b/Src/Plugins/DSP/dsp_sc/crossfader/c_crossfader.cpp new file mode 100644 index 00000000..776f2c61 --- /dev/null +++ b/Src/Plugins/DSP/dsp_sc/crossfader/c_crossfader.cpp @@ -0,0 +1,189 @@ +#include "c_crossfader.h" + +C_CROSSFADER::C_CROSSFADER(int length, int nCh, int sRate) : C_DATAPUMP(length * 1 * 1) { // in milliseconds + BufferLength = 0; + srate = sRate; + nch = nCh; + crossfade = 0; + mode = 0; + SetBufferLength(length); +} + +C_CROSSFADER::~C_CROSSFADER() { + C_DATAPUMP::~C_DATAPUMP(); +} + +// protected interfaces + +void C_CROSSFADER::SampleRateConvert(int newsrate) { // in samples per second // this needs work + srate = newsrate; + SetBufferLength(BufferLength); +/* + if (BufferBottom && BufferTop) { + int newsratestep = srate != 0 ? newsrate / srate : 0; + int oldsratestep = newsrate != 0 ? srate / newsrate : 0; + if (newsratestep && oldsratestep) { + int newbuflength = (BufferLength * newsrate) / srate; + int newbufsize = (newbuflength * nch * srate * sizeof(short)) / 1000; + short *newbuf = (short *)malloc(newbufsize); + short *newbufptr = newbuf; + short *endbufptr = newbufptr+(newbufsize/sizeof(short)); + short *oldbufptr = BufferBottom; + do { + for(int i = 0; i < newsratestep; i++, newbufptr+=nch) { + if (newbufptr >= endbufptr) break; + *newbufptr = *oldbufptr; + if (nch == 2) *(newbufptr+1) = *(oldbufptr+1); + } + oldbufptr += oldsratestep * nch; + } while(newbufptr < endbufptr); + free(BufferBottom); + BufferLength = newbuflength; + BufferBottom = newbuf; + BufferTop = endbufptr; + BufferStart = BufferEnd = BufferBottom; + } + } +*/ +} + +void C_CROSSFADER::ChannelConvert(int newnch) { // this needs work + nch = newnch; + SetBufferLength(BufferLength); +/* + if (BufferBottom && BufferTop) { + int newbuflength = (BufferLength * newnch) / nch; + int newbufsize = (newbuflength * nch * srate * sizeof(short)) / 1000; + short *newbuf = (short *)malloc(newbufsize); + short *newbufptr = newbuf; + short *endbufptr = newbufptr+(newbufsize/sizeof(short)); + short *oldbufptr = BufferBottom; + for(; newbufptr < endbufptr; newbufptr+=newnch, oldbufptr+=nch) { + if (newnch == 1) *newbufptr = (*oldbufptr + *(oldbufptr+1)) >> 1; + else *(newbufptr+1) = *newbufptr = *oldbufptr; + } + free(BufferBottom); + BufferLength = newbuflength; + BufferBottom = newbuf; + BufferTop = endbufptr; + BufferStart = BufferEnd = BufferBottom; + } +*/ +} + +// human interfaces + +void C_CROSSFADER::SetSampleRate(int sRate) { // in samples per second + if (sRate != srate) { + if (srate && sRate) SampleRateConvert(sRate); + } + if (sRate) srate = sRate; +} + +void C_CROSSFADER::SetChannels(int nCh) { + if (nCh != nch) { + if (nch && nCh) ChannelConvert(nCh); + } + if (nCh) nch = nCh; +} + +void C_CROSSFADER::SetBufferLength(int bufferLength) { // in milliseconds + BufferLength = bufferLength; + resizeBuffer((BufferLength * srate * nch) / 1000); +} + +void C_CROSSFADER::SetCrossfading(int onoff) { + if (crossfade == 0) { + crossfade = onoff ? 1 : 0; + if (onoff) BufferEnd = BufferStart; + } +} + +void C_CROSSFADER::SetCrossfadeMode(int Mode) { + mode = Mode; +} + +void C_CROSSFADER::addItems(short *inputBuffer, size_t inputSize) { + if (inputBuffer && inputSize) { + size_t numsamps = inputSize*nch; + if (crossfade==0) { + memcpy(BufferEnd,inputBuffer,numsamps*sizeof(short)); // copy our records in + BufferEnd += numsamps; + } else { // do our crappy crossfade + short *smpptr = inputBuffer; + size_t bufsamps = (BufferTop-BufferBottom)/nch; + size_t bufpos = (BufferEnd >= BufferStart ? BufferEnd-BufferStart : (BufferEnd-BufferBottom)+(BufferTop-BufferStart)) / nch; + size_t dist = ((BufferTop - BufferBottom) / nch) - bufpos; + for(size_t i = 0; i != numsamps; i++) { + if (BufferEnd >= BufferTop) BufferEnd = BufferBottom + (BufferEnd-BufferTop); + if (mode == 0) { // X-style (techno mix-style) + *BufferEnd = (short)(((((double)*BufferEnd++)*dist) + (((double)*smpptr++)*bufpos)) / bufsamps); + if (nch==1 || i&1) { // every-other I or always when mono. + bufpos++; + dist--; + } + } else if (mode == 1) { // h-style (rock radio station-style) + *BufferEnd = (short)(((((double)*BufferEnd++)*dist) + ((double)*smpptr++)) / bufsamps); + if (nch==1 || i&1) dist--; // every-other I or always when mono. + } + } + if (dist < inputSize) crossfade = 0; + } + if (BufferEnd >= BufferTop) BufferEnd = BufferBottom + (BufferEnd-BufferTop); + } +} + +size_t C_CROSSFADER::put(short *inputBuffer, size_t inputSize) { // in channel-less shorts + // returns number of records added to logical buffer + size_t retval = 0; + if (inputBuffer && inputSize) { + size_t fitting = (((BufferTop-BufferBottom)-1) - size()) / nch; // can't go over our logical boundary.... blah + if (fitting > inputSize) fitting = inputSize; // the entire thing can fit. yeay! + retval = fitting; + if (fitting > 0) { + short *bufptr = inputBuffer; + size_t top = (BufferEnd >= BufferStart ? BufferTop-BufferEnd : 0) / nch; // number of records free at top of physical buffer + size_t bottom = (BufferEnd >= BufferStart ? BufferStart-BufferBottom : (BufferStart-BufferEnd)) / nch; // number of records free at bottom of physical buffer + if (top > 0) { + if (top > fitting) top = fitting; + addItems(bufptr,top); + fitting -= top; + bufptr += top*nch; + } + if (bottom > 0 && fitting > 0) { + if (bottom > fitting) bottom = fitting; + addItems(bufptr,bottom); + } + } + } + return retval; +} + +size_t C_CROSSFADER::get(short *outputBuffer, size_t outputSize, int nCh) { // in channel-less shorts + // returns number of records pulled from the logical buffer + size_t retval = 0; + nch = nCh; + if (outputBuffer && outputSize) { + size_t fitting = size() / nch; + if (fitting > outputSize) fitting = outputSize; + retval = fitting; + if (fitting > 0) { + short *bufptr = outputBuffer; + size_t top = (BufferEnd >= BufferStart ? BufferEnd-BufferStart : BufferTop-BufferStart) / nch; // number of records at top of physical buffer + size_t bottom = (BufferEnd >= BufferStart ? 0 : BufferEnd-BufferBottom) / nch; // number of records at bottom of physical buffer + if (top > 0) { + if (top > fitting) top = fitting; + getItems(bufptr,top*nch); + delItems(0,top*nch); + fitting -= top; + bufptr += top*nch; + } + if (bottom > 0 && fitting > 0) { + if (bottom > fitting) bottom = fitting; + getItems(bufptr,bottom*nch); + delItems(0,bottom*nch); + } + } + } + return retval; +} \ No newline at end of file diff --git a/Src/Plugins/DSP/dsp_sc/crossfader/c_crossfader.h b/Src/Plugins/DSP/dsp_sc/crossfader/c_crossfader.h new file mode 100644 index 00000000..7c044a9a --- /dev/null +++ b/Src/Plugins/DSP/dsp_sc/crossfader/c_crossfader.h @@ -0,0 +1,33 @@ +#ifndef __C_CROSSFADER_H__ +#define __C_CROSSFADER_H__ + +#include "../Include/c_datapump.h" + +class C_CROSSFADER : public C_DATAPUMP { +private: +protected: + int BufferLength; // in milliseconds + int srate; + int nch; + int crossfade; + int mode; + + void SampleRateConvert(int newsrate); + void ChannelConvert(int newnch); + + virtual void addItems(short *inputBuffer, size_t inputSize); // overriding the addItems() function to do crossfading and channels +public: + C_CROSSFADER(int length, int nCh, int sRate); // length is in milliseconds + virtual ~C_CROSSFADER(); + + void SetChannels(int nCh); + void SetSampleRate(int sRate); // in samples per second + void SetBufferLength(int bufferLength); // in milliseconds + void SetCrossfading(int onoff); + void SetCrossfadeMode(int Mode); // 0 = X-style, 1 = h-style + + virtual size_t put(short *inputBuffer, size_t inputSize); // in channel-less shorts + virtual size_t get(short *outputBuffer, size_t outputSize, int nCh); // in channel-less shorts +}; + +#endif // !__C_CROSSFADER_H__ \ No newline at end of file diff --git a/Src/Plugins/DSP/dsp_sc/docs/Source_DSP_Changelog.html b/Src/Plugins/DSP/dsp_sc/docs/Source_DSP_Changelog.html new file mode 100644 index 00000000..36b83184 --- /dev/null +++ b/Src/Plugins/DSP/dsp_sc/docs/Source_DSP_Changelog.html @@ -0,0 +1,367 @@ + + + + + Shoutcast Source DSP Changelog + + + +
+

Shoutcast Source DSP Changelog

+

(Last Updated 25 August 2022)

+
+
+
Contents [hide]
+ +
+ +

2.4.1 Build 444

+
+
    +
  • Built with VS2019 using Winamp 5.9-specific internal code +
  • Will only install on Winamp 5.9.0 or newer +
  • Required VS2019 runtimes installed on Win7-8.1 (minimum OS = Win7) +
  • Updated: Lame encoder v3.100.1 +
+

 

+ +

2.3.5 Build 222

+
+
    +
  • Changed to use Winamp's networking library (jnetlib) instead of the older forked version being used (this will be helpful in the future)
  • +
  • Changed to prompt if Winamp is set to use 24-bit playback mode (which we do not support in this plug-in)
  • +
  • Fixed some setups not being able to connect to a 2.x DNAS in 2.x mode due to the recent 'automatic mode' support and slow DNAS handshaking
  • +
  • Fixed the next song title not being recognised by the DNAS (expects the XML with a specific case of the element names e.g. soon, not SOON)
  • +
  • Fixed the automatic reconnect time being reset to 1 second between sessions
  • +
  • Reduced the memory usage of connections to only allocate the memory needed (this typically only saves a few KB but it's still a saving!)
  • +
  • Miscellaneous code tidyups, documentation updates, branding changes and other small related changes
  • +
+


+ +

2.3.4 Build 210

+
+
    +
  • The first Radionomy provided Shoutcast Source DSP release after the sale of Shoutcast (and Winamp) in January 2014
  • +
  • This is primarily a maintenance release to resolve issues and broadcaster requests with the 2.3.x Source DSP since the last build provided under AOL ownership
  • +
  • Added 'automatic mode' for selecting the Shoutcast protocol to use which should ease setup issues (there is still the ability to explicitly set the protocol mode like before)
  • +
  • Changed minimum required version of Winamp to v5.6+ due to building changes (below) and to simplify version compatibility and testing
  • +
  • Changed building of the plug-in to better match with the Winamp style for dependent dlls (this saves ~132KB) +
  • Changed title update handling in respect to issues related to CVE-2014-4166 (which we were not informed about before it was disclosed!)
  • +
  • Changed to allow the plug-in to load if lame_enc.dll cannot be found (related to the change below) - previously it would not load at all
  • +
  • Updated genres to the current supported list of genres (as detailed in http://forums.shoutcast.com/showthread.php?t=303241)
  • +
  • Removed lame_enc.dll from the installer - if not present in your Winamp install you will need to manually obtain a copy and place in the Shared folder of your Winamp install
  • +
  • Miscellaneous code tidyups, optimisations, adjustments for future Winamp releases, branding resource changes and other related changes
  • +
+


+ +

2.3.3 Build 201

+
+
    +
  • Added support for multiple instances of the plug-in so as not to mangle the settings (ini name is now based on the plug-in dll name)
  • +
  • Added blocking of station names with only punctuation in them from being allowed to connect (matches YP-side) and updated illegal list
  • +
  • Added displaying of the metadata and artwork currently present in Winamp when that is the selected mode on the input tab
  • +
  • Added reporting of the 'type' of the artwork on the Winamp metadata panel (generally requires Winamp 5.64+ to work correctly)
  • +
  • Added double-click to view file in explorer on the Winamp metadata panel
  • +
  • Added Winamp v5.64+ safe mode support
  • +
  • Changed how loading of the dialog is handled to try to improve it to appear on top of Winamp if set to load on startup
  • +
  • Changed selecting appropriate input fields to select all text in the field (makes it quicker to enter new port values, etc)
  • +
  • Changed handling of the UI tabs to reduced memory usage where possible
  • +
  • Changed default playing artwork to use jpeg when not able to get the raw artwork on older (<5.6) Winamp clients
  • +
  • Changed main input dialog to hide the soundcard options when in Winamp mode instead of disabling and changing the text
  • +
  • Changed the 'quit' code to better ensure everything applicable is left in a clean state (helps to improve re-opening the window without a complete unload of the plug-in
  • +
  • Fixed loading of the plug-in to not crash / lockup Winamp's UI when lame_enc.dll cannot be found
  • +
  • Fixed Winamp / Soundcard options on the summary tab not being checked on loading
  • +
  • Fixed some of the error indicator drawing appearing incorrectly after closing the window and re-opening (without a complete unload of the plug-in)
  • +
  • Fixed closing the window not working properly next time / after a few repeat closes due to memory corruption issues and not correctly stopping the broadcast threads in all scenarios
  • +
  • Miscellaneous code tidyups, size optimisations (~40kb), crash fixes, improved memory handling and other related changes to improve useability of the plug-in
  • +
+


+ +

2.3.2 Build 189

+
+
    +
  • Fixed all reproduceable issues when switching between Winamp and Soundcard mode as well as switching between the different soundcard input modes (should fix all reported crashes when switching between these modes)
  • +
  • Fixed soundcard input not being initialised correctly in all of the previous v2.3.x releases
  • +
  • Fixed the 'connect' button getting disabled when switching from the 'summary' to the 'output' tab
  • +
  • Fixed crash when playlist was cleared and Winamp was in a specific playback state leading to information could not be properly handled
  • +
  • Updated list of station names not allows for being listed in the Directory
  • +
  • Updated albumart support to get the raw playing artwork instead of the decoded artwork and having to re-encode to png when using Winamp v5.6+ (still happens on pre-5.6 or if there is an external artwork file due to the Winamp artwork api)
  • +
+


+ +

2.3.1 Build 182

+
+
    +
  • Added logging of the metadata and artwork details obtained from Winamp before sent to the server (if logging it enabled)
  • +
  • Fixed some connection stability issues when connecting to a remote server (typically happens when in-stream artwork is enabled)
  • +
  • Fixed large metadata updates (typically in-stream artwork but also could affect title updates) not sending all frames to the server
  • +
  • Fixed some rare lockups when sending metadata frames to the server
  • +
  • Fixed the 'kill' action not working or responding as expected in certain scenarios
  • +
  • Changed the 'Directory' tab to enable Name, Url and Genre options when using v2 mode and the stream is set to be public
  • +
  • Changed toggling of the in-stream artwork options to refresh the cached artwork copy when re-enabled
  • +
  • Changed log messages to filter out excessive "Cipher Response Received" messages and changed to show "Unable To Connect To The Server. Try enabling 'Shoutcast v1 mode'." if stuck at that state
  • +
  • Removed the "Ignore 'Sent X bytes' status messages" option from the logging tab (should have been removed in v2.3.0)
  • +
+


+ +

2.3.0 Build 177

+
+
    +
  • Added a number of stream configuration details onto the summary view to see what each stream is configured for without having to go to the 'output' tab
  • +
  • Added clickable buttons on the summary listview to allow for quick control of the streams
  • +
  • Added toggling of the stream playing state in the summary listview view via the space key
  • +
  • Added support for saving the encoded stream output to a specified file to allow DJ's to keep a copy of their output
  • +
  • Added options for toggling between Winamp and Soundcard mode on the summary page
  • +
  • Added tooltips to the summary listview so any clipped text can be seen
  • +
  • Added peak level indication since the DSP was started for left and right
  • +
  • Added visual info on the artwork page if artwork will be sent or not
  • +
  • Added better checking of entered values to ensure only what is supported can be entered e.g. port range limit from 1-65535
  • +
  • Added better handling of NAK errors from the DNAS in v1 and v2 mode as well as required updates from v2 protocol changes e.g. for 'Bit Rate Error' and 'Stream Moved' responses
  • +
  • Added 'red' tab text to indicate the tab which has missing or invalid information which prevents a connection from starting
  • +
  • Added some details of the connection in the logs to make it easier to see the details
  • +
  • Added handling to remember custom titles between Winamp instances
  • +
  • Changed all of the known genres to support all changes made to the official genre list at the time of release including the adding of new genres (Decades -> 00s, Folk -> Old Time, International -> German) and changing some (Jewish to Hebrew or removing any dashes)
  • +
  • Changed some of the output sub-tab names to make things more consistent
  • +
  • Changed 'user id' to be able to accept the DJ name when used in v1 mode so it will automatically convert it to the 'name:password' format as used with v1 Transcoder DJ connections
  • +
  • Changed the title options to be on their own page with some layout changes
  • +
  • Changed a failed connection to now wait up to a second before trying again to prevent hammering the server
  • +
  • Changed '[xx:xx:xx] Sent xxx bytes' to now scale from bytes to KiB to MiB to GiB
  • +
  • Changed in-stream metadata (titles and artwork) to be included in the v2 stream bytes sent total shown
  • +
  • Changed paused / stopped silence filling to keep the output bitrate the same as playing now without the prior hacks
  • +
  • Changed the 'online documentation' link to open a local copy if available
  • +
  • Changed the GUID for the plug-in's language file to now be {88380E65-4068-49BA-8EA4-3F2AF12D0A4F} due to the large number of resource changes from the previous releases
  • +
  • Changed user-agent for v1 title updates to match with the v2 metadata's TENC field version (is now "Shoutcast Source DSP x.x.xx Title Update (Mozilla)")
  • +
  • Changed to use Lame 3.99.5 (lame_enc.dll) or the most current version shipped with Winamp (makes it easier to update without a custom built lamedll.dll as previously used)
  • +
  • Changed the 'connect' button to show 'set password' or 'change name' or 'set encoder' or 'set server' when disabled to make it clearer why it's not enabled e.g. if 'unnamed server' or nothing is set for the station name or password fields
  • +
  • Changed default page to be the output page instead of summary on new installs
  • +
  • Changed encoder default to be AAC+ if possible (so it's one less thing to do on loading) and fixed MP3 to default to 96kbps stereo on clean installs
  • +
  • Changed how the dialog is loaded to resolve a part close / crash seen in a few rare cases
  • +
  • Changed the waveInReset(..) change from 2.1.3 back to the pre-2.1.3 behaviour to see if it resolves some of the crash issues reported since the change
  • +
  • Fixed a small audio loss / silence injection when a stream starts or when a title update happens
  • +
  • Fixed the title cache update to only send an update if there is an actual change (filters out quirks with streaming from another stream)
  • +
  • Fixed 'invalid password' scenarios not being correctly reported
  • +
  • Fixed stream artwork not being correctly updated after being set to an invalid / empty file
  • +
  • Fixed unusually large cipherkeys causing a crash when attempting to connect to the server
  • +
  • Fixed v2 mode doubling up the sent bytes total in some specific scenarios
  • +
  • Fixed memory leak when processing the playing album art due to not always removing the original image
  • +
  • Fixed a disconnect-connect or re-connect scenario incorrectly trying to re-send the stream artwork when not present / not enabled
  • +
  • Fixed metadata packet creation some times going over the 16384 byte limit (16371 byte payload limit)
  • +
  • Fixed artwork cleared updates being sent when not applicable
  • +
  • Fixed playing state not being correctly detected if Winamp was already playing when the DSP is loaded
  • +
  • Fixed issue causing sparodic injection of invalid data into the output buffer for encoding
  • +
  • Fixed artwork not being correctly sent after a disconnect in some scenarios
  • +
  • Fixed random crash when updating the next playing song information
  • +
  • Fixed excessive updating of the controls on the output page
  • +
  • Fixed MP3 encoder not showing all encoding options when in Winamp mode under some incorrectly inherited settings
  • +
  • Fixed v1 metadata updates potentially causing a one handle leak for each title update
  • +
  • Fixed manual titles not being sent in all cases
  • +
  • Fixed next titles being sent even if option is unchecked
  • +
  • Removed dsp_sc_enc.ini usage with all temporary encoder settings now stored in dsp_sc.ini
  • +
  • Removed default values for userid and password to force a valid value to be entered
  • +
  • Miscellaneous code tidyups, optimisations, removal of unwanted code, resource changes and other related changes to improve useability of the plug-in
  • +
+


+ +

2.2.3 Build 112 [top]

+
+
    +
  • Fixed title updates to remove characters the v2 DNAS will abort a connection on
  • +
  • Fixed DSP not starting connections if Winamp is starting minimised
  • +
  • Fixed the AAC encoder not being re-loaded if closing the dialog and re-opening without re-loading the DSP
  • +
  • Fixed some rare issues preventing the dialog from loading correctly
  • +
+


+ +

2.2.2 Build 107 [top]

+
+
    +
  • Added support for Winamp 5.62's new AAC encoder dll (Winamp now uses Fraunhofer's AAC library instead of Coding Technologies)
  • +
  • Changed the genre to be chosen from a menu (in supported situations) so only allow supported values
  • +
  • Changed MP3 default settings to be 96 kbps Stereo (meant to have been this for a while but wasn't working)
  • +
  • Changed default genre to be 'Misc' on clean installs or on loading and not matching the supported genre list
  • +
  • Changed the version string so it's more like the v1 tools (and pending DNAS / Transcoder updates)
  • +
  • Changed 'Description' to 'Name' on the Yellow Pages tab
  • +
  • Fixed the vu input meters to not show a level if there is currently no audio input instead of keeping the last value
  • +
  • Fixed issue with loading of the config dialog not showing the tabs correctly in some situations
  • +
  • Fixed sending a manual title update in v2 mode also incorrectly sending inappropriate cached title data
  • +
  • Miscellaneous code tidyups, optimisations, removal of unwanted code
  • +
+


+ +

2.2.1 Build 99 [top]

+
+
    +
  • Fixed crash on some machines when the playlist editor is empty
  • +
  • Fixed some minor localisation issues with some of the error messages
  • +
  • Fixed the installer not setting the DSP as the default DSP for some non-standard installs
  • +
  • Changed message when loading in an invalid configuration to mention DSP stackers
  • +
+


+ +

2.2.0 Build 97 [top]

+
+
    +
  • Added new 'Artwork' tab which allows for configuration of how and what artwork will be sent for Shoutcast 2 streams to a compatible Shoutcast 2 DNAS)
  • +
  • Added support of the IPC_GETNEXTLISTPOS api in Winamp 5.61+ to better determine the next song to be played even if shuffle is enabled
  • +
  • Added explict blocking of trying to load the plug-in not in Winamp to resolve loading issues and crashes due to lacking api support required
  • +
  • Added to the logs tab the option to log the next tracks to be played from the DSP in plain txt or in xml format
  • +
  • Added sending of icypub data as per Shoutcast 2 protocol specifications (only needed for the Shoutcast 2 DNAS)
  • +
  • Added 'lookahead' ini only option for determining how many next tracks from the playback queue (if available) to report (default is 3)
  • +
  • Changed all of the Shoutcast 2 packet generation to fix a number of issues like large / invalid packets, being unable to connect, unstable connections
  • +
  • Changed all of the title gathering to no long poll Winamp but instead query it directly (reduces cpu usage and inproves reliability of metadata gathering)
  • +
  • Changed all of the plug-in UI to use unicode where possible to improve localisation support
  • +
  • Changed some of the UI elements to make certain information or errors more obvious (like the 'Cipher Response' message when using the wrong Shoutcast mode)
  • +
  • Changed the 'Logging' tab to 'Logs' due to the wider range of options it now provides
  • +
  • Changed next track logging to be a per-configuration feature instead of being applied globally (as in the previous DSP release)
  • +
  • Changed to send the full title in the metadata block for the first (current) title so it follows the Shoutcast 2 specs
  • +
  • Changed the 'Send Update' button to not be enabled unless there is a title to send as well as disabling the next title option as applicable
  • +
  • Changed YellowPages tab to disable options not applicable to Shoutcast 2 mode and when running as a public server (where the details relating to 'streamauthhash' for the DNAS are used instead)
  • +
  • Changed to send a default stream id if one is not specified in Shoutcast 2 mode to improve DJ connection issues (which can fail if not specified)
  • +
  • Fixed some metadata conversions leading to crashes
  • +
  • Fixed internal utf8 conversions to prevent malformed Shoutcast 2 metadata being generated which would cause the Shoutcast 2 DNAS to block the connection
  • +
  • Fixed some of the entered stream configuration options to not accept invalid input and revert to safe defaults as applicable if this happens
  • +
  • Fixed some issues with logging initialisation leading to random lockups in some rare cases
  • +
  • Fixed memory corruption using Shoutcast 2 mode preventing 'Connection 1' being used in rare cases (mainly affected Windows 2000 / XP systems)
  • +
  • Fixed metadata not being sent if the connection to the DNAS is lost and a connection then comes back or is manually started
  • +
  • Fixed clean up of resources if unloading whilst Winamp is still running to prevent a potential crash on close or UI corruption when the plug-in is loaded again
  • +
  • Fixed when Winamp is not playing or is paused outputting blank stream data at a higher rate compared to playing leading to higher bandwidth usage then should be happening
  • +
  • Fixed the Summary page listview flickering on update
  • +
  • Fixed rare crash when Winamp is not playing and certain playlist configurations are in use when trying to find the next track title
  • +
  • Fixed to not reset the music levels if not using the soundcard input on closing
  • +
  • Fixed to not reset the Winamp level if not using the soundcard input on startup but will instead apply it on changing to soundcard input
  • +
  • Fixed playback queue lookup issues on older 5.5x clients when api_queue is not present or not correctly loaded when queried
  • +
  • Fixed the 'Send Update' option to not send cached information from Winamp's title and to not crash in rare situations
  • +
  • Fixed rare lockup issue when using the soundcard input due to the input device taking longer to reset than expected
  • +
  • Fixed refresh capture device not setting to a valid selection if the number of devices changed
  • +
  • Updated help link for the plug-in to go to the new page at http://wiki.shoutcast.com/wiki/Source_DSP_Plug-in
  • +
  • Updated installer to allow the plug-in to be set as the default DSP as well as run Winamp after completion (with the checked states remembered for next time)
  • +
  • Miscellaneous code tidyups, optimisations, removal of unwanted code and other build related changes to make this more portable at a later date
  • +
+


+ +

2.1.3 Build 42 [top]

+
+
    +
  • Added passing of metadata from the playing track (if known) to the server so it acts like sc_trans from a client connecting to the stream
  • +
  • Added an option to not log 'Status X bytes' messages (enabled by default) and improved log file handling
  • +
  • Added a refresh capture device button to help update the plug-in if connected capture devices have changed
  • +
  • Changed status info duration to be the time connected rather than a relative date time and allows for more than 24hrs to be displayed e.g. 26:48:57 instead of looping back to 03:48:57
  • +
  • Changed logging to filter 'Status X bytes' messages to only 1 second (if the option to include them is enabled)
  • +
  • Changed log files to use CR+LF linebreaks instead of just LF
  • +
  • Changed logging to remove newlines so each message is a single line to match the status info
  • +
  • Fixed crash on Vista (and potentially Windows 7) where no capture devices are being present resulting in no default capture device known
  • +
  • Fixed crash in SC2 mode when a different cipher is set in the plug-in to the server as well as indicating this error in the status info
  • +
  • Fixed button images in the 'Soundcard Mixer Control' section not appearing on all OSes
  • +
+


+ +

2.1.1 Build 36 [top]

+
+
    +
  • Added new 'Logging' tab on the Output tab to log the connection status messages
  • +
  • Added a mini dropdown next to the 'Lock' button for 'Push to Talk' to allow the mode to be automatically enabled on startup
  • +
  • Fixed plug-in to not crash when the network connection is lost
  • +
  • Fixed random plug-in crashes whilst the plug-in is streaming (mainly in SC2 mode)
  • +
  • Fixed internal plug-in uninstall not always working
  • +
  • Fixed SC2 title updates to properly work as UTF-8 and to not strip out characters incorrectly
  • +
  • Fixed next track detection to only be reported if shuffle mode is off and not to act in an undefined manner when on the last playlist item (wraps around to the start of the playlist as needed)
  • +
  • Fixed title updates to cope with the same title being played but the next song title being different
  • +
  • Changed SC2 metadata to not output <soon> and <title seq="2"> tags in the xml metadata if they are not known (when shuffle mode is enabled)
  • +
  • Changed the <TENC/> tag in the xml metadata to include the plug-in version
  • +
+


+ +

2.1.0 Build 33 [top]

+
+
    +
  • Added a separate capture device fader timeout option
  • +
  • Added copies of the plug-in documentation as an installer option
  • +
  • Added help and documentation links to the 'About' tab
  • +
  • Changed on Vista / Windows 7 to only show actually connected capture devices (requires a restart of the plug-in if connecting a new device whilst the plug-in is active)
  • +
  • Changed the 'Open Mixer' button to open to the recording devices dialog on Vista / Windows 7
  • +
  • Changed wording of the legacy mode checkbox to be clearer (hopefully) and added an info panel below to deal with the 'Cipher response message'
  • +
  • Changed capture device level to not alter the device's level unless Push to Talk is active
  • +
  • Changed the resolution on the faders from 500ms to 100ms (will re-map old settings)
  • +
  • Changed opening of help links in the plug-in to follow Winamp's style of handling
  • +
  • Fixed major issue in the plug-in leading to breaking of Winamp (and 3rd party plug-in's) COM usage
  • +
  • Fixed running of the plug-in not starting auto-connect connections when 'Input' or 'About' were the opened tab
  • +
  • Fixed capture device level not being correctly handled leading to spiking in on transitions (affected at least Windows 2000 / XP where it is all known to work)
  • +
  • Fixed capture devices source selection not being remembered
  • +
  • Fixed capture device and source levels not being set back to the non-Push to Talk level if Push to Talk is active when the plug-in is closed
  • +
  • Fixed a few localisation issues with missing items on Windows 2000 / XP
  • +
  • Fixed capture deviceRemoved tooltip from the microphone slider on the line-in page
  • +
  • Fixed some issues with the installer and uninstaller
  • +
  • Miscellaneous code changes to make some things easier to manage
  • +
+


+ +

2.0.2 Build 27 [top]

+
+
    +
  • Fixed Shoutcast 1 connection errors to a remote connection
  • +
  • Fixed authorisation error checking for Ultravox 2 & 2.1
  • +
  • More changes to the output manager to avoid out of sync states
  • +
  • Fixed timing issue which caused out of sequence Ultravox audio data frames in some scenarios
  • +
  • Fixed some localisation and tabbing order issues on the config pages
  • +
  • Removed unwanted encoder option on the Output -> Connection tab
  • +
  • Added a Shoutcast 1 mode only information prompt on how to enter the password for DJ connections
  • +
+


+ +

2.0.0 [top]

+
+
    +
  • Added Shoutcast 2 (Ultravox 2.1) support for the generated stream data
  • +
  • Cleanup and general fixes to the streaming support in the plug-in
  • +
  • Fixed settings not being saved on Vista / Windows 7
  • +
  • Fixed a number of lock-ups in the plug-in (should be more stable now)
  • +
  • Fixed plug-in to not stall if Winamp is not playing
  • +
  • Fixed a number of UI issues (tabs not showing in all cases, controls not in the correct tabbing order, theming issues, notification icon handling)
  • +
  • Config window now remembers its last position between use
  • +
  • Improved Lame encoder quality
  • +
  • Attempted to resolve standard AAC (LC-AAC) not working (additionally this is reported as audio/aacp so it will work with the YP)
  • +
  • Uses the current enc_aacplus.dll (AAC / AAC+ encoder) from the Winamp install used instead of bundling an old version from Winamp 5.1)
  • +
  • Fixed Shoutcast 1 issue with titles containing "[" & "]"
  • +
  • Changes made to improve selection of the 'microphone' device allowing for more control over the capture device used
  • +
  • Added localisation support to the plug-in (including supporting localised encoder plug-ins when showing their configurations)
  • +
  • Some other minor changes including those from the 1.9.2 beta
  • +
+


\ No newline at end of file diff --git a/Src/Plugins/DSP/dsp_sc/docs/Source_DSP_Plug-in.html b/Src/Plugins/DSP/dsp_sc/docs/Source_DSP_Plug-in.html new file mode 100644 index 00000000..bb743171 --- /dev/null +++ b/Src/Plugins/DSP/dsp_sc/docs/Source_DSP_Plug-in.html @@ -0,0 +1,326 @@ + + + + Shoutcast Source DSP Plug-in v2.4.1 + + + +
+

Shoutcast Source DSP Plug-in 2.4.1

+

(Last Updated 24 August 2022)

+
+
+
Contents [hide]
+ +
+ +

1. Introduction to the Source DSP

+
+

The aim of this document is to show you the different features offered by the Source DSP plug-in. Version 2 of the plug-in is designed to work only on Winamp 5.9 and higher due to better api usage and integration with the Winamp player. If you want to use the Source DSP in an alternative player, then it would need to support all of the required Winamp api and services which the plug-in makes use off.

+

The key feature of the plug-in is the ability to use Winamp as a source to a DNAS server or a Transcoder / AutoDJ instance or any compatible tool which accepts Shoutcast streams.

+

Additionally the plug-in will allow you to capture an audio input from the soundcard and its line-in or microphone inputs (see section 3.3.2) subject to OS and the audio system.

+


+ +

2. Getting Started [top]

+
+

To start using the Source DSP you need a configured and running DNAS server (sc_serv) or an alternative server to connect to and to have all of the login details required to connect as a source. The plug-in can be used as either a full full source or it can be used as a DJ connection in the case of being used with a compatible Transcoder / AutoDJ instance.

+


+ +

2.1. Installing the Plug-in [top]

+
+
Select Source DSP in Winamp's Preferences
+

The installer will detect the Winamp install on your machine and will then install it to the correct location. If the detected Winamp version is prior to v5.6 or if there is no winamp.exe present in the folder chosen then the installer will abort the installation.

+

Once installed, if you have not chosen to make the Source DSP the default DSP plug-in, you will need to open Winamp and go to the following location:

+
Preferences -> Plug-ins -> DSP/Effect
+

follwed by selecting the 'Nullsoft Shoutcast Source DSP' entry shown in the plug-in list.

+


+ +

3. Configuration Window [top]

+
+

The configuration window is the main interface of the plug-in and is where login details for the connection to the server can be changed or the current status viewed.

+
When the configuration window is closed then any active connections will be closed.
+If you want to hide the window then you can click use the minimise button on the
+window and click on the notification area icon added when the minimise happened.
+


+ +

3.1. Summary Tab [top]

+
+
Shoutcast Source Summary Tab
+

Status / Info : This will show information about the status of the 5 possible outputs the plug-in is able to provide going from not connected to current duration of the connection.

+
If you double-click one of the output items you will be taken to the 'Output Tab'
+(see section 3.2) where it will show the current settings for the output selected.
+

Active Input Device : This allows you to toggle between using Winamp and the configured soundcard input as well seeing the current audio capture mode. For more configuration options go to the 'Input Tab' (see section 3.3).

+

Input Levels : These show the current and peak audio level of the left and right channels as is being passed through the plug-ins core. This can aid in seeing if the input source is possibly not working or to check the audio is clipped.

+


+ +

3.2. Output Tab [top]

+
+

This tab allows you to configure the 5 separate outputs the plug-in is able to generate where the settings for the output are selected by clicking the required item in the list.

+
Shoutcast Source Output Tab showing password configuration error
+

Status : This will show the current information about the output source ranging from not being connected to error messages due to invalid passwords to running correctly.

+

Auto Connect : This will make the plug-in attempt to run this output as soon as it is started or when the option is checked if not already running when checked.

+

Connect / Abort / Disconnect / Kill Button : This allows you to start a connection, abort a connection try or kill / disconnect an active connection. If 'Auto Connect' is checked and you click this for a disconnect action then the plug-in automatically re-starts the connection.

+
If there is an issue the 'Connect / Abort / Disconnect / Kill Button' will show
+the configuration setting which is invalid e.g. 'Set Password' if the encoder
+has not been specified. The tab and the title above where the value is not
+set will have it's text changed to red to make it easier to identify.
+


+ +

3.2.1. Login Tab [top]

+
+
Shoutcast Source Output Connection Tab in v1 (Legacy) mode
+

This tab allows you to specify the details needed for connecting to a DNAS server.

+

Server Address : This is the address of the server to connect to and will depend upon the setup which is being used. If the server being connected to is on the same machine then 'localhost' can be entered, otherwise the exact IP or DNS name of the server e.g. myserver.com needs to be entered here.

+

Port : This is the port related to the 'address' of the server to connect to. This needs to match 'portbase' (DNAS Server - section 4.8) or the port value given to use.

+

Stream ID : This is the identifier used to identify the source to the server when using a Shoutcast 2 supporting setup. This needs to match 'streamid' (DNAS Server - section 4.8) or the port value given to use.

+
This is disabled if running in v1.x mode.
+

DJ / User ID : This is the user id as specified on the server for the type of connection the plug-in is being asked to make. Most likely you will be provided with a user id only if it is applicable to your setup.

+
If using a compatible v2 DNAS server then this can be entered and will
+be used as an identifier of the current DJ but it is not used for the login.
+

Password : This is the password required for accessing the server (if set on the server). This needs to match 'password' (DNAS Server - section 4.8) or the password value given to use.

+

Automatic reconnection on connection failure : This will make the plug-in attempt to connect back to the server if there is a break in the connection. +

Reconnection timeout : This is the number of seconds for the plug-in to wait in-between any connection attempts which fail before it will try again.

+
Shoutcast Source Output Connection Tab in v2 mode
+

Connect using : This controls the mode the plug-in will run as. It provides 'automatic', 'v2.x' and 'v1.x' modes with 'automatic' being the preferred mode (and the default on new installs).

+

Not setting the correct mode for the server you want to connect to will cause the connection attempt to fail or enter into what appears to be a hung state where you are likely to see a 'Unable To Connect To The Server' error if connecting in v2 mode to v1 server. If the plug-in determines this is likely to have happened then it will show the following in status area: +

Unable To Connect To The Server.
+Enable 'Automatic' or 'v1.x' mode.

+


When 'automatic' mode is enabled the information panel displayed below this option shows the following message:

+
"Automatic mode" attempts to pick the most
+appropriate protocol mode to connect to the
+server. If this does not work correctly, you
+can select a specific protocol mode to use.
+


When 'v2.x' mode is enabled the information panel displayed below this option shows the following message:

+
Connecting to a v1.x server in v2.x mode will
+show the "Unable To Connect To The Server"
+error. To fix this error you will need to select
+either "Automatic mode" or "v1.x mode".
+


When 'v1.x' mode is enabled the information panel displayed below this option shows the following message:

+
When the DJ password is formatted as
+<djlogin>:<djpassword> e.g. dj_1:noise
+    
+Enter <djlogin> in 'DJ / User ID' e.g. dj_1
+Enter <djpassword> in 'Password' e.g. noise
+


+ +

3.2.2. Directory Tab [top]

+
+
Shoutcast Source Output Directory Tab
+

This tab allows you to specify values specific to the stream for being listed or for what +is provided to listeners when they connect to the DNAS server based on the version set.

+

Make this server public (Recommended) : With this enabled, the stream is indicated as being allowed to appear in the Shoutcast Directory. This will enable options as applicable based also on the mode the plug-in is set to run as.

+

Name : This is the name you want to use for the source (often what will be used in Shoutcast Directory listing).

+

URL : This is the url for the stream allowing listeners to view or get more information.

+

Genre : This is the genre for the source and is used to categorise the stream if listed on the Shoutcast Directory listing. Select the genre from the arrow button menu. It is not possible to manually enter the genre and the input field is read-only.

+

Arrow Button : This will show a menu with known genres and sub-genres allowed for any Shoutcast Directory listings. This will only be enabled if using v1.x mode or if using v2 mode and 'Make this server public' is unchecked.

+

AIM / ICQ / IRC : These allow you to specify some contact information for clients though support of these fields is only available when using v1.x mode.

+


+ +

3.2.3. Encoder Tab [top]

+
+
Shoutcast Source Output Encoder Tab
+

This tab allows you to specify the encoder to be used to create the output stream from the input stream the plug-in gets. The following encoders are available with the plug-in:

+
MP3    (audio/mpeg)
+AAC    (audio/aacp)
+


+

The AAC (actually ADTS-AAC) encoding is provided by enc_aacplus.dll (Winamp 5.1 to 5.61) or enc_fhgaac.dll (Winamp 5.62 and up). If this is not detected in the Winamp plug-ins folder then only MP3 encoding is available.

+

Based on the encoder selected, the 'encoder settings' section will provide different options for controlling the encoder settings as either a button to open a configuration window or a dropdown list with options to choose from.

+


+ +

3.2.3.1. Save Encoded Output [top]

+
+

This allows you to make a backup of the stream audio data sent to the DNAS server.

+

Save a copy of the encoded stream audio : Enables or disables saving a copy of the audio.

+
The extension of the output file is automatically changed based on the selected
+encoder option to ensure that the file can be easily played in most media players.
+


+ +

3.2.4. Titles Tab [top]

+
+
Shoutcast Source Output Titles Tab
+

This tab allows you to specify how the stream metadata is gathered from Winamp or if it is manually entered with the options provided.

+

Disable title updates : This will prevent the Source DSP from sending any title updates.

+

Follow Winamp's title updates : This makes the Source DSP use Winamp's title updates for stream title updates, sent in the format based on the 'Connect using' setting.

+

Send next track title to : This sends the next track title to the server when using the v2 mode and if the plug-in can determine the next track.

+
The current version of Winamp is always recommended to use
+due to the improved support for this feature since Winamp v5.61.
+

Manual title updates : This will only send titles updates when 'Send Update' is pressed which uses the custom title information entered into the 'now' and 'next' fields (which are enabled as applicable to the mode in use).

+
The 'Send Update' button is enabled when a title is entered or it is
+different from the existing title. When using Shoutcast v2 mode the
+'next' title field will become available as long as title field is not empty.
+


+ +

3.2.5. Artwork Tab [top]

+
+
Shoutcast Source Output Artwork in v2 mode
+

This tab allows you to specify whether in-stream artwork will be sent to the Shoutcast server and if so the type of artwork which will be sent which can be for the station in general as well as per file artwork (much like album art display in most media players).

+

Send in-stream artwork : Enables or disables sending of in-stream artwork.

+
If this is enabled and then disabled, it is possible that the
+plug-in will send some clear artwork messages after disabling
+this option to ensure there is no artwork cached by the server.
+

Send artwork from the playing file (if available) : This sends artwork from the currently playing song to the server and acts in the same way as the album art view in most media players.

+
If unchecked or there is no artwork for the playing song then the
+DNAS server may be sent a clear artwork message as applicable.
+
+This is sent as a PNG image to the Shoutcast server.
+

Send artwork for stream branding : This will send the image as selected in the box below to the server to act as the station or stream image.

+
If left empty then the DNAS server may be sent a clear artwork message as applicable.
+


+
Shoutcast Source Output Artwork in v1 (Legacy) mode
+

Using the plug-in with a connection to a legacy server will cause the following notice to be shown:

+
Stream is setup for a Shoutcast v1.x server
+which does not support in-stream artwork.
+
+To send in-stream artwork, select either
+"Automatic mode" or "v2.x mode" and
+ensure you are connecting to a v2.x server.
+


The plug-in is only able to send in-stream artwork upto 511 KiB (523680 bytes) in size due to the Shoutcast 2 protocol specification for metadata packets. If this limit is reached then the artwork will not be sent and instead the server will get a clear artwork message. This tab page will show if the artwork cannot be used.

+

Viewing the in-stream artwork depends on native playback support of Shoutcast v2 streams in the player used by the client so without a compatible player the client will not be able to view it is as it is not available with Shoutcast v1 streams.

+


+ +

3.2.6. Logs Tab [top]

+
+
Shoutcast Source Output Logs Tab
+

This tab allows you to specify the logging options of the status messages as shown at the top of this page. Additionally it also provides the means to log the filepath of the next tracks (if known) which are going to be played by Winamp with support for logging of the track titles if using the xml output mode.

+
The main logging options are not enabled by default though this can be used
+for tracking problems with the plug-in e.g. if you are having connection issues.
+


Enable logging of connection status messages : Enables or disables connection logging.

+

Clear log file on logging startup : This will reset the log everytime the plug-in starts.

+

Open log file... : This will open the log file in the associated program for .log files.

+

Clear log file : This will clear the log file if it exists. It will not remove the file.

+

Enable next track logging : This will enable creating a log file (based on the following options) of the known next tracks to be played by Winamp.

+

Save report as xml instead of plain text : Changing this will create the log as an xml file containing filepath and title with each item identified by the 'seq' attribute.

+
The next track logging is only updated when the plug-in detects a track change.
+If the plain text / xml mode is changed or the plug-in starts then the file contents
+will be cleared until the next track title change happens.
+


+ +

3.3. Input Tab [top]

+
+ +

3.3.1. Input Configuration [top]

+
+
Shoutcast Source Input Tab in Winamp mode
+

Input Device : This allows you to choose between using Winamp or your soundcard (usually the line-in) as the input source for the output stream the plug-in makes. Depending upon the selection made additional options will be shown below.

+

Input Levels : These show the current and peak audio level of the left and right channels as is being passed through the plug-ins core. This can aid in seeing if the input source is possibly not working or to check the audio is clipped.

+

Input Settings : When the soundcard input is selected then this allows for control over the sample rate used on the input source. When the Winamp input is selected then this will show information about what's currently playing.

+


+ +

3.3.2. Soundcard Mixer Control [top]

+
+

Choose Microphone : This will allow you to choose any of the input devices reported by the OS for use with the microphone overlay mode the plug-in provides.

+

Refresh Button : This allows you to refresh the capture device list on Vista / Windows 7 (is disabled otherwise) since the plug-in was started. This is useful if you have connected a device to the machine and now want to use it.

+
Shoutcast Source Input Tab in soundcard mode
+

Open Mixer : This will open the operating systems recording and playback options (when using Windows 2000 / XP) which will allow you to change any required input and output settings for the system (though the amount you can change does depend upon the operating system being used - (see section 4.0)).

+

Music Level : This controls the Winamp output level (from no audio to full audio level).

+

BG Level : This controls the Winamp output level when the 'Push to Talk' option is active (from no audio to full audio level).

+

Mic Level : This controls the chosen microphone device's output level when the 'Push to Talk' option is active (from no audio to full audio level).

+

Fade Time : This controls the amount of time it takes for the audio to fade from the non 'Push to Talk' mode to 'Push to Talk' being the active mode in usage (from no fade i.e. instantly changes to 2.5 second fade duration).

+

Capture Device Fade Time : This controls the amount of time it takes for the selected capture device to fade from the non 'Push to Talk' mode to 'Push to Talk' being the active mode in usage (from no fade i.e. instantly changes to 2.5 second fade duration).

+

Push to Talk : When this is pressed then the chosen microphone device becomes the active input source as used by any active output streams (see section 3.2). When enabled this button will appear in an activated state.

+

Lock : When this is pressed it will toggle the 'Push to Talk' mode on or off depending on the current state of this option when it pressed. When enabled this will appear in an activated state along with the 'Push to Talk' button.

+

Arrow Button : This will show a menu with the option "Enable 'Push to Talk' on startup" allowing for the mode to be re-enabled when the plug-in is started. This may be of use as the plug-in turns off the mode and sets the system levels back to the non-pushed mode when the plug-in's window is closed.

+


+ +

3.4. About Tab [top]

+
+
Shoutcast Source About Tab
+

This tab provides information about the version of the plug-in you are using - useful for determining if you are using an older version of the plug-in or when reporting issues.

+


+ +

3.4.1. Documentation and Support [top]

+
+

This part of the tab provides links to access the available documentation and also for going to the Shoutcast support forum if issues are being experienced with the plug-in.

+

The documentation is either the current version as shipped with the plug-in if selected during install (stored in <winampdir>\Plugins\Shoutcast Source DSP) or if not found it directs you to an online copy available at http://wiki.shoutcast.com/wiki/Source_DSP_Plug-in

+

The support forum is accessed via http://forums.shoutcast.com/forumdisplay.php?f=140

+


+ +

4. Known Issues [top]

+
+

The following are currently known issue(s) to affect the currently released build of the Source DSP plug-in:

+


+ +

4.1. Soundcard Mixer Control [top]

+
+

Issue: The soundcard mixer control does not work correctly or as expected on Vista / Windows 7 especially with the handling of the selected 'microphone' device due to changes in the audio system which prevent the capture handling from Windows 2000 / XP working in the same way. Windows 2000 / XP should still work as expected.

+

Workaround: The only obvious work around is to use the features the OS provides to enable the 'Listen to this device' option via the system's recording devices feature and then mix the levels with the controls the OS provides.

+

Expected Resolution: This issue is still being investigated and hopefully a solution will be provided to allow for control of the input device in unison with the selected 'microphone' device with-in the plug-ins interface when using this mode.

+


+ +

5. Shoutcast 2 Cipher Key [top]

+
+

If you find that you do need to change the uvoxcipherkey (DNAS Server - section 4.14) in you sc_serv setup, then you can change the cipher key the DSP uses. You will only need to do this if you get the following status message when making a connection:

+
Authentication Error:
+Cipher Does Not Match
+

This is done currently via editing 'Cipherkey' entry in dsp_sc.ini in your Winamp config folder where you just need to change the string after the equal sign to the value from 'uvoxcipherkey' or 'djcipher' depending upon what you are trying to connect to.

+

The dsp_sc.ini file can usually be found by entering %appdata%\Winamp\plugins into the address bar in Windows Explorer. If it is not there then you should search for dsp_sc.ini and make sure to have the search program you are using to look for hidden files (this is just incase the OS is hiding the settings folder).

+


+ +

6. Example Configurations [top]

+
+

If you are unsure of what to enter to get the Source DSP connected to the official tools, you should look at the Source DSP Plug-in Example Configurations. This shows you where to take configuration values from the official tool configuration file(s) and where in the plug-in configuration you need to enter them for the different operating modes available.

+

For 3rd party servers or broadcast tools, you may need to consult their documentation to determine where you need to get the required configuration values from.

+ \ No newline at end of file diff --git a/Src/Plugins/DSP/dsp_sc/docs/Source_DSP_Plug-in_Config_Examples.html b/Src/Plugins/DSP/dsp_sc/docs/Source_DSP_Plug-in_Config_Examples.html new file mode 100644 index 00000000..5b8ec295 --- /dev/null +++ b/Src/Plugins/DSP/dsp_sc/docs/Source_DSP_Plug-in_Config_Examples.html @@ -0,0 +1,87 @@ + + + + Shoutcast Source DSP Plug-in Configuration Examples + + + +
+

Shoutcast Source DSP Plug-in Configuration Examples

+

(Last Updated 26 September 2014)

+
+
+
Contents [hide]
+ +
+ +

1. Introduction

+
+

The aim of this document is to show you what needs to be entered in the different options in the plug-ins configuration window to allow it to work with the DNAS server (sc_serv) configuration examples. Although this does not cover cases of connecting to other Shoutcast compatible server software or Transcoder / AutoDJ instances, it should still allow you to get broadcasting as long as you have the basic information needed.

+


+ +

2. Configurations [top]

+
+

In all of the example configurations, it is assumed that Winamp has been chosen as the input source for the stream(s) being created and that all of the passwords used are the same as those in the DNAS server example configuration files. Remember that you should change the example passwords when setting up your Shoutcast system.

+

The choice of the encoder used is left as something for you to decide upon considering the DSP plug-in supports MP3 and AAC along with all of the different bitrates, etc. However this should not cause an issue with the example setups used but is something you need to decide upon as part of the general process in setting up a Shoutcast system.

+

Finally the name of the options as shown in the english translation of the plug-in on its 'Output' tab (see Source DSP - section 3.2) will be used in this file to identify the options which need to be entered. This is mentioned incase a localised version of the DSP plug-in is used (a nice feature implemented in version 2 of the plug-in).

+
Since v2.3.4, the Source DSP can automatically try to choose
+the correct mode to connect to the DNAS server. If this does
+not work as expected then you can select the expected mode.
+


+ +

2.1. Direct Source to a Shoutcast 2.x DNAS Server [top]

+
+
Direct Source to a Shoutcast 2.x DNAS Server
+

Connection Tab

+

Server Address : localhost (or the IP of the server if it is different from the local machine [see Source DSP - section 3.2.1] ).

+

Port : 8000 (or the value set for 'portbase' [see DNAS server - section 4.8] ).

+

Stream ID : 1 (or the value set for 'streamid' for the relevant connection being made to the server [see DNAS server - section 4.12] ).

+

DJ / User ID : this can be left blank and is not used with a source connection.

+

Password : testing (or the value set for 'password' [see DNAS server - section 4.8] ).

+

Connect using: : set to 'Automatic mode' (recommended) or 'v2.x mode'.

+


+

Directory Tab

+

Here you can enter any details as required to identify or provide contact details for your stream to any clients connecting or when viewed on the Shoutcast Directory listing.

+

Make this stream public (Recommended) : The usage of this setting depends upon the value 'publicserver' in your server configuration. See DNAS server - section 4.14 for details.

+


+ +

2.2. Direct Source to a Shoutcast 1.x DNAS Server (Legacy) [top]

+
+
Direct Source to a Shoutcast 1.x DNAS Server (Legacy)
+

Connection Tab

+

Server Address : localhost (or the IP of the server if it is different from the local machine [see Source DSP - section 3.2.1] ).

+

Port : 8000 (or the value set for 'portbase' for the Shoutcast 1.x DNAS server used).

+

Password : testing (or the value set for 'password' for the Shoutcast 1.x server used).

+

Connect using: : set to 'Automatic mode' (recommended) or 'v1.x mode'.

+


+

Directory Tab

+

Here you can enter any details as required to identify or provide contact details for your stream to any clients connecting or when viewed on the Shoutcast Directory listing.

+ \ No newline at end of file diff --git a/Src/Plugins/DSP/dsp_sc/docs/res/About_tab.png b/Src/Plugins/DSP/dsp_sc/docs/res/About_tab.png new file mode 100644 index 00000000..8c8b44d2 Binary files /dev/null and b/Src/Plugins/DSP/dsp_sc/docs/res/About_tab.png differ diff --git a/Src/Plugins/DSP/dsp_sc/docs/res/Direct_Source_to_a_SHOUTcast_v1_DNAS_Server_(Legacy).png b/Src/Plugins/DSP/dsp_sc/docs/res/Direct_Source_to_a_SHOUTcast_v1_DNAS_Server_(Legacy).png new file mode 100644 index 00000000..8ba1e792 Binary files /dev/null and b/Src/Plugins/DSP/dsp_sc/docs/res/Direct_Source_to_a_SHOUTcast_v1_DNAS_Server_(Legacy).png differ diff --git a/Src/Plugins/DSP/dsp_sc/docs/res/Direct_Source_to_a_SHOUTcast_v2_DNAS_Server.png b/Src/Plugins/DSP/dsp_sc/docs/res/Direct_Source_to_a_SHOUTcast_v2_DNAS_Server.png new file mode 100644 index 00000000..4932e8a8 Binary files /dev/null and b/Src/Plugins/DSP/dsp_sc/docs/res/Direct_Source_to_a_SHOUTcast_v2_DNAS_Server.png differ diff --git a/Src/Plugins/DSP/dsp_sc/docs/res/Input_tab_soundcard_input.png b/Src/Plugins/DSP/dsp_sc/docs/res/Input_tab_soundcard_input.png new file mode 100644 index 00000000..dda9c996 Binary files /dev/null and b/Src/Plugins/DSP/dsp_sc/docs/res/Input_tab_soundcard_input.png differ diff --git a/Src/Plugins/DSP/dsp_sc/docs/res/Input_tab_winamp_input.png b/Src/Plugins/DSP/dsp_sc/docs/res/Input_tab_winamp_input.png new file mode 100644 index 00000000..13964cad Binary files /dev/null and b/Src/Plugins/DSP/dsp_sc/docs/res/Input_tab_winamp_input.png differ diff --git a/Src/Plugins/DSP/dsp_sc/docs/res/Output_tab_artwork_tab_v1_enabled.png b/Src/Plugins/DSP/dsp_sc/docs/res/Output_tab_artwork_tab_v1_enabled.png new file mode 100644 index 00000000..31cd72e9 Binary files /dev/null and b/Src/Plugins/DSP/dsp_sc/docs/res/Output_tab_artwork_tab_v1_enabled.png differ diff --git a/Src/Plugins/DSP/dsp_sc/docs/res/Output_tab_artwork_tab_v2_enabled.png b/Src/Plugins/DSP/dsp_sc/docs/res/Output_tab_artwork_tab_v2_enabled.png new file mode 100644 index 00000000..a5932275 Binary files /dev/null and b/Src/Plugins/DSP/dsp_sc/docs/res/Output_tab_artwork_tab_v2_enabled.png differ diff --git a/Src/Plugins/DSP/dsp_sc/docs/res/Output_tab_directory_tab.png b/Src/Plugins/DSP/dsp_sc/docs/res/Output_tab_directory_tab.png new file mode 100644 index 00000000..4e335adc Binary files /dev/null and b/Src/Plugins/DSP/dsp_sc/docs/res/Output_tab_directory_tab.png differ diff --git a/Src/Plugins/DSP/dsp_sc/docs/res/Output_tab_encoder_tab.png b/Src/Plugins/DSP/dsp_sc/docs/res/Output_tab_encoder_tab.png new file mode 100644 index 00000000..f332bfc9 Binary files /dev/null and b/Src/Plugins/DSP/dsp_sc/docs/res/Output_tab_encoder_tab.png differ diff --git a/Src/Plugins/DSP/dsp_sc/docs/res/Output_tab_login_tab_v1_enabled.png b/Src/Plugins/DSP/dsp_sc/docs/res/Output_tab_login_tab_v1_enabled.png new file mode 100644 index 00000000..acf0908d Binary files /dev/null and b/Src/Plugins/DSP/dsp_sc/docs/res/Output_tab_login_tab_v1_enabled.png differ diff --git a/Src/Plugins/DSP/dsp_sc/docs/res/Output_tab_login_tab_v2_enabled.png b/Src/Plugins/DSP/dsp_sc/docs/res/Output_tab_login_tab_v2_enabled.png new file mode 100644 index 00000000..40674054 Binary files /dev/null and b/Src/Plugins/DSP/dsp_sc/docs/res/Output_tab_login_tab_v2_enabled.png differ diff --git a/Src/Plugins/DSP/dsp_sc/docs/res/Output_tab_logs_tab.png b/Src/Plugins/DSP/dsp_sc/docs/res/Output_tab_logs_tab.png new file mode 100644 index 00000000..7a3f9872 Binary files /dev/null and b/Src/Plugins/DSP/dsp_sc/docs/res/Output_tab_logs_tab.png differ diff --git a/Src/Plugins/DSP/dsp_sc/docs/res/Output_tab_titles_tab.png b/Src/Plugins/DSP/dsp_sc/docs/res/Output_tab_titles_tab.png new file mode 100644 index 00000000..0707cfba Binary files /dev/null and b/Src/Plugins/DSP/dsp_sc/docs/res/Output_tab_titles_tab.png differ diff --git a/Src/Plugins/DSP/dsp_sc/docs/res/Output_tag_configuration_error.png b/Src/Plugins/DSP/dsp_sc/docs/res/Output_tag_configuration_error.png new file mode 100644 index 00000000..2c1c82a5 Binary files /dev/null and b/Src/Plugins/DSP/dsp_sc/docs/res/Output_tag_configuration_error.png differ diff --git a/Src/Plugins/DSP/dsp_sc/docs/res/Select_Source_DSP_in_Winamp.png b/Src/Plugins/DSP/dsp_sc/docs/res/Select_Source_DSP_in_Winamp.png new file mode 100644 index 00000000..f327f76e Binary files /dev/null and b/Src/Plugins/DSP/dsp_sc/docs/res/Select_Source_DSP_in_Winamp.png differ diff --git a/Src/Plugins/DSP/dsp_sc/docs/res/Summary_tab.png b/Src/Plugins/DSP/dsp_sc/docs/res/Summary_tab.png new file mode 100644 index 00000000..659f97d3 Binary files /dev/null and b/Src/Plugins/DSP/dsp_sc/docs/res/Summary_tab.png differ diff --git a/Src/Plugins/DSP/dsp_sc/dsp_sc.vcxproj b/Src/Plugins/DSP/dsp_sc/dsp_sc.vcxproj new file mode 100644 index 00000000..6e842b13 --- /dev/null +++ b/Src/Plugins/DSP/dsp_sc/dsp_sc.vcxproj @@ -0,0 +1,424 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + 17.0 + {8CC45367-89FC-439A-94B3-A8FD1EA0346A} + dsp_sc + 10.0.19041.0 + + + + DynamicLibrary + v142 + false + MultiByte + + + DynamicLibrary + v142 + false + MultiByte + + + DynamicLibrary + v142 + false + MultiByte + true + + + DynamicLibrary + v142 + false + MultiByte + true + + + + + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>17.0.32505.173 + + + $(PlatformShortName)_$(Configuration)\ + $(PlatformShortName)_$(Configuration)\ + false + $(IncludePath) + $(LibraryPath) + false + + + false + $(IncludePath) + $(LibraryPath) + false + $(PlatformShortName)_$(Configuration)\ + $(PlatformShortName)_$(Configuration)\ + + + $(PlatformShortName)_$(Configuration)\ + $(PlatformShortName)_$(Configuration)\ + false + $(IncludePath) + $(LibraryPath) + + + false + $(IncludePath) + $(LibraryPath) + $(PlatformShortName)_$(Configuration)\ + $(PlatformShortName)_$(Configuration)\ + + + false + + + Debug + x86-windows-static-md + + + x86-windows-static-md + + + x86-windows-static-md + + + x86-windows-static-md + Debug + + + + NDEBUG;%(PreprocessorDefinitions) + true + true + Win32 + .\dsp_sc___Win32_Release_LAME_DLL0/dsp_sc.tlb + + + + MinSpace + true + Size + ..\..\..;..\..\..\Wasabi;..\..\..\Winamp;%(AdditionalIncludeDirectories) + _WINDOWS;_USRDLL;USELAME;LAMEDLL_EXPORTS;WINVER=0x601;WIN32_LEAN_AND_MEAN;NDEBUG;WIN32;%(PreprocessorDefinitions) + true + MultiThreadedDLL + true + + + + None + Level3 + $(IntDir)$(TargetName).pdb + true + + + NDEBUG;%(PreprocessorDefinitions) + 0x0409 + + + comctl32.lib;winmm.lib;ws2_32.lib;shlwapi.lib;avrt.lib;uxtheme.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)$(TargetExt) + + true + uxtheme.dll;%(DelayLoadDLLs) + false + true + true + + $(IntDir)$(TargetName).lib + MachineX86 + $(IntDir)$(TargetName).pdb + $(IntDir)$(TargetName).map + + + xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ + +xcopy /Y /D /S "..\..\..\resources\libraries\lame_enc.dll" "..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Shared\" + Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\' + + + + + NDEBUG;%(PreprocessorDefinitions) + true + true + .\dsp_sc___Win32_Release_LAME_DLL0/dsp_sc.tlb + + + + + MinSpace + true + Size + ..\..\..;..\..\..\Wasabi;..\..\..\Winamp;%(AdditionalIncludeDirectories) + _WINDOWS;_USRDLL;USELAME;LAMEDLL_EXPORTS;WINVER=0x601;WIN32_LEAN_AND_MEAN;NDEBUG;WIN32;%(PreprocessorDefinitions) + true + MultiThreadedDLL + true + + + + + + + None + Level3 + $(IntDir)$(TargetName).pdb + true + + + NDEBUG;%(PreprocessorDefinitions) + 0x0409 + + + comctl32.lib;winmm.lib;ws2_32.lib;shlwapi.lib;avrt.lib;uxtheme.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)$(TargetExt) + + + true + uxtheme.dll;%(DelayLoadDLLs) + false + true + true + + + $(IntDir)$(TargetName).lib + $(IntDir)$(TargetName).pdb + $(IntDir)$(TargetName).map + + + xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ + +xcopy /Y /D /S "..\..\..\resources\libraries\lame_enc.dll" "..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Shared\" + Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\' + + + + + _DEBUG;%(PreprocessorDefinitions) + true + true + Win32 + .\dsp_sc___Win32_Debug_LAME_DLL0/dsp_sc.tlb + + + + Disabled + ..\..\..;..\..\..\Wasabi;..\..\..\Winamp;%(AdditionalIncludeDirectories) + _WINDOWS;_USRDLL;USELAME;LAMEDLL_EXPORTS;WINVER=0x601;WIN32_LEAN_AND_MEAN;_DEBUG;WIN32;%(PreprocessorDefinitions) + MultiThreadedDebugDLL + Default + + true + true + ProgramDatabase + Level3 + $(IntDir)$(TargetName).pdb + true + + + _DEBUG;%(PreprocessorDefinitions) + 0x0409 + + + comctl32.lib;winmm.lib;ws2_32.lib;shlwapi.lib;avrt.lib;uxtheme.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)$(TargetExt) + + true + + + + + uxtheme.dll;%(DelayLoadDLLs) + true + true + $(IntDir)$(TargetName).pdb + true + $(IntDir)$(TargetName).map + false + + $(IntDir)$(TargetName).lib + MachineX86 + + + xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ +xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ + +xcopy /Y /D /S "..\..\..\resources\libraries\lame_enc.dll" "..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Shared\" + Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\' + + + + + _DEBUG;%(PreprocessorDefinitions) + true + true + .\dsp_sc___Win32_Debug_LAME_DLL0/dsp_sc.tlb + + + + + Disabled + ..\..\..;..\..\..\Wasabi;..\..\..\Winamp;%(AdditionalIncludeDirectories) + _WINDOWS;_USRDLL;USELAME;LAMEDLL_EXPORTS;WINVER=0x601;WIN32_LEAN_AND_MEAN;_DEBUG;WIN32;%(PreprocessorDefinitions) + MultiThreadedDebugDLL + Default + + + true + true + ProgramDatabase + Level3 + $(IntDir)$(TargetName).pdb + true + + + _DEBUG;%(PreprocessorDefinitions) + 0x0409 + + + comctl32.lib;winmm.lib;ws2_32.lib;shlwapi.lib;avrt.lib;uxtheme.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)$(TargetExt) + + + true + + + + + uxtheme.dll;%(DelayLoadDLLs) + true + true + $(IntDir)$(TargetName).pdb + true + $(IntDir)$(TargetName).map + false + + + $(IntDir)$(TargetName).lib + + + xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ +xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ + +xcopy /Y /D /S "..\..\..\resources\libraries\lame_enc.dll" "..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Shared\" + Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\' + + + + + + + + + + + + + + + + true + true + true + true + + + true + true + true + true + + + true + true + true + true + + + + + + + + + + + + + + + + + + + + + + + + true + true + true + true + + + true + true + true + true + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Src/Plugins/DSP/dsp_sc/dsp_sc.vcxproj.filters b/Src/Plugins/DSP/dsp_sc/dsp_sc.vcxproj.filters new file mode 100644 index 00000000..bbd5d760 --- /dev/null +++ b/Src/Plugins/DSP/dsp_sc/dsp_sc.vcxproj.filters @@ -0,0 +1,188 @@ + + + + + {6b20b659-1ced-4ee8-bf03-14e1689ce5e9} + cpp;c;cxx;rc;def;r;odl;idl;hpj;bat + + + {5544d45e-4f9f-4984-9d57-266bf79aebf3} + h;hpp;hxx;hm;inl + + + {9e7d499b-1f5b-4215-845b-b8b421a57b91} + ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe + + + {5be3772d-ed9f-42e1-9a1f-a903f36e4a59} + + + {309ad66f-437f-44ee-9e2d-7bed1fe02107} + + + {6f4b5174-fe26-4264-8a5d-a509ff994842} + + + {706fd0cd-d690-4d4d-a3e8-5ab5d1a85686} + + + {f2016de0-c2f5-4f04-a642-bf341dd800c6} + + + {66c1cf32-bd88-471e-9d7d-d9a72d855483} + *.h + + + {91cc5093-54b5-4477-a50a-b6922b8e2ac0} + *.cpp + + + {f922861e-01eb-4f1c-9092-c288fbf3c1d0} + + + {9a8d5ddf-f47d-4007-9ef3-20230df8ccb5} + + + + + Source Files + + + Source Files + + + crossfader + + + sc2srclib + + + sc2srclib\Encoders\Source Files + + + sc2srclib\Encoders\Source Files + + + sc2srclib\Encoders\Source Files + + + sc2srclib\Encoders\Source Files + + + sc2srclib\Encoders\Source Files + + + sc2srclib\Encoders\Source Files + + + sc2srclib\uvAuth21 + + + Wasapi + + + Wasapi + + + Wasapi + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + crossfader + + + sc2srclib\Include + + + sc2srclib\Include + + + sc2srclib\Include + + + sc2srclib\Encoders + + + sc2srclib\Encoders\Header Files + + + sc2srclib\Encoders\Header Files + + + sc2srclib\Encoders\Header Files + + + sc2srclib\Encoders\Header Files + + + sc2srclib\Encoders\Header Files + + + sc2srclib\uvAuth21 + + + Header Files + + + Header Files + + + Header Files + + + + + Resource Files + + + + + Resource Files\Icons + + + Resource Files\Icons + + + Resource Files\Icons + + + Resource Files\Icons + + + Resource Files\Icons + + + Resource Files\Icons + + + Resource Files\Icons + + + Resource Files\Icons + + + + + sc2srclib\Encoders\Header Files + + + Wasapi + + + Wasapi + + + \ No newline at end of file diff --git a/Src/Plugins/DSP/dsp_sc/main.cpp b/Src/Plugins/DSP/dsp_sc/main.cpp new file mode 100644 index 00000000..8a3236c1 --- /dev/null +++ b/Src/Plugins/DSP/dsp_sc/main.cpp @@ -0,0 +1,5804 @@ +// These are disabled for now they have unknown issues. +//#define USE_OGG +//#define CAPTURE_TESTING + +// TODO / BE NICE FOR FUTURE VERSIONS +// 1) Fix capture issues on Vista / Windows 7 +// 2) Allow metadata to be specified from file +// 3) Move over to using enc_lame (and ui changes for it) [partial] + +#define APP_Name "Shoutcast Source" +#define APP_Version "2.4.2" +#define APP_VersionW L"2.4.2" +#define APP_Build "449" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CAPTURE_TESTING +#include "Wasapi/WASAPICapture.h" +#endif +#include "resource/resource.h" +#include "sc2srclib/include/shoutcast_output.h" +#include "sc2srclib/encoders/c_encoder_mp3dll.h" +#include "sc2srclib/encoders/c_encoder_nsv.h" +#include "sc2srclib/encoders/c_encoder_fhgaac.h" +#include "sc2srclib/encoders/c_encoder_aacp.h" +#ifdef USE_OGG +#include "sc2srclib/Encoders/c_encoder_ogg.h" +#endif +// allows us to compile with the Wasabi sdk without having to change things +//#define __WASABI_TYPES_H +//#define _GUID_H +//typedef unsigned long ARGB32; +//static const GUID INVALID_GUID = { 0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0} }; +#include "api.h" +#include "include/c_wavein.h" +#include "crossfader/c_crossfader.h" +#include +#include +#include "nu/servicebuilder.h" +#include "utils.h" +#ifdef CAPTURE_TESTING +#include "wasapi/player.h" +#endif +#include + +#define NUM_OUTPUTS 5 +#define NUM_ENCODERS NUM_OUTPUTS +#define NUM_BUFFERS 3 +#define MAX_TABWNDS 4 +#define MAX_COLS 6 +#define MAX_CELLS 12 +#define MAX_INWNDS 2 +#define MAX_OUTWNDS 6 +#define SYSTRAY_BASE_ICON 1024 +#define SYSTRAY_ICY_ICON 1 +#define SYSTRAY_BASE_MSG WM_USER +#define SYSTRAY_MAXIMIZE_MSG 27 +#define DEFAULT_INPUTDEVICE 0 // winamp + +#define DOWNLOAD_URL L"http://www.shoutcast.com/BroadcastNow" +// 404, change to one of these? +// #define DOWNLOAD_URL L"http://wiki.shoutcast.com/wiki/Source_DSP_Plug-in" +// #define DOWNLOAD_URL L"http://www.shoutcast.com" + +char sourceVersion[64] = {APP_Version "." APP_Build}; +static char szDescription[256]; +static char szDescription2[256]; +static wchar_t szDescription2W[256]; + +#ifdef CAPTURE_TESTING +static Player *pPlayer = NULL; + +// +// The Player object calls the methods in this class to +// notify the application when certain audio events occur. +// +class CPlayerCallbacks : public PlayerCallbacks +{ + // Notification callback for volume change. Typically, the user + // adjusts the volume through the SndVol.exe application. + void VolumeChangeCallback(float volume, BOOL mute) + { + /*EnableWindowDlgItem(g_hDlg, IDC_SLIDER_VOLUME, TRUE); + SetWindowText(GetDlgItem(g_hDlg, IDC_STATIC_MUTE), + (mute == TRUE) ? L"Mute" : L""); + PostMessage(GetDlgItem(g_hDlg, IDC_SLIDER_VOLUME), + TBM_SETPOS, TRUE, LPARAM(volume*MAX_VOLUME_LEVEL));*/ + }; + + // Notification callback for when stream stops playing unexpectedly + // (typically, because the player reached the end of a wave file). + void PlayerStopCallback(void) + { + /*SetActiveWindow(g_hDlg); + PostMessage(GetDlgItem(g_hDlg, IDC_BUTTON_STOP), BM_CLICK, 0, 0);*/ + }; + + // Notification callback for when the endpoint capture device is + // disconnected (for example, the user pulls out the microphone plug). + void CaptureDisconnectCallback(void) + { + /*SetWindowText(GetDlgItem(g_hDlg, IDC_STATIC_LASTACTION), + L"Capture device disconnected!"); + SendMessage(GetDlgItem(g_hDlg, IDC_COMBO_CAPTUREDEVICE), CB_RESETCONTENT, 0, 0);*/ + }; + + // Notification callback for when the endpoint rendering device is + // disconnected (for example, the user pulls out the headphones plug). + void RenderDisconnectCallback(void) + { + /*SetWindowText(GetDlgItem(g_hDlg, IDC_STATIC_LASTACTION), + L"Playback device disconnected!"); + EnableWindowDlgItem(g_hDlg, IDC_SLIDER_VOLUME, FALSE); + SetWindowTextW(GetDlgItem(g_hDlg, IDC_STATIC_MUTE), L"Disconnected"); + SendMessage(GetDlgItem(g_hDlg, IDC_COMBO_RENDERDEVICE), CB_RESETCONTENT, 0, 0); + SendMessage(GetDlgItem(g_hDlg, IDC_SLIDER_VOLUME), TBM_SETPOS, TRUE, 0);*/ + }; +}; + + +static CPlayerCallbacks *pCallbacks = NULL; +#endif + +// this is used to help determine if we're running on an older +// version of Winamp where jnetlib has issues with the re-use +// of connection handles when the connection previously failed +int iscompatibility = 0; +// used for the about page link so we don't cause win2k issues +int isthemethere = 0; +// Wasabi based services for localisation support +api_service *WASABI_API_SVC = 0; +api_config *AGAVE_API_CONFIG = 0; +api_language *WASABI_API_LNG = 0; +api_queue *WASABI_API_QUEUEMGR = 0; +api_albumart *AGAVE_API_ALBUMART = 0; +api_memmgr *WASABI_API_MEMMGR = 0; +api_explorerfindfile* WASABI_API_EXPLORERFINDFILE = 0; +api_downloadManager *WAC_API_DOWNLOADMANAGER = 0; + +// these two must be declared as they're used by the language api's +// when the system is comparing/loading the different resources +HINSTANCE WASABI_API_LNG_HINST = 0, + WASABI_API_ORIG_HINST = 0; +HFONT boldFont = 0, normalFont = 0; +HICON icy = 0, wa_icy = 0; +static HHOOK nowPlayingHook, + nowPlayingHook2; +static LPARAM nowPlayingID = -1; +// just using these to track the paused and playing states +int was_paused = 0, + was_playing = 0; +DWORD play_duration = 0, + play_diff = 0; +int isplaying = -1, ptt_load = 0; +static wchar_t lastFile[MAX_PATH]; +int lastFilterIndex = 4; +static int lastSec[NUM_OUTPUTS], + lastMode[NUM_OUTPUTS] = {-1, -1, -1, -1, -1}, + lastEnable[NUM_OUTPUTS]; + +static HWND buttonWnd[ NUM_OUTPUTS ]; +static HWND tabWnd; +static HWND outTabWnd; +static HWND updateWnd; + +static ARGB32 *playingImage; +static ARGB32 *streamImage[NUM_OUTPUTS] = {(ARGB32 *)-1, (ARGB32 *)-1, (ARGB32 *)-1, (ARGB32 *)-1, (ARGB32 *)-1}; +static int playingImage_w, playingImage_h, playingLength, playingType; +static int streamLength[NUM_OUTPUTS]; +static bool secChanged[NUM_OUTPUTS]; +void Config(struct winampDSPModule *this_mod); +int Init(struct winampDSPModule *this_mod); +int ModifySamples(struct winampDSPModule *this_mod, short int *samples, int numsamples, int bps, int nch, int srate); +void Quit(struct winampDSPModule *this_mod); +int secureFunc(int key){ + int res = key * (unsigned long)1103515245; + res += (unsigned long)13293; + res &= (unsigned long)0x7FFFFFFF; + res ^= key; + return res; +} +winampDSPModule *getModule(int which); +winampDSPModule module = { + "nullsoft(dsp_sc.dll)", + NULL, + NULL, + Config, + Init, + ModifySamples, + Quit, + NULL +}; + +winampDSPHeader header = { + DSP_HDRVER+1, + "Nullsoft " APP_Name " DSP " APP_Version, + getModule, + secureFunc +}; + + +static ARGB32 * writeImg(const ARGB32 *data, int w, int h, int *length, const wchar_t *ext) { + if (!ext || ext && !*ext) return NULL; + if (*ext == L'.') ext++; + FOURCC imgwrite = svc_imageWriter::getServiceType(); + int n = WASABI_API_SVC->service_getNumServices(imgwrite); + for (int i=0; iservice_enumService(imgwrite,i); + if (sf) { + svc_imageWriter * l = (svc_imageWriter*)sf->getInterface(); + if (l) { + if (wcsstr(l->getExtensions(),ext)) { + void* ret = l->convert(data, 32, w, h, length); + sf->releaseInterface(l); + return (ARGB32 *)ret; + } + sf->releaseInterface(l); + } + } + } + return NULL; +} + +HICON GetICYIcon(bool winamp = false) { + if (!winamp) { + if (!icy) { + icy = (HICON)LoadImage(WASABI_API_ORIG_HINST?WASABI_API_ORIG_HINST:module.hDllInstance, + MAKEINTRESOURCE(IDI_ICY), IMAGE_ICON, 0, 0, + LR_SHARED | LR_LOADTRANSPARENT | LR_CREATEDIBSECTION); + } + return icy; + } else { + if (!wa_icy) { + wa_icy = (HICON)LoadImage(GetModuleHandle("winamp.exe"), + MAKEINTRESOURCE(102), IMAGE_ICON, 0, 0, + LR_SHARED | LR_LOADTRANSPARENT | LR_CREATEDIBSECTION); + } + return (wa_icy ? wa_icy : icy); + } +} + +BOOL InitLocalisation(HWND winamp) { + // if this is valid then we should be running on Winamp 5.5+ so try to get the localisation api + if (IsWindow(winamp)) { + iscompatibility = 1; ////// SendMessage( winamp, WM_WA_IPC, 0, IPC_IS_COMPATIBILITY_ENABLED ); + isthemethere = !SendMessage(winamp, WM_WA_IPC, IPC_ISWINTHEMEPRESENT, IPC_USE_UXTHEME_FUNC); + if (!WASABI_API_LNG_HINST) { + // loader so that we can get the localisation service api for use + WASABI_API_SVC = (api_service*)SendMessage(winamp, WM_WA_IPC, 0, IPC_GET_API_SERVICE); + if (WASABI_API_SVC == (api_service*)1) { + WASABI_API_SVC = NULL; + return FALSE; + } + + // initialise all of the wasabi based services + ServiceBuild(WASABI_API_SVC, AGAVE_API_CONFIG, AgaveConfigGUID); + ServiceBuild(WASABI_API_SVC, WASABI_API_LNG, languageApiGUID); + ServiceBuild(WASABI_API_SVC, WASABI_API_MEMMGR, memMgrApiServiceGuid); + ServiceBuild(WASABI_API_SVC, AGAVE_API_ALBUMART, albumArtGUID); + ServiceBuild(WASABI_API_SVC, WASABI_API_QUEUEMGR, QueueManagerApiGUID); + ServiceBuild(WASABI_API_SVC, WASABI_API_EXPLORERFINDFILE, ExplorerFindFileApiGUID); + ServiceBuild(WASABI_API_SVC, WAC_API_DOWNLOADMANAGER, DownloadManagerGUID); + + // need to have this initialised before we try to do anything with localisation features + WASABI_API_START_LANG(GetMyInstance(), DspShoutcastLangGUID); + + // do this here so if there is no localisation support then the module names go to defaults + if (!szDescription[0]) { + StringCchPrintfA(szDescription, ARRAYSIZE(szDescription), LocalisedStringA(IDS_PLUGIN_NAME, NULL, 0), APP_Version); + header.description = szDescription; + } + + if (!szDescription2[0]) { + module.description = LocalisedStringA(IDS_MODULE_NAME, szDescription2, 256); + LocalisedString(IDS_MODULE_NAME, szDescription2W, 256); + } + } + return TRUE; + } + return FALSE; +} + +#ifdef __cplusplus +extern "C" { +#endif + __declspec(dllexport) winampDSPHeader *winampDSPGetHeader2(HWND hwndParent) { + if (InitLocalisation(hwndParent)) { + return &header; + } + MessageBoxA(module.hwndParent, + "You are attempting to use the " APP_Name " plug-in in an\n" + "unsupported version of Winamp or in a non-Winamp install or via a\n" + "DSP stacker which does not implement the Winamp 5.5+ DSP api.\n\n" + "To work this plug-in requires Winamp 5.5 and higher (the most current\n" + "release is recommended) or for the non-Winamp install or DSP stacker\n" + "to be updated to support the required Winamp api's the plug-in uses.", + "Nullsoft " APP_Name, MB_ICONEXCLAMATION|MB_APPLMODAL); + return 0; + } + + __declspec(dllexport) int winampUninstallPlugin(HINSTANCE hDllInst, HWND hwndDlg, int param) { + // this isn't ideal but it ensures that we show a localised version of the message + // if not it'll make sure that we're using the plug-in dll's internal resources + // though for ease of code handling we have to fill in some of the dsp structures + // as the plug-in has effectively been unloaded at this stage for the uninstall. + HWND winamp = GetWinampHWND(0); + module.hDllInstance = hDllInst; + module.hwndParent = winamp; + InitLocalisation(winamp); + + wchar_t title[256] = {0}; + StringCchPrintfW(title, ARRAYSIZE(title), LocalisedString(IDS_PLUGIN_NAME, NULL, 0), APP_Version); + + // prompt to remove the settings files (defaults to no just incase) + if (MessageBoxW(hwndDlg, LocalisedString(IDS_PLUGIN_UNINSTALL, NULL, 0), title, MB_YESNO|MB_DEFBUTTON2) == IDYES) { + DeleteFile(GetSCIniFile(winamp)); + } + return DSP_PLUGIN_UNINSTALL_NOW; + } + +#ifdef __cplusplus +} +#endif + +winampDSPModule *getModule(int which) { + if (which == 0) return &module; + return NULL; +} +// Client's proprietary event-context GUID +//extern GUID g_guidMyContext; + +// Maximum volume level on trackbar +#define MAX_VOL 100 + +#define SAFE_RELEASE(what) \ + if ((what) != NULL) \ +{ (what)->Release(); (what) = NULL; } + +//GUID g_guidMyContext = GUID_NULL; + +static IAudioStreamVolume *g_pStreamVol = NULL; +static IAudioEndpointVolume *g_pEndptVol = NULL; +static IAudioClient *g_pAudioClient = NULL; +static IAudioCaptureClient *g_pCaptureClient = NULL; + +/*#define EXIT_ON_ERROR(hr) \ + if (FAILED(hr)) { goto Exit; } +#define ERROR_CANCEL(hr) \ + if (FAILED(hr)) { \ + MessageBox(hDlg, TEXT("The program will exit."), \ + TEXT("Fatal error"), MB_OK); \ + EndDialog(hDlg, TRUE); return TRUE; }*/ + +HANDLE cf_mutex = NULL; +C_CROSSFADER *Crossfader = NULL; +int CrossfadeLen = 5000; +unsigned int Input_Device_ID=0; +int Restore_PTT = 0; +unsigned int numInputs=0; +char updateStr[16] = {0}; + +typedef struct { + // BladeEnc DLL Version number + BYTE byDLLMajorVersion; + BYTE byDLLMinorVersion; + // BladeEnc Engine Version Number + BYTE byMajorVersion; + BYTE byMinorVersion; + // DLL Release date + BYTE byDay; + BYTE byMonth; + WORD wYear; + // BladeEnc Homepage URL + #define BE_MAX_HOMEPAGE 128 + CHAR zHomepage[BE_MAX_HOMEPAGE + 1]; + BYTE byAlphaLevel; + BYTE byBetaLevel; + BYTE byMMXEnabled; + BYTE btReserved[125]; +} BE_VERSION, *PBE_VERSION; +typedef VOID (*BEVERSION)(PBE_VERSION); +BEVERSION beVersion = NULL; +void *init = NULL; +void *params = NULL; +void *encode = NULL; +void *finish = NULL; +void *lameclose = NULL; + +int CALLBACK DialogFunc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); +HINSTANCE instance = NULL; +HINSTANCE libinst = NULL; +HWND hMainDLG = NULL; +RECT mainrect; +HWND inWin = NULL, inWinWa = NULL; + +struct T_TABWND { + HWND hWnd; + int id; + int timer_freq; + TCITEMW tcitem; +} wnd[MAX_TABWNDS] = {0}, + out_wnd[MAX_OUTWNDS] = {0}; +int num_tabwnds = 0, + num_outwnds = 0; + +struct T_COL { + LVCOLUMNW lvcol; + LVITEMW lvitem[MAX_CELLS]; + int num_cells; +} col[MAX_COLS] = {0}; +int num_cols = 0; + +struct T_INPUTWND{ + HWND hWnd; + int id; + int timer_freq; +} in_wnd[MAX_INWNDS] = {0}; +int num_inwnds = 0; + +// shoutcast source +struct T_INPUT_CONFIG { + int srate; + int nch; +}; +struct MY_T_OUTPUT { + T_OUTPUT_CONFIG Config; + int Encoder; // encoder this config is used by + int Handle; // handle that the encoder understands + int AutoTitle; + int AutoConnect; + int Logging; + int LogCOS; + int NextTitles; + int nextTrackLog; + int nextTrackLogXML; + wchar_t nextTrackPath[MAX_PATH]; + int useArt; + int usePlayingArt; + int useStreamArt; + wchar_t stationArtPath[MAX_PATH]; + int saveEncoded; + wchar_t saveEncodedPath[MAX_PATH]; +} Output[NUM_OUTPUTS] = {0}; +SHOUTCAST_OUTPUT Encoder[NUM_ENCODERS]; +HANDLE Enc_mutex[NUM_ENCODERS] = {0}; +int Enc_LastType[NUM_ENCODERS] = {0}; +C_WAVEIN Soundcard; + +int last_buffer = 0; +int Connection_CurSelPos = 0; +int Encoder_CurSelPos = 0; +int Input_CurSelPos = 3; +int InputDevice = DEFAULT_INPUTDEVICE; +clock_t audiolag = 0; +clock_t lastaudio = 0; +int curtab = 1; +int curouttab = 0; +int lookAhead = 3; +bool skipMetada = false; +bool doNextLookAhead = false; +HANDLE hthread = NULL; +DWORD threadid = 0; +HANDLE hthreadout = NULL; +DWORD threadoutid = 0; +HWND hWinamp = NULL; +int ini_modified = 0; +HANDLE logFiles[NUM_OUTPUTS] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE}; + +struct T_VU { + int vu_l; + int vu_r; + int update; + int lastUpdate; +} VU; +int peak_vu_l = -90; +int peak_vu_r = -90; + +T_INPUT_CONFIG InputConfig; +int MusVol = 9; +int Mus2Vol = 3; +int MicVol = 9; +int FadeTime = 20; +int MicFadeTime = 10; // mimic old behaviour with a faster fade up/down of the capture device +int devopts = 0; +clock_t FadeStartTime; +clock_t MicFadeStartTime; +int FadeOut = 0; +WNDPROC prevButtonProc = NULL, + prevListViewProc = NULL, + prevHeaderProc = NULL, + prevTabWndProc = NULL, + prevOutTabWndProc = NULL; +int blockmousemove = 0; +T_INPUT_CONFIG LineInputAttribs[]= { + {22050, 1}, + {44100, 1}, + {22050, 2}, + {44100, 2}, +}; + +void AddSystrayIcon(HWND hWnd, UINT uIconId, HICON hIcon, UINT uMsg, LPWSTR lpszToolTip) { + NOTIFYICONDATAW tnid = {0}; + tnid.cbSize = sizeof (NOTIFYICONDATAW); + tnid.hWnd = hWnd; + tnid.uID = SYSTRAY_BASE_ICON + uIconId; + tnid.uFlags = NIF_ICON | NIF_TIP | NIF_MESSAGE; + tnid.uCallbackMessage = SYSTRAY_BASE_MSG + uMsg; + tnid.hIcon = hIcon; + wcsncpy(tnid.szTip, lpszToolTip, ARRAYSIZE(tnid.szTip)); + Shell_NotifyIconW(NIM_ADD, &tnid); + return; +} + +void RemoveSystrayIcon(HWND hWnd, UINT uIconId) { + NOTIFYICONDATAW tnid = {0}; + tnid.cbSize = sizeof (NOTIFYICONDATAW); + tnid.hWnd = hWnd; + tnid.uID = SYSTRAY_BASE_ICON + uIconId; + Shell_NotifyIconW(NIM_DELETE, &tnid); + return; +} + +void AddTab(int dialog_id, wchar_t *tab_name, HWND hWndParent, DLGPROC DlgProc, int tab_id, int rect_id, int timer_freq) { + RECT r = {0}; + T_TABWND *twnd = &wnd[num_tabwnds]; + GetWindowRect(GetDlgItem(hWndParent, rect_id), &r); + ScreenToClient(hWndParent, (POINT *) & r); + twnd->id = dialog_id; + twnd->timer_freq = timer_freq; + twnd->hWnd = LocalisedCreateDialog(instance, dialog_id, hWndParent, DlgProc, dialog_id); + ShowWindow(twnd->hWnd, SW_HIDE); + SetWindowPos(twnd->hWnd, NULL, r.left, r.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE); + twnd->tcitem.mask = TCIF_TEXT; + twnd->tcitem.pszText = tab_name; + twnd->tcitem.cchTextMax = wcslen(tab_name); + SendDlgItemMessage(hWndParent, tab_id, TCM_INSERTITEMW, num_tabwnds++, (LPARAM)&twnd->tcitem); + if (IsWindow(twnd->hWnd) && isthemethere) { + SendMessage(module.hwndParent, WM_WA_IPC, (WPARAM)twnd->hWnd, IPC_USE_UXTHEME_FUNC); + } +} + +void SetTab(int tabnum, HWND hWndParent, int tab_id) { + NMHDR nmh; + nmh.code = TCN_SELCHANGE; + nmh.hwndFrom = GetDlgItem(hWndParent, tab_id); + nmh.idFrom = tab_id; + curtab = tabnum; + SendMessage(nmh.hwndFrom, TCM_SETCURSEL, curtab, 0); + SendMessage(hWndParent, WM_NOTIFY, tab_id, (LPARAM) & nmh); +} + +void AddInTab(int dialog_id, wchar_t *tab_name, HWND hWndParent) { + RECT r = {0}; + T_INPUTWND *twnd = &in_wnd[num_inwnds++]; + GetWindowRect(GetDlgItem(hWndParent, IDC_PANEL_RECT), &r); + ScreenToClient(hWndParent, (POINT *)&r); + twnd->id = dialog_id; + twnd->timer_freq = 0; + twnd->hWnd = LocalisedCreateDialog(instance, dialog_id, hWndParent, DialogFunc, dialog_id); + ShowWindow(twnd->hWnd, SW_HIDE); + SetWindowPos(twnd->hWnd, NULL, r.left, r.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE); + SendDlgItemMessageW(hWndParent, IDC_INPUTDEVICE, CB_ADDSTRING, 0, (LPARAM)tab_name); + if (IsWindow(twnd->hWnd) && isthemethere) { + SendMessage(module.hwndParent, WM_WA_IPC, (WPARAM)twnd->hWnd, IPC_USE_UXTHEME_FUNC); + } +} + +void SetInTab(int tabnum, HWND hWndParent, int combo_id) { + SendDlgItemMessage(hWndParent, combo_id, CB_SETCURSEL, tabnum, 0); + SendMessage(hWndParent, WM_COMMAND, MAKEWPARAM(combo_id, CBN_SELCHANGE), (LPARAM) GetDlgItem(hWndParent, combo_id)); +} + +void AddOutTab(int dialog_id, wchar_t *tab_name, HWND hWndParent, DLGPROC DlgProc, int tab_id, int rect_id, int timer_freq) { + RECT r = {0}; + T_TABWND *twnd = &out_wnd[num_outwnds]; + GetWindowRect(GetDlgItem(hWndParent, IDC_PANELRECT_C), &r); + ScreenToClient(hWndParent, (POINT *)&r); + twnd->id = dialog_id; + twnd->timer_freq = 0; + twnd->hWnd = LocalisedCreateDialog(instance, dialog_id, hWndParent, DlgProc, dialog_id); + ShowWindow(twnd->hWnd, SW_HIDE); + SetWindowPos(twnd->hWnd, NULL, r.left, r.top, 0, 0, SWP_NOZORDER | SWP_NOSIZE); + twnd->tcitem.mask = TCIF_TEXT; + twnd->tcitem.pszText = tab_name; + twnd->tcitem.cchTextMax = wcslen(tab_name); + SendDlgItemMessageW(hWndParent, IDC_CONTAB, TCM_INSERTITEMW, num_outwnds++, (LPARAM) & twnd->tcitem); + if (IsWindow(twnd->hWnd) && isthemethere) { + SendMessage(module.hwndParent, WM_WA_IPC, (WPARAM)twnd->hWnd, IPC_USE_UXTHEME_FUNC); + } +} + +void SetOutTab(int tabnum, HWND hWndParent, int tab_id) { + NMHDR nmh; + nmh.code = TCN_SELCHANGE; + nmh.hwndFrom = GetDlgItem(hWndParent, tab_id); + nmh.idFrom = tab_id; + curouttab = tabnum; + SendMessage(nmh.hwndFrom, TCM_SETCURSEL, curouttab, 0); + SendMessage(hWndParent, WM_NOTIFY, tab_id, (LPARAM) & nmh); +} + +void AddColumn(wchar_t *column_text, HWND listView) { + T_COL *tcol = &col[num_cols]; + tcol->lvcol.mask = LVCF_TEXT; + tcol->lvcol.pszText = column_text; + tcol->lvcol.cchTextMax = wcslen(column_text); + SendMessageW(listView, LVM_INSERTCOLUMNW, num_cols++, (LPARAM)&tcol->lvcol); +} + +void AddColItem(wchar_t *cell_text, int colnum, HWND hWndParent, int list_id, int pos = -1) { + LVITEMW *tcell = &col[colnum].lvitem[col[colnum].num_cells]; + tcell->mask = TVIF_TEXT; + tcell->iItem = pos == -1 ? col[colnum].num_cells++ : pos; + tcell->iSubItem = colnum; + tcell->pszText = cell_text; + tcell->cchTextMax = wcslen(cell_text); + SendDlgItemMessageW(hWndParent, list_id, pos == -1 && colnum == 0 ? LVM_INSERTITEMW : LVM_SETITEMW, 0, (LPARAM)tcell); +} + +void __inline interleave_buffer(const short * inLeft, short *outputbuf, const size_t num_samples) { + for (size_t i = 0; i < num_samples; ++i) { + outputbuf[i * 2] = inLeft[i]; + outputbuf[i * 2 + 1] = inLeft[i]; + } +} + +DWORD WINAPI ThreadInput(LPVOID lpParameter) { + do { + // this is needed when doing soundcard capture + if (InputDevice == 1) DialogFunc((HWND) lpParameter, WM_TIMER, MAKEWPARAM(1234,0), 0); + short mybuf[32768] = {0}; + size_t mysamps = 0; + + if (WaitForSingleObject(cf_mutex, INFINITE) == WAIT_OBJECT_0) { + if (Input_CurSelPos != -1) { + if (InputDevice == 0) { + if (isplaying != 1) { + // when stopped or paused, we need to pump silent output + // and from testing, sending 2 emtpy samples appears to + // keep the output bitrate about the same as playback's + mysamps = sizeof(mybuf)/8; + } else { + mysamps = Crossfader->get(mybuf, sizeof (mybuf) / (InputConfig.nch * sizeof (short)), InputConfig.nch) * InputConfig.nch; + } + } else { + int samps = (LineInputAttribs[Input_CurSelPos].nch * sizeof (short)); + if(LineInputAttribs[Input_CurSelPos].nch == 1) { + mysamps = Crossfader->get(mybuf, (samps ? sizeof (mybuf) / samps : sizeof (mybuf)) / 2, LineInputAttribs[Input_CurSelPos].nch) * LineInputAttribs[Input_CurSelPos].nch; + short *newbuf = (short*) malloc(mysamps * 2 * sizeof(short)); + interleave_buffer(mybuf, newbuf, mysamps); + mysamps *= 2; + memcpy(mybuf, newbuf, mysamps * sizeof (short)); + free(newbuf); + } else { + mysamps = Crossfader->get(mybuf, (samps ? sizeof (mybuf) / samps : sizeof (mybuf)), LineInputAttribs[Input_CurSelPos].nch) * LineInputAttribs[Input_CurSelPos].nch; + } + } + } + ReleaseMutex(cf_mutex); + } + + if (mysamps > 0) { + short *tmp = mybuf; + for (size_t k = 0; k != NUM_ENCODERS; k++) { + if (WaitForSingleObject(Enc_mutex[k], INFINITE) == WAIT_OBJECT_0) { + size_t size = mysamps * sizeof (short); + short * newbuf = (short*) malloc(size); + if (newbuf) { + memcpy(newbuf, tmp, size); + Encoder[k].Run(OM_ENCODE, newbuf, size, k); // this seems to be modifying newbuf + free(newbuf); + } + ReleaseMutex(Enc_mutex[k]); + } + } + } + Sleep(25); + } while (hthread != NULL); + return 0; +} + +DWORD WINAPI ThreadOutput(LPVOID lpParameter) { + do { + for (int k = 0; k < NUM_ENCODERS; k++) { + if (WaitForSingleObject(Enc_mutex[k], 25) == WAIT_OBJECT_0) { + if (Encoder[k].GetEncoder()) { + Encoder[k].Run(OM_OUTPUT | OM_OTHER); + } + ReleaseMutex(Enc_mutex[k]); + } + } + Sleep(25); + } while (hthreadout != NULL); + return 0; +} + +char olddev[256] = {0}; +int DisplayDeviceName(void) { + if (InputDevice) { + char deviceBuf[256] = {0}; + char *deviceName = Soundcard.getDeviceName(0); + + if (deviceName && *deviceName) { + char tmp[128] = {0}; + StringCchPrintfA(deviceBuf, ARRAYSIZE(deviceBuf), WASABI_API_LNGSTRING_BUF(IDS_DEVICE_STRING, tmp, 128), deviceName); + } else { + WASABI_API_LNGSTRING_BUF(IDS_NO_DEVICES_FOUND, deviceBuf, ARRAYSIZE(deviceBuf)); + olddev[0] = 0; + } + SendDlgItemMessage(wnd[2].hWnd, IDC_CURDEVICE, WM_SETTEXT, 0,(LPARAM)deviceBuf); + + // vista - 7 check for default device and restart soundcard if needed + if (IsVistaUp()) { + if (strcmp(deviceBuf, olddev) != 0) { + lstrcpyn(olddev, deviceBuf, ARRAYSIZE(olddev)); + SuspendThread(hthread); + Soundcard.Close(); + Soundcard.Create((InputDevice == 0 ? InputConfig.srate : LineInputAttribs[Input_CurSelPos].srate), (InputDevice == 0 ? InputConfig.nch : LineInputAttribs[Input_CurSelPos].nch)); + ResumeThread(hthread); + ini_modified = 1; + } + } + } else { + SendDlgItemMessage(wnd[2].hWnd, IDC_CURDEVICE, WM_SETTEXT, 0,(LPARAM)""); + } + + return 1; +} + +#ifdef CAPTURE_TESTING +//----------------------------------------------------------- +// The input argument to this function is a pointer to the +// IMMDevice interface for a capture endpoint device. The +// function traverses the data path that extends from the +// endpoint device to the system bus (for example, PCI) +// or external bus (USB). If the function discovers a MUX +// (input selector) in the path, it selects the MUX input +// that connects to the stream from the endpoint device. +//----------------------------------------------------------- +#define EXIT_ON_ERROR(hres) \ + if (FAILED(hres)) { goto Exit; } +#define SAFE_RELEASE(punk) \ + if ((punk) != NULL) \ + { (punk)->Release(); (punk) = NULL; } + +const IID IID_IDeviceTopology = __uuidof(IDeviceTopology); +const IID IID_IPart = __uuidof(IPart); +const IID IID_IConnector = __uuidof(IConnector); +const IID IID_IAudioInputSelector = __uuidof(IAudioInputSelector); + +HRESULT SelectCaptureDevice(IMMDevice *pEndptDev) +{ + HRESULT hr = S_OK; + DataFlow flow; + IDeviceTopology *pDeviceTopology = NULL; + IConnector *pConnFrom = NULL; + IConnector *pConnTo = NULL; + IPart *pPartPrev = NULL; + IPart *pPartNext = NULL; + IAudioInputSelector *pSelector = NULL; + + if (pEndptDev == NULL) + { + EXIT_ON_ERROR(hr = E_POINTER) + } + + // Get the endpoint device's IDeviceTopology interface. + hr = pEndptDev->Activate( + IID_IDeviceTopology, CLSCTX_ALL, NULL, + (void**)&pDeviceTopology); + EXIT_ON_ERROR(hr) + + // The device topology for an endpoint device always + // contains just one connector (connector number 0). + hr = pDeviceTopology->GetConnector(0, &pConnFrom); + SAFE_RELEASE(pDeviceTopology) + EXIT_ON_ERROR(hr) + + // Make sure that this is a capture device. + hr = pConnFrom->GetDataFlow(&flow); + EXIT_ON_ERROR(hr) + + if (flow != Out) + { + // Error -- this is a rendering device. + EXIT_ON_ERROR(hr = AUDCLNT_E_WRONG_ENDPOINT_TYPE) + } + + // Outer loop: Each iteration traverses the data path + // through a device topology starting at the input + // connector and ending at the output connector. + while (TRUE) + { + BOOL bConnected; + hr = pConnFrom->IsConnected(&bConnected); + EXIT_ON_ERROR(hr) + + // Does this connector connect to another device? + if (bConnected == FALSE) + { + // This is the end of the data path that + // stretches from the endpoint device to the + // system bus or external bus. Verify that + // the connection type is Software_IO. + ConnectorType connType; + hr = pConnFrom->GetType(&connType); + EXIT_ON_ERROR(hr) + + if (connType == Software_IO) + { + break; // finished + } + EXIT_ON_ERROR(hr = E_FAIL) + } + + // Get the connector in the next device topology, + // which lies on the other side of the connection. + hr = pConnFrom->GetConnectedTo(&pConnTo); + EXIT_ON_ERROR(hr) + SAFE_RELEASE(pConnFrom) + + // Get the connector's IPart interface. + hr = pConnTo->QueryInterface( + IID_IPart, (void**)&pPartPrev); + EXIT_ON_ERROR(hr) + SAFE_RELEASE(pConnTo) + + // Inner loop: Each iteration traverses one link in a + // device topology and looks for input multiplexers. + while (TRUE) + { + PartType parttype; + UINT localId; + IPartsList *pParts; + + // Follow downstream link to next part. + hr = pPartPrev->EnumPartsOutgoing(&pParts); + EXIT_ON_ERROR(hr) + + hr = pParts->GetPart(0, &pPartNext); + pParts->Release(); + EXIT_ON_ERROR(hr) + + hr = pPartNext->GetPartType(&parttype); + EXIT_ON_ERROR(hr) + + if (parttype == Connector) + { + // We've reached the output connector that + // lies at the end of this device topology. + hr = pPartNext->QueryInterface( + IID_IConnector, + (void**)&pConnFrom); + EXIT_ON_ERROR(hr) + + SAFE_RELEASE(pPartPrev) + SAFE_RELEASE(pPartNext) + break; + } + + // Failure of the following call means only that + // the part is not a MUX (input selector). + hr = pPartNext->Activate( + CLSCTX_ALL, + IID_IAudioInputSelector, + (void**)&pSelector); + if (hr == S_OK) + { + // We found a MUX (input selector), so select + // the input from our endpoint device. + hr = pPartPrev->GetLocalId(&localId); + EXIT_ON_ERROR(hr) + + hr = pSelector->SetSelection(localId, NULL); + EXIT_ON_ERROR(hr) + + SAFE_RELEASE(pSelector) + } + + SAFE_RELEASE(pPartPrev) + pPartPrev = pPartNext; + pPartNext = NULL; + } + } + +Exit: + SAFE_RELEASE(pConnFrom) + SAFE_RELEASE(pConnTo) + SAFE_RELEASE(pPartPrev) + SAFE_RELEASE(pPartNext) + SAFE_RELEASE(pSelector) + return hr; +} +#endif + +void setlev(int cs, int va) { + if (IsVistaUp() && + (cs == MIXERLINE_COMPONENTTYPE_SRC_LINE || cs == MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE)) { + //HRESULT hr = S_OK; + IMMDeviceEnumerator *pEnumerator = NULL; + IMMDevice *pDevice = NULL; + IMMDeviceCollection *ppDevices = NULL; + //hr = CoCreateGuid(&g_guidMyContext); + //EXIT_ON_ERROR(hr) + + // Get enumerator for audio endpoint devices. + CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), + NULL, CLSCTX_INPROC_SERVER, + __uuidof(IMMDeviceEnumerator), + (void**)&pEnumerator); + EXIT_ON_ERROR(hr) + + hr = pEnumerator->EnumAudioEndpoints(eCapture, DEVICE_STATE_ACTIVE | DEVICE_STATE_UNPLUGGED, &ppDevices); + EXIT_ON_ERROR(hr) + + hr = ppDevices->Item(Input_Device_ID, &pDevice); + EXIT_ON_ERROR(hr) + + //activate + hr = pDevice->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_ALL, NULL, (void**)&g_pEndptVol); + EXIT_ON_ERROR(hr) + + //set mic volume + float fVolume = (float)(va / 100.0f); + if (va > 2) { + hr = g_pEndptVol->SetMasterVolumeLevelScalar(fVolume, NULL/*&g_guidMyContext*/); + EXIT_ON_ERROR(hr) + } else {//mute + hr = g_pEndptVol->SetMasterVolumeLevelScalar((float)0.0f, NULL/*&g_guidMyContext*/); + } + + /*hr = pDevice->Activate(__uuidof(IAudioStreamVolume ), CLSCTX_ALL, NULL, (void**)&g_pStreamVol); + EXIT_ON_ERROR(hr) + + g_pStreamVol->SetChannelVolume(0, fVolume); + g_pStreamVol->SetChannelVolume(1, fVolume); + + IMMDevice *pDefaultDevice = NULL; + hr = pEnumerator->GetDefaultAudioEndpoint(eCapture,eConsole,&pDefaultDevice); + EXIT_ON_ERROR(hr) + + hr = pDefaultDevice->Activate(__uuidof(IAudioStreamVolume ), CLSCTX_ALL, NULL, (void**)&g_pStreamVol); + EXIT_ON_ERROR(hr) + + g_pStreamVol->SetChannelVolume(0, fVolume); + g_pStreamVol->SetChannelVolume(1, fVolume);*/ + +Exit: + SAFE_RELEASE(pEnumerator) + SAFE_RELEASE(pDevice) + SAFE_RELEASE(ppDevices) + SAFE_RELEASE(g_pEndptVol) + CoUninitialize(); + } // end if mic + + for (UINT i = 0; i < (IsVistaUp() ? mixerGetNumDevs() : 1); i++) { + HMIXER hmix; + #ifdef FOLLOW_MIXER + // TODO use a different handle?? + mixerOpen(&hmix, i, (DWORD_PTR)hMainDLG, 0, MIXER_OBJECTF_MIXER|CALLBACK_WINDOW); + #endif + if (mixerOpen(&hmix, i, (DWORD_PTR)hMainDLG, 0, MIXER_OBJECTF_MIXER) == MMSYSERR_NOERROR) { + MIXERLINE ml = {sizeof (ml), 0}; + ml.dwComponentType = cs; + if (mixerGetLineInfo((HMIXEROBJ) hmix, &ml, MIXER_GETLINEINFOF_COMPONENTTYPE|MIXER_OBJECTF_MIXER) == MMSYSERR_NOERROR) { + MIXERLINECONTROLS mlc = {sizeof (mlc), ml.dwLineID,}; + MIXERCONTROL mc = {sizeof (mc),}; + mlc.cControls = 1; + mlc.cbmxctrl = sizeof (mc); + mlc.pamxctrl = &mc; + mlc.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME; + if (mixerGetLineControls((HMIXEROBJ) hmix, &mlc, MIXER_GETLINECONTROLSF_ONEBYTYPE|MIXER_OBJECTF_MIXER) == MMSYSERR_NOERROR) { + MIXERCONTROLDETAILS mcd = {sizeof (mcd), mc.dwControlID, ml.cChannels,}; + MIXERCONTROLDETAILS_UNSIGNED v[2]; + mcd.cbDetails = sizeof (MIXERCONTROLDETAILS_UNSIGNED); + mcd.paDetails = v; + v[0].dwValue = mc.Bounds.dwMinimum + (va * (mc.Bounds.dwMaximum - mc.Bounds.dwMinimum)) / 100; + v[1].dwValue = mc.Bounds.dwMinimum + (va * (mc.Bounds.dwMaximum - mc.Bounds.dwMinimum)) / 100; + /*MMRESULT result = */mixerSetControlDetails((HMIXEROBJ) hmix, &mcd, MIXER_OBJECTF_MIXER); + } + } + mixerClose(hmix); + } + } + + DisplayDeviceName(); +} + +int SetDeviceName(void) { + HRESULT hr = S_OK; + IMMDeviceEnumerator *pEnumerate = NULL; + IMMDevice *pDevice = NULL; + IMMDevice *pDefaultDevice = NULL; + IMMDeviceCollection *ppDevices = NULL; + IPropertyStore *pProps = NULL; + PROPVARIANT varName; + + if (IsVistaUp()) { + CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + PropVariantInit(&varName); + // Get enumerator for audio endpoint devices. + hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), + NULL, CLSCTX_INPROC_SERVER, + __uuidof(IMMDeviceEnumerator), + (void**)&pEnumerate); + EXIT_ON_ERROR(hr) + + hr = pEnumerate->EnumAudioEndpoints(eCapture, DEVICE_STATE_ACTIVE | DEVICE_STATE_UNPLUGGED, &ppDevices); + EXIT_ON_ERROR(hr) + + numInputs = 0; + hr = ppDevices->GetCount(&numInputs); + EXIT_ON_ERROR(hr) + + // treat this as a dummy stop + Exit:; + } + + int oldCount = SendDlgItemMessage(inWin, IDC_DEVBOX, CB_GETCOUNT, 0, 0); + SendDlgItemMessage(inWin, IDC_DEVBOX, CB_RESETCONTENT, 0,0); + EnableWindowDlgItem(inWin, IDC_REFRESH_DEVICES, IsVistaUp()); + + if (!IsVistaUp()) {//change back to true when vista enabled ! + SendDlgItemMessageW(inWin, IDC_DEVBOX, CB_ADDSTRING, 0,(LPARAM)LocalisedString(IDS_MIC_LEGACY_MODE, NULL, 0)); + SendDlgItemMessageW(inWin, IDC_DEVBOX, CB_ADDSTRING, 0,(LPARAM)LocalisedString(IDS_LINEIN_LEGACY_MODE, NULL, 0)); + } else { + hr = pEnumerate->GetDefaultAudioEndpoint(eCapture, eConsole, &pDefaultDevice); + if (SUCCEEDED(hr) && pDefaultDevice != NULL) { + LPWSTR defaultName = NULL; + pDefaultDevice->GetId(&defaultName); + + //jkey: This is for vista or 7, so we scan through and add friendly device names + // though need to make sure that we don't re-add the current input device + // otherwise with the waveout fudge we'll get really bad feedback on output + for (unsigned int i=0; i < numInputs; i++) { + LPWSTR itemName = NULL; + ppDevices->Item(i, &pDevice); + pDevice->GetId(&itemName); + // check the id of the endpoints to prevent adding in the default output device + if (defaultName && wcsicmp(itemName, defaultName)) { + pDevice->OpenPropertyStore(STGM_READ, &pProps); + pProps->GetValue(PKEY_Device_FriendlyName, &varName); + SendDlgItemMessageW(inWin, IDC_DEVBOX, CB_ADDSTRING, 0,(LPARAM)varName.pwszVal); + SendDlgItemMessage(inWin, IDC_DEVBOX, CB_SETITEMDATA, i,(LPARAM)i); + } + CoTaskMemFree(itemName); + } + CoTaskMemFree(defaultName); + } + + int count = SendDlgItemMessage(inWin, IDC_DEVBOX, CB_GETCOUNT, 0, 0); + if (!count) { + SendDlgItemMessageW(inWin, IDC_DEVBOX, CB_ADDSTRING, 0, (LPARAM)WASABI_API_LNGSTRINGW(IDS_NO_CAPTURE_DEVICES)); + } + EnableWindowDlgItem(inWin, IDC_DEVBOX, count); + // reset to the first item in the list if there's any changes + if (!oldCount || count != oldCount) { + SendDlgItemMessage(inWin, IDC_DEVBOX, CB_SETCURSEL, 0, 0); + } + + PropVariantClear(&varName); + SAFE_RELEASE(pProps) + SAFE_RELEASE(pEnumerate) + SAFE_RELEASE(pDevice) + SAFE_RELEASE(ppDevices) + CoUninitialize(); + } + + SendDlgItemMessage(inWin, IDC_DEVBOX, CB_SETCURSEL, Input_Device_ID, 0); + DisplayDeviceName(); + return 1; +} + +/*int getlev(int cs) { + HMIXER hmix; + int retval = -1; +#ifdef USE_VISTA_SOUND_FIX + HRESULT hr = S_OK; + IMMDeviceEnumerator *pEnumerator = NULL; + IMMDevice *pDevice = NULL; + IMMDeviceCollection *ppDevices = NULL; + hr = CoCreateGuid(&g_guidMyContext); + EXIT_ON_ERROR(hr) + + // Get enumerator for audio endpoint devices. + CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); + hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), + NULL, CLSCTX_INPROC_SERVER, + __uuidof(IMMDeviceEnumerator), + (void**)&pEnumerator); + EXIT_ON_ERROR(hr) + + hr = pEnumerator->EnumAudioEndpoints(eCapture,DEVICE_STATE_ACTIVE,&ppDevices); + EXIT_ON_ERROR(hr) + + hr = ppDevices->Item(Input_Device_ID,&pDevice); + EXIT_ON_ERROR(hr) + + //activate + hr = pDevice->Activate(__uuidof(IAudioEndpointVolume), + CLSCTX_ALL, NULL, (void**)&g_pEndptVol); + EXIT_ON_ERROR(hr) + //set mic volume + float fVolume =0.0; + hr = g_pEndptVol->GetMasterVolumeLevel(&fVolume); + EXIT_ON_ERROR(hr) + +Exit: + if (FAILED(hr)) { + useXpSound = true; + } else { + retval = (int)fVolume * 100; + return retval; + } + + SAFE_RELEASE(pEnumerator) + SAFE_RELEASE(pDevice) + SAFE_RELEASE(ppDevices) + SAFE_RELEASE(g_pEndptVol) + CoUninitialize(); + +#endif //USE_VISTA_SOUND_FIX + if (mixerOpen(&hmix, 0, 0, 0, 0) == MMSYSERR_NOERROR) { + MIXERLINE ml = {sizeof (ml), 0}; + ml.dwComponentType = cs; + if (mixerGetLineInfo((HMIXEROBJ) hmix, &ml, MIXER_GETLINEINFOF_COMPONENTTYPE) == MMSYSERR_NOERROR) { + MIXERLINECONTROLS mlc = {sizeof (mlc), ml.dwLineID,}; + MIXERCONTROL mc = {sizeof (mc),}; + mlc.cControls = 1; + mlc.cbmxctrl = sizeof (mc); + mlc.pamxctrl = &mc; + mlc.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME; + if (mixerGetLineControls((HMIXEROBJ) hmix, &mlc, MIXER_GETLINECONTROLSF_ONEBYTYPE) == MMSYSERR_NOERROR) { + MIXERCONTROLDETAILS mcd = {sizeof (mcd), mc.dwControlID, ml.cChannels,}; + MIXERCONTROLDETAILS_UNSIGNED v[2]; + mcd.cbDetails = sizeof (MIXERCONTROLDETAILS_UNSIGNED); + mcd.paDetails = v; + if (mixerGetControlDetails((HMIXEROBJ) hmix, &mcd, 0) == MMSYSERR_NOERROR) { + retval = (v[0].dwValue * 100) / (mc.Bounds.dwMaximum - mc.Bounds.dwMinimum); + // retval = ((v[0].dwValue + v[1].dwValue) * 5) / (mc.Bounds.dwMaximum-mc.Bounds.dwMinimum); + } + } + } + mixerClose(hmix); + } + return retval; +}*/ + +void TitleCallback(int Connection, int Mode) { + MY_T_OUTPUT *Out = &Output[Connection]; + // title update + if (Mode == 0) { + if (Out->AutoTitle == 1) { + // look at the playback queue so we can get the correct 'next song' + if (!WASABI_API_QUEUEMGR) { + // due to loading orders its possible the queue won't have been loaded on init so check + ServiceBuild(WASABI_API_SVC, WASABI_API_QUEUEMGR, QueueManagerApiGUID); + } + + std::vector nextList; + nextList.clear(); + Encoder[Out->Encoder].UpdateTitle(0, nextList, Out->Handle, !!Out->NextTitles); + } + } else if (Mode == 1) { + // album art update + Encoder[Out->Encoder].UpdateAlbumArt(Out->Handle); + } +} + +void CenterWindow(void) { + RECT rect, rectP; + int width, height; + int screenwidth, screenheight; + int x, y; + + GetWindowRect(hMainDLG, &rect); + GetWindowRect(GetDesktopWindow(), &rectP); + + width = rect.right - rect.left; + height = rect.bottom - rect.top; + + x = ((rectP.right-rectP.left) - width) / 2 + rectP.left; + y = ((rectP.bottom-rectP.top) - height) / 2 + rectP.top; + + screenwidth = GetSystemMetrics(SM_CXSCREEN); + screenheight = GetSystemMetrics(SM_CYSCREEN); + + if (x < 0) x = 0; + if (y < 0) y = 0; + if (x + width > screenwidth) x = screenwidth - width; + if (y + height > screenheight) y = screenheight - height; + + mainrect.left = x; + mainrect.top = y; +} + +int LoadConfig(void) { + lookAhead = GetPrivateProfileInt(APP_Name, "lookAhead", lookAhead, IniName); + skipMetada = !!GetPrivateProfileInt(APP_Name, "skipMetada", skipMetada, IniName); + lastFilterIndex = GetPrivateProfileInt(APP_Name, "ofnidx", lastFilterIndex, IniName); + + curtab = GetPrivateProfileInt(APP_Name, "CurTab", curtab, IniName); + Connection_CurSelPos = GetPrivateProfileInt(APP_Name, "Connection_CurSelPos", Connection_CurSelPos, IniName); + curouttab = GetPrivateProfileInt(APP_Name, "Connection_CurTab", curouttab, IniName); + Encoder_CurSelPos = GetPrivateProfileInt(APP_Name, "Encoder_CurSelPos", Encoder_CurSelPos, IniName); + InputDevice = GetPrivateProfileInt(APP_Name, "InputDevice", InputDevice, IniName); + + Input_CurSelPos = GetPrivateProfileInt(APP_Name, "Input_CurSelPos", Input_CurSelPos, IniName); + InputConfig.srate = LineInputAttribs[3].srate; + InputConfig.nch = LineInputAttribs[3].nch; + cf_mutex = CreateMutex(NULL, TRUE, NULL); + Crossfader = new C_CROSSFADER(CrossfadeLen, + LineInputAttribs[(InputDevice == 0 ? 3 : Input_CurSelPos)].nch, + LineInputAttribs[(InputDevice == 0 ? 3 : Input_CurSelPos)].srate); + + MusVol = GetPrivateProfileInt(APP_Name, "MusicVolume", MusVol, IniName); + Mus2Vol = GetPrivateProfileInt(APP_Name, "BGMusicVolume", Mus2Vol, IniName); + MicVol = GetPrivateProfileInt(APP_Name, "MicVolume", MicVol, IniName); + + // as we've changed the scaling then we will need to adjust from old to new + int tempFadeTime = GetPrivateProfileInt(APP_Name, "PTT_FadeTime", -1, IniName); + if (tempFadeTime == -1) { + FadeTime = GetPrivateProfileInt(APP_Name, "PTT_FT", FadeTime, IniName); + } else { + FadeTime = tempFadeTime * 5; + // remove the old instance of the settings + WritePrivateProfileString(APP_Name, "PTT_FadeTime", 0, IniName); + } + + int tempMicFadeTime = GetPrivateProfileInt(APP_Name, "PTT_MicFadeTime", -1, IniName); + if (tempMicFadeTime == -1) { + MicFadeTime = GetPrivateProfileInt(APP_Name, "PTT_MicFT", MicFadeTime, IniName); + } else { + MicFadeTime = tempMicFadeTime * 5; + // remove the old instance of the settings + WritePrivateProfileString(APP_Name, "PTT_MicFadeTime", 0, IniName); + } + + Restore_PTT = GetPrivateProfileInt(APP_Name, "PTT_Restore", 0, IniName); + Input_Device_ID = GetPrivateProfileInt(APP_Name, "PTT_MicInput",0, IniName); + + // align to middle of screen on new installs + CenterWindow(); + mainrect.left = GetPrivateProfileInt(APP_Name, "WindowLeft", mainrect.left, IniName); + mainrect.top = GetPrivateProfileInt(APP_Name, "WindowTop", mainrect.top, IniName); + + GetPrivateProfileString(APP_Name, "Update", 0, updateStr, 16, IniName); + // no point in indicating a new version if we're now showing as ahead + if (!CompareVersions(updateStr)) { + WritePrivateProfileString(APP_Name, "Update", 0, IniName); + updateStr[0] = 0; + } + + for (int i = 0; i < NUM_OUTPUTS; i++) { + T_OUTPUT_CONFIG *Out = &Output[i].Config; + StringCchPrintfA(Out->Name, 32, "Output %u", i + 1); + StringCchPrintfW(Out->DisplayName, 32, WASABI_API_LNGSTRINGW(IDS_OUTPUT_X), i + 1); + Output[i].Encoder = -1; + Output[i].Handle = -1; + GetPrivateProfileString(Out->Name, "Address", "localhost", Out->Address, ARRAYSIZE(Out->Address), IniName); + GetPrivateProfileString(Out->Name, "UserID", "", Out->UserID, ARRAYSIZE(Out->UserID), IniName); + GetPrivateProfileString(Out->Name, "StreamID", "1", Out->StationID, ARRAYSIZE(Out->StationID), IniName); + Out->Port = GetPrivateProfileInt(Out->Name, "Port", 8000, IniName); + GetPrivateProfileString(Out->Name, "Password", "", Out->Password, ARRAYSIZE(Out->Password), IniName); + GetPrivateProfileString(Out->Name, "Cipherkey", "foobar", Out->cipherkey, ARRAYSIZE(Out->cipherkey), IniName); + GetPrivateProfileString(Out->Name, "Description", "Unnamed Server", Out->Description, ARRAYSIZE(Out->Description), IniName); + GetPrivateProfileString(Out->Name, "URL", "http://www.shoutcast.com", Out->ServerURL, ARRAYSIZE(Out->ServerURL), IniName); + GetPrivateProfileString(Out->Name, "Genre3", "Misc", Out->Genre, ARRAYSIZE(Out->Genre), IniName); + // check that the genre is a support value otherwise reset it to 'misc' + bool foundGenre = false; + for (int g = 0; g < ARRAYSIZE(genres); g++) { + if (!strcmpi(genres[g].name, Out->Genre)) { + foundGenre = true; + break; + } + } + if (foundGenre == false) { + lstrcpyn(Out->Genre, "Misc", ARRAYSIZE(Out->Genre)); + } + + GetPrivateProfileString(Out->Name, "AIM", "N/A", Out->AIM, ARRAYSIZE(Out->AIM), IniName); + GetPrivateProfileString(Out->Name, "ICQ", "0", Out->ICQ, ARRAYSIZE(Out->ICQ), IniName); + GetPrivateProfileString(Out->Name, "IRC", "N/A", Out->IRC, ARRAYSIZE(Out->IRC), IniName); + Out->Public = GetPrivateProfileInt(Out->Name, "Public", 1, IniName); + Out->AutoRecon = GetPrivateProfileInt(Out->Name, "AutoRecon", 1, IniName); + Out->ReconTime = GetPrivateProfileInt(Out->Name, "ReconTime", 5, IniName); + if (Out->ReconTime < 1) { + Out->ReconTime = 5; + } + Out->doTitleUpdate = GetPrivateProfileInt(Out->Name, "doTitleUpdate", 1, IniName); + GetPrivateProfileString(Out->Name, "now", "", Out->Now, ARRAYSIZE(Out->Now), IniName); + GetPrivateProfileString(Out->Name, "next", "", Out->Next, ARRAYSIZE(Out->Next), IniName); + Output[i].AutoTitle = GetPrivateProfileInt(Out->Name, "AutoTitle", 1, IniName); + Output[i].AutoConnect = GetPrivateProfileInt(Out->Name, "AutoConnect", 0, IniName); + Output[i].Logging = GetPrivateProfileInt(Out->Name, "Logging", 0, IniName); + Output[i].LogCOS = GetPrivateProfileInt(Out->Name, "LogCOS", 0, IniName); + Output[i].NextTitles = GetPrivateProfileInt(Out->Name, "NextTitles", 1, IniName); + Output[i].Config.protocol = GetPrivateProfileInt(Out->Name, "protocol", -1, IniName); + if (Output[i].Config.protocol == -1) { + Output[i].Config.protocol = MAKEWORD(2, 1); + } + // check the v1 password for : and split it if the dj/user id is empty (i.e. post 2.2.3 import) + if (LOBYTE(Output[i].Config.protocol) == 1 && Out->Password[0] && !Out->UserID[0]) { + char* password = strstr(Out->Password, ":"); + if (password) { + *password = 0; + lstrcpyn(Out->UserID, Out->Password, ARRAYSIZE(Out->UserID)); + lstrcpyn(Out->Password, ++password, ARRAYSIZE(Out->Password)); + } + } + Output[i].nextTrackLog = GetPrivateProfileInt(Out->Name, "nextTrackLog", 0, IniName); + Output[i].nextTrackLogXML = GetPrivateProfileInt(Out->Name, "nextTrackLogXML", 0, IniName); + if (!GetPrivateProfileStringUTF8(Out->Name, "nextTrackPath", 0, Output[i].nextTrackPath, ARRAYSIZE(Output[i].nextTrackPath), IniName)) { + GetDefaultNextTracksLogFile(module.hwndParent, ARRAYSIZE(Output[i].nextTrackPath), Output[i].nextTrackPath, i); + } + + Output[i].useArt = GetPrivateProfileInt(Out->Name, "useArt", 0, IniName); + Output[i].usePlayingArt = GetPrivateProfileInt(Out->Name, "usePlayingArt", 0, IniName); + Output[i].useStreamArt = GetPrivateProfileInt(Out->Name, "useStreamArt", 1, IniName); + GetPrivateProfileStringUTF8(Out->Name, "stationArtPath", 0, Output[i].stationArtPath, ARRAYSIZE(Output[i].stationArtPath), IniName); + + Output[i].saveEncoded = GetPrivateProfileInt(Out->Name, "saveEncoded", 0, IniName); + GetPrivateProfileStringUTF8(Out->Name, "saveEncodedPath", 0, Output[i].saveEncodedPath, ARRAYSIZE(Output[i].saveEncodedPath), IniName); + + Output[i].Encoder = GetPrivateProfileInt(Out->Name, "Encoder", i == 0 ? 0 : -1, IniName); + if (Output[i].Encoder != -1) { + if (WaitForSingleObject(Enc_mutex[Output[i].Encoder], INFINITE) == WAIT_OBJECT_0) { + Output[i].Handle = Encoder[Output[i].Encoder].AddOutput(i, Out, TitleCallback); + ReleaseMutex(Enc_mutex[Output[i].Encoder]); + } + } + } + return 1; +} + +wchar_t* BuildLameVersion(void) { + static wchar_t version[128] = {0}; + if (libinst != NULL && !version[0]) { + BE_VERSION ver; + beVersion(&ver); + + if (ver.byBetaLevel) { + StringCchPrintfW(version, ARRAYSIZE(version), L"%u.%ub%u", (unsigned int)ver.byMajorVersion, (unsigned int)ver.byMinorVersion, (unsigned int)ver.byBetaLevel); + } else if (ver.byAlphaLevel) { + StringCchPrintfW(version, ARRAYSIZE(version), L"%u.%ua%u", (unsigned int)ver.byMajorVersion, (unsigned int)ver.byMinorVersion, (unsigned int)ver.byAlphaLevel); + } else { + StringCchPrintfW(version, ARRAYSIZE(version), L"%u.%u", (unsigned int)ver.byMajorVersion, (unsigned int)ver.byMinorVersion); + } + } + return version; +} + +void LoadEncoders() { + /* load lame_enc.dll */ + wchar_t dllname[MAX_PATH] = {0}; + StringCchPrintfW(dllname, ARRAYSIZE(dllname), L"%s\\lame_enc.dll", GetSharedDirectoryW(module.hwndParent)); + + libinst = LoadLibraryW(dllname); + if (libinst == NULL) { + wchar_t title[128] = {0}, message[512] = {0}; + StringCchPrintfW(title, ARRAYSIZE(title), LocalisedString(IDS_PLUGIN_NAME, NULL, 0), APP_VersionW); + StringCchPrintfW(message, ARRAYSIZE(message), LocalisedString(IDS_FAILED_LOAD_LAMEDLL, NULL, 0), GetSharedDirectoryW(module.hwndParent)); + MessageBoxW(module.hwndParent, message, title, MB_ICONWARNING); + } else { + beVersion = (BEVERSION) GetProcAddress(libinst, "beVersion"); + init = (void *) GetProcAddress(libinst, "lame_init"); + params = (void *) GetProcAddress(libinst, "lame_init_params"); + encode = (void *) GetProcAddress(libinst, "lame_encode_buffer_interleaved"); + finish = (void *) GetProcAddress(libinst, "lame_encode_flush"); + + if (!init || !params || !encode || !finish) { + wchar_t title[128] = {0}; + StringCchPrintfW(title, ARRAYSIZE(title), LocalisedString(IDS_PLUGIN_NAME, NULL, 0), APP_VersionW); + MessageBoxW(module.hwndParent, LocalisedString(IDS_LAMEDLL_ISSUE, NULL, 0), title, MB_ICONWARNING); + FreeLibrary(libinst); + libinst = 0; + } + } + + /* load encoder type */ + for (int i = 0; i < NUM_ENCODERS; i++) { + char name[32] = {0}; + Enc_mutex[i] = CreateMutex(NULL, TRUE, NULL); + StringCchPrintfA(name, ARRAYSIZE(name), "Encoder %u", i + 1); + int EncType = GetPrivateProfileInt(name, "Type", 2, IniName); + // store our type for later on + Enc_LastType[i] = EncType; + switch (EncType) { + /* mp3 */ + case 1: + { + fallback: + Encoder[i].SetLame(init, params, encode, finish); + Encoder[i].SetEncoder(DEFAULT_ENCODER); + C_ENCODER *Enc = Encoder[i].GetEncoder(); + if (Enc) { + if (strcmp(Enc->GetName(), "MP3 Encoder") == 0) { + T_ENCODER_MP3_INFO EncSettings; + int infosize = sizeof (EncSettings); + T_ENCODER_MP3_INFO *encset = (T_ENCODER_MP3_INFO *) Enc->GetExtInfo(&infosize); + if (encset && infosize) memcpy(&EncSettings, encset, infosize); + EncSettings.input_numChannels = (InputDevice == 0 ? InputConfig.nch : LineInputAttribs[Input_CurSelPos].nch); + EncSettings.input_sampleRate = (InputDevice == 0 ? InputConfig.srate : LineInputAttribs[Input_CurSelPos].srate); + EncSettings.output_bitRate = GetPrivateProfileInt(name, "BitRate", EncSettings.output_bitRate, IniName); + EncSettings.output_sampleRate = GetPrivateProfileInt(name, "SampleRate", EncSettings.output_sampleRate, IniName); + EncSettings.output_numChannels = GetPrivateProfileInt(name, "NumChannels", EncSettings.output_numChannels, IniName); + EncSettings.QualityMode = GetPrivateProfileInt(name, "QualityMode", 8, IniName); + Enc->ChangeSettings(&EncSettings); + } + } + } + break; + + case 2: + // map any AAC LC from the prior versions to FHG AAC with 5.62+ + case 3: + { // FHG AAC + if (C_ENCODER_FHGAAC::isPresent(module.hwndParent)) { + Encoder[i].SetEncoder(new C_ENCODER_FHGAAC(module.hwndParent), 1); + C_ENCODER *Enc = Encoder[i].GetEncoder(); + if (Enc) { + T_ENCODER_FHGAAC_INFO EncSettings; + int infosize = sizeof (EncSettings); + T_ENCODER_FHGAAC_INFO *encset = (T_ENCODER_FHGAAC_INFO *) Enc->GetExtInfo(&infosize); + if (encset && infosize) memcpy(&EncSettings, encset, infosize); + EncSettings.input_numChannels = (InputDevice == 0 ? InputConfig.nch : LineInputAttribs[Input_CurSelPos].nch); + EncSettings.input_sampleRate = (InputDevice == 0 ? InputConfig.srate : LineInputAttribs[Input_CurSelPos].srate); + Enc->ChangeSettings(&EncSettings); + ((C_ENCODER_NSV*) Enc)->ReadConfFile(IniName, name); + } + } else if (C_ENCODER_AACP::isPresent(module.hwndParent)) { // AAC+ + Encoder[i].SetEncoder(new C_ENCODER_AACP(module.hwndParent), 1); + C_ENCODER *Enc = Encoder[i].GetEncoder(); + if (Enc) { + T_ENCODER_AACP_INFO EncSettings; + int infosize = sizeof (EncSettings); + T_ENCODER_AACP_INFO *encset = (T_ENCODER_AACP_INFO *) Enc->GetExtInfo(&infosize); + if (encset && infosize) memcpy(&EncSettings, encset, infosize); + EncSettings.input_numChannels = (InputDevice == 0 ? InputConfig.nch : LineInputAttribs[Input_CurSelPos].nch); + EncSettings.input_sampleRate = (InputDevice == 0 ? InputConfig.srate : LineInputAttribs[Input_CurSelPos].srate); + Enc->ChangeSettings(&EncSettings); + ((C_ENCODER_NSV*) Enc)->ReadConfFile(IniName, name); + } + } else { + //Encoder[i].SetEncoder(NULL); + // attempt to get to a valid encoder if the aac one disappeared + goto fallback; + } + } + break; +#ifdef USE_OGG + case 4: + { // OGG + if (C_ENCODER_OGG::isPresent(module.hwndParent)) { + Encoder[i].SetEncoder(new C_ENCODER_OGG(module.hwndParent), 1); + C_ENCODER *Enc = Encoder[i].GetEncoder(); + if (Enc) { + T_ENCODER_OGG_INFO EncSettings; + int infosize = sizeof (EncSettings); + T_ENCODER_OGG_INFO *encset = (T_ENCODER_OGG_INFO *) Enc->GetExtInfo(&infosize); + if (encset && infosize) memcpy(&EncSettings, encset, infosize); + EncSettings.input_numChannels = (InputDevice == 0 ? InputConfig.nch : LineInputAttribs[Input_CurSelPos].nch); + EncSettings.input_sampleRate = (InputDevice == 0 ? InputConfig.srate : LineInputAttribs[Input_CurSelPos].srate); + Enc->ChangeSettings(&EncSettings); + ((C_ENCODER_NSV*) Enc)->ReadConfFile(IniName, name); + } + } else Encoder[i].SetEncoder(NULL); + } + break; +#endif // USE_OGG + default: + { + Encoder[i].SetEncoder(NULL); + } + break; + } + ReleaseMutex(Enc_mutex[i]); + } +} + +void SetEncoderPanelMode(HWND hDlg, C_ENCODER * Enc) { + BOOL show = (Enc != NULL); + if (show) { + if (Enc->UseNsvConfig() == true) { + ShowWindowDlgItem(hDlg, IDC_ENCSETTINGS, SW_HIDE); + ShowWindowDlgItem(hDlg, IDC_ENCSETTINGS_BUTTON, SW_SHOW); + wchar_t tmp[128] = {0}; + StringCchPrintfW(tmp, ARRAYSIZE(tmp), LocalisedString(IDS_CURRENT_BITRATE, NULL, 0), ((T_EncoderIOVals*) Enc->GetExtInfo())->output_bitRate); + SetDlgItemTextW(hDlg, IDC_ENCSETTINGS_LABEL, tmp); + ShowWindowDlgItem(hDlg, IDC_ENCSETTINGS_LAME_VER, SW_HIDE); + } else { + wchar_t *lame_version = BuildLameVersion(); + if (lame_version && *lame_version) + { + ShowWindowDlgItem(hDlg, IDC_ENCSETTINGS, SW_SHOW); + ShowWindowDlgItem(hDlg, IDC_ENCSETTINGS_BUTTON, SW_HIDE); + SetDlgItemTextW(hDlg, IDC_ENCSETTINGS_LABEL, LocalisedString(IDS_ENCODER_SETTINGS, NULL, 0)); + + wchar_t tmp[128] = {0}; + StringCchPrintfW(tmp, ARRAYSIZE(tmp), LocalisedString(IDS_LAME_ENCODER_VER, NULL, 0), lame_version); + SetDlgItemTextW(hDlg, IDC_ENCSETTINGS_LAME_VER, tmp); + ShowWindowDlgItem(hDlg, IDC_ENCSETTINGS_LAME_VER, SW_SHOW); + } else { + ShowWindowDlgItem(hDlg, IDC_ENCSETTINGS, SW_HIDE); + ShowWindowDlgItem(hDlg, IDC_ENCSETTINGS_BUTTON, SW_HIDE); + SetDlgItemTextW(hDlg, IDC_ENCSETTINGS_LABEL, LocalisedString(IDS_MP3_ENCODING_NOT_AVAILABLE, NULL, 0)); + } + } + } else { + ShowWindowDlgItem(hDlg, IDC_ENCSETTINGS_LAME_VER, SW_HIDE); + } + + // show / hide the groupbox around the main encoder options + // which is setup to make it look like it's only around the + // options available depending upon the mode that is in use + ShowWindowDlgItem(hDlg, IDC_INFO_FRAME4, !show); + ShowWindowDlgItem(hDlg, IDC_INFO_FRAME5, show); + + // show / hide the save encoded audio options as applicable + ShowWindowDlgItem(hDlg, IDC_INFO_FRAME3, show); + ShowWindowDlgItem(hDlg, IDC_SAVE_ENCODED_AUDIO, show); + ShowWindowDlgItem(hDlg, IDC_SAVE_ENCODED_AUDIO_EDIT, show); + ShowWindowDlgItem(hDlg, IDC_SAVE_ENCODED_AUDIO_BROWSE, show); +} + +void FreeStreamAlbumArt(int Index) { + if (streamImage[Index] > (ARGB32 *)0 && + streamImage[Index] != (ARGB32 *)-1 && + WASABI_API_MEMMGR) { + WASABI_API_MEMMGR->sysFree(streamImage[Index]); + streamImage[Index] = (ARGB32 *)-1; + } + streamLength[Index] = 0; +} + +// destroy dlg and exit +int doQuit(void) { + if (nowPlayingHook) { + UnhookWindowsHookEx(nowPlayingHook); + nowPlayingHook = 0; + } + if (nowPlayingHook2) { + UnhookWindowsHookEx(nowPlayingHook2); + nowPlayingHook2 = 0; + } + + RemoveSystrayIcon(hMainDLG, SYSTRAY_ICY_ICON); + + GetWindowRect(hMainDLG, &mainrect); + + KillTimer(hMainDLG, wnd[curtab].id); + KillTimer(hMainDLG, IDD_ENCODER); + KillTimer(hMainDLG, 666); + KillTimer(hMainDLG, 1234); + KillTimer(hMainDLG, 1337); + KillTimer(hMainDLG, 2234); + KillTimer(hMainDLG, 2235); + if (curtab == 1) KillTimer(wnd[curtab].hWnd, out_wnd[curouttab].id); + if (curtab == 2) KillTimer(wnd[curtab].hWnd, in_wnd[InputDevice].id); + ini_modified = 1; + /* Disconnect all outputs */ + int done; + do { + done = 1; + for (int i = 0; i < NUM_OUTPUTS; i++) { + MY_T_OUTPUT *Out = &Output[i]; + if (Out->Encoder != -1 && Out->Handle != -1) { + SHOUTCAST_OUTPUT *Enc = &Encoder[Out->Encoder]; + int state = Enc->GetState(Out->Handle); + if (state != OUT_DISCONNECTED && state != OUT_ERROR) { + done = 0; + Enc->DisconnectOutput(Out->Handle); + } else { + // shutdown the logging options + if (Out->Logging) { + StopLogging(i); + } + if (Out->nextTrackLog) { + StopNextTracks(i); + } + if (Out->saveEncoded) { + StopSaveEncoded(i); + } + } + Enc->Run(OM_OUTPUT | OM_OTHER); + } + } + Sleep(200); + } while (!done); + + // reset levels if PTT is enabled when we are closing + if (FadeOut && InputDevice) { + int micsrc = Input_Device_ID >= 1 ? MIXERLINE_COMPONENTTYPE_SRC_LINE : MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE; + setlev(micsrc, 0); + setlev(MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT, MusVol * 10); + } + + if (hthread) { + CloseHandle(hthread); + hthread = NULL; + } + + if (hthreadout) { + CloseHandle(hthreadout); + hthreadout = NULL; + } + + ReleaseMutex(cf_mutex); + Soundcard.Close(); + + SendMessage(hMainDLG, WM_TIMER, MAKEWPARAM(666,0), 0); // force an INI save + + if (playingImage && WASABI_API_MEMMGR) { + WASABI_API_MEMMGR->sysFree(playingImage); + playingImage = 0; + } + + for (int i = 0; i < NUM_OUTPUTS; i++) { + FreeStreamAlbumArt(i); + } + + if (libinst) { + FreeLibrary(libinst); + libinst = 0; + } + C_ENCODER_FHGAAC::Unload(); + C_ENCODER_AACP::Unload(); +#ifdef USE_OGG + C_ENCODER_OGG::Unload(); +#endif // USE_OGG + + for (int i = 0; i < num_tabwnds; i++) DestroyWindow(wnd[i].hWnd); + for (int i = 0; i < num_inwnds; i++) DestroyWindow(in_wnd[i].hWnd); + for (int i = 0; i < num_outwnds; i++) DestroyWindow(out_wnd[i].hWnd); + + for (int ii = 0; ii < NUM_OUTPUTS; ii++) { + MY_T_OUTPUT *Out = &Output[ii]; + // removed encoder selection + if (Out->Encoder != -1) { + if (Out->Handle != -1) { + if (WaitForSingleObject(Enc_mutex[Out->Encoder], INFINITE) == WAIT_OBJECT_0) { + Encoder[Out->Encoder].RemoveOutput(Out->Handle); + ReleaseMutex(Enc_mutex[Out->Encoder]); + } + } + } + } + + DestroyWindow(hMainDLG); + hMainDLG = NULL; + + num_cols = num_outwnds = num_inwnds = num_tabwnds = 0; + memset(&col, 0, sizeof(col)); + memset(&wnd, 0, sizeof(wnd)); + memset(&out_wnd, 0, sizeof(out_wnd)); + memset(&in_wnd, 0, sizeof(in_wnd)); + memset(&Output, 0, sizeof(Output)); + WASABI_API_LNG_HINST = WASABI_API_ORIG_HINST = 0; + + memset(&lastFile, 0, sizeof(lastFile)); + memset(&lastSec, 0, sizeof(lastSec)); + memset(&lastFile, 0, sizeof(lastFile)); + memset(&lastMode, -1, sizeof(lastMode)); + memset(&lastEnable, 0, sizeof(lastEnable)); + memset(&buttonWnd, 0, sizeof(buttonWnd)); + memset(&tabWnd, 0, sizeof(tabWnd)); + memset(&outTabWnd, 0, sizeof(outTabWnd)); + memset(&streamLength, 0, sizeof(streamLength)); + memset(&secChanged, 0, sizeof(secChanged)); + + playingImage_w = playingImage_h = playingLength = playingType; + + if (boldFont) { + DeleteObject(boldFont); + boldFont = NULL; + } + normalFont = NULL; + + ServiceRelease(WASABI_API_SVC, AGAVE_API_CONFIG, AgaveConfigGUID); + ServiceRelease(WASABI_API_SVC, WASABI_API_LNG, languageApiGUID); + ServiceRelease(WASABI_API_SVC, WASABI_API_MEMMGR, memMgrApiServiceGuid); + ServiceRelease(WASABI_API_SVC, AGAVE_API_ALBUMART, albumArtGUID); + ServiceRelease(WASABI_API_SVC, WASABI_API_QUEUEMGR, QueueManagerApiGUID); + ServiceRelease(WASABI_API_SVC, WASABI_API_EXPLORERFINDFILE,ExplorerFindFileApiGUID); + ServiceRelease(WASABI_API_SVC, WAC_API_DOWNLOADMANAGER, DownloadManagerGUID); + WASABI_API_SVC = NULL; + + return 1; +} + +void SetBoldDialogItemFont(HWND hwndControl) { + if (!boldFont) { + HFONT hFont = (HFONT)SendMessageW(hMainDLG, WM_GETFONT, 0, 0); + LOGFONTW lf = {0}; + GetObjectW(hFont, sizeof(LOGFONTW), &lf); + lf.lfWeight = FW_BOLD; + boldFont = CreateFontIndirectW(&lf); + } + if (boldFont) { + SendMessageW(hwndControl, WM_SETFONT, (WPARAM)boldFont, MAKELPARAM(1,0)); + } +} + +LRESULT WINAPI headerProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { + if (uMsg == WM_SETCURSOR) { + return TRUE; + } + return CallWindowProcW(prevHeaderProc, hWnd, uMsg, wParam, lParam); +} + +LRESULT WINAPI listViewProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { + switch (uMsg) { + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDC_STREAM_1: + case IDC_STREAM_2: + case IDC_STREAM_3: + case IDC_STREAM_4: + case IDC_STREAM_5: + { + int oldCurSelPos = Connection_CurSelPos; + Connection_CurSelPos = (LOWORD(wParam) - IDC_STREAM_1); + ListView_SetItemState(hWnd, Connection_CurSelPos, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED); + SetFocus(hWnd); + SendMessage(wnd[1].hWnd, WM_COMMAND, MAKEWPARAM(IDC_CONNECT, BN_CLICKED), (LPARAM)GetDlgItem(wnd[1].hWnd, IDC_CONNECT)); + Connection_CurSelPos = oldCurSelPos; + } + return 0; + } + break; + + case WM_NOTIFY: + if(((LPNMHDR)lParam)->code == HDN_BEGINTRACKW || + ((LPNMHDR)lParam)->code == HDN_ITEMCHANGINGW) { + return TRUE; + } + break; + } + return CallWindowProcW(prevListViewProc, hWnd, uMsg, wParam, lParam); +} + +void SetTabErrorText(HWND hwnd, int index, int current, LPRECT r, bool update = false) { + HDC hDC = GetDC(hwnd); + if (!normalFont) { + normalFont = (HFONT)SendMessageW(hMainDLG, WM_GETFONT, 0, 0); + } + HFONT hOldFont = (HFONT)SelectObject(hDC, normalFont); + int oldTextColor = SetTextColor(hDC, (!update ? RGB(255,0,0) : RGB(0,0,255))); + int oldBkMode = SetBkMode(hDC, TRANSPARENT); + + wchar_t buf[128] = {0}; + TCITEMW pitem = {0}; + pitem.mask = TCIF_TEXT; + pitem.pszText = buf; + pitem.cchTextMax = ARRAYSIZE(buf); + SendMessageW(hwnd, TCM_GETITEMW, index, (LPARAM)&pitem); + + r->top += (current ? 1 : 3); + r->left += 6; + + // if themeing is enabled then we need to paint the background to avoid the font going weird / double-bold like + // and to ensure we're correct, am taking a 1px sliver and stretching it across the area before drawing the text + if (isthemethere) { + StretchBlt(hDC, r->left, r->top + 1, r->right - r->left - 3, r->bottom - r->top - 3, hDC, r->left - 4, r->top + 1, 1, r->bottom - r->top - 3, SRCCOPY); + } + + DrawTextW(hDC, pitem.pszText, wcslen(pitem.pszText), r, DT_SINGLELINE); + SetTextColor(hDC, oldTextColor); + SetBkMode(hDC, oldBkMode); + SelectObject(hDC, hOldFont); + ReleaseDC(hwnd, hDC); +} + +LRESULT WINAPI tabWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { + LRESULT ret = CallWindowProcW(prevTabWndProc, hWnd, uMsg, wParam, lParam); + if (uMsg == WM_PAINT) { + RECT r = {0}; + int item = ListView_GetNextItem(GetDlgItem(wnd[0].hWnd, IDC_OUTPUTSTATUS), -1, LVNI_SELECTED); + if ((curtab == 1 ? + (lastMode[Connection_CurSelPos] >= 3 && lastMode[Connection_CurSelPos] <= 6) : + (lastMode[item] >= 3 && lastMode[item] <= 6) + ) && TabCtrl_GetItemRect(hWnd, 1, &r)) { + SetTabErrorText(hWnd, 1, (curtab == 1), &r); + } + + // show the update flag on things! + if (updateStr && updateStr[0]) { + RECT r = {0}; + TabCtrl_GetItemRect(tabWnd, 3, &r); + SetTabErrorText(tabWnd, 3, (curtab == 3), &r, TRUE); + } + } + return ret; +} + +LRESULT WINAPI outTabWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { + LRESULT ret = CallWindowProcW(prevOutTabWndProc, hWnd, uMsg, wParam, lParam); + if (uMsg == WM_PAINT) { + for (int i = 0; i < NUM_OUTPUTS; i++) { + RECT r = {0}; + int index[] = {0, 0, 1, 2}; + if ((lastMode[i] >= 3 && lastMode[i] <= 6) && (i == Connection_CurSelPos) && TabCtrl_GetItemRect(hWnd, index[(lastMode[i] - 3)], &r)) { + SetTabErrorText(hWnd, index[(lastMode[i] - 3)], (curouttab == index[(lastMode[i] - 3)]), &r); + } + } + } + return ret; +} + +LRESULT WINAPI buttonProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { + int ret; + switch (uMsg) { + case WM_LBUTTONDOWN: + ret = CallWindowProcW(prevButtonProc, hWnd, uMsg, wParam, lParam); + if (SendDlgItemMessage(GetParent(hWnd), IDC_LOCK, BM_GETSTATE, 0, 0) != BST_CHECKED) { + blockmousemove = 1; + KillTimer(hMainDLG, 2234); + KillTimer(hMainDLG, 2235); + if ((MicFadeStartTime != 0 || FadeStartTime != 0) && FadeOut != 1) { + clock_t myTime = clock(); + if (FadeStartTime != 0) FadeStartTime = myTime - ((FadeTime * 100)-(myTime - FadeStartTime)); + if (MicFadeStartTime != 0) MicFadeStartTime = myTime - ((MicFadeTime * 100)-(myTime - MicFadeStartTime)); + } + FadeOut = 1; + SetTimer(hMainDLG, 2234, 10, NULL); // fade out + SetTimer(hMainDLG, 2235, 10, NULL); // fade out + //if (FadeOut) do_capture(); + #ifdef CAPTURE_TESTING + if (FadeOut) { + if (!pPlayer) { + pPlayer = new Player(hMainDLG); + } + if (!pCallbacks) { + pCallbacks = new CPlayerCallbacks(); + } + pPlayer->SetPlayerCallbacks(pCallbacks); + pPlayer->RefreshDeviceList(eRender); + pPlayer->RefreshDeviceList(eCapture); + pPlayer->SelectDefaultDevice(eRender, eConsole); + pPlayer->SelectDefaultDevice(eCapture, eConsole); + pPlayer->SelectDeviceFromList(eCapture, 0); + //pPlayer->SelectDeviceFromList(eRender, 2); + if (pPlayer->Play(eCaptureEndpoint) == FALSE) { + return TRUE; + } + } + #endif + } else { + SendMessage(hWnd, BM_SETSTATE, TRUE, 0); + ret = 0; + } + return ret; + + case WM_MOUSEMOVE: + if (blockmousemove) return 0; + break; + + case WM_LBUTTONUP: + ret = CallWindowProcW(prevButtonProc, hWnd, uMsg, wParam, lParam); + if (SendDlgItemMessage(GetParent(hWnd), IDC_LOCK, BM_GETSTATE, 0, 0) != BST_CHECKED) { + blockmousemove = 0; + KillTimer(hMainDLG, 2234); + KillTimer(hMainDLG, 2235); + if ((MicFadeStartTime != 0 || FadeStartTime != 0) && FadeOut != 0) { + clock_t myTime = clock(); + if (FadeStartTime != 0) FadeStartTime = myTime - ((FadeTime * 100)-(myTime - FadeStartTime)); + if (MicFadeStartTime != 0) MicFadeStartTime = myTime - ((MicFadeTime * 100)-(myTime - MicFadeStartTime)); + } + FadeOut = 0; + SetTimer(hMainDLG, 2234, 10, NULL); // fade in + SetTimer(hMainDLG, 2235, 10, NULL); // fade in + } else { + SendMessage(hWnd, BM_SETSTATE, TRUE, 0); + ret = 0; + } + return ret; + } + return CallWindowProcW(prevButtonProc, hWnd, uMsg, wParam, lParam); +} + +int CALLBACK EncFunc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { + switch (uMsg) { + case WM_INITDIALOG: + { + // doing this to get the button + combobox to appear as the same size when scrolling through them all + RECT r; + GetClientRect(GetDlgItem(hDlg, IDC_ENCSETTINGS), &r); + MapWindowPoints(GetDlgItem(hDlg, IDC_ENCSETTINGS), hDlg, (LPPOINT)&r,2); + InflateRect(&r, 1, 1); + SetWindowPos(GetDlgItem(hDlg, IDC_ENCSETTINGS_BUTTON), 0, r.left, r.top, r.right-r.left, r.bottom-r.top, SWP_NOZORDER|SWP_NOACTIVATE); + } + break; + case WM_COMMAND: + { + switch (LOWORD(wParam)) { + case IDC_ENCODERLIST: + { + if (HIWORD(wParam) == LBN_SELCHANGE) { + MY_T_OUTPUT *Out = &Output[Connection_CurSelPos]; + Encoder_CurSelPos = Out->Encoder; + + if (WaitForSingleObject(Enc_mutex[Encoder_CurSelPos], INFINITE) == WAIT_OBJECT_0) { + C_ENCODER *Enc = Encoder[Encoder_CurSelPos].GetEncoder(); + HWND e = hDlg; + SendDlgItemMessage(e, IDC_ENCTYPE, CB_RESETCONTENT, 0, 0); + int item = SendDlgItemMessageW(e, IDC_ENCTYPE, CB_ADDSTRING, 0, (LPARAM) LocalisedString(IDS_NONE, NULL, 0)); + SendDlgItemMessage(e, IDC_ENCTYPE, CB_SETITEMDATA, item, (LPARAM)"None"); + + item = SendDlgItemMessageW(e, IDC_ENCTYPE, CB_ADDSTRING, 0, (LPARAM) LocalisedString(IDS_MP3_ENCODER, NULL, 0)); + SendDlgItemMessage(e, IDC_ENCTYPE, CB_SETITEMDATA, item, (LPARAM)"MP3 Encoder"); + + if (C_ENCODER_FHGAAC::isPresent(module.hwndParent)) { + item = SendDlgItemMessageW(e, IDC_ENCTYPE, CB_ADDSTRING, 0, (LPARAM) LocalisedString(IDS_FHGAAC_ENCODER, NULL, 0)); + SendDlgItemMessage(e, IDC_ENCTYPE, CB_SETITEMDATA, item, (LPARAM)"Fraunhofer Encoder"); + } else if (C_ENCODER_AACP::isPresent(module.hwndParent)) { + item = SendDlgItemMessageW(e, IDC_ENCTYPE, CB_ADDSTRING, 0, (LPARAM) LocalisedString(IDS_AACP_ENCODER, NULL, 0)); + SendDlgItemMessage(e, IDC_ENCTYPE, CB_SETITEMDATA, item, (LPARAM)"AAC+ Encoder"); + } +#ifdef USE_OGG + if (C_ENCODER_OGG::isPresent(module.hwndParent)) { + // TODO + item = SendDlgItemMessageW(e, IDC_ENCTYPE, CB_ADDSTRING, 0, (LPARAM) L"OGG Vorbis Encoder"/*LocalisedString(IDS_OGG_ENCODER, NULL, 0)*/); + SendDlgItemMessage(e, IDC_ENCTYPE, CB_SETITEMDATA, item, (LPARAM)"OGG Vorbis Encoder"); + } +#endif // USE_OGG + + SendDlgItemMessage(e, IDC_ENCSETTINGS, CB_RESETCONTENT, 0, 0); + SetEncoderPanelMode(e, Enc); + + int attribnum = 0, typeval = 0; + if (Enc) { + int i; + for (int i = 0; i < NUM_ENCODERS; i++) { + char* encoder = (char*)SendDlgItemMessage(e, IDC_ENCTYPE, CB_GETITEMDATA, i, 0); + if (!strcmp(Enc->GetName(), encoder)) { + typeval = i; + break; + } + } + + int infosize = sizeof (T_EncoderIOVals); + T_ENCODER_MP3_INFO *EncInfo = (T_ENCODER_MP3_INFO *) Enc->GetExtInfo(&infosize); + for (i = Enc->GetNumAttribs() - 1; i >= 0; i--) { + T_ATTRIB attrib; + Enc->EnumAttrib(i, &attrib); + SendDlgItemMessage(e, IDC_ENCSETTINGS, CB_INSERTSTRING, 0, (LPARAM) attrib.Text); + T_ENCODER_MP3_INFO *OutVal = (T_ENCODER_MP3_INFO *) attrib.OutputVals; + if (OutVal && EncInfo && infosize) { + T_ENCODER_MP3_INFO *EncSettings = (T_ENCODER_MP3_INFO *) EncInfo; + if(OutVal->output_bitRate == EncSettings->output_bitRate && + OutVal->output_sampleRate == EncSettings->output_sampleRate && + OutVal->output_numChannels == EncSettings->output_numChannels) { + attribnum = i; + } + } + } + } else { + SendDlgItemMessageW(e, IDC_ENCSETTINGS, CB_ADDSTRING, 0, (LPARAM) LocalisedString(IDS_NONE, NULL, 0)); + ShowWindowDlgItem(e, IDC_ENCSETTINGS, SW_HIDE); + ShowWindowDlgItem(e, IDC_ENCSETTINGS_BUTTON, SW_HIDE); + SetDlgItemTextW(e, IDC_ENCSETTINGS_LABEL, LocalisedString(IDS_NO_ENCODER_SELECTED, NULL, 0)); + } + + SendDlgItemMessage(e, IDC_ENCSETTINGS, CB_SETCURSEL, attribnum, 0); + SendDlgItemMessage(e, IDC_ENCTYPE, CB_SETCURSEL, typeval, 0); + ReleaseMutex(Enc_mutex[Encoder_CurSelPos]); + ini_modified = 1; + SendMessage(hDlg, WM_COMMAND, MAKEWPARAM(IDC_ENCSETTINGS, CBN_SELCHANGE), (LPARAM) GetDlgItem(hDlg, IDC_ENCSETTINGS)); + } + } + } + break; + + case IDC_ENCTYPE: + { + if (HIWORD(wParam) == CBN_SELCHANGE) { + int typenum = SendMessage((HWND) lParam, CB_GETCURSEL, 0, 0); + + // check to see if this is the same encoder as last time as + // there's no need to update it if there hasn't been a change + // so selecting the same encoder won't cause a settings reset + if (typenum && typenum == Enc_LastType[Encoder_CurSelPos]) break; + else Enc_LastType[Encoder_CurSelPos] = typenum; + + char* typestr = (char*)SendMessage((HWND) lParam, CB_GETITEMDATA, typenum, 0); + if (WaitForSingleObject(Enc_mutex[Encoder_CurSelPos], INFINITE) == WAIT_OBJECT_0) { + if (!strcmpi("MP3 Encoder", typestr)) { + //lame setup + Encoder[Encoder_CurSelPos].SetLame(init, params, encode, finish); + Encoder[Encoder_CurSelPos].SetEncoder(new C_ENCODER_MP3(init, params, encode, finish), 1); + + C_ENCODER *Enc = Encoder[Encoder_CurSelPos].GetEncoder(); + + if (Enc) { + if (strcmpi(Enc->GetName(), "MP3 Encoder") == 0) { + T_ENCODER_MP3_INFO EncSettings; + int infosize = sizeof (EncSettings); + T_ENCODER_MP3_INFO *encset = (T_ENCODER_MP3_INFO *) Enc->GetExtInfo(&infosize); + if (encset && infosize) + memcpy(&EncSettings, encset, infosize); + + EncSettings.input_numChannels = (InputDevice == 0 ? InputConfig.nch : LineInputAttribs[Input_CurSelPos].nch); + EncSettings.input_sampleRate = (InputDevice == 0 ? InputConfig.srate : LineInputAttribs[Input_CurSelPos].srate); + EncSettings.output_bitRate = MP3_DEFAULT_OUTPUTBITRATE; + EncSettings.output_sampleRate = MP3_DEFAULT_OUTPUTSAMPLERATE; + EncSettings.output_numChannels = MP3_DEFAULT_OUTPUTNUMCHANNELS; + Enc->ChangeSettings(&EncSettings); + } + } + } else if (!strcmpi("Fraunhofer Encoder", typestr)) { // FHG AAC + Encoder[Encoder_CurSelPos].SetEncoder(new C_ENCODER_FHGAAC(module.hwndParent), 1); + + C_ENCODER *Enc = Encoder[Encoder_CurSelPos].GetEncoder(); + if (Enc) { + T_ENCODER_FHGAAC_INFO EncSettings; + int infosize = sizeof (EncSettings); + T_ENCODER_FHGAAC_INFO *encset = (T_ENCODER_FHGAAC_INFO *) Enc->GetExtInfo(&infosize); + if (encset && infosize) memcpy(&EncSettings, encset, infosize); + EncSettings.input_numChannels = (InputDevice == 0 ? InputConfig.nch : LineInputAttribs[Input_CurSelPos].nch); + EncSettings.input_sampleRate = (InputDevice == 0 ? InputConfig.srate : LineInputAttribs[Input_CurSelPos].srate); + Enc->ChangeSettings(&EncSettings); + } + } else if (!strcmpi("AAC+ Encoder", typestr)) { //AAC+ + Encoder[Encoder_CurSelPos].SetEncoder(new C_ENCODER_AACP(module.hwndParent), 1); + + C_ENCODER *Enc = Encoder[Encoder_CurSelPos].GetEncoder(); + if (Enc) { + T_ENCODER_AACP_INFO EncSettings; + int infosize = sizeof (EncSettings); + T_ENCODER_AACP_INFO *encset = (T_ENCODER_AACP_INFO *) Enc->GetExtInfo(&infosize); + if (encset && infosize) memcpy(&EncSettings, encset, infosize); + EncSettings.input_numChannels = (InputDevice == 0 ? InputConfig.nch : LineInputAttribs[Input_CurSelPos].nch); + EncSettings.input_sampleRate = (InputDevice == 0 ? InputConfig.srate : LineInputAttribs[Input_CurSelPos].srate); + Enc->ChangeSettings(&EncSettings); + } + } +#ifdef USE_OGG + else if (!strcmpi("OGG Vorbis Encoder", typestr)) { //OGG + Encoder[Encoder_CurSelPos].SetEncoder(new C_ENCODER_OGG(module.hwndParent), 1); + + C_ENCODER *Enc = Encoder[Encoder_CurSelPos].GetEncoder(); + if (Enc) { + T_ENCODER_OGG_INFO EncSettings; + int infosize = sizeof (EncSettings); + T_ENCODER_OGG_INFO *encset = (T_ENCODER_OGG_INFO *) Enc->GetExtInfo(&infosize); + if (encset && infosize) memcpy(&EncSettings, encset, infosize); + EncSettings.input_numChannels = (InputDevice == 0 ? InputConfig.nch : LineInputAttribs[Input_CurSelPos].nch); + EncSettings.input_sampleRate = (InputDevice == 0 ? InputConfig.srate : LineInputAttribs[Input_CurSelPos].srate); + Enc->ChangeSettings(&EncSettings); + } + } +#endif // USE_OGG + else { + int i; + for (i = 0; i < NUM_OUTPUTS; i++) { + MY_T_OUTPUT *Out = &Output[i]; + if (Out->Encoder == Encoder_CurSelPos && Out->Handle != -1) Encoder[Encoder_CurSelPos].DisconnectOutput(Out->Handle); + } + Encoder[Encoder_CurSelPos].SetEncoder(NULL); + } + + // if we can re-map the extension then we do so against the encoder selected + // which will override what was manually set but this ensures it is correct! + MY_T_OUTPUT *Out = &Output[Connection_CurSelPos]; + if (Out->saveEncodedPath[0] && typenum != 0) { + PathRemoveExtensionW(Out->saveEncodedPath); + PathAddExtensionW(Out->saveEncodedPath, + (Enc_LastType[Connection_CurSelPos] != 0 ? + (!strcmp(Encoder[Out->Encoder].GetEncoder()->GetContentType(),"audio/mpeg") ? L".mp3" : + (!strcmp(Encoder[Out->Encoder].GetEncoder()->GetContentType(),"audio/ogg") ? L".ogg" : L".aac")) : L"")); + + // update things as the filename was changed + SetDlgItemTextW(out_wnd[2].hWnd, IDC_SAVE_ENCODED_AUDIO_EDIT, Out->saveEncodedPath); + if (Out->saveEncoded) { + StopSaveEncoded(Connection_CurSelPos); + StartSaveEncoded(Connection_CurSelPos, Out->saveEncodedPath); + } + } + + SetEncoderPanelMode(hDlg, Encoder[Encoder_CurSelPos].GetEncoder()); + ReleaseMutex(Enc_mutex[Encoder_CurSelPos]); + ini_modified = 1; + SendMessage(hDlg, WM_COMMAND, MAKEWPARAM(IDC_ENCODERLIST, LBN_SELCHANGE), (LPARAM) GetDlgItem(hDlg, IDC_ENCODERLIST)); + } + } + } + break; + + case IDC_ENCSETTINGS_BUTTON: + { + C_ENCODER *Enc = Encoder[Encoder_CurSelPos].GetEncoder(); + if (Enc) if (Enc->UseNsvConfig()) { + ((C_ENCODER_NSV*) Enc)->Configure(hDlg, module.hDllInstance); + if (WaitForSingleObject(Enc_mutex[Encoder_CurSelPos], INFINITE) == WAIT_OBJECT_0) { + ini_modified = 1; + for (int i = 0; i < NUM_OUTPUTS; i++) { + MY_T_OUTPUT *Out = &Output[i]; + if (Out->Encoder == Encoder_CurSelPos && Out->Handle != -1) { + Encoder[Encoder_CurSelPos].DisconnectOutput(Out->Handle, 1, 5); + } + } + ReleaseMutex(Enc_mutex[Encoder_CurSelPos]); + } + } + SetEncoderPanelMode(hDlg, Enc); + } + break; + + case IDC_ENCSETTINGS: + { + if (HIWORD(wParam) == CBN_SELCHANGE) { + T_ATTRIB attrib; + int attribnum = SendMessage((HWND) lParam, CB_GETCURSEL, 0, 0); + if (WaitForSingleObject(Enc_mutex[Encoder_CurSelPos], INFINITE) == WAIT_OBJECT_0) { + C_ENCODER *Enc = Encoder[Encoder_CurSelPos].GetEncoder(); + if (Enc) { + int i; + if (attribnum < 0 || attribnum >= Enc->GetNumAttribs()) attribnum = 0; + int oldattrib = -1; + int infosize = sizeof (T_EncoderIOVals); + T_EncoderIOVals *EncInfo = (T_EncoderIOVals *) Enc->GetExtInfo(&infosize); + for (i = Enc->GetNumAttribs() - 1; i >= 0 && oldattrib == -1; i--) { + Enc->EnumAttrib(i, &attrib); + if (attrib.OutputVals && EncInfo && infosize) { + T_ENCODER_MP3_INFO *EncSettings = (T_ENCODER_MP3_INFO *) attrib.OutputVals; + T_ENCODER_MP3_INFO *EncSettings2 = (T_ENCODER_MP3_INFO *) EncInfo; + //if (memcmp(attrib.OutputVals, EncInfo, infosize) == 0) oldattrib = i; + if(EncSettings2->output_bitRate == EncSettings->output_bitRate && + EncSettings2->output_sampleRate == EncSettings->output_sampleRate && + EncSettings2->output_numChannels == EncSettings->output_numChannels) { + oldattrib = i; + } + } + } + if (attribnum != oldattrib) { + if (Enc->EnumAttrib(attribnum, &attrib)) { + // we make sure that we set the input channels and samplerate to + // that of the mode being used instead of the default values as + // this allows us to get things to work correctly (done in 2.3.2) + T_ENCODER_MP3_INFO *EncSettings = (T_ENCODER_MP3_INFO *) attrib.OutputVals; + if(InputDevice == 1) { + EncSettings->input_numChannels = (InputDevice == 0 ? InputConfig.nch : LineInputAttribs[Input_CurSelPos].nch); + EncSettings->input_sampleRate = (InputDevice == 0 ? InputConfig.srate : LineInputAttribs[Input_CurSelPos].srate); + } + Enc->ChangeSettings(EncSettings); + ini_modified = 1; + for (i = 0; i < NUM_OUTPUTS; i++) { + MY_T_OUTPUT *Out = &Output[i]; + if (Out->Encoder == Encoder_CurSelPos && Out->Handle != -1) { + Encoder[Encoder_CurSelPos].DisconnectOutput(Out->Handle, 1, 5); + } + } + } + } + } + ReleaseMutex(Enc_mutex[Encoder_CurSelPos]); + } + } + } + break; + }//switch + } + break; + }// umsg + return CallWindowProcW((WNDPROC)DialogFunc, hDlg, uMsg, wParam, lParam); +} + +/* Connection Page callback */ +INT_PTR CALLBACK ConnectionFunc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { + switch (uMsg) { + case WM_INITDIALOG: + { + switch (lParam) { + case IDD_CONNECTION: + { + int i; + wchar_t temp[128]; + SendDlgItemMessage(hDlg, IDC_OUTPUTLIST, LB_RESETCONTENT, 0, 0); + for (i = 0; i < NUM_OUTPUTS; i++) { + T_OUTPUT_CONFIG *Out = &Output[i].Config; + SendDlgItemMessageW(hDlg, IDC_OUTPUTLIST, LB_ADDSTRING, 0, (LPARAM) Out->DisplayName); + } + SendDlgItemMessage(hDlg, IDC_OUTPUTLIST, LB_SETCURSEL, Connection_CurSelPos, 0); + num_outwnds = 0; + SendDlgItemMessage(hDlg, IDC_CONTAB, TCM_DELETEALLITEMS, 0, 0); + AddOutTab(IDD_PANEL_LOGIN, LocalisedString(IDS_PANEL_LOGIN, temp, 128), hDlg, DialogFunc, IDC_CONTAB, IDC_PANELRECT_C, 0); + AddOutTab(IDD_PANEL_DIRECTORY, LocalisedString(IDS_PANEL_DIRECTORY, temp, 128), hDlg, DialogFunc, IDC_CONTAB, IDC_PANELRECT_C, 0); + AddOutTab(IDD_ENCODER, LocalisedString(IDS_PANEL_ENCODERS, temp, 128), hDlg, EncFunc, IDC_CONTAB, IDC_PANELRECT_C, 0); + AddOutTab(IDD_PANEL_TITLE, LocalisedString(IDS_PANEL_TITLES, temp, 128), hDlg, DialogFunc, IDC_CONTAB, IDC_PANELRECT_C, 0); + AddOutTab(IDD_ARTWORK, LocalisedString(IDS_PANEL_ART, temp, 128), hDlg, DialogFunc, IDC_CONTAB, IDC_PANELRECT_C, 0); + AddOutTab(IDD_LOGGING, LocalisedString(IDS_PANEL_LOGGING, temp, 128), hDlg, DialogFunc, IDC_CONTAB, IDC_PANELRECT_C, 0); + SetOutTab(curouttab, hDlg, IDC_CONTAB); + SendMessage(hDlg, WM_COMMAND, MAKEWPARAM(IDC_OUTPUTLIST, LBN_SELCHANGE), (LPARAM) GetDlgItem(hDlg, IDC_OUTPUTLIST)); + SendDlgItemMessage(wnd[1].hWnd, IDC_AUTOCONNECT, BM_SETCHECK,Output[Connection_CurSelPos].AutoConnect ? BST_CHECKED : BST_UNCHECKED, 0); + } + break; + }//lparam + } + return 0; + }//uMsg + return CallWindowProcW((WNDPROC)DialogFunc, hDlg, uMsg, wParam, lParam); +} + +void ShowUpdateMessage(HWND hDlg) +{ + bool update = (updateStr && updateStr[0]); + if (IsWindow(hDlg)) + { + HWND control = GetDlgItem(hDlg, IDC_UPDATE_HEADER); + if (update) + { + wchar_t message[128] = {0}; + StringCchPrintfW(message, ARRAYSIZE(message), WASABI_API_LNGSTRINGW(IDS_UPDATE_HEADER), updateStr); + + SetWindowTextW(control, message); + SetBoldDialogItemFont(control); + } + else + { + SetWindowTextW(control, WASABI_API_LNGSTRINGW(IDS_UPDATE)); + SendMessageW(control, WM_SETFONT, SendMessageW(hMainDLG, WM_GETFONT, 0, 0), 0); + InvalidateRect(hDlg, 0, TRUE); + } + + ShowWindowDlgItem(hDlg, IDC_STATIC_UPDATE, update); + ShowWindowDlgItem(hDlg, IDC_UPDATELINK, update); + } + + SetWindowTextW(hMainDLG, WASABI_API_LNGSTRINGW((update ? IDS_UPDATE_TITLE : IDS_MODULE_NAME))); + + if (IsWindow(tabWnd)) { + RECT r = {0}; + TabCtrl_GetItemRect(tabWnd, 3, &r); + InvalidateRect(tabWnd, &r, 0); + } +} + +class VersionCheckCallback : public ifc_downloadManagerCallback +{ +public: + void OnInit( DownloadToken token ) + { + api_httpreceiver *http = WAC_API_DOWNLOADMANAGER->GetReceiver( token ); + if ( http ) + { + http->AllowCompression(); + http->addheader( "Accept: */*" ); + } + } + + void OnFinish( DownloadToken token ) + { + api_httpreceiver *http = WAC_API_DOWNLOADMANAGER->GetReceiver( token ); + if ( http && http->getreplycode() == 200 ) + { + char *buf = 0; + size_t size = 0; + if ( WAC_API_DOWNLOADMANAGER->GetBuffer( token, (void **)&buf, &size ) == 0 && size > 0 ) + { + buf[ size - 1 ] = 0; + + char *p = buf; + while ( size && ( *p == ' ' || *p == '\t' || *p == '\r' || *p == '\n' ) ) + { + p++; + size--; + } + + // e.g. dsp_sc,2.3.4.210,http://download.nullsoft.com/shoutcast/tools/shoutcast-dsp-2-3-4-windows.exe + if ( p && *p ) + { + char *tok = strtok( p, "," ); + if ( tok ) + { + if ( !strncmp( tok, "dsp_sc", 6 ) ) + { + tok = strtok( NULL, "," ); + if ( tok ) + { + bool needsUpdating = CompareVersions( tok ); + lstrcpyn( updateStr, ( needsUpdating ? tok : "" ), ARRAYSIZE( updateStr ) ); + WritePrivateProfileString( APP_Name, "Update", ( needsUpdating ? updateStr : 0 ), IniName ); + ShowUpdateMessage( updateWnd ); + if ( !needsUpdating ) + { + ShowWindowDlgItem( updateWnd, IDC_STATIC_UPDATE, TRUE ); + } + SetDlgItemTextW( updateWnd, IDC_STATIC_UPDATE, WASABI_API_LNGSTRINGW( ( needsUpdating ? IDS_HAS_NEW_UPDATE : IDS_NO_NEW_UPDATE ) ) ); + } + } + } + } + } + } + + if ( IsWindow( updateWnd ) ) + { + EnableWindowDlgItem( updateWnd, IDC_GET_UPDATE, TRUE ); + SetDlgItemTextW( updateWnd, IDC_GET_UPDATE, WASABI_API_LNGSTRINGW( IDS_CHECK_FOR_UPDATES ) ); + } + } + + void OnError( DownloadToken token ) + { + if ( IsWindow( updateWnd ) ) + { + ShowWindowDlgItem( updateWnd, IDC_STATIC_UPDATE, TRUE ); + SetDlgItemTextW( updateWnd, IDC_STATIC_UPDATE, WASABI_API_LNGSTRINGW( IDS_CHECK_UPDATE_FAIL ) ); + EnableWindowDlgItem( updateWnd, IDC_GET_UPDATE, TRUE ); + SetDlgItemTextW( updateWnd, IDC_GET_UPDATE, WASABI_API_LNGSTRINGW( IDS_CHECK_FOR_UPDATES ) ); + } + } + + RECVS_DISPATCH; +}; + +#define CBCLASS VersionCheckCallback +START_DISPATCH; +VCB( IFC_DOWNLOADMANAGERCALLBACK_ONINIT, OnInit ) +VCB( IFC_DOWNLOADMANAGERCALLBACK_ONFINISH, OnFinish ) +VCB( IFC_DOWNLOADMANAGERCALLBACK_ONERROR, OnError ) +END_DISPATCH; +#undef CBCLASS + +static VersionCheckCallback versionCheckCallback; + +static void CheckVersion( HWND hDlg ) +{ + ShowWindowDlgItem( hDlg, IDC_STATIC_UPDATE, FALSE ); + ShowWindowDlgItem( hDlg, IDC_UPDATELINK, FALSE ); + + if ( WAC_API_DOWNLOADMANAGER ) + { + char url[ 128 ] = { 0 }; + StringCchPrintf( url, ARRAYSIZE( url ), "http://yp.shoutcast.com/update?c=dsp_sc&v=%s&wa=%x", APP_Version, GetWinampVersion( module.hwndParent ) ); + updateWnd = hDlg; + WAC_API_DOWNLOADMANAGER->DownloadEx( url, &versionCheckCallback, api_downloadManager::DOWNLOADEX_BUFFER ); + } + else + { + wchar_t title[ 128 ] = { 0 }, message[ 512 ] = { 0 }; + StringCchPrintfW( title, ARRAYSIZE( title ), LocalisedString( IDS_PLUGIN_NAME, NULL, 0 ), APP_VersionW ); + StringCchPrintfW( message, ARRAYSIZE( message ), LocalisedString( IDS_UPDATE_CHECK_ERROR, NULL, 0 ) ); + if ( MessageBoxW( module.hwndParent, message, title, MB_ICONWARNING | MB_YESNO ) == IDYES ) + { + SendMessage( module.hwndParent, WM_WA_IPC, (WPARAM)DOWNLOAD_URL, IPC_OPEN_URL ); + } + SetDlgItemTextW( hDlg, IDC_GET_UPDATE, WASABI_API_LNGSTRINGW( IDS_CHECK_FOR_UPDATES ) ); + EnableWindowDlgItem( hDlg, IDC_GET_UPDATE, TRUE ); + } +} + +INT_PTR CALLBACK AboutFunc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { + if (uMsg == WM_COMMAND) { + switch(LOWORD(wParam)) + { + case IDC_ABOUTLINK: + { + SendMessage(module.hwndParent, WM_WA_IPC, (WPARAM)"http://www.shoutcast.com/", IPC_OPEN_URL); + } + break; + + case IDC_HELPLINK: + { + // look for a lcoal copy of the documentation otherwise then do as before and use the wiki verison + wchar_t path[MAX_PATH] = {0}; + StringCchPrintfW(path, ARRAYSIZE(path), L"%s\\SHOUTcast Source DSP\\Source_DSP_Plug-in.html", GetPluginDirectoryW(module.hwndParent)); + if(ShellExecuteW(module.hwndParent, L"open", path, NULL, NULL, SW_SHOWNORMAL) < (HINSTANCE)32) + { + SendMessage(module.hwndParent, WM_WA_IPC, (WPARAM)L"http://wiki.shoutcast.com/wiki/Source_DSP_Plug-in", IPC_OPEN_URL); + } + } + break; + + case IDC_FORUMLINK: + { + SendMessage(module.hwndParent, WM_WA_IPC, (WPARAM)L"http://forums.shoutcast.com/forumdisplay.php?f=140", IPC_OPEN_URL); + } + break; + + case IDC_UPDATELINK: + { + SendMessage(module.hwndParent, WM_WA_IPC, (WPARAM)DOWNLOAD_URL, IPC_OPEN_URL); + } + break; + + case IDC_ABOUT_ICON: + { + if (HIWORD(wParam) == STN_DBLCLK) { + static bool toggle; + SendDlgItemMessage(hDlg, IDC_ABOUT_ICON, STM_SETIMAGE, IMAGE_ICON, (LPARAM)GetICYIcon(!toggle)); + SendMessage(hMainDLG, WM_SETICON, ICON_SMALL, (LPARAM)GetICYIcon(!toggle)); + toggle = !toggle; + } + } + break; + + case IDC_GET_UPDATE: + { + EnableWindowDlgItem(hDlg, IDC_GET_UPDATE, FALSE); + SetDlgItemTextW(hDlg, IDC_GET_UPDATE, WASABI_API_LNGSTRINGW(IDS_CHECKING_FOR_UPDATES)); + CheckVersion(hDlg); + } + break; + } + } + LRESULT ret = CallWindowProcW((WNDPROC)DialogFunc, hDlg, uMsg, wParam, lParam); + link_handledraw(hDlg, uMsg, wParam, lParam); + return ret; +} + +void UpdateTitleControls(void) { + MY_T_OUTPUT *Out = &Output[Connection_CurSelPos]; + if (Out->Config.doTitleUpdate) { + int length = (GetWindowTextLength(GetDlgItem(out_wnd[3].hWnd, IDC_TITLE)) > 0); + int sc2mode = (LOBYTE(Output[Connection_CurSelPos].Config.protocol) == 1); + + EnableWindowDlgItem(out_wnd[3].hWnd, IDC_SENDNEXTTITLES, Out->AutoTitle && !sc2mode); + + if (Out->AutoTitle) { + EnableWindowDlgItem(out_wnd[3].hWnd, IDC_SEND, FALSE); + EnableWindowDlgItem(out_wnd[3].hWnd, IDC_TITLE, FALSE); + EnableWindowDlgItem(out_wnd[3].hWnd, IDC_NEXT, FALSE); + } else { + EnableWindowDlgItem(out_wnd[3].hWnd, IDC_SEND, length); + EnableWindowDlgItem(out_wnd[3].hWnd, IDC_TITLE, TRUE); + EnableWindowDlgItem(out_wnd[3].hWnd, IDC_NEXT, !sc2mode && length); + } + } else { + EnableWindowDlgItem(out_wnd[3].hWnd, IDC_SEND, FALSE); + EnableWindowDlgItem(out_wnd[3].hWnd, IDC_TITLE, FALSE); + EnableWindowDlgItem(out_wnd[3].hWnd, IDC_NEXT, FALSE); + EnableWindowDlgItem(out_wnd[3].hWnd, IDC_SENDNEXTTITLES, FALSE); + } +} + +void LockOptionControls(BOOL unlock) { + int protocol = LOBYTE(Output[Connection_CurSelPos].Config.protocol), + automatic = (HIBYTE(Output[Connection_CurSelPos].Config.protocol) == 1); + BOOL sc1 = (protocol == 1); + + // First Connection panel + EnableWindowDlgItem(out_wnd[0].hWnd, IDC_ADDRESS, unlock); + EnableWindowDlgItem(out_wnd[0].hWnd, IDC_PORT, unlock); + // ensure the SC2 items are enabled only if SC1 mode is disabled + EnableWindowDlgItem(out_wnd[0].hWnd, IDC_STATIONID, (sc1 ? FALSE : unlock)); + + EnableWindowDlgItem(out_wnd[0].hWnd, IDC_USERID, unlock); + EnableWindowDlgItem(out_wnd[0].hWnd, IDC_PASSWORD, unlock); + EnableWindowDlgItem(out_wnd[0].hWnd, IDC_RECONNECT, unlock); + EnableWindowDlgItem(out_wnd[0].hWnd, IDC_TIMEOUT, unlock); + EnableWindowDlgItem(out_wnd[0].hWnd, IDC_PROTOCOL, unlock); + + // controls the information shown on the connection panel + //ShowWindowDlgItem(out_wnd[0].hWnd, IDC_INFO_FRAME2, !sc1); + ShowWindowDlgItem(out_wnd[0].hWnd, IDC_INFO_TEXT2, (protocol == 2) && !automatic); + ShowWindowDlgItem(out_wnd[0].hWnd, IDC_INFO_TEXT3, (protocol == 1) && !automatic); + ShowWindowDlgItem(out_wnd[0].hWnd, IDC_INFO_TEXT4, automatic); + + // artwork panel controls + ShowWindowDlgItem(out_wnd[4].hWnd, IDC_USE_ART, !sc1); + ShowWindowDlgItem(out_wnd[4].hWnd, IDC_USE_ART_PLAYING, !sc1); + ShowWindowDlgItem(out_wnd[4].hWnd, IDC_USE_ART_STREAM, !sc1); + ShowWindowDlgItem(out_wnd[4].hWnd, IDC_ART_EDIT, !sc1); + ShowWindowDlgItem(out_wnd[4].hWnd, IDC_ART_BROWSE, !sc1); + ShowWindowDlgItem(out_wnd[4].hWnd, IDC_ART_V1_NOTE, sc1); + ShowWindowDlgItem(out_wnd[4].hWnd, IDC_ARTWORK_V1_FRAME, sc1); + ShowWindowDlgItem(out_wnd[4].hWnd, IDC_ART_V2_NOTE, !sc1); + ShowWindowDlgItem(out_wnd[4].hWnd, IDC_ARTWORK_V2_FRAME, !sc1); + + // Second Connection panel + EnableWindowDlgItem(out_wnd[1].hWnd, IDC_PUBLIC, unlock); + EnableWindowDlgItem(out_wnd[1].hWnd, IDC_DESCRIPTION, unlock); + EnableWindowDlgItem(out_wnd[1].hWnd, IDC_SERVERURL, unlock); + EnableWindowDlgItem(out_wnd[1].hWnd, IDC_GENRE, FALSE); + EnableWindowDlgItem(out_wnd[1].hWnd, IDC_GENRES, unlock); + // ensure the SC1 items are enabled only if SC1 mode is enabled + EnableWindowDlgItem(out_wnd[1].hWnd, IDC_AIM, (!sc1 ? FALSE : unlock)); + EnableWindowDlgItem(out_wnd[1].hWnd, IDC_ICQ, (!sc1 ? FALSE : unlock)); + EnableWindowDlgItem(out_wnd[1].hWnd, IDC_IRC, (!sc1 ? FALSE : unlock)); + + // Encoder tab + EnableWindowDlgItem(out_wnd[2].hWnd, IDC_ENCTYPE, unlock); + EnableWindowDlgItem(out_wnd[2].hWnd, IDC_ENCSETTINGS_BUTTON, unlock); + EnableWindowDlgItem(out_wnd[2].hWnd, IDC_ENCSETTINGS, unlock); +} + +void WritePrivateProfileInt(LPCSTR lpKeyName, int value, LPCSTR lpAppName, LPCSTR lpFileName) { + char tmp[64] = {0}; + StringCchPrintfA(tmp, ARRAYSIZE(tmp), "%u", value); + WritePrivateProfileString((!lpAppName ? APP_Name : lpAppName), lpKeyName, tmp, (!lpFileName ? IniName : lpFileName)); +} + +wchar_t* GetFileMetaData(wchar_t* file, wchar_t* metadata, wchar_t* buffer, int buffer_len) { +extendedFileInfoStructW efs; + efs.filename=file; + efs.metadata=metadata; + efs.ret=buffer; + efs.retlen=buffer_len; + SendMessage(module.hwndParent, WM_WA_IPC, (WPARAM)&efs, IPC_GET_EXTENDED_FILE_INFOW); + return buffer; +} + +void UpdateNextTracks(wchar_t* next, int pos, std::vector &nextListIdx, std::vector &nextList) { + nextList.clear(); + nextListIdx.clear(); + + int queued = (WASABI_API_QUEUEMGR ? WASABI_API_QUEUEMGR->GetNumberOfQueuedItems() : 0); + if (WASABI_API_QUEUEMGR && queued) { + for (int i = 0; i < queued && (lookAhead == -1 ? 1 : i < lookAhead); i++) { + int idx = WASABI_API_QUEUEMGR->GetQueuedItemFromIndex(i); + nextList.push_back((wchar_t*)SendMessage(module.hwndParent, WM_WA_IPC, idx, IPC_GETPLAYLISTTITLEW)); + nextListIdx.push_back(idx); + } + } else { + if (next[0]) { + nextList.push_back(next); + nextListIdx.push_back(pos); + } + } +} + +int GetNextTracks(int len, int pos, wchar_t* next) { + int nextpos = SendMessage(module.hwndParent, WM_WA_IPC, 0, IPC_GETNEXTLISTPOS); + + // attempt to use the new look ahead api in 5.61+ otherwise use existing + // method or if the new api returns a failure just to cover all bases + if (doNextLookAhead && nextpos != -1 && len >= 1) { + wcscpy_s((wchar_t*)next, 1024, (wchar_t*)SendMessage(module.hwndParent, WM_WA_IPC, nextpos, IPC_GETPLAYLISTTITLEW)); + } else { + // get the next title (as long as shuffle is off, etc) + // with it mapped back to the first track as appropriate + if (len >= 2 && !SendMessage(module.hwndParent, WM_WA_IPC, 0, IPC_GET_SHUFFLE)) { + pos = (pos >= len ? 0 : pos); + wcscpy_s((wchar_t*)next, 1024, (wchar_t*)SendMessage(module.hwndParent, WM_WA_IPC, pos, IPC_GETPLAYLISTTITLEW)); + } + } + return pos; +} + +void FillNextTracks(int index, bool xml) { + // on change then we refresh the file as applicable + std::vector nextList; + std::vector nextListIdx; + wchar_t next[1024] = {0}; + int curpos = SendMessage(module.hwndParent, WM_WA_IPC, 0, IPC_GETLISTPOS); + int len = SendMessage(module.hwndParent, WM_WA_IPC, 0, IPC_GETLISTLENGTH); + int pos = curpos + 1; + pos = GetNextTracks(len, pos, next); + UpdateNextTracks(next, pos, nextListIdx, nextList); + WriteNextTracks(index, module.hwndParent, nextListIdx, nextList, xml); +} + +static ARGB32 *loadImgFromFile(const wchar_t *file, int *len) +{ + *len = 0; + HANDLE hf = CreateFileW(file, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); + if (hf != INVALID_HANDLE_VALUE) + { + *len = GetFileSize(hf, 0); + if (WASABI_API_MEMMGR) { + ARGB32* im = (ARGB32 *)WASABI_API_MEMMGR->sysMalloc(*len); + if (im) { + DWORD bytes_read; + ReadFile(hf, im, *len, &bytes_read, 0); + CloseHandle(hf); + return im; + } + } + CloseHandle(hf); + } + return (ARGB32 *)-1; +} + +static wchar_t bytes[32], kilo[32], mega[32], giga[32], tera[32]; +wchar_t* sizeStr(unsigned int size) { + static wchar_t temp[256]; + if (GetWinampVersion(module.hwndParent) >= 0x5064) { + // TODO swap over to the native Winamp version on newer clients + return WASABI_API_LNG->FormattedSizeString(temp, ARRAYSIZE(temp), size); + } else { + if (!bytes[0]) { + LocalisedString(IDS_B, bytes, ARRAYSIZE(bytes)); + LocalisedString(IDS_KIB, kilo, ARRAYSIZE(kilo)); + LocalisedString(IDS_MIB, mega, ARRAYSIZE(mega)); + LocalisedString(IDS_GIB, giga, ARRAYSIZE(giga)); + } + + if(size < 1024) { + StringCchPrintfW(temp, ARRAYSIZE(temp), L"%u %s", size, bytes); + } else if(size < 1048576) { + StringCchPrintfW(temp, ARRAYSIZE(temp), L"%.02f %s", (size / 1024.0f), kilo); + } else if(size < 1073741824) { + StringCchPrintfW(temp, ARRAYSIZE(temp), L"%.02f %s", (size / 1048576.0f), mega); + } else if(size < 1099511627776){ + StringCchPrintfW(temp, ARRAYSIZE(temp), L"%.02f %s", (size / 1073741824.0f), giga); + } else{ + StringCchPrintfW(temp, ARRAYSIZE(temp), L"%.02f %s", (size / 1073741824.0f), tera); + } + } + return temp; +} + +void UpdateArtworkMessage() { + MY_T_OUTPUT *Out = &Output[Connection_CurSelPos]; + SHOUTCAST_OUTPUT *Enc = Out->Encoder != -1 ? &Encoder[Out->Encoder] : NULL; + wchar_t buf[1024], playing[256], stream[256]; + T_OUTPUT_INFO *Info = Enc->GetOutputInfo(Out->Handle); + int streamSize = (Info ? Info->art_cached_length[0] : 0), + playingSize = (Info ? Info->art_cached_length[1] : 0); + + StringCchPrintfW(stream, ARRAYSIZE(stream), + WASABI_API_LNGSTRINGW(!Out->useArt || !Out->useStreamArt ? IDS_DISABLED : + (streamSize == 0 ? IDS_EMPTY_ART : IDS_ENABLED_SIZE)), + sizeStr(streamSize)); + + StringCchPrintfW(playing, ARRAYSIZE(playing), + WASABI_API_LNGSTRINGW(!Out->useArt || !Out->usePlayingArt ? IDS_DISABLED : + (playingSize == 0 ? IDS_EMPTY_ART : IDS_ENABLED_SIZE)), + sizeStr(playingSize)); + + StringCchPrintfW(buf, ARRAYSIZE(buf), WASABI_API_LNGSTRINGW(IDS_V2_ARTWORK), stream, playing); + SetDlgItemTextW(out_wnd[4].hWnd, IDC_ART_V2_NOTE, buf); +} + +void UpdatePlayingAlbumArt(int Connection, int Index, bool usePlayingArt) { + // do some quick checks as no need to send if not applicable to do so + if (!usePlayingArt && !playingImage) { + return; + } + + // hard code the playing art to be sent in png format + Encoder[Index].UpdateAlbumArtCache((usePlayingArt ? playingImage : 0), + (usePlayingArt ? playingLength : 0), + playingType, Connection); + UpdateArtworkMessage(); +} + +bool UpdateStreamAlbumArt(int Connection, int Index, const wchar_t* stationArtPath, bool useStreamArt) { + int artType = 0x0; + bool update = false; + + // if not enabled and loaded then unload and refresh + if (!useStreamArt && streamImage[Index] != (ARGB32 *)-1) { + FreeStreamAlbumArt(Index); + // bit of a fiddle to force a dummy update + artType = 0x4000; + update = true; + } else if (useStreamArt && streamImage[Index] != (ARGB32 *)-1) { + FreeStreamAlbumArt(Index); + } + + // if enabled and not loaded then attempt to load + if (!update && streamImage[Index] == (ARGB32 *)-1) { + if (useStreamArt) { + streamImage[Index] = loadImgFromFile(stationArtPath, &streamLength[Index]); + + wchar_t* ext = PathFindExtensionW(stationArtPath); + if (ext) { + if (*ext) ext++; + + update = true; + if (!wcsnicmp(ext, L"jpeg", 4) || !wcsnicmp(ext, L"jpg", 3)) { + artType = 0x4000; + } else if (!wcsnicmp(ext, L"png", 3)) { + artType = 0x4001; + } else if (!wcsnicmp(ext, L"bmp", 3)) { + artType = 0x4002; + } else if (!wcsnicmp(ext, L"gif", 3)) { + artType = 0x4003; + } else { + update = false; + } + } + } else { + // if not enabled and not loaded then do nothing + UpdateArtworkMessage(); + return false; + } + } + + if (update) { + Encoder[Index].UpdateAlbumArtCache((!useStreamArt || streamImage[Index] == (ARGB32 *)-1 ? 0 : streamImage[Index]), + (!useStreamArt ? 0: streamLength[Index]), artType, Connection); + } + UpdateArtworkMessage(); + return update; +} + +void UpdateVUMeters() +{ + int volume = 0; + int vu_l = VU.vu_l; + int vu_r = VU.vu_r; + VU.vu_l = 0; + VU.vu_r = 0; + VU.lastUpdate = VU.update; + VU.update = 0; + wchar_t tmp[256], temp[32], temp2[32]; + + if (vu_l != 0) { + volume = (int) (20 * log10((double) vu_l)); + StringCchPrintfW(tmp, ARRAYSIZE(tmp), LocalisedString(IDS_X_DB, temp, 32), volume - 90); + } else { + volume = 0; + LocalisedString(IDS_INF_DB, tmp, 256); + } + if (volume - 90 > peak_vu_l) { + StringCchPrintfW(temp2, ARRAYSIZE(temp2), LocalisedString(IDS_X_DB_PEAK, temp, 32), (peak_vu_l = volume - 90)); + SetDlgItemTextW(wnd[0].hWnd, IDC_VOLUMETEXT_LP, temp2); + SetDlgItemTextW(wnd[2].hWnd, IDC_VOLUMETEXT_LP, temp2); + } + + SetDlgItemTextW(wnd[0].hWnd, IDC_VOLUMETEXT_L, tmp); + SendDlgItemMessage(wnd[0].hWnd, IDC_VOLUMEGRAPH_L, PBM_SETPOS, volume, 0); + SetDlgItemTextW(wnd[2].hWnd, IDC_VOLUMETEXT_L, tmp); + SendDlgItemMessage(wnd[2].hWnd, IDC_VOLUMEGRAPH_L, PBM_SETPOS, volume, 0); + if (vu_r != 0) { + volume = (int) (20 * log10((double) vu_r)); + StringCchPrintfW(tmp, ARRAYSIZE(tmp), LocalisedString(IDS_X_DB, temp, 32), volume - 90); + } else { + volume = 0; + LocalisedString(IDS_INF_DB, tmp, 256); + } + if (volume - 90 > peak_vu_r) { + StringCchPrintfW(temp2, ARRAYSIZE(temp2), LocalisedString(IDS_X_DB_PEAK, temp, 32), (peak_vu_r = volume - 90)); + SetDlgItemTextW(wnd[0].hWnd, IDC_VOLUMETEXT_RP, temp2); + SetDlgItemTextW(wnd[2].hWnd, IDC_VOLUMETEXT_RP, temp2); + } + + SetDlgItemTextW(wnd[0].hWnd, IDC_VOLUMETEXT_R, tmp); + SendDlgItemMessage(wnd[0].hWnd, IDC_VOLUMEGRAPH_R, PBM_SETPOS, volume, 0); + SetDlgItemTextW(wnd[2].hWnd, IDC_VOLUMETEXT_R, tmp); + SendDlgItemMessage(wnd[2].hWnd, IDC_VOLUMEGRAPH_R, PBM_SETPOS, volume, 0); +} + +void UpdateSummaryDetails(int item) { + if (item == -1) { + return; + } + + wchar_t message[2048], temp[128], temp2[128], temp3[128], + temp4[128] = {0}, temp5[128], temp6[128], temp7[128]; + char temp8[128]; + MY_T_OUTPUT *Out = &Output[item]; + C_ENCODER *Enc = Encoder[Out->Encoder].GetEncoder(); + + if (Enc) { + StringCchPrintfW(temp4, ARRAYSIZE(temp4), LocalisedString(IDS_SUMMARY_KBPS, NULL, 0), ((T_EncoderIOVals*) Enc->GetExtInfo())->output_bitRate); + } + + StringCchPrintfW(message, ARRAYSIZE(message), LocalisedString(IDS_SUMMARY, NULL, 0), + // TODO show automatic mode ? + (LOBYTE(Out->Config.protocol) != 1 ? 2 : 1), + LocalisedString((Out->Config.Public ? IDS_PUBLIC : IDS_PRIVATE), temp, 128), + (Out->Config.Address[0] ? Out->Config.Address : LocalisedStringA(IDS_NOT_SET_SUMMARY, temp8, 128)), + Out->Config.Port, + LocalisedString((Out->Config.doTitleUpdate ? (Out->AutoTitle ? IDS_FOLLOW_WA : IDS_MANUAL) : IDS_DISABLED), temp2, 128), + (Enc_LastType[item] != 0 ? (!strcmp(Encoder[Out->Encoder].GetEncoder()->GetContentType(),"audio/mpeg")?L"MP3":L"AAC+") : LocalisedString(IDS_NOT_SET, temp3, 128)), + temp4, + LocalisedString((Out->Logging ? IDS_YES : IDS_NO), temp5, 128), + LocalisedString((Out->AutoConnect ? IDS_YES : IDS_NO), temp6, 128), + LocalisedString((Out->saveEncoded ? IDS_YES : IDS_NO), temp7, 128)); + + SetDlgItemTextW(wnd[0].hWnd, IDC_SUMMARY, message); + + // see if we're looking at a incomplete setup and flag up the output tab as applicable + if (IsWindow(tabWnd)) { + RECT r = {0}; + TabCtrl_GetItemRect(tabWnd, 1, &r); + if ((lastMode[item] >= 3 && lastMode[item] <= 6)) { + SetTabErrorText(tabWnd, 1, 0, &r); + } else { + InvalidateRect(tabWnd, &r, 0); + } + } +} + +void ProcessPlayingStatusUpdate(void) { + if (isplaying == 1 && SendMessage(module.hwndParent, WM_WA_IPC, 0, IPC_GETOUTPUTTIME)) { + if (was_paused) { + was_paused = 0; + play_diff = GetTickCount(); + } + } else if (isplaying == 3) { + // pause + was_paused = 1; + } else if (isplaying == 0 && was_playing == 1) { + // stop + was_playing = was_paused = 0; + } else if (isplaying == 0 && was_playing == 0) { + // non-playing track advance + PostMessage(hMainDLG, WM_USER, 0, nowPlayingID); + } +} + +// check against a list of known "illegal" names as well as not allowing +// the creation of a station with the name the same as a genre string +bool stationNameAllowed(char* name){ + if(name && *name) + { + int checked = 0; + for (int i = 0; i < ARRAYSIZE(genres); i++) { + checked += !lstrcmpi(name, genres[i].name); + } + + // check for punctuation only titles with double-processing + // to strip out alphanum+space and space in second and then + // we compare a difference in lengths where different is ok + if (lstrlen(name) > 0) { + char* stripped = (char*)malloc(lstrlen(name)+1); + char* stripped2 = (char*)malloc(lstrlen(name)+1); + stripped[0] = 0; + for(int i = 0, j = 0; i < lstrlen(name); i++) { + if(!isalnum(name[i]) && name[i] != ' ') { + stripped[j] = name[i]; + stripped[++j] = 0; + } + } + for(int i = 0, j = 0; i < lstrlen(name); i++) { + if(name[i] != ' ') { + stripped2[j] = name[i]; + stripped2[++j] = 0; + } + } + checked += !(lstrlen(stripped2) > lstrlen(stripped)); + free(stripped); + free(stripped2); + } + + char* invalidNames[] = {"127.0.0.1", "admin", "auto dj", "auto jedi", "auto pj", + "auto-dj", "autodj", "autopj", "demo", "dj", "internet radio", + "live", "local server", "localhost", "localserver", "music", + "my radio", "my server", "my station name", "my test server", + "n/a", "pj", "playlist", "radio", "radio station", "test", + "test server", "unnamed server", "virtual dj", "virtualdj", + "web rdio", "web radio", "song", "teste", "default stream", + "radio stream", "whmsonic autodj", "autopilot", + "this is my server name"}; + for(int i = 0; i < ARRAYSIZE(invalidNames); i++) + { + checked += !lstrcmpi(name, invalidNames[i]); + } + return (!checked); + } + return false; +} + +HBITMAP WAResizeImage(HBITMAP hbmp, INT cx, INT cy) { + BITMAP bi = {0}; + if (!hbmp || !GetObjectW(hbmp, sizeof(BITMAP), &bi)) return hbmp; + + if (bi.bmWidth != cx || bi.bmHeight != cy) { + HDC hdc, hdcDst, hdcSrc; + HBITMAP hbmpOld1, hbmpOld2; + + int ix = cy*(bi.bmWidth*1000/bi.bmHeight)/1000, iy; + if (ix > cx) { + iy = cx*(bi.bmHeight*1000/bi.bmWidth)/1000; + ix = cx; + } + else iy = cy; + + hdc = GetDC(NULL); + hdcSrc = CreateCompatibleDC(hdc); + hdcDst = CreateCompatibleDC(hdc); + hbmpOld1 = (HBITMAP)SelectObject(hdcSrc, hbmp); + hbmp = CreateCompatibleBitmap(hdc, cx, cy); + hbmpOld2 = (HBITMAP)SelectObject(hdcDst, hbmp); + if (ix != cx || iy != cy) { + RECT r; + SetRect(&r, 0, 0, cx, cy); + FillRect(hdcDst, &r, GetSysColorBrush(COLOR_BTNFACE)); + } + SetStretchBltMode(hdcDst, HALFTONE); + StretchBlt(hdcDst, (cx - ix)/2, (cy - iy)/2, ix, iy, hdcSrc, 0, 0, bi.bmWidth, bi.bmHeight, SRCCOPY); + + SelectObject(hdcDst, hbmpOld2); + hbmpOld2 = (HBITMAP)SelectObject(hdcSrc, hbmpOld1); + if (hbmpOld2) DeleteObject(hbmpOld2); + + DeleteDC(hdcSrc); + DeleteDC(hdcDst); + ReleaseDC(NULL, hdc); + } + return hbmp; +} + +HBITMAP getBitmap(ARGB32 * data, int w, int h) +{ + BITMAPINFO info={0}; + info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + info.bmiHeader.biWidth = w; + info.bmiHeader.biHeight = -h; + info.bmiHeader.biPlanes = 1; + info.bmiHeader.biBitCount = 32; + info.bmiHeader.biCompression = BI_RGB; + HDC dc = GetDC(NULL); + HBITMAP bm = CreateCompatibleBitmap(dc, w, h); + SetDIBits(dc, bm, 0, h, data, &info, DIB_RGB_COLORS); + ReleaseDC(NULL, dc); + return WAResizeImage(bm, 155, 88); +} + +ARGB32 * decompressImage(const void *data, int datalen, int * dataW, int * dataH) { + ARGB32* ret=NULL; + FOURCC imgload = svc_imageLoader::getServiceType(); + int n = WASABI_API_SVC->service_getNumServices(imgload); + for (int i = 0; i < n; i++) { + waServiceFactory *sf = WASABI_API_SVC->service_enumService(imgload,i); + if (sf) { + svc_imageLoader * l = (svc_imageLoader*)sf->getInterface(); + if (l) { + if (l->testData(data,datalen)) { + ret = l->loadImage(data,datalen,dataW,dataH); + sf->releaseInterface(l); + break; + } + sf->releaseInterface(l); + } + } + } + return ret; +} + +INT_PTR CALLBACK DialogFunc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { + if (SYSTRAY_BASE_MSG + SYSTRAY_MAXIMIZE_MSG == uMsg) { + int which = LOWORD(wParam) - SYSTRAY_BASE_ICON; + switch (LOWORD(lParam)) { + case WM_LBUTTONDOWN: + { + switch (which) { + case 1: + { + RemoveSystrayIcon(hDlg, SYSTRAY_ICY_ICON); + ShowWindow(hDlg, SW_SHOW); + SendMessage(hDlg, WM_SYSCOMMAND, SC_RESTORE, 0); + } + break; + } + } + break; + } + } + switch (uMsg) { +#ifdef FOLLOW_MIXER + case MM_MIXM_CONTROL_CHANGE: + { + HMIXER mix = (HMIXER)wParam; + DWORD dwControlID = (DWORD)lParam; + //if (dwControlID == 3) + { + MIXERLINE ml = {sizeof (ml), 0}; + ml.dwComponentType = MIXERLINE_COMPONENTTYPE_SRC_LINE; + ml.dwLineID = dwControlID; + if (mixerGetLineInfo((HMIXEROBJ) mix, &ml, MIXER_GETLINEINFOF_COMPONENTTYPE|MIXER_OBJECTF_MIXER) == MMSYSERR_NOERROR) { + MIXERLINECONTROLS mlc = {sizeof (mlc), dwControlID,}; + MIXERCONTROL mc = {sizeof (mc),}; + //mlc.cControls = 1; + mlc.cbmxctrl = sizeof (mc); + mlc.pamxctrl = &mc; + mlc.dwControlType = MIXERCONTROL_CONTROLTYPE_VOLUME; + //mlc.dwControlID = dwControlID; + mlc.dwLineID = dwControlID;//ml.dwLineID; + char a[128]; + sprintf(a,"MM_MIXM_CONTROL_CHANGE: %d\n",dwControlID); + OutputDebugString(a); + if (mixerGetLineControls((HMIXEROBJ) mix, &mlc, MIXER_GETLINECONTROLSF_ONEBYTYPE|MIXER_OBJECTF_MIXER) == MMSYSERR_NOERROR) { + //MIXERCONTROLDETAILS mcd = {sizeof (mcd), 0,}; + char a[128]; + sprintf(a,"MM_MIXM_CONTROL_CHANGE 2: %d\n",dwControlID); + OutputDebugString(a); + /*MIXERCONTROLDETAILS_UNSIGNED v[2]; + mcd.cbDetails = sizeof (MIXERCONTROLDETAILS_UNSIGNED); + mcd.paDetails = v; + /*v[0].dwValue = mc.Bounds.dwMinimum + (va * (mc.Bounds.dwMaximum - mc.Bounds.dwMinimum)) / 100; + v[1].dwValue = mc.Bounds.dwMinimum + (va * (mc.Bounds.dwMaximum - mc.Bounds.dwMinimum)) / 100; + MMRESULT result = mixerSetControlDetails((HMIXEROBJ) hmix, &mcd, MIXER_OBJECTF_MIXER);*/ + } + } + } + } + break; +#endif + + case WM_LBUTTONUP: + GetWindowRect(hDlg, &mainrect); + break; + + case WM_INITDIALOG: + { + switch (lParam) { + case IDD_DIALOG: + { + int i; + wchar_t temp[128] = {0}; + hMainDLG = hDlg; + for (i = 0; i < NUM_ENCODERS; i++) Enc_mutex[i] = NULL; + INITCOMMONCONTROLSEX ICCE; + ICCE.dwSize = sizeof (ICCE); + ICCE.dwICC = ICC_WIN95_CLASSES; + InitCommonControlsEx(&ICCE); + GetSCIniFile(module.hwndParent); + VU.update = VU.lastUpdate = 0; + memset(&VU, 0, sizeof (VU)); + memset(&Output, 0, sizeof (Output)); + + // as only 5.61+ supports the IPC_GETNEXTLISTPOS api we check and cache version now + doNextLookAhead = (GetWinampVersion(module.hwndParent) >= 0x5061); + + /* Load config */ + LoadConfig(); + + /* Load Encoders */ + LoadEncoders(); + + /* Start logging and encoded output saving */ + for (i = 0; i < NUM_OUTPUTS; i++) { + if (Output[i].Logging) { + StartLogging(i, Output[i].LogCOS); + } + if (Output[i].nextTrackLog) { + StartNextTracks(i, Output[i].nextTrackPath); + } + if (Output[i].saveEncoded) { + StartSaveEncoded(i, Output[i].saveEncodedPath); + } + } + + // ensure we've cached the title information, etc as needed before starting + isplaying = SendMessage(module.hwndParent, WM_WA_IPC, 0, IPC_ISPLAYING); + if (isplaying) ProcessPlayingStatusUpdate(); + PostMessage(hDlg, WM_USER, 0, nowPlayingID); + + /* Setup main tabs */ + num_tabwnds = 0; + SendDlgItemMessage(hDlg, IDC_TAB, TCM_DELETEALLITEMS, 0, 0); + AddTab(IDD_MAIN, LocalisedString(IDS_TAB_MAIN, temp, 128), hDlg, DialogFunc, IDC_TAB, IDC_RECT, 100); + AddTab(IDD_CONNECTION, LocalisedString(IDS_TAB_OUTPUT, temp, 128), hDlg, ConnectionFunc, IDC_TAB, IDC_RECT, 100); + AddTab(IDD_INPUT, LocalisedString(IDS_TAB_INPUT, temp, 128), hDlg, DialogFunc, IDC_TAB, IDC_RECT, 100); + AddTab(IDD_ABOUT, LocalisedString(IDS_TAB_ABOUT, temp, 128), hDlg, AboutFunc, IDC_TAB, IDC_RECT, 100); + SetTab(curtab, hDlg, IDC_TAB); + + /* threads and timer */ + hthread = CreateThread(NULL, 0, ThreadInput, hDlg, CREATE_SUSPENDED, &threadid); + SetThreadPriority(hthread, THREAD_PRIORITY_HIGHEST); + hthreadout = CreateThread(NULL, 0, ThreadOutput, hDlg, CREATE_SUSPENDED, &threadoutid); + SetThreadPriority(hthreadout, THREAD_PRIORITY_HIGHEST); + Soundcard.Close(); + Soundcard.Create((InputDevice == 0 ? InputConfig.srate : LineInputAttribs[Input_CurSelPos].srate), (InputDevice == 0 ? InputConfig.nch : LineInputAttribs[Input_CurSelPos].nch)); + SetTimer(hDlg, 666, 300000, NULL); // start up INI save timer + FadeStartTime = 0; + MicFadeStartTime = 0; + FadeOut = 0; + ini_modified = 1; + ReleaseMutex(cf_mutex); + SendDlgItemMessage(wnd[1].hWnd, IDC_AUTOCONNECT, BM_SETCHECK,Output[Connection_CurSelPos].AutoConnect ? BST_CHECKED : BST_UNCHECKED, 0); + + // deal with the PTT restore mode along with setting up the 'down arrow' button + SendDlgItemMessage(in_wnd[1].hWnd, IDC_LOCK, BM_SETCHECK, Restore_PTT ? BST_CHECKED : BST_UNCHECKED, 0); + PostMessage(in_wnd[1].hWnd, WM_COMMAND, MAKEWPARAM(IDC_LOCK, BN_CLICKED), (LPARAM)GetDlgItem(in_wnd[1].hWnd, IDC_LOCK)); + + SendDlgItemMessage(in_wnd[1].hWnd, IDC_LOCK_MODE, BM_SETIMAGE, IMAGE_ICON, + (LPARAM)LoadImage(module.hDllInstance, MAKEINTRESOURCE(IDI_DOWNARROW), + IMAGE_ICON, 0, 0, LR_SHARED | LR_LOADTRANSPARENT | LR_CREATEDIBSECTION)); + + SendDlgItemMessage(in_wnd[1].hWnd, IDC_REFRESH_DEVICES, BM_SETIMAGE, IMAGE_ICON, + (LPARAM)LoadImage(module.hDllInstance, MAKEINTRESOURCE(IDI_REFRESH), + IMAGE_ICON, 0, 0, LR_SHARED | LR_LOADTRANSPARENT | LR_CREATEDIBSECTION)); + + /* setup information parts to be in bold */ + SetBoldDialogItemFont(GetDlgItem(wnd[0].hWnd, IDC_SUMMARY)); + SetBoldDialogItemFont(GetDlgItem(out_wnd[1].hWnd, IDC_INFO_TEXT)); + SetBoldDialogItemFont(GetDlgItem(out_wnd[0].hWnd, IDC_INFO_TEXT2)); + SetBoldDialogItemFont(GetDlgItem(out_wnd[0].hWnd, IDC_INFO_TEXT3)); + SetBoldDialogItemFont(GetDlgItem(out_wnd[0].hWnd, IDC_INFO_TEXT4)); + SetBoldDialogItemFont(GetDlgItem(wnd[1].hWnd, IDC_CONNECT)); + SetBoldDialogItemFont(GetDlgItem(out_wnd[4].hWnd, IDC_ART_V1_NOTE)); + SetBoldDialogItemFont(GetDlgItem(out_wnd[4].hWnd, IDC_ART_V2_NOTE)); + + /* subclass the listview on the summary page for specific handling */ + HWND listView = GetDlgItem(wnd[0].hWnd, IDC_OUTPUTSTATUS); + ListView_SetItemState(listView, 0, LVIS_SELECTED, LVIS_SELECTED); + prevListViewProc = (WNDPROC) SetWindowLongPtrW(listView, GWLP_WNDPROC, (LONG_PTR) listViewProc); + prevHeaderProc = (WNDPROC) SetWindowLongPtrW(ListView_GetHeader(listView), GWLP_WNDPROC, (LONG_PTR) headerProc); + + /* subclass the tab control on the main dialog for error notifications */ + tabWnd = GetDlgItem(hMainDLG, IDC_TAB); + prevTabWndProc = (WNDPROC) SetWindowLongPtrW(tabWnd, GWLP_WNDPROC, (LONG_PTR) tabWndProc); + + /* subclass the tab control on the connection page for error notifications */ + outTabWnd = GetDlgItem(wnd[1].hWnd, IDC_CONTAB); + prevOutTabWndProc = (WNDPROC) SetWindowLongPtrW(outTabWnd, GWLP_WNDPROC, (LONG_PTR) outTabWndProc); + + /* only once everything else has been done then we start the threads */ + ResumeThread(hthread); + ResumeThread(hthreadout); + break; + } + + case IDD_MAIN: + { + HWND listView = GetDlgItem(hDlg, IDC_OUTPUTSTATUS); + ListView_DeleteAllItems(listView); + ListView_SetExtendedListViewStyle(listView, LVS_EX_FULLROWSELECT|LVS_EX_DOUBLEBUFFER|LVS_EX_LABELTIP); + AddColumn(L"", listView); + AddColumn(LocalisedString(IDS_COL_OUTPUT_NAME, NULL, 0), listView); + AddColumn(LocalisedString(IDS_COL_STATUS, NULL, 0), listView); + for (int i = 0; i < NUM_OUTPUTS; i++) { + AddColItem(L"", 0, hDlg, IDC_OUTPUTSTATUS); + AddColItem(Output[i].Config.DisplayName, 1, hDlg, IDC_OUTPUTSTATUS); + AddColItem(L"", 2, hDlg, IDC_OUTPUTSTATUS); + } + + // fill the header of the output list with the column headers + ListView_SetColumnWidth(listView, 0, 24);//LVSCW_AUTOSIZE_USEHEADER); + ListView_SetColumnWidth(listView, 1, LVSCW_AUTOSIZE_USEHEADER); + int width = ListView_GetColumnWidth(listView, 0) + ListView_GetColumnWidth(listView, 1); + RECT listViewRect; + GetClientRect(listView, &listViewRect); + ListView_SetColumnWidth(listView, 2, (listViewRect.right - listViewRect.left) - width); + + // add in status / action buttons for the first column... + for (int i = 0; i < NUM_OUTPUTS; i++) { + POINT pt; + ListView_GetItemPosition(listView, i, &pt); + ListView_GetItemRect(listView, i, &listViewRect, LVIR_BOUNDS); + buttonWnd[i] = CreateWindowW(L"button", L"", WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS | WS_DISABLED | BS_ICON, + 1, pt.y, ListView_GetColumnWidth(listView, 0) - 3, + (listViewRect.bottom - listViewRect.top), + listView, (HMENU)(IDC_STREAM_1+i), 0, 0); + SendMessage(buttonWnd[i], BM_SETIMAGE, IMAGE_ICON, + (LPARAM)LoadImage(module.hDllInstance, MAKEINTRESOURCE(IDI_PLAY), + IMAGE_ICON, 0, 0, LR_SHARED | LR_LOADTRANSPARENT | LR_CREATEDIBSECTION)); + } + + CheckRadioButton(hDlg, IDC_INPUT_WINAMP, IDC_INPUT_SOUNDCARD, (IDC_INPUT_WINAMP + InputDevice)); + SendDlgItemMessage(hDlg, IDC_VOLUMEGRAPH_L, PBM_SETRANGE, 0, MAKELONG(0, 90)); + SendDlgItemMessage(hDlg, IDC_VOLUMEGRAPH_R, PBM_SETRANGE, 0, MAKELONG(0, 90)); + for (int ii = 0; ii < NUM_OUTPUTS; ii++) { + int encnum = ii; + MY_T_OUTPUT *Out = &Output[ii]; + // removed encoder selection + if (Out->Encoder != -1) { + if (Out->Handle != -1) { + if (WaitForSingleObject(Enc_mutex[Out->Encoder], INFINITE) == WAIT_OBJECT_0) { + Encoder[Out->Encoder].RemoveOutput(Out->Handle); + ReleaseMutex(Enc_mutex[Out->Encoder]); + } + } + } + + Out->Encoder = encnum; + if (Out->Encoder != -1) { + if (WaitForSingleObject(Enc_mutex[Out->Encoder], INFINITE) == WAIT_OBJECT_0) { + Out->Handle = Encoder[Out->Encoder].AddOutput(Out->Encoder, &Out->Config, TitleCallback); + ReleaseMutex(Enc_mutex[Out->Encoder]); + } + ini_modified = 1; + } + // TODO + //SendMessage(hDlg, WM_COMMAND, MAKEWPARAM(IDC_NOTITLES, BN_CLICKED), (LPARAM) GetDlgItem(hDlg, IDC_NOTITLES)); + } + break; + } + + case IDD_INPUT: + { + SendDlgItemMessage(hDlg, IDC_INPUTDEVICE, CB_RESETCONTENT, 0, 0); + AddInTab(IDD_PANEL_WINAMP, LocalisedString(IDS_INPUT_WINAMP, NULL, 0), hDlg); + AddInTab(IDD_PANEL_LINEIN, LocalisedString(IDS_INPUT_SOUNDCARD, NULL, 0), hDlg); + SetInTab(InputDevice, hDlg, IDC_INPUTDEVICE); + EnableWindowDlgItem(hDlg, IDC_INPUTDEVICE, 1); + EnableWindowDlgItem(hDlg, IDC_INPUTDEVICESTATIC, 1); + SendDlgItemMessage(hDlg, IDC_VOLUMEGRAPH_L, PBM_SETRANGE, 0, MAKELONG(0, 90)); + SendDlgItemMessage(hDlg, IDC_VOLUMEGRAPH_R, PBM_SETRANGE, 0, MAKELONG(0, 90)); + break; + } + + case IDD_ABOUT: + { + // this will make sure that we've got the icon shown even when using a localised version + // as well as setting the dialog icon to the icy icon irrespective of the dialog class's + SendDlgItemMessage(hDlg, IDC_ABOUT_ICON, STM_SETIMAGE, IMAGE_ICON, (LPARAM)GetICYIcon()); + SendMessage(hMainDLG, WM_SETICON, ICON_SMALL, (LPARAM)GetICYIcon()); + + wchar_t about[1024], tmp[256]; + StringCchPrintfW(about, ARRAYSIZE(about), LocalisedString(IDS_ABOUT_MESSAGE, NULL, 0), + LocalisedString(IDS_MODULE_NAME, tmp, 256), + APP_Version, APP_Build, __DATE__); + SetDlgItemTextW(hDlg, IDC_PROGRAMNAME, about); + link_startsubclass(hDlg, IDC_ABOUTLINK); + link_startsubclass(hDlg, IDC_HELPLINK); + link_startsubclass(hDlg, IDC_FORUMLINK); + link_startsubclass(hDlg, IDC_UPDATELINK); + + ShowUpdateMessage(hDlg); + break; + } + + case IDD_PANEL_WINAMP: + { + wchar_t buf[128] = {0}; + inWinWa = hDlg; + HWND metalist = GetDlgItem(hDlg, IDC_METALIST); + ListView_SetExtendedListViewStyle(metalist, LVS_EX_FULLROWSELECT|LVS_EX_DOUBLEBUFFER|LVS_EX_LABELTIP); + AddColumn(L"", metalist); + AddColumn(L"", metalist); + AddColItem(LocalisedString(IDS_FILEPATH, buf, ARRAYSIZE(buf)), 0, hDlg, IDC_METALIST); + AddColItem(LocalisedString(IDS_TITLE, buf, ARRAYSIZE(buf)), 0, hDlg, IDC_METALIST); + AddColItem(LocalisedString(IDS_ARTIST, buf, ARRAYSIZE(buf)), 0, hDlg, IDC_METALIST); + AddColItem(LocalisedString(IDS_ALBUM, buf, ARRAYSIZE(buf)), 0, hDlg, IDC_METALIST); + AddColItem(LocalisedString(IDS_GENRE, buf, ARRAYSIZE(buf)), 0, hDlg, IDC_METALIST); + AddColItem(LocalisedString(IDS_YEAR, buf, ARRAYSIZE(buf)), 0, hDlg, IDC_METALIST); + AddColItem(LocalisedString(IDS_COMMENT, buf, ARRAYSIZE(buf)), 0, hDlg, IDC_METALIST); + ListView_SetColumnWidth(metalist, 0, LVSCW_AUTOSIZE); + RECT metalistRect; + GetClientRect(metalist, &metalistRect); + ListView_SetColumnWidth(metalist, 1, (metalistRect.right - metalistRect.left) - ListView_GetColumnWidth(metalist, 0)); + break; + } + + case IDD_PANEL_LINEIN: + { + inWin = NULL; + prevButtonProc = (WNDPROC) SetWindowLongPtrW(GetDlgItem(hDlg, IDC_PTT), GWLP_WNDPROC, (LONG_PTR) buttonProc); + SendDlgItemMessage(hDlg, IDC_MUSSLIDER, TBM_SETRANGE, TRUE, MAKELONG(0, 10)); + SendDlgItemMessage(hDlg, IDC_MUS2SLIDER, TBM_SETRANGE, TRUE, MAKELONG(0, 10)); + SendDlgItemMessage(hDlg, IDC_MICSLIDER, TBM_SETRANGE, TRUE, MAKELONG(0, 10)); + SendDlgItemMessage(hDlg, IDC_FADESLIDER, TBM_SETRANGE, TRUE, MAKELONG(0, 25)); + SendDlgItemMessage(hDlg, IDC_MICFADESLIDER, TBM_SETRANGE, TRUE, MAKELONG(0, 25)); + SendDlgItemMessage(hDlg, IDC_MUSSLIDER, TBM_SETPOS, TRUE, MusVol); + SendDlgItemMessage(hDlg, IDC_MUS2SLIDER, TBM_SETPOS, TRUE, Mus2Vol); + SendDlgItemMessage(hDlg, IDC_MICSLIDER, TBM_SETPOS, TRUE, MicVol); + SendDlgItemMessage(hDlg, IDC_FADESLIDER, TBM_SETPOS, TRUE, FadeTime); + SendDlgItemMessage(hDlg, IDC_MICFADESLIDER, TBM_SETPOS, TRUE, MicFadeTime); + SendMessage(hDlg, WM_HSCROLL, 0, (LPARAM) GetDlgItem(hDlg, IDC_MUSSLIDER)); + SendMessage(hDlg, WM_HSCROLL, 0, (LPARAM) GetDlgItem(hDlg, IDC_MUS2SLIDER)); + SendMessage(hDlg, WM_HSCROLL, 0, (LPARAM) GetDlgItem(hDlg, IDC_MICSLIDER)); + SendMessage(hDlg, WM_HSCROLL, 0, (LPARAM) GetDlgItem(hDlg, IDC_FADESLIDER)); + SendMessage(hDlg, WM_HSCROLL, 0, (LPARAM) GetDlgItem(hDlg, IDC_MICFADESLIDER)); + inWin = hDlg; + SetDeviceName(); + SendMessage(hDlg, WM_COMMAND, MAKEWPARAM(IDC_DEVBOX, CBN_SELCHANGE), (LPARAM)GetDlgItem(hDlg,IDC_DEVBOX)); + break; + } + + case IDD_PANEL_LOGIN: + { + SendDlgItemMessageW(hDlg, IDC_PROTOCOL, CB_ADDSTRING, 0,(LPARAM)LocalisedString(IDS_AUTOMATIC, NULL, 0)); + SendDlgItemMessage(hDlg, IDC_PROTOCOL, CB_SETITEMDATA, 0,(LPARAM)0); + SendDlgItemMessageW(hDlg, IDC_PROTOCOL, CB_ADDSTRING, 0,(LPARAM)LocalisedString(IDS_V2_MODE, NULL, 0)); + SendDlgItemMessage(hDlg, IDC_PROTOCOL, CB_SETITEMDATA, 1,(LPARAM)2); + SendDlgItemMessageW(hDlg, IDC_PROTOCOL, CB_ADDSTRING, 0,(LPARAM)LocalisedString(IDS_V1_MODE, NULL, 0)); + SendDlgItemMessage(hDlg, IDC_PROTOCOL, CB_SETITEMDATA, 2,(LPARAM)1); + SendDlgItemMessage(hDlg, IDC_TIMEOUT, EM_SETLIMITTEXT, 5, 0); + break; + } + + case IDD_PANEL_DIRECTORY: + { + SendDlgItemMessage(hDlg, IDC_GENRES, BM_SETIMAGE, IMAGE_ICON, + (LPARAM)LoadImage(module.hDllInstance, MAKEINTRESOURCE(IDI_DOWNARROW), + IMAGE_ICON, 0, 0, LR_SHARED | LR_LOADTRANSPARENT | LR_CREATEDIBSECTION)); + break; + } + } + } + return 1; + + case WM_CLOSE: + { + if (hDlg == hMainDLG) + { + doQuit(); + } + } + break; + + case WM_USER: + { + if (lParam == nowPlayingID && wParam == 0) { + // Winamp Title handling + wchar_t title[1024] = {0}, + next[1024] = {0}, + song[1024] = {0}, + artist[1024] = {0}, + album[1024] = {0}, + genre[1024] = {0}, + comment[1024] = {0}, + year[32] = {0}, + tmp2[1024] = {0}; + char buffer[1024] = {0}, + buffer2[1024] = {0}; + + // winamp playlist length in tracks + int len = SendMessage(module.hwndParent, WM_WA_IPC, 0, IPC_GETLISTLENGTH); + int curpos = SendMessage(module.hwndParent, WM_WA_IPC, 0, IPC_GETLISTPOS); + int pos = curpos + 1; + int len2 = 0; + + // get the current title + if (len >= 1) { + wcscpy_s(lastFile, MAX_PATH, (wchar_t*)SendMessage(module.hwndParent, WM_WA_IPC, curpos, IPC_GETPLAYLISTFILEW)); + wcscpy_s(title, ARRAYSIZE(title), (wchar_t*)SendMessage(module.hwndParent, WM_WA_IPC, curpos, IPC_GETPLAYLISTTITLEW)); + } else { + title[0] = lastFile[0] = 0; + } + + // get the position of the next track if possible + pos = GetNextTracks(len, pos, next); + + if (skipMetada == false) { + AddColItem(lastFile, 1, inWinWa, IDC_METALIST, 0); + GetFileMetaData(lastFile, L"title", song, ARRAYSIZE(song)); + AddColItem((song && song[0] ? song : title), 1, inWinWa, IDC_METALIST, 1); + GetFileMetaData(lastFile, L"artist", artist, ARRAYSIZE(artist)); + AddColItem((artist[0] ? artist : L""), 1, inWinWa, IDC_METALIST, 2); + GetFileMetaData(lastFile, L"album", album, ARRAYSIZE(album)); + AddColItem((album[0] ? album : L""), 1, inWinWa, IDC_METALIST, 3); + GetFileMetaData(lastFile, L"genre", genre, ARRAYSIZE(genre)); + AddColItem((genre[0] ? genre : L""), 1, inWinWa, IDC_METALIST, 4); + GetFileMetaData(lastFile, L"year", year, ARRAYSIZE(year)); + AddColItem((year[0] ? year : L""), 1, inWinWa, IDC_METALIST, 5); + GetFileMetaData(lastFile, L"comment", comment, ARRAYSIZE(comment)); + AddColItem((comment[0] ? comment : L""), 1, inWinWa, IDC_METALIST, 6); + + if (WASABI_API_MEMMGR && playingImage) { + WASABI_API_MEMMGR->sysFree(playingImage); playingImage = 0; + } + + playingLength = 0; + playingType = 0x4100; // default in-case of issue + + // attempt to get the type of the image, defaulting to jpeg for older versions + // as only on 5.64+ are we able to query Winamp properly for the artwork type + wchar_t* mimeType = 0, *uiType = L"jpeg"; + if (GetWinampVersion(module.hwndParent) >= 0x5064) + { + LPVOID bits; + size_t len; + if (AGAVE_API_ALBUMART && AGAVE_API_ALBUMART->GetAlbumArtData(lastFile, L"cover", &bits, &len, &mimeType) == ALBUMART_SUCCESS) { + // make sure to free the original image after we've converted + playingImage = (ARGB32 *)bits; + playingLength = len; + + wchar_t *ext = &mimeType[0]; + if (ext && *ext) { + uiType = ext; + if (!wcsnicmp(ext, L"jpeg", 4) || !wcsnicmp(ext, L"jpg", 3)) { + playingType = 0x4100; + } else if (!wcsnicmp(ext, L"png", 3)) { + playingType = 0x4101; + } else if (!wcsnicmp(ext, L"bmp", 3)) { + playingType = 0x4102; + } else if (!wcsnicmp(ext, L"gif", 3)) { + playingType = 0x4103; + } + } + + // update the current artwork on the winamp panel + // have to decode as we get the raw data normally + HWND artwork = GetDlgItem(inWinWa, IDC_ARTWORK); + HBITMAP bm = getBitmap(decompressImage(playingImage, playingLength, &playingImage_w, &playingImage_h), playingImage_w, playingImage_h); + HBITMAP bmold = (HBITMAP)SendMessage(artwork, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)bm); + if (bmold) DeleteObject(bmold); + InvalidateRect(artwork, NULL, TRUE); + } + } else { + playingImage_w = 0, playingImage_h = 0; + if (AGAVE_API_ALBUMART && AGAVE_API_ALBUMART->GetAlbumArt(lastFile, L"cover", &playingImage_w, &playingImage_h, &playingImage) == ALBUMART_SUCCESS) { + // make sure to free the original image after we've converted + ARGB32 *firstPlayingImage = playingImage; + + // update the current artwork on the winamp panel + // no need to decode as it's already done by this + HWND artwork = GetDlgItem(inWinWa, IDC_ARTWORK); + HBITMAP bm = getBitmap(playingImage, playingImage_w, playingImage_h); + HBITMAP bmold = (HBITMAP)SendMessage(artwork, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)bm); + if (bmold) DeleteObject(bmold); + InvalidateRect(artwork, NULL, TRUE); + + playingImage = writeImg(playingImage, playingImage_w, playingImage_h, &playingLength, L"jpeg"); + WASABI_API_MEMMGR->sysFree(firstPlayingImage); + } else { + playingImage = 0; + } + } + + wchar_t tmp3[1024] = {0}; + if(playingImage) + { + StringCchPrintfW(tmp3, ARRAYSIZE(tmp3), L"Playing Artwork: Width=%d; Height=%d; Data=%s;", + playingImage_w, playingImage_h, sizeStr(playingLength)); + + // only use this on compatible Winamp installs where possible + wchar_t *mime = 0; + if (GetWinampVersion(module.hwndParent) >= 0x5064 && + AGAVE_API_ALBUMART && AGAVE_API_ALBUMART->GetAlbumArtOrigin(lastFile, L"cover", &mime)) { + if (mime) + { + uiType = wcschr(mime, L'/'); + if (uiType && *uiType) + { + uiType++; + } + } + } else { + if (mimeType) + { + uiType = wcschr(mimeType, L'/'); + if (uiType && *uiType) + { + uiType++; + } + } + } + + wchar_t buf[256] = {0}; + StringCchPrintfW(buf, ARRAYSIZE(buf), LocalisedString(IDS_ARTWORK_SIZES, NULL, 0), + playingImage_w, playingImage_h, sizeStr(playingLength), + (uiType && *uiType ? uiType : mime)); + SetDlgItemTextW(inWinWa, IDC_ARTWORK3, buf); + WASABI_API_MEMMGR->sysFree(mime); + } + else + { + // update the current artwork on the winamp panel + // by setting a generic image when nothing loaded + HWND artwork = GetDlgItem(inWinWa, IDC_ARTWORK); + HBITMAP bmold = (HBITMAP)SendMessage(artwork, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)0); + if (bmold) DeleteObject(bmold); + SetDlgItemTextW(inWinWa, IDC_ARTWORK3, L""); + StringCchPrintfW(tmp3, ARRAYSIZE(tmp3), L"Playing Artwork: Cleared;"); + } + if (mimeType) WASABI_API_MEMMGR->sysFree(mimeType); + ShowWindowDlgItem(inWinWa, IDC_ARTWORK, (playingLength > 0)); + ShowWindowDlgItem(inWinWa, IDC_ARTWORK2, (playingLength == 0)); + ShowWindowDlgItem(inWinWa, IDC_ARTWORK3, (playingLength > 0)); + CreateLogFileMessage(buffer2, tmp3, &len2); + } + + // look at the playback queue so we can get the correct 'next song' + if (WASABI_API_SVC && !WASABI_API_QUEUEMGR) { + // due to loading orders its possible the queue won't have been loaded on init so check + ServiceBuild(WASABI_API_SVC, WASABI_API_QUEUEMGR, QueueManagerApiGUID); + } + + std::vector nextList; + std::vector nextListIdx; + UpdateNextTracks(next, pos, nextListIdx, nextList); + + StringCchPrintfW(tmp2, ARRAYSIZE(tmp2), L"Metadata: Artist=%s; Album=%s; Genre=%s; Year=%s; Comment=%s; Title=%s;", + artist, album, genre, year, comment, (song && song[0] ? song : title)); + len = 0; + CreateLogFileMessage(buffer, tmp2, &len); + + // update the title cache for all of the encoders otherwise we might fail, etc + for (int i = 0; i < NUM_OUTPUTS; i++) { + MY_T_OUTPUT *Out = &Output[i]; + + if (Out->nextTrackLog) { + WriteNextTracks(i, module.hwndParent, nextListIdx, nextList, !!Out->nextTrackLogXML); + } + + if (WaitForSingleObject(Enc_mutex[Out->Encoder], INFINITE) == WAIT_OBJECT_0) { + Encoder[i].UpdateTitleCache(title, nextList, song, album, artist, genre, comment, year, Out->Handle, !!Out->NextTitles); + ReleaseMutex(Enc_mutex[Out->Encoder]); + } + + // skip this all if the mode is not enabled and only in SC2 mode + if ((LOBYTE(Out->Config.protocol) != 1) && Out->useArt) { + // this will only update generally on first connection + if (Out->useStreamArt && streamImage[i] == (ARGB32 *)-1) { + UpdateStreamAlbumArt(Out->Handle, i, Out->stationArtPath, !!Out->useStreamArt); + } + + // this will update against what is read from the playing + if (Out->usePlayingArt) { + UpdatePlayingAlbumArt(Out->Handle, i, Out->useArt && Out->usePlayingArt); + } + } + + // save the updated metadata to the log file (if enabled) + if (Out->Logging && logFiles[i] != INVALID_HANDLE_VALUE) { + DWORD written = 0; + WriteFile(logFiles[i], buffer, len, &written, 0); + WriteFile(logFiles[i], buffer2, len2, &written, 0); + } + } + } + } + break; + + case WM_TIMER: + { + if (wParam == IDD_MAIN || wParam == IDD_INPUT) { + if (VU.update || VU.update != VU.lastUpdate) + { + UpdateVUMeters(); + } + } + + if (wParam == 1234) { // input timer + if (InputDevice == 1) { + for (int i = 0; i < NUM_BUFFERS; i++) { + if (Soundcard.isFilled(last_buffer)) { + short *buffer = Soundcard[last_buffer]; + int scardsmps = Soundcard.getNumSamples(last_buffer); + int numsamps = scardsmps * InputConfig.nch; + if (!VU.update) { + for (int j = 0; j < numsamps; j++) { + if (VU.vu_l < buffer[j]) VU.vu_l = buffer[j]; + if (LineInputAttribs[Input_CurSelPos].nch == 2) { + if (VU.vu_r < buffer[j + 1]) VU.vu_r = buffer[j + 1]; + j++; + } + } + if (LineInputAttribs[Input_CurSelPos].nch == 1) VU.vu_r = VU.vu_l; + VU.update = 1; + } + if (WaitForSingleObject(cf_mutex, INFINITE) == WAIT_OBJECT_0) { + Crossfader->put(buffer, scardsmps); + ReleaseMutex(cf_mutex); + } + Soundcard.cycleBuffer(last_buffer++); + if (last_buffer >= NUM_BUFFERS) last_buffer = 0; + } + } + } + } else if (wParam == IDD_MAIN || wParam == IDD_CONNECTION || + wParam == IDD_INPUT || wParam == IDD_ABOUT) { + wchar_t title[1024] = {0}; + wchar_t next[1024] = {0}; + int states[NUM_OUTPUTS] = {OUT_ERROR,OUT_ERROR,OUT_ERROR,OUT_ERROR,OUT_ERROR}; + + for (int i = 0; i < NUM_OUTPUTS; i++) { + wchar_t tmp[1024] = {0}; + static wchar_t old_tmp[NUM_ENCODERS][1024] = {0}; + MY_T_OUTPUT *Out = &Output[i]; + SHOUTCAST_OUTPUT *Enc = Out->Encoder != -1 ? &Encoder[Out->Encoder] : NULL; + bool encoder_ok = false; + if (Enc) { + C_ENCODER *Encoder = Enc->GetEncoder(); + if (Encoder) { + if (strcmp(Encoder->GetName(), "MP3 Encoder") == 0) { + encoder_ok = (libinst != NULL); + } else { + encoder_ok = true; + } + } + } + lastEnable[i] = (encoder_ok && Out->Config.Address[0] && Out->Config.Password[0] && + stationNameAllowed(Out->Config.Description)); + + if (Out->Encoder == -1 || Out->Handle == -1) { + LocalisedString(IDS_NOT_CONNECTED, tmp, ARRAYSIZE(tmp)); + if (wParam == IDD_CONNECTION && i == Connection_CurSelPos) { + GetDlgItemTextW(out_wnd[3].hWnd, IDC_TITLE, title, sizeof (title) / sizeof (wchar_t)); + GetDlgItemTextW(out_wnd[3].hWnd, IDC_NEXT, next, sizeof (next) / sizeof (wchar_t)); + } + } else { + if (WaitForSingleObject(Enc_mutex[Out->Encoder], INFINITE) == WAIT_OBJECT_0) { + T_OUTPUT_INFO *Info = Enc->GetOutputInfo(Out->Handle); + if (title != NULL && next != NULL && Info != NULL) { + if (Info->Title) + { + free(Info->Title); + } + Info->Title = _wcsdup(title); + } + + states[i] = Enc->GetState(Out->Handle); + switch (states[i]) { + case OUT_ERROR: + { + LocalisedString(IDS_ERROR, tmp, ARRAYSIZE(tmp)); + } + break; + case OUT_DISCONNECTED: + { + if (Info->Succeeded < 0) { + if (Info->ErrorMsg && Info->ErrorMsg[0]) { + // if an error is reported, try to give a user-friendly error message + if (!strcmp("NAK:Deny", Info->ErrorMsg)) + // localised Password error + LocalisedString(IDS_PASS_ERROR, tmp, ARRAYSIZE(tmp)); + else if (!strcmp("CipherFail",Info->ErrorMsg)) + // localised cipher error + LocalisedString(IDS_CIPHER_ERROR, tmp, ARRAYSIZE(tmp)); + else if (!strcmp("BitrateError",Info->ErrorMsg)) + // localised bitrate error (not allowed / not supported) + LocalisedString(IDS_BITRATE_ERROR, tmp, ARRAYSIZE(tmp)); + else if (!strcmp("StreamID",Info->ErrorMsg)) + // localised stream moved error + LocalisedString(IDS_STREAMID_ERROR, tmp, ARRAYSIZE(tmp)); + else if (!strcmp("StreamMoved",Info->ErrorMsg)) + LocalisedString(IDS_STREAM_MOVED_ERROR, tmp, ARRAYSIZE(tmp)); + else if (!strcmp("VersionError",Info->ErrorMsg)) + // localised version error + LocalisedString(IDS_VERSION_ERROR, tmp, ARRAYSIZE(tmp)); + else if (!strcmp("Blocked",Info->ErrorMsg)) + // localised blocked error + LocalisedString(IDS_BLOCKED_ERROR, tmp, ARRAYSIZE(tmp)); + else if (!strcmp("InUse",Info->ErrorMsg)) + // localised in use error + LocalisedString(IDS_IN_USE_ERROR, tmp, ARRAYSIZE(tmp)); + else if (!strcmp("ParseError",Info->ErrorMsg)) + // localised parse error + LocalisedString(IDS_PARSE_ERROR, tmp, ARRAYSIZE(tmp)); + else + // non localised dynamic nak error + StringCchPrintfW(tmp, ARRAYSIZE(tmp), L"%hs", Info->ErrorMsg); + } else { + // localised Password error + LocalisedString(IDS_PASS_ERROR, tmp, ARRAYSIZE(tmp)); + } + Out->Config.AutoRecon = 0; + } else { + if (Info->last_state == OUT_RECV_CIPHER || + Info->last_state == OUT_REQUEST_CIPHER) { + StringCchPrintfW(tmp, ARRAYSIZE(tmp), LocalisedString(IDS_ENABLE_OTHER_MODE, NULL, 0), (Info->last_state == OUT_RECV_CIPHER ? 1 : 2)); + } else { + LocalisedString((lastEnable[i] ? IDS_NOT_CONNECTED : IDS_NOT_CONFIGURED), tmp, ARRAYSIZE(tmp)); + } + lastSec[i] = 0; + } + } + break; + case OUT_CONNECT: + { + if ((clock() - Info->ConnectionTime) / CLOCKS_PER_SEC < 1) { + StringCchPrintfW(tmp, ARRAYSIZE(tmp), LocalisedString(IDS_RECONNECTING_X, NULL, 0), 1); + } else { + LocalisedString(IDS_CONNECTING, tmp, ARRAYSIZE(tmp)); + } + } + break; + case OUT_REQUEST_CIPHER: + { + LocalisedString(IDS_SEND_CIPHER_REQUEST, tmp, ARRAYSIZE(tmp)); + } + break; + case OUT_RECV_CIPHER: + { + LocalisedString(IDS_CIPHER_RESPONSE_RECEIVED, tmp, ARRAYSIZE(tmp)); + } + break; + case OUT_SENDAUTH: + { + LocalisedString(IDS_SENDING_AUTH, tmp, ARRAYSIZE(tmp)); + } + break; + case OUT_RECVAUTHRESPONSE: + { + LocalisedString(IDS_RECEIVING_AUTH_RESPONSE, tmp, ARRAYSIZE(tmp)); + } + break; + case OUT_SEND_MIME: + { + LocalisedString(IDS_SENDING_CONTENT_TYPE, tmp, ARRAYSIZE(tmp)); + } + break; + case OUT_RECV_MIME: + { + LocalisedString(IDS_RESPONSE_RECEIVED, tmp, ARRAYSIZE(tmp)); + } + break; + case OUT_SEND_BITRATE: + { + LocalisedString(IDS_SENDING_BITRATE, tmp, ARRAYSIZE(tmp)); + } + break; + case OUT_RECV_BITRATE: + { + LocalisedString(IDS_RESPONSE_RECEIVED, tmp, ARRAYSIZE(tmp)); + } + break; + case OUT_SEND_BUFSIZE: + { + LocalisedString(IDS_SEND_BUF_SIZE, tmp, ARRAYSIZE(tmp)); + } + break; + case OUT_RECV_BUFSIZE: + { + LocalisedString(IDS_RESPONSE_RECEIVED, tmp, ARRAYSIZE(tmp)); + } + break; + case OUT_SEND_MAX: + { + LocalisedString(IDS_SEND_MAX_PAYLOAD_SIZE, tmp, ARRAYSIZE(tmp)); + } + break; + case OUT_RECV_MAX: + { + LocalisedString(IDS_RESPONSE_RECEIVED, tmp, ARRAYSIZE(tmp)); + } + break; + case OUT_SENDYP: + { + LocalisedString(IDS_SEND_YP_INFO, tmp, ARRAYSIZE(tmp)); + } + break; + case OUT_SEND_INITFLUSH: + { + LocalisedString(IDS_SEND_FLUSH, tmp, ARRAYSIZE(tmp)); + } + break; + case OUT_RECV_INITFLUSH: + { + LocalisedString(IDS_RESPONSE_RECEIVED, tmp, ARRAYSIZE(tmp)); + } + break; + case OUT_SEND_INITSTANDBY: + { + LocalisedString(IDS_SEND_STANDBY, tmp, ARRAYSIZE(tmp)); + } + break; + case OUT_RECV_INITSTANDBY: + { + LocalisedString(IDS_RESPONSE_RECEIVED, tmp, ARRAYSIZE(tmp)); + } + break; + /*case OUT_SEND_INTRO: + { + LocalisedString(IDS_SEND_INTRO_FILE, tmp, ARRAYSIZE(tmp)); + } + break; + case OUT_RECV_INTRO: + { + LocalisedString(IDS_RESPONSE_RECEIVED, tmp, ARRAYSIZE(tmp)); + } + break; + case OUT_SEND_BACKUP: + { + LocalisedString(IDS_SEND_BACKUP_FILE, tmp, ARRAYSIZE(tmp)); + } + break; + case OUT_RECV_BACKUP: + { + LocalisedString(IDS_RESPONSE_RECEIVED, tmp, ARRAYSIZE(tmp)); + } + break;*/ + case OUT_SEND_ARTWORK: + case OUT_SEND_METADATA: + break; + case OUT_SENDCONTENT: + { + time_t time_value = time(NULL) - Info->ConnectedAt; + long hour = (long)(time_value/3600); + long min = (time_value/60)%60; + long sec = time_value%60; + static wchar_t format[256]; + if (!format[0]) LocalisedString(IDS_SENT_X, format, ARRAYSIZE(format)); + StringCchPrintfW(tmp, ARRAYSIZE(tmp), format, hour, min, sec, sizeStr(Info->BytesSent)); + lastSec[i] = sec; + + // do this to filter out some of the log + // events so it'll only happen every second + if (lastSec[i] == sec - 1) { + secChanged[i] = true; + } + } + break; + case OUT_DISCONNECT: + { + LocalisedString(IDS_DISCONNECTING, tmp, ARRAYSIZE(tmp)); + } + break; + case OUT_RECONNECT: + { + if (Info->Reconnect) { + int seconds = Info->ReconnectTime - ((clock() - Info->ConnectionTime) / CLOCKS_PER_SEC); + if (seconds > 0) { + StringCchPrintfW(tmp, ARRAYSIZE(tmp), LocalisedString(IDS_RECONNECTING_X, NULL, 0), seconds); + } else { + if (Info->Switching) { + StringCchPrintfW(tmp, ARRAYSIZE(tmp), LocalisedString(IDS_SWITCHING_PROTOCOL, NULL, 0), + (Info->Switching == 2 ? 2 : 1), (Info->Switching == 2 ? 1 : 2)); + } else { + StringCchPrintfW(tmp, ARRAYSIZE(tmp), LocalisedString(IDS_RECONNECTING_X, NULL, 0), seconds); + } + } + } else { + if (Info->Switching) { + StringCchPrintfW(tmp, ARRAYSIZE(tmp), LocalisedString(IDS_SWITCHING_PROTOCOL, NULL, 0), + (Info->Switching == 2 ? 2 : 1), (Info->Switching == 2 ? 1 : 2)); + } else { + LocalisedString(IDS_DISCONNECTING, tmp, ARRAYSIZE(tmp)); + } + } + } + break; + case OUT_TITLESENDUPDATE: + { + LocalisedString(IDS_SEND_TITLE_UPDATE, tmp, ARRAYSIZE(tmp)); + } + break; + } + + if (Out->AutoConnect && states[i] == OUT_DISCONNECTED) { + Enc->ConnectOutput(Out->Handle); + } + + ReleaseMutex(Enc_mutex[Out->Encoder]); + } + } + + // output log messages to the log files if enabled + // but filter things a bit more with the sent state + if (tmp[0] && wcsnicmp(old_tmp[i], tmp, ARRAYSIZE(tmp))) { + if (Out->Logging && logFiles[i] != INVALID_HANDLE_VALUE) { + if (states[i] != OUT_SENDCONTENT || (states[i] == OUT_SENDCONTENT && secChanged[i] == true)) { + DWORD written = 0; + int len = 0; + char buffer[1024]; + if (states[i] == OUT_CONNECT) { + wchar_t tmp2[2048] = {0}; + int protocol = (LOBYTE(Out->Config.protocol) != 1 ? 2 : 1); + StringCchPrintfW(tmp2, ARRAYSIZE(tmp2), + L"Connecting to... Server: %hs; Port: %d; Mode: v%d; Stream ID: %hs; DJ / User ID: %hs", + Out->Config.Address, Out->Config.Port, protocol, + (protocol == 1 ? "n/a" : Out->Config.StationID), + (!Out->Config.UserID[0] ? "n/a" : Out->Config.UserID)); + CreateLogFileMessage(buffer, tmp2, &len); + WriteFile(logFiles[i], buffer, len, &written, 0); + } else { + CreateLogFileMessage(buffer, tmp, &len); + WriteFile(logFiles[i], buffer, len, &written, 0); + } + } + } + secChanged[i] = false; + } + + // update summary and output page view and text states as applicable + if (wParam == IDD_MAIN || + (wParam == IDD_CONNECTION && i == Connection_CurSelPos)) { + // update status text for the output being processed + if (tmp[0]) { + if (wParam == IDD_CONNECTION && i == Connection_CurSelPos) { + SetDlgItemTextW(wnd[1].hWnd, IDC_STATUS, tmp); + } + AddColItem(tmp, 2, wnd[0].hWnd, IDC_OUTPUTSTATUS, i); + } + + bool encoder_ok = false; + if (Enc) { + C_ENCODER *Encoder = Enc->GetEncoder(); + if (Encoder) { + if (strcmp(Encoder->GetName(), "MP3 Encoder") == 0) { + encoder_ok = (libinst != NULL); + } else { + encoder_ok = true; + } + } + } + + // update the playback buttons on the summary page + int mode = (Out->Config.Address[0] ? (Out->Config.Password[0] ? + (stationNameAllowed(Out->Config.Description) ? + (encoder_ok ? (states[i] == OUT_DISCONNECTED ? 0 : + states[i] == OUT_DISCONNECT ? 1 : states[i] == OUT_RECONNECT ? 7 : 2) : 6) : 5) : 4) : 3); + + // used to limit the amount of processing which is done for this to keep updates to just what is needed + if (lastMode[i] == -1 || lastMode[i] != mode) { + int image_id[] = {IDI_PLAY, IDI_KILL, IDI_STOP, IDI_PLAY, IDI_PLAY, IDI_PLAY, IDI_PLAY, IDI_STOP}; + + // do checks to see if we need to update the error state of the tab items + int oldMode = lastMode[i]; + lastMode[i] = mode; + + RECT r = {0}; + if (IsWindow(outTabWnd)) { + int index[] = {0, 0, 1, 2}; + if (lastMode[i] >= 3 && lastMode[i] <= 6) { + TabCtrl_GetItemRect(outTabWnd, index[(lastMode[i] - 3)], &r); + InvalidateRect(outTabWnd, &r, 0); + } + + if (oldMode >= 3 && oldMode <= 6) { + TabCtrl_GetItemRect(outTabWnd, index[(oldMode - 3)], &r); + InvalidateRect(outTabWnd, &r, 0); + } + } + + TabCtrl_GetItemRect(GetDlgItem(hMainDLG, IDC_TAB), 1, &r); + InvalidateRect(GetDlgItem(hMainDLG, IDC_TAB), &r, 0); + + // control the button states on the pages + EnableWindow(buttonWnd[i], lastEnable[i]); + + EnableWindowDlgItem(wnd[1].hWnd, IDC_CONNECT, lastEnable[i]); + EnableWindowDlgItem(wnd[1].hWnd, IDC_AUTOCONNECT, lastEnable[i]); + + SendMessage(buttonWnd[i], BM_SETIMAGE, IMAGE_ICON, + (LPARAM)LoadImage(module.hDllInstance, MAKEINTRESOURCE(image_id[mode]), + IMAGE_ICON, 0, 0, LR_SHARED | LR_LOADTRANSPARENT | LR_CREATEDIBSECTION)); + + // control the 'connect' button to be disabled when no encoder / no password / invalid station name + if (i == Connection_CurSelPos) { + int button_id[] = {IDS_CONNECT, IDS_KILL, IDS_DISCONNECT, IDS_SET_SERVER, IDS_SET_PASSWORD, IDS_CHANGE_NAME, IDS_SET_ENCODER, IDS_ABORT}; + SetDlgItemTextW(wnd[1].hWnd, IDC_CONNECT, LocalisedString(button_id[mode], NULL, 0)); + LockOptionControls((states[i] == OUT_DISCONNECTED && Out->Handle != -1)); + + InvalidateRect(GetDlgItem(out_wnd[0].hWnd, IDC_ADDRESS_HEADER), 0, 0); + InvalidateRect(GetDlgItem(out_wnd[0].hWnd, IDC_PASSWORD_HEADER), 0, 0); + InvalidateRect(GetDlgItem(out_wnd[1].hWnd, IDC_NAME_HEADER), 0, 0); + InvalidateRect(GetDlgItem(out_wnd[2].hWnd, IDC_ENCODER_HEADER), 0, 0); + } + } + + // update the title set + if (Out->AutoTitle) SetDlgItemTextW(out_wnd[0].hWnd, IDC_TITLE, title); + } + + // preserve the last status message for filtering of the log output, etc + if (tmp[0]) wcsncpy(old_tmp[i], tmp, 1024); + } + } else if (wParam == 1337) { // stream title update + KillTimer(hDlg, wParam); + PostMessage(hMainDLG, WM_USER, 0, nowPlayingID); + } else if (wParam == 2234) { // fade (music) + if (InputDevice) { + clock_t MusCurTime = clock(); + if (FadeStartTime == 0) { + FadeStartTime = MusCurTime; + } + MusCurTime -= FadeStartTime; + int micsrc = Input_Device_ID >= 1 ? MIXERLINE_COMPONENTTYPE_SRC_LINE : MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE; + if (MusCurTime < (FadeTime * 100)) { + int musvol = FadeOut ? (MusVol * 10)+((((Mus2Vol - MusVol)*10) * MusCurTime) / (FadeTime * 100)) : (Mus2Vol * 10)+((((MusVol - Mus2Vol)*10) * MusCurTime) / (FadeTime * 100)); + setlev(MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT, musvol); + } else { + if (FadeOut) { + setlev(MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT, Mus2Vol * 10); + } else { + setlev(MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT, MusVol * 10); + } + FadeStartTime = 0; + KillTimer(hDlg, wParam); + // kill captured device + #ifdef CAPTURE_TESTING + if (!FadeOut) {// end_capture(); + if (pPlayer != NULL) { + pPlayer->Stop(); + delete pPlayer; + pPlayer = NULL; + } + } + #endif + } + } + } else if (wParam == 2235) { // fade (capture device) + if (InputDevice) { + clock_t MicCurTime = clock(); + if (MicFadeStartTime == 0) { + MicFadeStartTime = MicCurTime; + } + MicCurTime -= MicFadeStartTime; + int micsrc = Input_Device_ID >= 1 ? MIXERLINE_COMPONENTTYPE_SRC_LINE : MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE; + if (MicCurTime < (MicFadeTime * 100)) { + int micvol = FadeOut ? ((MicVol * 10) * MicCurTime) / (MicFadeTime * 100) : (MicVol * 10)+(((MicVol*-10) * MicCurTime) / (MicFadeTime * 100)); + setlev(micsrc, micvol); + } else { + if (FadeOut) { + setlev(micsrc, MicVol * 10); + } else { + setlev(micsrc, 0); + } + MicFadeStartTime = 0; + KillTimer(hDlg, wParam); + // kill captured device + #ifdef CAPTURE_TESTING + if (!FadeOut) {// end_capture(); + if (pPlayer != NULL) { + pPlayer->Stop(); + delete pPlayer; + pPlayer = NULL; + } + } + #endif + } + } + } else if (wParam == 666) { // INI save timer + if (!ini_modified) break; + ini_modified = 0; + int i; + WritePrivateProfileInt("ofnidx", lastFilterIndex, 0, 0); + WritePrivateProfileInt("CurTab", curtab, 0, 0); + WritePrivateProfileInt("Connection_CurSelPos", Connection_CurSelPos, 0, 0); + WritePrivateProfileInt("Connection_CurTab", curouttab, 0, 0); + WritePrivateProfileInt("Encoder_CurSelPos", Encoder_CurSelPos, 0, 0); + WritePrivateProfileInt("Input_CurSelPos", Input_CurSelPos, 0, 0); + WritePrivateProfileInt("InputDevice", InputDevice, 0, 0); + WritePrivateProfileInt("MusicVolume", MusVol, 0, 0); + WritePrivateProfileInt("BGMusicVolume", Mus2Vol, 0, 0); + WritePrivateProfileInt("MicVolume", MicVol, 0, 0); + WritePrivateProfileInt("PTT_FT", FadeTime, 0, 0); + WritePrivateProfileInt("PTT_MicFT", MicFadeTime, 0, 0); + WritePrivateProfileInt("PTT_MicInput", Input_Device_ID, 0, 0); + WritePrivateProfileInt("PTT_Restore", Restore_PTT, 0, 0); + + if (!IsIconic(hDlg)) { + WritePrivateProfileInt("WindowLeft", mainrect.left, 0, 0); + WritePrivateProfileInt("WindowTop", mainrect.top, 0, 0); + } + + for (i = 0; i < NUM_ENCODERS; i++) { + int Type = 0; + char name[32]; + StringCchPrintfA(name, ARRAYSIZE(name), "Encoder %u", i + 1); + + if (WaitForSingleObject(Enc_mutex[i], INFINITE) == WAIT_OBJECT_0) { + C_ENCODER *Enc = Encoder[i].GetEncoder(); + if (Enc) { + if (strcmp(Enc->GetName(), "MP3 Encoder") == 0) { + int infosize = sizeof (T_ENCODER_MP3_INFO); + T_ENCODER_MP3_INFO *EncInfo = (T_ENCODER_MP3_INFO *) Enc->GetExtInfo(&infosize); + WritePrivateProfileInt("BitRate", EncInfo->output_bitRate, name, 0); + WritePrivateProfileInt("SampleRate", EncInfo->output_sampleRate, name, 0); + WritePrivateProfileInt("NumChannels", EncInfo->output_numChannels, name, 0); + WritePrivateProfileInt("QualityMode", EncInfo->QualityMode, name, 0); + Type = 1; + } else if (strcmp(Enc->GetName(), "Fraunhofer Encoder") == 0) { // FHG AAC + ((C_ENCODER_NSV*) Enc)->FillConfFile(IniName, name); + Type = 2; + } else if (strcmp(Enc->GetName(), "AAC+ Encoder") == 0) { // AAC+ + ((C_ENCODER_NSV*) Enc)->FillConfFile(IniName, name); + Type = 2; + } +#ifdef USE_OGG + else if (strcmp(Enc->GetName(), "OGG Vorbis Encoder") == 0) { // OGG + ((C_ENCODER_NSV*) Enc)->FillConfFile(IniName, name); + Type = 4; + } +#endif + } + ReleaseMutex(Enc_mutex[i]); + } + WritePrivateProfileInt("Type", Type, name, 0); + } + + for (i = 0; i < NUM_OUTPUTS; i++) { + T_OUTPUT_CONFIG *Out = &Output[i].Config; + WritePrivateProfileString(Out->Name, "Address", Out->Address, IniName); + WritePrivateProfileInt("Port", Out->Port, Out->Name, 0); + WritePrivateProfileString(Out->Name, "UserID", Out->UserID, IniName); + WritePrivateProfileString(Out->Name, "StreamID", Out->StationID, IniName); + WritePrivateProfileString(Out->Name, "Password", Out->Password, IniName); + // disabled saving of this as it defeats the point of setting it on load + // (as it's otherwise over-written with the correct value from the server) + //WritePrivateProfileString(Out->Name, "Cipherkey", Out->cipherkey, IniName); + WritePrivateProfileString(Out->Name, "Description", Out->Description, IniName); + WritePrivateProfileString(Out->Name, "URL", Out->ServerURL, IniName); + WritePrivateProfileString(Out->Name, "Genre3", Out->Genre, IniName); + WritePrivateProfileString(Out->Name, "AIM", Out->AIM, IniName); + WritePrivateProfileString(Out->Name, "ICQ", Out->ICQ, IniName); + WritePrivateProfileString(Out->Name, "IRC", Out->IRC, IniName); + WritePrivateProfileInt("Public", Out->Public ? 1 : 0, Out->Name, 0); + WritePrivateProfileInt("AutoRecon", Out->AutoRecon ? 1 : 0, Out->Name, 0); + WritePrivateProfileInt("ReconTime", Out->ReconTime ? Out->ReconTime : 1, Out->Name, 0); + WritePrivateProfileInt("doTitleUpdate", Out->doTitleUpdate ? 1 : 0, Out->Name, 0); + WritePrivateProfileString(Out->Name, "now", Out->Now, IniName); + WritePrivateProfileString(Out->Name, "next", Out->Next, IniName); + WritePrivateProfileInt("AutoTitle", Output[i].AutoTitle ? 1 : 0, Out->Name, 0); + WritePrivateProfileInt("AutoConnect", Output[i].AutoConnect ? 1 : 0, Out->Name, 0); + WritePrivateProfileInt("Logging", Output[i].Logging ? 1 : 0, Out->Name, 0); + WritePrivateProfileInt("LogCOS", Output[i].LogCOS ? 1 : 0, Out->Name, 0); + WritePrivateProfileInt("NextTitles", Output[i].NextTitles ? 1 : 0, Out->Name, 0); + WritePrivateProfileInt("Protocol", Output[i].Config.protocol, Out->Name, 0); + WritePrivateProfileInt("nextTrackLog", Output[i].nextTrackLog ? 1 : 0, Out->Name, 0); + WritePrivateProfileInt("nextTrackLogXML", Output[i].nextTrackLogXML ? 1 : 0, Out->Name, 0); + WritePrivateProfileString(Out->Name, "nextTrackPath", ConvertToUTF8(Output[i].nextTrackPath), IniName); + WritePrivateProfileInt("useArt", Output[i].useArt ? 1 : 0, Out->Name, 0); + WritePrivateProfileInt("usePlayingArt", Output[i].usePlayingArt ? 1 : 0, Out->Name, 0); + WritePrivateProfileInt("useStreamArt", Output[i].useStreamArt ? 1 : 0, Out->Name, 0); + WritePrivateProfileString(Out->Name, "stationArtPath", ConvertToUTF8(Output[i].stationArtPath), IniName); + WritePrivateProfileInt("saveEncoded", Output[i].saveEncoded ? 1 : 0, Out->Name, 0); + WritePrivateProfileString(Out->Name, "saveEncodedPath", ConvertToUTF8(Output[i].saveEncodedPath), IniName); + WritePrivateProfileInt("Encoder", Output[i].Encoder, Out->Name, 0); + } + } + } + break; + + case WM_SHOWWINDOW: + { + if (IsWindow(hDlg) && hDlg == hMainDLG && wnd[curtab].timer_freq != 0) { + KillTimer(hDlg, wnd[curtab].id); + SetTimer(hDlg, wnd[curtab].id, wnd[curtab].timer_freq, NULL); + } else if (IsWindow(hDlg) && hDlg == wnd[2].hWnd && in_wnd[InputDevice].timer_freq != 0) { + KillTimer(hDlg, in_wnd[InputDevice].id); + SetTimer(hDlg, in_wnd[InputDevice].id, in_wnd[InputDevice].timer_freq, NULL); + } else if (IsWindow(hDlg) && hDlg == wnd[1].hWnd && out_wnd[curouttab].timer_freq != 0) { + KillTimer(hDlg, out_wnd[curouttab].id); + SetTimer(hDlg, out_wnd[curouttab].id, out_wnd[curouttab].timer_freq, NULL); + } + } + break; + + //Handle Minimize to systray here + case WM_SIZE: + { + switch (wParam) { + case SIZE_RESTORED: + { + RemoveSystrayIcon(hDlg, SYSTRAY_ICY_ICON); + ShowWindow(hDlg, SW_SHOW); + } + break; + case SIZE_MINIMIZED: + { + AddSystrayIcon(hDlg, SYSTRAY_ICY_ICON, GetICYIcon(), + SYSTRAY_MAXIMIZE_MSG, szDescription2W); + ShowWindow(hDlg, SW_HIDE); + } + break; + } + } + break; + + case WM_COMMAND: + { + switch (LOWORD(wParam)) { + case IDC_NOTITLES: + case IDC_AUTOTITLE: + case IDC_MANUALTITLE: + //case IDC_EXTERNALTITLE: + { + // TODO will need to change around some of the logic to cope with the external option + if (HIWORD(wParam) == BN_CLICKED) { + MY_T_OUTPUT *Out = &Output[Connection_CurSelPos]; + int lastAutoTitle = Out->AutoTitle; + Out->Config.doTitleUpdate = (LOWORD(wParam) != IDC_NOTITLES); + Out->AutoTitle = (LOWORD(wParam) == IDC_AUTOTITLE); + ini_modified = 1; + CheckRadioButton(hDlg, IDC_NOTITLES, IDC_MANUALTITLE, LOWORD(wParam)); + UpdateTitleControls(); + // if enabling then send the title update + if ((LOWORD(wParam) == IDC_AUTOTITLE) && lastAutoTitle != Out->AutoTitle) { + if (Out->Encoder != -1 && Out->Handle != -1) { + SHOUTCAST_OUTPUT *Enc = &Encoder[Out->Encoder]; + if (WaitForSingleObject(Enc_mutex[Out->Encoder], INFINITE) == WAIT_OBJECT_0) { + std::vector nextList; + nextList.clear(); + Enc->UpdateTitle(0, nextList, Out->Handle, !!Out->NextTitles, true); + ReleaseMutex(Enc_mutex[Out->Encoder]); + } + } + } + } + } + break; + + case IDC_SENDNEXTTITLES: + { + if (HIWORD(wParam) == BN_CLICKED) { + MY_T_OUTPUT *Out = &Output[Connection_CurSelPos]; + Out->NextTitles = SendMessage((HWND) lParam, BM_GETCHECK, 0, 0) == BST_CHECKED; + ini_modified = 1; + } + } + break; + + case IDC_VIEW_LOG: + { + wchar_t file[MAX_PATH]; + ShellExecuteW(hMainDLG, L"open", GetSCLogFile(module.hwndParent, ARRAYSIZE(file), file, Connection_CurSelPos), 0, NULL, SW_SHOW); + } + break; + + case IDC_CLEAR_LOG: + { + MY_T_OUTPUT *Out = &Output[Connection_CurSelPos]; + if (Out->Logging && logFiles[Connection_CurSelPos] != INVALID_HANDLE_VALUE) { + SetFilePointer(logFiles[Connection_CurSelPos], 0, NULL, FILE_BEGIN); + SetEndOfFile(logFiles[Connection_CurSelPos]); + } else { + wchar_t file[MAX_PATH]; + HANDLE handle = CreateFileW(GetSCLogFile(module.hwndParent, ARRAYSIZE(file), file, Connection_CurSelPos), + GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0); + if (handle != INVALID_HANDLE_VALUE) { + SetFilePointer(handle, 0, NULL, FILE_BEGIN); + SetEndOfFile(handle); + CloseHandle(handle); + } + } + } + break; + + case IDC_LOGGING: + { + if (HIWORD(wParam) == BN_CLICKED) { + MY_T_OUTPUT *Out = &Output[Connection_CurSelPos]; + Out->Logging = SendMessage((HWND) lParam, BM_GETCHECK, 0, 0) == BST_CHECKED; + if (Out->Logging) { + StartLogging(Connection_CurSelPos,Out->LogCOS); + } else { + StopLogging(Connection_CurSelPos); + } + ini_modified = 1; + } + } + break; + + case IDC_CLEAR_ON_STARTUP: + { + if (HIWORD(wParam) == BN_CLICKED) { + MY_T_OUTPUT *Out = &Output[Connection_CurSelPos]; + Out->LogCOS = SendMessage((HWND) lParam, BM_GETCHECK, 0, 0) == BST_CHECKED; + ini_modified = 1; + } + } + break; + + case IDC_NEXT_TRACK_LOG: + { + if (HIWORD(wParam) == BN_CLICKED) { + MY_T_OUTPUT *Out = &Output[Connection_CurSelPos]; + Out->nextTrackLog = SendMessage((HWND) lParam, BM_GETCHECK, 0, 0) == BST_CHECKED; + if (Out->nextTrackLog) { + StartNextTracks(Connection_CurSelPos, Out->nextTrackPath); + FillNextTracks(Connection_CurSelPos, !!Out->nextTrackLogXML); + } else { + StopNextTracks(Connection_CurSelPos); + } + EnableWindowDlgItem(out_wnd[5].hWnd, IDC_NEXT_TRACK_XML, Out->nextTrackLog); + EnableWindowDlgItem(out_wnd[5].hWnd, IDC_NEXT_TRACK_EDIT, Out->nextTrackLog); + EnableWindowDlgItem(out_wnd[5].hWnd, IDC_NEXT_TRACK_BROWSE, Out->nextTrackLog); + + ini_modified = 1; + } + } + break; + + case IDC_NEXT_TRACK_XML: + { + if (HIWORD(wParam) == BN_CLICKED) { + MY_T_OUTPUT *Out = &Output[Connection_CurSelPos]; + Out->nextTrackLogXML = SendMessage((HWND) lParam, BM_GETCHECK, 0, 0) == BST_CHECKED; + // reset the file if changing the state + if (Out->nextTrackLog) { + StopNextTracks(Connection_CurSelPos); + StartNextTracks(Connection_CurSelPos, Out->nextTrackPath); + FillNextTracks(Connection_CurSelPos, !!Out->nextTrackLogXML); + } + ini_modified = 1; + } + } + break; + + case IDC_NEXT_TRACK_EDIT: + { + if (HIWORD(wParam) == EN_SETFOCUS) { + PostMessage((HWND)lParam, EM_SETSEL, 0, (LPARAM)-1); + } + } + break; + + case IDC_NEXT_TRACK_BROWSE: + { + if (HIWORD(wParam) == BN_CLICKED) { + wchar_t filepath[MAX_PATH] = {0}, + file[MAX_PATH] = {0}, + filter[64] = {0}; + // strip path so we can set initial directory, etc + MY_T_OUTPUT *Out = &Output[Connection_CurSelPos]; + wcsncpy(filepath, Out->nextTrackPath, MAX_PATH); + wcsncpy(file, Out->nextTrackPath, MAX_PATH); + PathRemoveFileSpecW(filepath); + PathStripPathW(file); + + OPENFILENAMEW ofn = {0}; + ofn.lStructSize=sizeof(ofn); + ofn.hwndOwner=hMainDLG; + + WASABI_API_LNGSTRINGW_BUF(IDS_ALL_FILES, filter, 64); + wchar_t * ptr=filter; + while(ptr && *ptr) { + if (*ptr==L'|') *ptr=0; + ptr++; + } + + ofn.lpstrFilter=filter; + ofn.lpstrInitialDir=filepath; + ofn.lpstrFile=file; + ofn.nMaxFile=MAX_PATH; + ofn.Flags=OFN_HIDEREADONLY|OFN_PATHMUSTEXIST; + ofn.lpstrDefExt=L"log"; + if (GetSaveFileNameW(&ofn)) + { + // update things if the filename changed, etc + wcsncpy(Out->nextTrackPath, file, MAX_PATH); + SetDlgItemTextW(out_wnd[5].hWnd, IDC_NEXT_TRACK_EDIT, Out->nextTrackPath); + if (Out->nextTrackLog) { + StopNextTracks(Connection_CurSelPos); + StartNextTracks(Connection_CurSelPos, Out->nextTrackPath); + } + } + } + } + break; + + case IDC_SAVE_ENCODED_AUDIO: + { + if (HIWORD(wParam) == BN_CLICKED) { + MY_T_OUTPUT *Out = &Output[Connection_CurSelPos]; + Out->saveEncoded = SendMessage((HWND) lParam, BM_GETCHECK, 0, 0) == BST_CHECKED; + + // reset the file if changing the state + if (Out->saveEncoded) { + StartSaveEncoded(Connection_CurSelPos, Out->saveEncodedPath); + } else { + StopSaveEncoded(Connection_CurSelPos); + } + ini_modified = 1; + } + } + break; + + case IDC_SAVE_ENCODED_AUDIO_EDIT: + { + if (HIWORD(wParam) == EN_SETFOCUS) { + PostMessage((HWND)lParam, EM_SETSEL, 0, (LPARAM)-1); + } + } + break; + + case IDC_SAVE_ENCODED_AUDIO_BROWSE: + { + if (HIWORD(wParam) == BN_CLICKED) { + wchar_t filepath[MAX_PATH] = {0}, + file[MAX_PATH] = {0}, + filter[64] = {0}; + // strip path so we can set initial directory, etc + MY_T_OUTPUT *Out = &Output[Connection_CurSelPos]; + wcsncpy(filepath, Out->saveEncodedPath, MAX_PATH); + wcsncpy(file, Out->saveEncodedPath, MAX_PATH); + PathRemoveFileSpecW(filepath); + PathStripPathW(file); + + OPENFILENAMEW ofn = {0}; + ofn.lStructSize=sizeof(ofn); + ofn.hwndOwner=hMainDLG; + // sets the default extension if not specified to the type of the encoder being used + ofn.lpstrDefExt=(Enc_LastType[Connection_CurSelPos] != 0 ? + (!strcmp(Encoder[Out->Encoder].GetEncoder()->GetContentType(),"audio/mpeg") ? L"mp3" : + (!strcmp(Encoder[Out->Encoder].GetEncoder()->GetContentType(),"audio/ogg") ? L"ogg" : L"aac")) : L""); + + StringCchPrintfW(filter, ARRAYSIZE(filter), WASABI_API_LNGSTRINGW(IDS_MPEG_AUDIO_FILES), ofn.lpstrDefExt, ofn.lpstrDefExt); + wchar_t* ptr=filter; + while(ptr && *ptr) { + if (*ptr==L'|') *ptr=0; + ptr++; + } + + ofn.lpstrFilter=filter; + ofn.lpstrInitialDir=filepath; + ofn.lpstrFile=file; + ofn.nMaxFile=MAX_PATH; + ofn.Flags=OFN_HIDEREADONLY|OFN_PATHMUSTEXIST; + + if (GetSaveFileNameW(&ofn)) + { + // update things if the filename changed, etc + wcsncpy(Out->saveEncodedPath, file, MAX_PATH); + SetDlgItemTextW(out_wnd[2].hWnd, IDC_SAVE_ENCODED_AUDIO_EDIT, Out->saveEncodedPath); + if (Out->saveEncoded) { + StopSaveEncoded(Connection_CurSelPos); + StartSaveEncoded(Connection_CurSelPos, Out->saveEncodedPath); + } + } + } + } + break; + + case IDC_USE_ART: + { + if (HIWORD(wParam) == BN_CLICKED) { + MY_T_OUTPUT *Out = &Output[Connection_CurSelPos]; + Out->useArt = SendMessage((HWND) lParam, BM_GETCHECK, 0, 0) == BST_CHECKED; + + EnableWindowDlgItem(out_wnd[4].hWnd, IDC_USE_ART_PLAYING, Out->useArt); + EnableWindowDlgItem(out_wnd[4].hWnd, IDC_USE_ART_STREAM, Out->useArt); + EnableWindowDlgItem(out_wnd[4].hWnd, IDC_ART_EDIT, Out->useArt && Out->useStreamArt); + EnableWindowDlgItem(out_wnd[4].hWnd, IDC_ART_BROWSE, Out->useArt && Out->useStreamArt); + + // only update if we need to do so + UpdateStreamAlbumArt(Out->Handle, Connection_CurSelPos, Out->stationArtPath, Out->useArt && Out->useStreamArt); + + // this will update against what is read from the playing + UpdatePlayingAlbumArt(Out->Handle, Connection_CurSelPos, Out->useArt && Out->usePlayingArt); + + Encoder[Connection_CurSelPos].UpdateArtwork(Out->Handle); + + ini_modified = 1; + } + } + break; + + case IDC_USE_ART_PLAYING: + { + if (HIWORD(wParam) == BN_CLICKED) { + MY_T_OUTPUT *Out = &Output[Connection_CurSelPos]; + Out->usePlayingArt = SendMessage((HWND) lParam, BM_GETCHECK, 0, 0) == BST_CHECKED; + ini_modified = 1; + + if(Out->usePlayingArt){ + playingLength = 0; + playingType = 0x4101; // default in-case of issue + + int w = 0, h = 0; + if (AGAVE_API_ALBUMART && AGAVE_API_ALBUMART->GetAlbumArt(lastFile, L"cover", &w, &h, &playingImage) == ALBUMART_SUCCESS) { + // make sure to free the original image after we've converted + ARGB32 *firstPlayingImage = playingImage; + playingImage = writeImg(playingImage, w, h, &playingLength, L"png"); + WASABI_API_MEMMGR->sysFree(firstPlayingImage); + } else { + playingImage = 0; + } + } + + UpdatePlayingAlbumArt(Out->Handle, Connection_CurSelPos, Out->useArt && Out->usePlayingArt); + Encoder[Connection_CurSelPos].UpdateArtwork(Out->Handle); + } + } + break; + + case IDC_USE_ART_STREAM: + { + if (HIWORD(wParam) == BN_CLICKED) { + MY_T_OUTPUT *Out = &Output[Connection_CurSelPos]; + Out->useStreamArt = SendMessage((HWND) lParam, BM_GETCHECK, 0, 0) == BST_CHECKED; + ini_modified = 1; + + // skip this all if the mode is not enabled and only in SC2 mode + if ((LOBYTE(Out->Config.protocol) != 1) && Out->useArt) { + // this will only update generally on first connection + if (Out->useStreamArt && streamImage[Connection_CurSelPos] == (ARGB32 *)-1) { + UpdateStreamAlbumArt(Out->Handle, Connection_CurSelPos, Out->stationArtPath, !!Out->useStreamArt); + } + } + + if (UpdateStreamAlbumArt(Out->Handle, Connection_CurSelPos, Out->stationArtPath, !!Out->useStreamArt)) { + Encoder[Connection_CurSelPos].UpdateArtwork(Out->Handle); + } + + EnableWindowDlgItem(out_wnd[4].hWnd, IDC_ART_EDIT, Out->useStreamArt); + EnableWindowDlgItem(out_wnd[4].hWnd, IDC_ART_BROWSE, Out->useStreamArt); + } + } + break; + + case IDC_ART_EDIT: + { + if (HIWORD(wParam) == EN_SETFOCUS) { + PostMessage((HWND)lParam, EM_SETSEL, 0, (LPARAM)-1); + } + } + break; + + case IDC_ART_BROWSE: + { + if (HIWORD(wParam) == BN_CLICKED) { + wchar_t filepath[MAX_PATH] = {0}, + file[MAX_PATH] = {0}; + // strip path so we can set initial directory, etc + MY_T_OUTPUT *Out = &Output[Connection_CurSelPos]; + wcsncpy(filepath, Out->stationArtPath, MAX_PATH); + wcsncpy(file, Out->stationArtPath, MAX_PATH); + PathRemoveFileSpecW(filepath); + PathStripPathW(file); + + OPENFILENAMEW ofn = {0}; + ofn.lStructSize=sizeof(ofn); + ofn.hwndOwner=hMainDLG; + ofn.lpstrInitialDir=filepath; + ofn.lpstrFile=file; + ofn.nMaxFile=MAX_PATH; + ofn.Flags=OFN_HIDEREADONLY|OFN_PATHMUSTEXIST; + ofn.lpstrDefExt=L"png"; + ofn.nFilterIndex=lastFilterIndex; + + static int tests_run = 0; + static wchar_t filter[1024] = {0}, *sff = filter; + + if (!tests_run) { + tests_run = 1; + FOURCC imgload = svc_imageLoader::getServiceType(); + int n = WASABI_API_SVC->service_getNumServices(imgload); + for (int i=0; iservice_enumService(imgload, i); + if (sf) { + svc_imageLoader * l = (svc_imageLoader*)sf->getInterface(); + if (l) { + static int tests_idx[4] = {0, 1, 2, 3}; + size_t size = 1024; + int j = 0, tests_str[] = {IDS_JPEG_FILE, IDS_PNG_FILE, IDS_BMP_FILE, IDS_GIF_FILE}; + wchar_t *tests[] = {L"*.jpg", L"*.png", L"*.bmp", L"*.gif"}; + for (int i = 0; i < ARRAYSIZE(tests); i++) { + if (l->isMine(tests[i])) { + tests_idx[j] = i; + j++; + int len = 0; + LocalisedString(tests_str[i], sff, size); + size-=(len = wcslen(sff)+1); + sff+=len; + wcsncpy(sff, (!i ? L"*.jpg;*.jpeg" : tests[i]), size); + size-=(len = wcslen(sff)+1); + sff+=len; + } + } + sf->releaseInterface(l); + } + } + } + } + + ofn.lpstrFilter = filter; + + if (GetOpenFileNameW(&ofn)) + { + // update things if the filename changed, etc + wcsncpy(Out->stationArtPath, file, MAX_PATH); + SetDlgItemTextW(out_wnd[4].hWnd, IDC_ART_EDIT, Out->stationArtPath); + + if (UpdateStreamAlbumArt(Out->Handle, Connection_CurSelPos, Out->stationArtPath, !!Out->useStreamArt)) { + Encoder[Connection_CurSelPos].UpdateArtwork(Out->Handle); + } + } + lastFilterIndex = ofn.nFilterIndex; + } + } + break; + + case IDC_AUTOCONNECT: + { + if (HIWORD(wParam) == BN_CLICKED) { + Output[Connection_CurSelPos].AutoConnect = SendMessage((HWND) lParam, BM_GETCHECK, 0, 0) == BST_CHECKED; + if (Output[Connection_CurSelPos].Encoder) { + if (WaitForSingleObject(Enc_mutex[Output[Connection_CurSelPos].Encoder], INFINITE) == WAIT_OBJECT_0) { + Encoder[Output[Connection_CurSelPos].Encoder].UpdateOutput(Output[Connection_CurSelPos].Handle); + ReleaseMutex(Enc_mutex[Output[Connection_CurSelPos].Encoder]); + } + } + } + } + break; + + case IDC_RECONNECT: + { + if (HIWORD(wParam) == BN_CLICKED) { + Output[Connection_CurSelPos].Config.AutoRecon = SendMessage((HWND) lParam, BM_GETCHECK, 0, 0) == BST_CHECKED; + if (Output[Connection_CurSelPos].Encoder) { + if (WaitForSingleObject(Enc_mutex[Output[Connection_CurSelPos].Encoder], INFINITE) == WAIT_OBJECT_0) { + Encoder[Output[Connection_CurSelPos].Encoder].UpdateOutput(Output[Connection_CurSelPos].Handle); + ReleaseMutex(Enc_mutex[Output[Connection_CurSelPos].Encoder]); + } + } + } + } + break; + + case IDC_PROTOCOL: + { + if (HIWORD(wParam) == CBN_SELCHANGE) { + MY_T_OUTPUT *Out = &Output[Connection_CurSelPos]; + + int cur_sel = SendMessage((HWND) lParam, CB_GETCURSEL, 0, 0), + protocol = SendMessage((HWND) lParam, CB_GETITEMDATA, cur_sel, 0); + + Out->Config.protocol = MAKEWORD(protocol, !protocol); + + // force a refresh on selection change + lastMode[Connection_CurSelPos] = -1; + + // jkey: disable or enable userid stationid based on protocol. + if (Output[Connection_CurSelPos].Encoder) { + if (WaitForSingleObject(Enc_mutex[Output[Connection_CurSelPos].Encoder], INFINITE) == WAIT_OBJECT_0) { + Encoder[Output[Connection_CurSelPos].Encoder].UpdateOutput(Output[Connection_CurSelPos].Handle); + ReleaseMutex(Enc_mutex[Output[Connection_CurSelPos].Encoder]); + } + } + //shoutcast 2 else 1 enable aim,icq,irc + EnableWindowDlgItem(hDlg, IDC_STATIONID, (LOBYTE(Output[Connection_CurSelPos].Config.protocol) == 1)); + UpdateTitleControls(); + } + } + break; + + case IDC_CONNECT: + { + if (HIWORD(wParam) == BN_CLICKED) { + MY_T_OUTPUT *Out = &Output[Connection_CurSelPos]; + SHOUTCAST_OUTPUT *Enc = Out->Encoder != -1 ? &Encoder[Out->Encoder] : NULL; + if (Enc && Out->Handle != -1) { + int state = Enc->GetState(Out->Handle); + if (state == OUT_DISCONNECTED) { // disconnected... connect now + Enc->ConnectOutput(Out->Handle); + } else { // connected... disconnect now + Enc->DisconnectOutput(Out->Handle); + } + } + } + } + break; + + case IDC_DEVBOX: + { + if (HIWORD(wParam) == CBN_SELCHANGE) { + Input_Device_ID = SendMessage((HWND) lParam, CB_GETCURSEL, 0, 0); + } + } + break; + + case IDC_LOCK: + { + if (HIWORD(wParam) == BN_CLICKED) { + int checked = SendMessage((HWND) lParam, BM_GETCHECK, -1, -1) == BST_CHECKED; + EnableWindowDlgItem(hDlg, IDC_PTT, !checked); + SendDlgItemMessage(hDlg, IDC_PTT, BM_SETSTATE, checked ? BST_CHECKED : BST_UNCHECKED, 0); + KillTimer(hMainDLG, 2234); + KillTimer(hMainDLG, 2235); + if ((MicFadeStartTime != 0 || FadeStartTime != 0) && FadeOut != checked) { + clock_t myTime = clock(); + if (FadeStartTime != 0) FadeStartTime = myTime - ((FadeTime * 100)-(myTime - FadeStartTime)); + if (MicFadeStartTime != 0) MicFadeStartTime = myTime - ((MicFadeTime * 100)-(myTime - MicFadeStartTime)); + } + if(!ptt_load && Restore_PTT || ptt_load) { + FadeOut = checked; + SetTimer(hMainDLG, 2234, 10, NULL); // fade out + SetTimer(hMainDLG, 2235, 10, NULL); // fade out + } else { + SetDeviceName(); + } + ptt_load = 1; + //if (FadeOut) do_capture(); + #ifdef CAPTURE_TESTING + if (FadeOut) { + if (!pPlayer) { + pPlayer = new Player(hMainDLG); + } + if (!pCallbacks) { + pCallbacks = new CPlayerCallbacks(); + } + pPlayer->SetPlayerCallbacks(pCallbacks); + pPlayer->RefreshDeviceList(eRender); + pPlayer->RefreshDeviceList(eCapture); + pPlayer->SelectDefaultDevice(eRender, eConsole); + pPlayer->SelectDefaultDevice(eCapture, eConsole); + pPlayer->SelectDeviceFromList(eCapture, 0); + //pPlayer->SelectDeviceFromList(eRender, 2); + if (pPlayer->Play(eCaptureEndpoint) == FALSE) { + return TRUE; + } + } + #endif + } + } + break; + + case IDC_LOCK_MODE: + { + HMENU hmenu = CreatePopupMenu(); + RECT r; + GetWindowRect((HWND)lParam, &r); + + MENUITEMINFOW i = {sizeof(i), MIIM_ID | MIIM_STATE | MIIM_TYPE, MFT_STRING, + Restore_PTT ? MFS_CHECKED : 0, 1337}; + i.dwTypeData = LocalisedString(IDS_PTT_ON_STARTUP, NULL, 0); + InsertMenuItemW(hmenu, 0, TRUE, &i); + if (TrackPopupMenu(hmenu, TPM_LEFTALIGN | TPM_TOPALIGN | TPM_RETURNCMD, r.left, r.bottom, 0, (HWND)lParam, NULL) == 1337) { + Restore_PTT = !Restore_PTT; + } + DestroyMenu(hmenu); + } + break; + + case IDC_REFRESH_DEVICES: + { + if (IsVistaUp()) { + SendDlgItemMessage(inWin,IDC_DEVBOX, CB_RESETCONTENT, 0, 0); + SetDeviceName(); + } + } + break; + + case IDC_MIXER: + { + if (HIWORD(wParam) == BN_CLICKED) { + // open vista / win7 or win2k / xp recording panel + // (more sensible esp. for vista / win7) + if (IsVistaUp()) { + ShellExecuteW(hMainDLG, L"open", L"control.exe", L"mmsys.cpl,,1", NULL, SW_SHOW); + } else { + ShellExecuteW(hMainDLG, L"open", L"sndvol32.exe", L"", NULL, SW_SHOW); + ShellExecuteW(hMainDLG, L"open", L"sndvol32.exe", L"/r", NULL, SW_SHOW); + } + } + } + break; + + case IDC_OUTPUTLIST: + { + if (HIWORD(wParam) == LBN_SELCHANGE) { + Connection_CurSelPos = SendMessage((HWND) lParam, LB_GETCURSEL, 0, 0); + + // force a refresh on selection change + lastMode[Connection_CurSelPos] = -1; + + // do checks to see if we need to update the error state of the tab items + if (IsWindow(outTabWnd)) { + RECT r = {0}; + for (int i = 0; i < MAX_OUTWNDS; i++) { + TabCtrl_GetItemRect(outTabWnd, i, &r); + InvalidateRect(outTabWnd, &r, 0); + } + + TabCtrl_GetItemRect(GetDlgItem(hMainDLG, IDC_TAB), 1, &r); + InvalidateRect(GetDlgItem(hMainDLG, IDC_TAB), &r, 0); + } + + T_OUTPUT_CONFIG *Out = &Output[Connection_CurSelPos].Config; + MY_T_OUTPUT * OutEnc = &Output[Connection_CurSelPos]; + int sc2mode = (LOBYTE(Out->protocol) != 1); + // Output page 1 + SendDlgItemMessage(out_wnd[0].hWnd, IDC_ADDRESS, EM_SETLIMITTEXT, ARRAYSIZE(Out->Address) - 1, 0); + SetDlgItemText(out_wnd[0].hWnd, IDC_ADDRESS, Out->Address); + SendDlgItemMessage(out_wnd[0].hWnd, IDC_STATIONID, EM_SETLIMITTEXT, 10, 0); + SetDlgItemText(out_wnd[0].hWnd, IDC_STATIONID, Out->StationID); + SendDlgItemMessage(out_wnd[0].hWnd, IDC_USERID, EM_SETLIMITTEXT, ARRAYSIZE(Out->UserID) - 1, 0); + SetDlgItemText(out_wnd[0].hWnd, IDC_USERID, Out->UserID); + SetDlgItemInt(out_wnd[0].hWnd, IDC_PORT, Out->Port, 0); + SendDlgItemMessage(out_wnd[0].hWnd, IDC_PASSWORD, EM_SETLIMITTEXT, ARRAYSIZE(Out->Password) - 1, 0); + SetDlgItemText(out_wnd[0].hWnd, IDC_PASSWORD, Out->Password); + int encval = OutEnc->Encoder; + SendDlgItemMessage(out_wnd[0].hWnd, IDC_RECONNECT, BM_SETCHECK, Out->AutoRecon ? BST_CHECKED : BST_UNCHECKED, 0); + SendDlgItemMessage(wnd[1].hWnd, IDC_AUTOCONNECT, BM_SETCHECK, OutEnc->AutoConnect ? BST_CHECKED : BST_UNCHECKED, 0); + + SetDlgItemInt(out_wnd[0].hWnd, IDC_TIMEOUT, Out->ReconTime, 0); + + EnableWindowDlgItem(out_wnd[0].hWnd, IDC_STATIONID, sc2mode); + SendDlgItemMessage(out_wnd[0].hWnd, IDC_PROTOCOL, CB_SETCURSEL, (HIBYTE(Out->protocol) ? 0 : (sc2mode ? 1 : 2)), 0); + + + // Output page 2 + SendDlgItemMessage(out_wnd[1].hWnd, IDC_PUBLIC, BM_SETCHECK, Out->Public ? BST_CHECKED : BST_UNCHECKED, 0); + SendDlgItemMessage(out_wnd[1].hWnd, IDC_DESCRIPTION, EM_SETLIMITTEXT, ARRAYSIZE(Out->Description) - 1, 0); + SetDlgItemText(out_wnd[1].hWnd, IDC_DESCRIPTION, Out->Description); + SendDlgItemMessage(out_wnd[1].hWnd, IDC_SERVERURL, EM_SETLIMITTEXT, ARRAYSIZE(Out->ServerURL) - 1, 0); + SetDlgItemText(out_wnd[1].hWnd, IDC_SERVERURL, Out->ServerURL); + SendDlgItemMessage(out_wnd[1].hWnd, IDC_GENRE, EM_SETLIMITTEXT, ARRAYSIZE(Out->Genre) - 1, 0); + SetDlgItemText(out_wnd[1].hWnd, IDC_GENRE, Out->Genre); + SendDlgItemMessage(out_wnd[1].hWnd, IDC_AIM, EM_SETLIMITTEXT, ARRAYSIZE(Out->AIM) - 1, 0); + SetDlgItemText(out_wnd[1].hWnd, IDC_AIM, Out->AIM); + SendDlgItemMessage(out_wnd[1].hWnd, IDC_ICQ, EM_SETLIMITTEXT, ARRAYSIZE(Out->ICQ) - 1, 0); + SetDlgItemText(out_wnd[1].hWnd, IDC_ICQ, Out->ICQ); + SendDlgItemMessage(out_wnd[1].hWnd, IDC_IRC, EM_SETLIMITTEXT, ARRAYSIZE(Out->IRC) - 1, 0); + SetDlgItemText(out_wnd[1].hWnd, IDC_IRC, Out->IRC); + + SendDlgItemMessage(out_wnd[1].hWnd, IDC_AUTOCONNECT, BM_SETCHECK, OutEnc->AutoConnect ? BST_CHECKED : BST_UNCHECKED, 0); + + // setup the handling of the encoder saving options + SendDlgItemMessage(out_wnd[2].hWnd, IDC_SAVE_ENCODED_AUDIO_EDIT, EM_SETLIMITTEXT, ARRAYSIZE(OutEnc->stationArtPath) - 1, 0); + SetDlgItemTextW(out_wnd[2].hWnd, IDC_SAVE_ENCODED_AUDIO_EDIT, OutEnc->saveEncodedPath); + SendDlgItemMessage(out_wnd[2].hWnd, IDC_SAVE_ENCODED_AUDIO, BM_SETCHECK, OutEnc->saveEncoded ? BST_CHECKED : BST_UNCHECKED, 0); + + // setup the handling of the titles options + SendDlgItemMessage(out_wnd[3].hWnd, IDC_TITLE, EM_SETLIMITTEXT, ARRAYSIZE(Out->Now) - 1, 0); + SetDlgItemText(out_wnd[3].hWnd, IDC_TITLE, Out->Now); + SendDlgItemMessage(out_wnd[3].hWnd, IDC_NEXT, EM_SETLIMITTEXT, ARRAYSIZE(Out->Next) - 1, 0); + SetDlgItemText(out_wnd[3].hWnd, IDC_NEXT, Out->Next); + CheckRadioButton(out_wnd[3].hWnd, IDC_NOTITLES, IDC_MANUALTITLE, (Out->doTitleUpdate ? (OutEnc->AutoTitle ? IDC_AUTOTITLE : IDC_MANUALTITLE) : IDC_NOTITLES)); + SendDlgItemMessage(out_wnd[3].hWnd, IDC_SENDNEXTTITLES, BM_SETCHECK, OutEnc->NextTitles ? BST_CHECKED : BST_UNCHECKED, 0); + UpdateTitleControls(); + + // setup the handling of the artwork options + SendDlgItemMessage(out_wnd[4].hWnd, IDC_ART_EDIT, EM_SETLIMITTEXT, ARRAYSIZE(OutEnc->stationArtPath) - 1, 0); + SetDlgItemTextW(out_wnd[4].hWnd, IDC_ART_EDIT, OutEnc->stationArtPath); + SendDlgItemMessage(out_wnd[4].hWnd, IDC_USE_ART, BM_SETCHECK, OutEnc->useArt ? BST_CHECKED : BST_UNCHECKED, 0); + SendDlgItemMessage(out_wnd[4].hWnd, IDC_USE_ART_PLAYING, BM_SETCHECK, OutEnc->usePlayingArt ? BST_CHECKED : BST_UNCHECKED, 0); + SendDlgItemMessage(out_wnd[4].hWnd, IDC_USE_ART_STREAM, BM_SETCHECK, OutEnc->useStreamArt ? BST_CHECKED : BST_UNCHECKED, 0); + EnableWindowDlgItem(out_wnd[4].hWnd, IDC_USE_ART_PLAYING, OutEnc->useArt); + EnableWindowDlgItem(out_wnd[4].hWnd, IDC_USE_ART_STREAM, OutEnc->useArt); + EnableWindowDlgItem(out_wnd[4].hWnd, IDC_ART_EDIT, OutEnc->useArt && OutEnc->useStreamArt); + EnableWindowDlgItem(out_wnd[4].hWnd, IDC_ART_BROWSE, OutEnc->useArt && OutEnc->useStreamArt); + UpdateArtworkMessage(); + + // setup the handling of the next track logging option + SendDlgItemMessage(out_wnd[5].hWnd, IDC_LOGGING, BM_SETCHECK, OutEnc->Logging ? BST_CHECKED : BST_UNCHECKED, 0); + SendDlgItemMessage(out_wnd[5].hWnd, IDC_CLEAR_ON_STARTUP, BM_SETCHECK, OutEnc->LogCOS ? BST_CHECKED : BST_UNCHECKED, 0); + + SendDlgItemMessage(out_wnd[5].hWnd, IDC_NEXT_TRACK_LOG, BM_SETCHECK, OutEnc->nextTrackLog ? BST_CHECKED : BST_UNCHECKED, 0); + SendDlgItemMessage(out_wnd[5].hWnd, IDC_NEXT_TRACK_XML, BM_SETCHECK, OutEnc->nextTrackLogXML ? BST_CHECKED : BST_UNCHECKED, 0); + SendDlgItemMessage(out_wnd[5].hWnd, IDC_NEXT_TRACK_EDIT, EM_SETLIMITTEXT, ARRAYSIZE(OutEnc->nextTrackPath) - 1, 0); + SetDlgItemTextW(out_wnd[5].hWnd, IDC_NEXT_TRACK_EDIT, OutEnc->nextTrackPath); + EnableWindowDlgItem(out_wnd[5].hWnd, IDC_NEXT_TRACK_XML, OutEnc->nextTrackLog); + EnableWindowDlgItem(out_wnd[5].hWnd, IDC_NEXT_TRACK_EDIT, OutEnc->nextTrackLog); + EnableWindowDlgItem(out_wnd[5].hWnd, IDC_NEXT_TRACK_BROWSE, OutEnc->nextTrackLog); + + // this is sent to the encoders tab so it will update the selection for the current instance + // note: this is a change in build 009 to remove the prior listbox and reduce ui inconsistency + SendMessage(out_wnd[2].hWnd, WM_COMMAND, MAKEWPARAM(IDC_ENCODERLIST, LBN_SELCHANGE), (LPARAM) GetDlgItem(wnd[2].hWnd, IDC_ENCODERLIST)); + + ini_modified = 1; + } + } + break; + + case IDC_INPUTSETUP: + { + if (HIWORD(wParam) == CBN_SELCHANGE) { + if (WaitForSingleObject(cf_mutex, INFINITE) == WAIT_OBJECT_0) { + Crossfader->SetChannels(LineInputAttribs[(InputDevice == 0 ? 3 : Input_CurSelPos)].nch); + Crossfader->SetSampleRate(LineInputAttribs[(InputDevice == 0 ? 3 : Input_CurSelPos)].srate); + ReleaseMutex(cf_mutex); + } + + int attrib = SendMessage((HWND) lParam, CB_GETCURSEL, 0, 0); + if (attrib != Input_CurSelPos) { + SuspendThread(hthread); + Soundcard.Close(); + if (InputDevice == 1) { + Input_CurSelPos = attrib; + } + for (int i = 0; i < NUM_ENCODERS; i++) { + if (WaitForSingleObject(Enc_mutex[i], INFINITE) == WAIT_OBJECT_0) { + C_ENCODER *Enc = Encoder[i].GetEncoder(); + if (Enc) { + int infosize = sizeof (T_EncoderIOVals); + T_EncoderIOVals *encset = (T_EncoderIOVals *) Enc->GetExtInfo(&infosize); + if (encset && infosize) { + T_EncoderIOVals *EncSettings = (T_EncoderIOVals *) malloc(infosize); + memcpy(EncSettings, encset, infosize); + + if (strcmp(Enc->GetName(), "MP3 Encoder") == 0) { + ((T_ENCODER_MP3_INFO *) EncSettings)->input_numChannels = (InputDevice == 0 ? InputConfig.nch : LineInputAttribs[Input_CurSelPos].nch); + ((T_ENCODER_MP3_INFO *) EncSettings)->input_sampleRate = (InputDevice == 0 ? InputConfig.srate : LineInputAttribs[Input_CurSelPos].srate); + } else if (strcmp(Enc->GetName(), "Fraunhofer Encoder") == 0) { + ((T_ENCODER_FHGAAC_INFO *) EncSettings)->input_numChannels = (InputDevice == 0 ? InputConfig.nch : LineInputAttribs[Input_CurSelPos].nch); + ((T_ENCODER_FHGAAC_INFO *) EncSettings)->input_sampleRate = (InputDevice == 0 ? InputConfig.srate : LineInputAttribs[Input_CurSelPos].srate); + } else if (strcmp(Enc->GetName(), "AAC+ Encoder") == 0) { + ((T_ENCODER_AACP_INFO *) EncSettings)->input_numChannels = (InputDevice == 0 ? InputConfig.nch : LineInputAttribs[Input_CurSelPos].nch); + ((T_ENCODER_AACP_INFO *) EncSettings)->input_sampleRate = (InputDevice == 0 ? InputConfig.srate : LineInputAttribs[Input_CurSelPos].srate); + } +#ifdef USE_OGG + else if (strcmp(Enc->GetName(), "OGG Vorbis Encoder") == 0) { + ((T_ENCODER_OGG_INFO *) EncSettings)->input_numChannels = (InputDevice == 0 ? InputConfig.nch : LineInputAttribs[Input_CurSelPos].nch); + ((T_ENCODER_OGG_INFO *) EncSettings)->input_sampleRate = (InputDevice == 0 ? InputConfig.srate : LineInputAttribs[Input_CurSelPos].srate); + } +#endif // USE_OGG + Enc->ChangeSettings(EncSettings); + free(EncSettings); + } + } + ReleaseMutex(Enc_mutex[i]); + } + } + Soundcard.Create((InputDevice == 0 ? InputConfig.srate : LineInputAttribs[Input_CurSelPos].srate), (InputDevice == 0 ? InputConfig.nch : LineInputAttribs[Input_CurSelPos].nch)); + ResumeThread(hthread); + ini_modified = 1; + } + } + } + break; + + case IDC_INPUT_WINAMP: + case IDC_INPUT_SOUNDCARD: + { + // update the input mode from the summary page options + HWND inputCtrl = GetDlgItem(wnd[2].hWnd, IDC_INPUTDEVICE); + SendMessage(inputCtrl, CB_SETCURSEL, (LOWORD(wParam) - IDC_INPUT_WINAMP), 0); + SendMessage(wnd[2].hWnd, WM_COMMAND, MAKEWPARAM(IDC_INPUTDEVICE,CBN_SELCHANGE), (LPARAM)inputCtrl); + } + break; + + case IDC_INPUTDEVICE: + { + if (HIWORD(wParam) == CBN_SELCHANGE) { + int this_device = SendMessage((HWND) lParam, CB_GETCURSEL, 0, 0); + if (InputDevice != this_device) { + SuspendThread(hthread); + Soundcard.Close(); + InputDevice = this_device; + if (InputDevice == 0) { // winamp + SendMessage(in_wnd[this_device].hWnd, WM_HSCROLL, 0, (LPARAM) GetDlgItem(in_wnd[this_device].hWnd, IDC_MUSSLIDER)); + SendMessage(in_wnd[this_device].hWnd, WM_HSCROLL, 0, (LPARAM) GetDlgItem(in_wnd[this_device].hWnd, IDC_MUS2SLIDER)); + SendMessage(in_wnd[this_device].hWnd, WM_HSCROLL, 0, (LPARAM) GetDlgItem(in_wnd[this_device].hWnd, IDC_MICSLIDER)); + SendMessage(in_wnd[this_device].hWnd, WM_HSCROLL, 0, (LPARAM) GetDlgItem(in_wnd[this_device].hWnd, IDC_FADESLIDER)); + SendMessage(in_wnd[this_device].hWnd, WM_HSCROLL, 0, (LPARAM) GetDlgItem(in_wnd[this_device].hWnd, IDC_MICFADESLIDER)); + } + for (int i = 0; i < NUM_ENCODERS; i++) { + if (WaitForSingleObject(Enc_mutex[i], INFINITE) == WAIT_OBJECT_0) { + C_ENCODER *Enc = Encoder[i].GetEncoder(); + if (Enc) { + int infosize = sizeof (T_EncoderIOVals); + T_EncoderIOVals *encset = (T_EncoderIOVals *) Enc->GetExtInfo(&infosize); + if (encset && infosize) { + T_EncoderIOVals *EncSettings = (T_EncoderIOVals *) malloc(infosize); + memcpy(EncSettings, encset, infosize); + + if (strcmp(Enc->GetName(), "MP3 Encoder") == 0) { + ((T_ENCODER_MP3_INFO *) EncSettings)->input_numChannels = (InputDevice == 0 ? InputConfig.nch : LineInputAttribs[Input_CurSelPos].nch); + ((T_ENCODER_MP3_INFO *) EncSettings)->input_sampleRate = (InputDevice == 0 ? InputConfig.srate : LineInputAttribs[Input_CurSelPos].srate); + } else if (strcmp(Enc->GetName(), "Fraunhofer Encoder") == 0) { + ((T_ENCODER_FHGAAC_INFO *) EncSettings)->input_numChannels = (InputDevice == 0 ? InputConfig.nch : LineInputAttribs[Input_CurSelPos].nch); + ((T_ENCODER_FHGAAC_INFO *) EncSettings)->input_sampleRate = (InputDevice == 0 ? InputConfig.srate : LineInputAttribs[Input_CurSelPos].srate); + } else if (strcmp(Enc->GetName(), "AAC+ Encoder") == 0) { + ((T_ENCODER_AACP_INFO *) EncSettings)->input_numChannels = (InputDevice == 0 ? InputConfig.nch : LineInputAttribs[Input_CurSelPos].nch); + ((T_ENCODER_AACP_INFO *) EncSettings)->input_sampleRate = (InputDevice == 0 ? InputConfig.srate : LineInputAttribs[Input_CurSelPos].srate); + } +#ifdef USE_OGG + else if (strcmp(Enc->GetName(), "OGG Vorbis Encoder") == 0) { + ((T_ENCODER_OGG_INFO *) EncSettings)->input_numChannels = (InputDevice == 0 ? InputConfig.nch : LineInputAttribs[Input_CurSelPos].nch); + ((T_ENCODER_OGG_INFO *) EncSettings)->input_sampleRate = (InputDevice == 0 ? InputConfig.srate : LineInputAttribs[Input_CurSelPos].srate); + } +#endif // USE_OGG + Enc->ChangeSettings(EncSettings); + free(EncSettings); + } + } + ReleaseMutex(Enc_mutex[i]); + } + } + Soundcard.Create((InputDevice == 0 ? InputConfig.srate : LineInputAttribs[Input_CurSelPos].srate), (InputDevice == 0 ? InputConfig.nch : LineInputAttribs[Input_CurSelPos].nch)); + + DisplayDeviceName(); + CheckRadioButton(wnd[0].hWnd, IDC_INPUT_WINAMP, IDC_INPUT_SOUNDCARD, (IDC_INPUT_WINAMP + InputDevice)); + + peak_vu_l = peak_vu_r = -90; + ResumeThread(hthread); + ini_modified = 1; + } + SendDlgItemMessage(hDlg, IDC_INPUTSETUP, CB_RESETCONTENT, 0, 0); + if (InputDevice == 1) { + wchar_t temp[128]; + int num_input_items = ARRAYSIZE(LineInputAttribs); + for (int i = 0; i < num_input_items; i++) { + wchar_t tmp[32]; + StringCchPrintfW(temp, ARRAYSIZE(temp), LocalisedString(IDS_X_HZ_X, tmp, 32), LineInputAttribs[i].srate, LocalisedString(LineInputAttribs[i].nch == 1 ? IDS_MONO : IDS_STEREO, NULL, 0)); + SendDlgItemMessageW(hDlg, IDC_INPUTSETUP, CB_ADDSTRING, 0, (LPARAM) temp); + } + SendDlgItemMessage(hDlg, IDC_INPUTSETUP, CB_SETCURSEL, Input_CurSelPos, 0); + } + for (int i = 0; i < num_inwnds; i++) ShowWindow(in_wnd[i].hWnd, i == InputDevice && curtab == 2 ? SW_SHOW : SW_HIDE); + SendMessage(hDlg, WM_COMMAND, MAKEWPARAM(IDC_INPUTSETUP, CBN_SELCHANGE), (LPARAM) GetDlgItem(hDlg, IDC_INPUTSETUP)); + ShowWindowDlgItem(hDlg, IDC_INPUTSETUPSTATIC, InputDevice == 1); + ShowWindowDlgItem(hDlg, IDC_INPUTSETUP, InputDevice == 1); + } + } + break; + + // server box + case IDC_ADDRESS: + { + if (HIWORD(wParam) == EN_UPDATE) { + T_OUTPUT_CONFIG *Out = &Output[Connection_CurSelPos].Config; + GetWindowText((HWND) lParam, Out->Address, ARRAYSIZE(Out->Address)); + ini_modified = 1; + } else if (HIWORD(wParam) == EN_SETFOCUS) { + PostMessage((HWND)lParam, EM_SETSEL, 0, (LPARAM)-1); + } + } + break; + + // stream ID box + case IDC_STATIONID: + { + if (HIWORD(wParam) == EN_UPDATE) { + BOOL success = FALSE; + int value = GetDlgItemInt(hDlg, LOWORD(wParam), &success, TRUE); + T_OUTPUT_CONFIG *Out = &Output[Connection_CurSelPos].Config; + GetWindowText((HWND) lParam, Out->StationID, ARRAYSIZE(Out->StationID)); + // check and set the default as required + if (!Out->StationID[0] || (success && value < 1 || !success && value == 0)) { + SetWindowTextW((HWND) lParam, L"1"); + lstrcpyn(Out->StationID, "1", ARRAYSIZE(Out->StationID)); + } else if (Out->StationID[0] && (success && value > 2147483647)) { + SetWindowTextW((HWND) lParam, L"2147483647"); + lstrcpyn(Out->StationID, "2147483647", ARRAYSIZE(Out->StationID)); + } + ini_modified = 1; + } else if (HIWORD(wParam) == EN_SETFOCUS) { + PostMessage((HWND)lParam, EM_SETSEL, 0, (LPARAM)-1); + } + } + break; + + case IDC_USERID: + { + if (HIWORD(wParam) == EN_UPDATE) { + T_OUTPUT_CONFIG *Out = &Output[Connection_CurSelPos].Config; + GetWindowText((HWND) lParam, Out->UserID, ARRAYSIZE(Out->UserID)); + ini_modified = 1; + } else if (HIWORD(wParam) == EN_SETFOCUS) { + PostMessage((HWND)lParam, EM_SETSEL, 0, (LPARAM)-1); + } + } + break; + + // server port + case IDC_PORT: + { + if (HIWORD(wParam) == EN_UPDATE) { + BOOL success = FALSE; + int value = GetDlgItemInt(hDlg, LOWORD(wParam), &success, TRUE); + T_OUTPUT_CONFIG *Out = &Output[Connection_CurSelPos].Config; + // check and set the default as required + if ((success && value < 1 || !success && value == 0)) { + SetWindowTextW((HWND) lParam, L"8000"); + Out->Port = 8000; + } else if ((success && value > 65535)) { + SetWindowTextW((HWND) lParam, L"65535"); + Out->Port = 65535; + } else { + Out->Port = value; + } + + ini_modified = 1; + } else if (HIWORD(wParam) == EN_SETFOCUS) { + PostMessage((HWND)lParam, EM_SETSEL, 0, (LPARAM)-1); + } + } + break; + + // password + case IDC_PASSWORD: + { + if (HIWORD(wParam) == EN_UPDATE) { + T_OUTPUT_CONFIG *Out = &Output[Connection_CurSelPos].Config; + GetWindowText((HWND) lParam, Out->Password, ARRAYSIZE(Out->Password)); + ini_modified = 1; + } else if (HIWORD(wParam) == EN_SETFOCUS) { + PostMessage((HWND)lParam, EM_SETSEL, 0, (LPARAM)-1); + } + } + break; + + case IDC_SEND: + { + EnableWindowDlgItem(hDlg, IDC_SEND, FALSE); + MY_T_OUTPUT *Out = &Output[Connection_CurSelPos]; + if (Out->Encoder != -1 && Out->Handle != -1) { + wchar_t title[1024] = {0}, next[1024] = {0}; + SHOUTCAST_OUTPUT *Enc = &Encoder[Out->Encoder]; + GetWindowTextW(GetDlgItem(out_wnd[3].hWnd, IDC_TITLE), title, ARRAYSIZE(title)); + GetWindowTextW(GetDlgItem(out_wnd[3].hWnd, IDC_NEXT), next, ARRAYSIZE(next)); + ini_modified = 1; + if (WaitForSingleObject(Enc_mutex[Out->Encoder], INFINITE) == WAIT_OBJECT_0) { + std::vector nextList; + std::vector nextListIdx; + nextList.clear(); + nextListIdx.clear(); + if (((Out->AutoTitle == 1 && Out->NextTitles) || Out->AutoTitle == 0) && next[0]) { + nextList.push_back(next); + nextListIdx.push_back(-1); + } + + if (Out->nextTrackLog) { + WriteNextTracks(Connection_CurSelPos, module.hwndParent, nextListIdx, nextList, !!Out->nextTrackLogXML); + } + + // check if in v2 mode and have a next title specified so as to change the send flag + Enc->UpdateTitle(title, nextList, Out->Handle, !!nextList.size(), false); + ReleaseMutex(Enc_mutex[Out->Encoder]); + } + } + } + break; + + case IDC_TITLE: + { + if (HIWORD(wParam) == EN_UPDATE) { + int length = GetWindowTextLength((HWND)lParam); + EnableWindowDlgItem(out_wnd[3].hWnd, IDC_SEND, (length > 0)); + MY_T_OUTPUT *Out = &Output[Connection_CurSelPos]; + EnableWindowDlgItem(out_wnd[3].hWnd, IDC_NEXT, + (length > 0 && (LOBYTE(Out->Config.protocol) != 1))); + + char temp[sizeof(Out->Config.Now)]; + GetWindowText((HWND) lParam, temp, ARRAYSIZE(temp)); + if (strcmp(temp, Out->Config.Now) != 0) { + lstrcpyn(Out->Config.Now, temp, ARRAYSIZE(Out->Config.Now)); + ini_modified = 1; + } + } + } + break; + + case IDC_NEXT: + { + if (HIWORD(wParam) == EN_UPDATE) { + EnableWindowDlgItem(out_wnd[3].hWnd, IDC_SEND, TRUE); + MY_T_OUTPUT *Out = &Output[Connection_CurSelPos]; + + char temp[sizeof(Out->Config.Next)]; + GetWindowText((HWND) lParam, temp, ARRAYSIZE(temp)); + if (strcmp(temp, Out->Config.Next) != 0) { + lstrcpyn(Out->Config.Next, temp, ARRAYSIZE(Out->Config.Next)); + ini_modified = 1; + } + } + } + break; + + case IDC_TIMEOUT: + { + if (HIWORD(wParam) == EN_UPDATE) { + MY_T_OUTPUT *Out = &Output[Connection_CurSelPos]; + char temp[128] = {0}; + GetWindowText((HWND) lParam, temp, ARRAYSIZE(temp)); + + int rt = atoi(temp); + if (rt < 1) { + rt = 5; + SetDlgItemInt(out_wnd[0].hWnd, IDC_TIMEOUT, 5, 0); + } + + if (Out->Config.ReconTime != rt) { + Out->Config.ReconTime = rt; + if (Out->Encoder) { + if (WaitForSingleObject(Enc_mutex[Out->Encoder], INFINITE) == WAIT_OBJECT_0) { + Encoder[Out->Encoder].UpdateOutput(Out->Handle); + ReleaseMutex(Enc_mutex[Out->Encoder]); + } + } + ini_modified = 1; + } + } else if (HIWORD(wParam) == EN_SETFOCUS) { + PostMessage((HWND)lParam, EM_SETSEL, 0, (LPARAM)-1); + } + } + break; + + // these will reconnect the Output on edit + case IDC_PUBLIC: + { + if (HIWORD(wParam) == BN_CLICKED) { + MY_T_OUTPUT *Out = &Output[Connection_CurSelPos]; + Out->Config.Public = SendMessage((HWND) lParam, BM_GETCHECK, 0, 0) == BST_CHECKED; + + // force a refresh on selection change + lastMode[Connection_CurSelPos] = -1; + + ini_modified = 1; + if (Out->Encoder != -1 && Out->Handle != -1) { + SHOUTCAST_OUTPUT *Enc = &Encoder[Out->Encoder]; + if (WaitForSingleObject(Enc_mutex[Out->Encoder], INFINITE) == WAIT_OBJECT_0) { + Enc->DisconnectOutput(Out->Handle, 1, 5); + ReleaseMutex(Enc_mutex[Out->Encoder]); + } + } + } + } + break; + + case IDC_GENRES: + { + // build up a menu to allow the user to select only supported genres for use with YP + if (HIWORD(wParam) == BN_CLICKED) { + MY_T_OUTPUT *Out = &Output[Connection_CurSelPos]; + HMENU hmenu = CreatePopupMenu(), submenu = 0; + RECT r; + GetWindowRect((HWND)lParam, &r); + + for (unsigned int i = 0; i < ARRAYSIZE(genres); i++) { + MENUITEMINFO mii = {sizeof(mii), MIIM_ID | MIIM_STATE | MIIM_TYPE, MFT_STRING, 0, 1+i, 0, 0, 0, 0, 0, 0}; + bool reAdd = false; + + // fix up genres with & in it to work around menu accelerator display quirks + std::string str = genres[i].name; + if (str.find("&") != std::string::npos) + str.replace(str.find("&"),1,"&&"); + mii.dwTypeData = (LPSTR)str.c_str(); + + if (genres[i].parent) { + if (genres[i].children) { + reAdd = true; + mii.fMask |= MIIM_SUBMENU; + submenu = mii.hSubMenu = CreatePopupMenu(); + } + } + + if (reAdd == false && !strcmpi(Out->Config.Genre, genres[i].name)) { + mii.fState = MFS_CHECKED; + } + + InsertMenuItem((genres[i].parent ? hmenu : submenu), i, TRUE, &mii); + + if (reAdd == true) { + mii.fMask -= MIIM_SUBMENU; + if (!strcmpi(Out->Config.Genre, genres[i].name)) { + mii.fState = MFS_CHECKED; + } + InsertMenuItem(submenu, i, TRUE, &mii); + } + } + + int ret = TrackPopupMenu(hmenu, TPM_LEFTALIGN | TPM_TOPALIGN | TPM_RETURNCMD, r.left, r.bottom, 0, (HWND)hDlg, NULL); + if (ret > 0) { + int update = 0; + ret -= 1; + if (strcmp(genres[ret].name, Out->Config.Genre) != 0) { + update = 1; + SetDlgItemText(hDlg, IDC_GENRE, genres[ret].name); + lstrcpyn(Out->Config.Genre, genres[ret].name, ARRAYSIZE(Out->Config.Genre)); + ini_modified = 1; + } + if (update && Out->Encoder != -1 && Out->Handle != -1) { + SHOUTCAST_OUTPUT *Enc = &Encoder[Out->Encoder]; + if (WaitForSingleObject(Enc_mutex[Out->Encoder], INFINITE) == WAIT_OBJECT_0) { + Enc->DisconnectOutput(Out->Handle, 1, 5); + ReleaseMutex(Enc_mutex[Out->Encoder]); + } + } + } + + DestroyMenu(hmenu); + } + } + break; + + case IDC_DESCRIPTION: + { + if (HIWORD(wParam) == EN_UPDATE) { + MY_T_OUTPUT *Out = &Output[Connection_CurSelPos]; + char temp[sizeof (Out->Config.Description)]; + int update = 0; + GetWindowText((HWND) lParam, temp, ARRAYSIZE(temp)); + if (strcmp(temp, Out->Config.Description) != 0) { + update = 1; + lstrcpyn(Out->Config.Description, temp, ARRAYSIZE(Out->Config.Description)); + ini_modified = 1; + } + if (update && Out->Encoder != -1 && Out->Handle != -1) { + SHOUTCAST_OUTPUT *Enc = &Encoder[Out->Encoder]; + if (WaitForSingleObject(Enc_mutex[Out->Encoder], INFINITE) == WAIT_OBJECT_0) { + Enc->DisconnectOutput(Out->Handle, 1, 5); + ReleaseMutex(Enc_mutex[Out->Encoder]); + } + } + } else if (HIWORD(wParam) == EN_SETFOCUS) { + PostMessage((HWND)lParam, EM_SETSEL, 0, (LPARAM)-1); + } + } + break; + + case IDC_SERVERURL: + { + if (HIWORD(wParam) == EN_UPDATE) { + MY_T_OUTPUT *Out = &Output[Connection_CurSelPos]; + char temp[sizeof (Out->Config.ServerURL)]; + int update = 0; + GetWindowText((HWND) lParam, temp, ARRAYSIZE(temp)); + if (strcmp(temp, Out->Config.ServerURL) != 0) { + update = 1; + lstrcpyn(Out->Config.ServerURL, temp, ARRAYSIZE(Out->Config.ServerURL)); + ini_modified = 1; + } + if (update && Out->Encoder != -1 && Out->Handle != -1) { + SHOUTCAST_OUTPUT *Enc = &Encoder[Out->Encoder]; + if (WaitForSingleObject(Enc_mutex[Out->Encoder], INFINITE) == WAIT_OBJECT_0) { + Enc->DisconnectOutput(Out->Handle, 1, 5); + ReleaseMutex(Enc_mutex[Out->Encoder]); + } + } + } else if (HIWORD(wParam) == EN_SETFOCUS) { + PostMessage((HWND)lParam, EM_SETSEL, 0, (LPARAM)-1); + } + } + break; + + case IDC_AIM: + { + if (HIWORD(wParam) == EN_UPDATE) { + MY_T_OUTPUT *Out = &Output[Connection_CurSelPos]; + char temp[sizeof (Out->Config.AIM)]; + int update = 0; + GetWindowText((HWND) lParam, temp, ARRAYSIZE(temp)); + if (strcmp(temp, Out->Config.AIM) != 0) { + update = 1; + lstrcpyn(Out->Config.AIM, temp, ARRAYSIZE(Out->Config.AIM)); + ini_modified = 1; + } + if (update && Out->Encoder != -1 && Out->Handle != -1) { + SHOUTCAST_OUTPUT *Enc = &Encoder[Out->Encoder]; + if (WaitForSingleObject(Enc_mutex[Out->Encoder], INFINITE) == WAIT_OBJECT_0) { + Enc->DisconnectOutput(Out->Handle, 1, 5); + ReleaseMutex(Enc_mutex[Out->Encoder]); + } + } + } else if (HIWORD(wParam) == EN_SETFOCUS) { + PostMessage((HWND)lParam, EM_SETSEL, 0, (LPARAM)-1); + } + } + break; + + case IDC_ICQ: + { + if (HIWORD(wParam) == EN_UPDATE) { + MY_T_OUTPUT *Out = &Output[Connection_CurSelPos]; + char temp[sizeof (Out->Config.ICQ)]; + int update = 0; + GetWindowText((HWND) lParam, temp, ARRAYSIZE(temp)); + if (strcmp(temp, Out->Config.ICQ) != 0) { + update = 1; + lstrcpyn(Out->Config.ICQ, temp, ARRAYSIZE(Out->Config.ICQ)); + ini_modified = 1; + } + if (update && Out->Encoder != -1 && Out->Handle != -1) { + SHOUTCAST_OUTPUT *Enc = &Encoder[Out->Encoder]; + if (WaitForSingleObject(Enc_mutex[Out->Encoder], INFINITE) == WAIT_OBJECT_0) { + Enc->DisconnectOutput(Out->Handle, 1, 5); + ReleaseMutex(Enc_mutex[Out->Encoder]); + } + } + } else if (HIWORD(wParam) == EN_SETFOCUS) { + PostMessage((HWND)lParam, EM_SETSEL, 0, (LPARAM)-1); + } + } + break; + + case IDC_IRC: + { + if (HIWORD(wParam) == EN_UPDATE) { + MY_T_OUTPUT *Out = &Output[Connection_CurSelPos]; + char temp[sizeof (Out->Config.IRC)]; + int update = 0; + GetWindowText((HWND) lParam, temp, ARRAYSIZE(temp)); + if (strcmp(temp, Out->Config.IRC) != 0) { + update = 1; + lstrcpyn(Out->Config.IRC, temp, ARRAYSIZE(Out->Config.IRC)); + ini_modified = 1; + } + if (update && Out->Encoder != -1 && Out->Handle != -1) { + SHOUTCAST_OUTPUT *Enc = &Encoder[Out->Encoder]; + if (WaitForSingleObject(Enc_mutex[Out->Encoder], INFINITE) == WAIT_OBJECT_0) { + Enc->DisconnectOutput(Out->Handle, 1, 5); + ReleaseMutex(Enc_mutex[Out->Encoder]); + } + } + } else if (HIWORD(wParam) == EN_SETFOCUS) { + PostMessage((HWND)lParam, EM_SETSEL, 0, (LPARAM)-1); + } + } + break; + } + } + break; + + case WM_CTLCOLORSTATIC: + { + // this is used to update the header text of the options which need to be checked if there is a config error... + int id = GetDlgCtrlID((HWND)lParam); + if (id == IDC_ADDRESS_HEADER || id == IDC_PASSWORD_HEADER || id == IDC_NAME_HEADER || id == IDC_ENCODER_HEADER) { + int header_id[] = {0, 0, 0, IDC_ADDRESS_HEADER, IDC_PASSWORD_HEADER, IDC_NAME_HEADER, IDC_ENCODER_HEADER, 0}; + if (lastMode[Connection_CurSelPos] >= 3 && lastMode[Connection_CurSelPos] <= 6 && + header_id[lastMode[Connection_CurSelPos]] == id) { + SetTextColor((HDC)wParam, RGB(255,0,0)); + } + } + } + break; + + case WM_NOTIFY: + { + LPNMHDR pnmh = (LPNMHDR) lParam; + switch (LOWORD(wParam)) { + case IDC_TAB: + if (pnmh->code == TCN_SELCHANGE) { + int i; + KillTimer(hDlg, wnd[curtab].id); + curtab = SendMessage(pnmh->hwndFrom, TCM_GETCURSEL, 0, 0); + + // send this to update the tab just incase we're showing invalid items + InvalidateRect(pnmh->hwndFrom, 0, 0); + + ini_modified = 1; + if (wnd[curtab].timer_freq != 0) SetTimer(hDlg, wnd[curtab].id, wnd[curtab].timer_freq, NULL); + for (i = 0; i < num_tabwnds; i++) ShowWindow(wnd[i].hWnd, curtab == i ? SW_SHOW : SW_HIDE); + for (i = 0; i < num_inwnds; i++) ShowWindow(in_wnd[i].hWnd, i == InputDevice && curtab == 2 ? SW_SHOW : SW_HIDE); + for (i = 0; i < num_outwnds; i++) ShowWindow(out_wnd[i].hWnd, i == curouttab && curtab == 1 ? SW_SHOW : SW_HIDE); + // update the summary details when going back to it + if (curtab == 0) { + UpdateSummaryDetails(ListView_GetNextItem(GetDlgItem(wnd[0].hWnd, IDC_OUTPUTSTATUS), -1, LVNI_SELECTED)); + } else if(curtab == 1) { + // force a refresh on selection change + lastMode[Connection_CurSelPos] = -1; + } + } + break; + + case IDC_CONTAB: + if (pnmh->code == TCN_SELCHANGE) { + int i; + KillTimer(hDlg, out_wnd[curouttab].id); + curouttab = SendMessage(pnmh->hwndFrom, TCM_GETCURSEL, 0, 0); + + // send this to update the tab just incase we're showing invalid items + InvalidateRect(pnmh->hwndFrom, 0, 0); + + ini_modified = 1; + if (out_wnd[curouttab].timer_freq != 0) SetTimer(hDlg, out_wnd[curouttab].id, out_wnd[curouttab].timer_freq, NULL); + for (i = 0; i < num_outwnds; i++) ShowWindow(out_wnd[i].hWnd, i == curouttab ? SW_SHOW : SW_HIDE); + bool enable = (LOBYTE(Output[Connection_CurSelPos].Config.protocol) == 1); + EnableWindowDlgItem(out_wnd[1].hWnd, IDC_AIM, enable); + EnableWindowDlgItem(out_wnd[1].hWnd, IDC_IRC, enable); + EnableWindowDlgItem(out_wnd[1].hWnd, IDC_ICQ, enable); + } + break; + + case IDC_METALIST: + if (pnmh->code == NM_DBLCLK) { + LPNMITEMACTIVATE lpnmitem = (LPNMITEMACTIVATE) lParam; + if (lpnmitem->iItem != -1 && WASABI_API_EXPLORERFINDFILE) { + wchar_t fn[MAX_PATH]= {0}; + lstrcpynW(fn, lastFile, MAX_PATH); + if (!PathIsURLW(fn)) { + // this will attempt to find the path of the parent folder of the file selected + // as spc files in a rsn archive can display album art (with the compatibility + // wrapper) and so it should cope with such scenarios... + wchar_t *filews = fn + lstrlenW(fn) - 1; + while(filews && *filews && (*filews != L'.') && (filews != fn)){filews = CharPrevW(fn,filews);} + while(filews && *filews && (*filews != L',' && *filews != L':')){filews = CharNextW(filews);} + if (filews) *filews = 0; + + filews = wcsstr(fn,L".rsn\\"); + if(filews) { + *(filews+4) = 0; + } + + WASABI_API_EXPLORERFINDFILE->AddFile(fn); + WASABI_API_EXPLORERFINDFILE->ShowFiles(); + } + } + } + break; + + case IDC_OUTPUTSTATUS: + // on double-click go to the output tab and select the output we used as the currently shown + if (pnmh->code == NM_DBLCLK) { + LPNMITEMACTIVATE lpnmitem = (LPNMITEMACTIVATE) lParam; + if (lpnmitem->iItem != -1 && lpnmitem->iSubItem != 0) { + // only change the viewed output mode if it is different, otherwise just switch tab + if (Connection_CurSelPos != lpnmitem->iItem) { + Connection_CurSelPos = lpnmitem->iItem; + + // force a refresh on selection change + lastMode[Connection_CurSelPos] = -1; + + SendDlgItemMessage(wnd[1].hWnd, IDC_OUTPUTLIST, LB_SETCURSEL, Connection_CurSelPos, 0); + SendMessage(wnd[1].hWnd, WM_COMMAND, MAKEWPARAM(IDC_OUTPUTLIST, LBN_SELCHANGE), (LPARAM) GetDlgItem(wnd[1].hWnd, IDC_OUTPUTLIST)); + SetFocus(GetDlgItem(wnd[1].hWnd, IDC_OUTPUTLIST)); + } + SetTab(1, hMainDLG, IDC_TAB); + } + } else if (pnmh->code == LVN_ITEMCHANGED) { + // on single-click / keyboard change, show some information about the stream such as most, encoder, etc (makes the page useful) + LPNMLISTVIEW lpnmitem = (LPNMLISTVIEW) lParam; + if (lpnmitem->iItem != -1) { + UpdateSummaryDetails(lpnmitem->iItem); + } + } else if(pnmh->code == LVN_KEYDOWN) { + LPNMLVKEYDOWN pnkd = (LPNMLVKEYDOWN) lParam; + // toggle state in the output list via 'space' + if (pnkd->wVKey == VK_SPACE) { + int item = ListView_GetNextItem(GetDlgItem(wnd[0].hWnd, IDC_OUTPUTSTATUS), -1, LVNI_SELECTED); + if (lastEnable[item]) { + int oldCurSelPos = Connection_CurSelPos; + Connection_CurSelPos = item; + SendMessage(wnd[1].hWnd, WM_COMMAND, MAKEWPARAM(IDC_CONNECT, BN_CLICKED), (LPARAM)GetDlgItem(wnd[1].hWnd, IDC_CONNECT)); + Connection_CurSelPos = oldCurSelPos; + } + } + } + break; + } + } + break; + + case WM_HSCROLL: + { + int curpos = SendMessage((HWND) lParam, TBM_GETPOS, 0, 0); + if (GetDlgItem(hDlg, IDC_MUSSLIDER) == (HWND) lParam) { + MusVol = curpos; + if (InputDevice == 1 && !FadeOut) setlev(MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT, MusVol * 10); + wchar_t tmp[256] = {0}; + if (curpos != 0) { + wchar_t temp[32] = {0}; + int volume = (int) (20 * log10(curpos * 3276.)); + StringCchPrintfW(tmp, ARRAYSIZE(tmp), LocalisedString(IDS_X_DB, temp, 32), volume - 90); + } else { + LocalisedString(IDS_INF_DB, tmp, 256); + } + SetDlgItemTextW(hDlg, IDC_MUSLEV1_TEXT, tmp); + } else if (GetDlgItem(hDlg, IDC_MUS2SLIDER) == (HWND) lParam) { + Mus2Vol = curpos; + if (InputDevice == 1 && FadeOut) setlev(MIXERLINE_COMPONENTTYPE_SRC_WAVEOUT, Mus2Vol * 10); + wchar_t tmp[256] = {0}; + if (curpos != 0) { + wchar_t temp[32] = {0}; + int volume = (int) (20 * log10(curpos * 3276.)); + StringCchPrintfW(tmp, ARRAYSIZE(tmp), LocalisedString(IDS_X_DB, temp, 32), volume - 90); + } else { + LocalisedString(IDS_INF_DB, tmp, 256); + } + SetDlgItemTextW(hDlg, IDC_MUSLEV2_TEXT, tmp); + } else if (GetDlgItem(hDlg, IDC_MICSLIDER) == (HWND) lParam) { + MicVol = curpos; + int micsrc = Input_Device_ID >= 1 ? MIXERLINE_COMPONENTTYPE_SRC_LINE : MIXERLINE_COMPONENTTYPE_SRC_MICROPHONE; + // changed this so it will only change the capture device level if PTT is pressed + if (InputDevice == 1 && FadeOut) setlev(micsrc, MicVol *10); + wchar_t tmp[256] = {0}; + if (curpos != 0) { + wchar_t temp[32] = {0}; + int volume = (int) (20 * log10(curpos * 3276.)); + StringCchPrintfW(tmp, ARRAYSIZE(tmp), LocalisedString(IDS_X_DB, temp, 32), volume - 90); + } else { + LocalisedString(IDS_INF_DB, tmp, 256); + } + SetDlgItemTextW(hDlg, IDC_MICLEV_TEXT, tmp); + } else if (GetDlgItem(hDlg, IDC_FADESLIDER) == (HWND) lParam) { + FadeTime = curpos; + wchar_t tmp[256] = {0}, temp[32] = {0}; + StringCchPrintfW(tmp, ARRAYSIZE(tmp), LocalisedString(IDS_X_MS, temp, 32), curpos * 100); + SetDlgItemTextW(hDlg, IDC_FADETIME_TEXT, tmp); + } else if (GetDlgItem(hDlg, IDC_MICFADESLIDER) == (HWND) lParam) { + MicFadeTime = curpos; + wchar_t tmp[256] = {0}, temp[32] = {0}; + StringCchPrintfW(tmp, ARRAYSIZE(tmp), LocalisedString(IDS_X_MS, temp, 32), curpos * 100); + SetDlgItemTextW(hDlg, IDC_MICFADETIME_TEXT, tmp); + } + } + break; + } + + if (FALSE != DirectMouseWheel_ProcessDialogMessage(hDlg, uMsg, wParam, lParam)) { + return TRUE; + } + return 0; +} + +LRESULT CALLBACK CallWndProc(int nCode, WPARAM wParam, LPARAM lParam) { + if (nCode == HC_ACTION) { + LPCWPSTRUCT msg = (LPCWPSTRUCT)lParam; + // catch the new file playing message and update the cached metadata + if (msg->message == WM_WA_IPC) { + if (msg->lParam == IPC_PLAYING_FILEW) { + DWORD diff = GetTickCount(); + was_paused = 0; + play_duration = 0; + play_diff = diff; + was_playing = 1; + if (wcsnicmp(lastFile, (wchar_t*)msg->wParam, MAX_PATH)) { + PostMessage(hMainDLG, WM_USER, 0, nowPlayingID); + } + } + } + } + return (nowPlayingHook ? CallNextHookEx(nowPlayingHook, nCode, wParam, lParam) : 0); +} + +LRESULT CALLBACK GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam) { + if (nCode == HC_ACTION) { + LPMSG msg = (LPMSG)lParam; + // catch the new file playing message and update the cached metadata + if (msg->message == WM_WA_IPC) { + if (msg->lParam == IPC_CB_MISC && msg->wParam == IPC_CB_MISC_STATUS) { + isplaying = SendMessage(module.hwndParent, WM_WA_IPC, 0, IPC_ISPLAYING); + ProcessPlayingStatusUpdate(); + } else if (msg->lParam == IPC_UPDTITLE) { + // attempt to keep a track of other title updates + // e.g. the re-streaming an already playing stream + wchar_t currentFile[MAX_PATH] = {0}; + wchar_t *file=(wchar_t*)SendMessage(module.hwndParent, WM_WA_IPC, + SendMessage(module.hwndParent, WM_WA_IPC, 0, IPC_GETLISTPOS), + IPC_GETPLAYLISTFILEW); + wcsncpy(currentFile, (file ? file : L""), MAX_PATH); + if (!wcsnicmp(currentFile, lastFile, MAX_PATH)) { + // do a 1 second delay since it's likely Winamp will send + // this a few times so we reset the timer everytime so we + // only do a proper title update once everything settles + KillTimer(hMainDLG, 1337); + SetTimer(hMainDLG, 1337, 1000, NULL); + } + } + } + } + return (nowPlayingHook2 ? CallNextHookEx(nowPlayingHook2, nCode, wParam, lParam) : 0); +} + +VOID CALLBACK TimerProc2(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime) +{ + KillTimer(hwnd, idEvent); + SetForegroundWindow(hMainDLG); +} + +void doConfig(HWND hwndParent) { + if (WASABI_API_SVC) { + if (!IsWindow(hMainDLG)) { + if (AGAVE_API_CONFIG) { + if (AGAVE_API_CONFIG->GetUnsigned(playbackConfigGroupGUID, L"bits", 16) > 16) { + wchar_t title[128] = {0}, message[512] = {0}; + StringCchPrintfW(title, ARRAYSIZE(title), LocalisedString(IDS_PLUGIN_NAME, NULL, 0), APP_VersionW); + StringCchPrintfW(message, ARRAYSIZE(message), LocalisedString(IDS_24BIT_MODE_DETECTED, NULL, 0)); + MessageBoxW(module.hwndParent, message, title, MB_ICONWARNING); + return; + } + } + + // using this to lessen issues with new track events with higher bitrates leading to failures + if (!nowPlayingHook) { + nowPlayingHook = SetWindowsHookExW(WH_CALLWNDPROC, CallWndProc, instance, GetCurrentThreadId()); + } + if (!nowPlayingHook2) { + nowPlayingHook2 = SetWindowsHookExW(WH_GETMESSAGE, GetMsgProc, instance, GetCurrentThreadId()); + } + if (nowPlayingID == -1) { + nowPlayingID = SendMessage(hwndParent, WM_WA_IPC, (WPARAM)&"dsp_sc_np", IPC_REGISTER_WINAMP_IPCMESSAGE); + } + + HWND hwnd = LocalisedCreateDialog(instance, IDD_DIALOG, hwndParent, DialogFunc, IDD_DIALOG); + SetWindowPos(hwnd, HWND_TOP, mainrect.left, mainrect.top, 0, 0, SWP_NOSIZE | SWP_SHOWWINDOW | SWP_NOSENDCHANGING); + SetTimer(hMainDLG, 999, 1, TimerProc2); + } else { + if (IsIconic(hMainDLG)) { + DialogFunc(hMainDLG, WM_SIZE, SIZE_RESTORED, 0); + ShowWindow(hMainDLG, SW_RESTORE); + ShowWindow(hMainDLG, SW_SHOW); + SetActiveWindow(hMainDLG); + } + SetForegroundWindow(hMainDLG); + } + } +} + +VOID CALLBACK TimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime) +{ + KillTimer(hwnd, idEvent); + doConfig(hwnd); +} + +void Config(winampDSPModule *this_mod) { + // this will hold back opening the config dialog on loading until Winamp is in a ready state + // this resolves a partial fail to load i've often been seeing (plus from some users afaict) + SetTimer(this_mod->hwndParent, 999, 1, TimerProc); +} + +int Init(winampDSPModule *this_mod) { + instance = this_mod->hDllInstance; + + // this will hold back opening the config dialog on loading until Winamp is in a ready state + // this resolves a partial fail to load i've often been seeing (plus from some users afaict) + SetTimer(this_mod->hwndParent, 999, 1, TimerProc); + return 1; +} + +int ModifySamples(winampDSPModule *this_mod, short int *samples, int numsamples, int bps, int nch, int srate) { + int numorig = numsamples; + + //connect but only if we're meant to be i.e. there's at least 1 active output + if (InputDevice == 0) { + if (WaitForSingleObject(cf_mutex, INFINITE) == WAIT_OBJECT_0) { + // CT> Resample into the desired srate and nch if needed + // TODO check out the handling of this for 24-bit output + short cf_buf[256 * 1024] = {0}; + if (srate != InputConfig.srate || nch != InputConfig.nch) { + char *s = (char *) samples; + int ns = numsamples * 2; + if (InputConfig.nch == 1) { + if (nch != 1 || bps != 16 || srate != (int) InputConfig.srate) { + if (nch == 2) { + int x; + int nns = MulDiv(numsamples, InputConfig.srate, srate); + int r = 0; + int dr = MulDiv(numsamples, 1 << 12, nns); + if (bps == 16) + { + for (x = 0; x < nns; x++) { + cf_buf[x] = samples[(r >> 12)*2] / 2 + samples[(r >> 12)*2 + 1] / 2; + r += dr; + } + } + else + { + for (x = 0; x < nns; x++) { + cf_buf[x] = ((((char *) samples)[(r >> 12)*2]^128) << 8) / 2 + + ((((char *) samples)[(r >> 12)*2 + 1]^128) << 8) / 2; + r += dr; + } + } + ns = nns * 2; + } else { + int x; + int nns = MulDiv(numsamples, InputConfig.srate, srate); + int r = 0; + int dr = MulDiv(numsamples, 1 << 12, nns); + if (bps == 16) + { + for (x = 0; x < nns; x++) { + cf_buf[x] = samples[r >> 12]; + r += dr; + } + } + else + { + for (x = 0; x < nns; x++) { + cf_buf[x] = (((char *) samples)[r >> 12]^128) << 8; + r += dr; + } + } + ns = nns * 2; + } + s = (char *) cf_buf; + } + } else { + if (nch != 2 || bps != 16 || srate != (int) InputConfig.srate) { + if (nch == 2) { + int x; + int nns = MulDiv(numsamples, InputConfig.srate, srate); + int r = 0; + int dr = MulDiv(numsamples, 1 << 12, nns); + if (bps == 16) + { + for (x = 0; x < nns; x++) { + cf_buf[x * 2] = samples[(r >> 12)*2]; + cf_buf[x * 2 + 1] = samples[(r >> 12)*2 + 1]; + r += dr; + } + } + else + { + for (x = 0; x < nns; x++) { + cf_buf[x * 2] = (((char *) samples)[(r >> 12)*2]^128) << 8; + cf_buf[x * 2 + 1] = (((char *) samples)[(r >> 12)*2 + 1]^128) << 8; + r += dr; + } + ns = nns * 4; + } + } else { + int x; + int nns = MulDiv(numsamples, InputConfig.srate, srate); + int r = 0; + int dr = MulDiv(numsamples, 1 << 12, nns); + if (bps == 16) + { + for (x = 0; x < nns; x++) { + cf_buf[x * 2] = cf_buf[x * 2 + 1] = samples[r >> 12]; + r += dr; + } + } + else + { + for (x = 0; x < nns; x++) + { + cf_buf[x * 2] = cf_buf[x * 2 + 1] = (((char *) samples)[r >> 12]^128) << 8; + r += dr; + } + ns = nns * 4; + } + } + + s = (char *) cf_buf; + } + else ns *= 2; + } + samples = (short *) s; + numsamples = ns / (InputConfig.nch * 2); + } + + if (!VU.update) { + for (int j = 0; j < numsamples; j++) { + if (VU.vu_l < samples[j]) VU.vu_l = samples[j]; + if (InputConfig.nch == 2) { + if (VU.vu_r < samples[j + 1]) VU.vu_r = samples[j + 1]; + j++; + } + } + if (InputConfig.nch == 1) VU.vu_r = VU.vu_l; + VU.update = 1; + } + Crossfader->put(samples, numsamples); + ReleaseMutex(cf_mutex); + } + } + return numorig; +} + + +void Quit(winampDSPModule *this_mod) { + doQuit(); +} \ No newline at end of file diff --git a/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/_ptrlist.h b/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/_ptrlist.h new file mode 100644 index 00000000..d81a8b7c --- /dev/null +++ b/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/_ptrlist.h @@ -0,0 +1,411 @@ +//PORTABLE +#ifndef _PTRLIST_H +#define _PTRLIST_H + +#define POS_LAST -1 + +// 1k each, leaving 16 bytes for MALLOC overhead +#define PTRLIST_INCREMENT 252 + +template +class PtrList +{ +public: + PtrList() + { + nitems = 0; + nslots = 0; + items = NULL; + } + + PtrList( PtrList *other ) + { + nitems = other->nitems; + nslots = other->nslots; + items = (T **)memdup( other->items, nslots * sizeof( T * ) ); + } + + virtual ~PtrList() + { + if ( items ) + free( items ); + } + + int getNumItems() { return nitems; }; + + T *enumItem( int n ) + { + if ( items == NULL ) + return NULL; + + if ( n < 0 ) + return NULL; + + if ( n >= nitems ) + return NULL; + + return items[ n ]; + } + + T *operator[]( int n ) { return enumItem( n ); } + +// this will safely return NULL if 0 items due to enumItems's boundscheck + T *getFirst() { return enumItem( 0 ); } + + T *getLast() { return enumItem( nitems - 1 ); } + + virtual T *addItem( T *item, int pos = POS_LAST ) + { +// ASSERTPR(item != NULL, "can't put NULLs into ptrlists"); +// ASSERT(nitems <= nslots); + if ( items == NULL ) + { + nslots = PTRLIST_INCREMENT; + items = (T **)malloc( sizeof( T * ) * nslots ); + nitems = 0; + } + else if ( nslots == nitems ) + { // time to expand + T **newitems; + nslots += PTRLIST_INCREMENT; + newitems = (T **)malloc( sizeof( T * ) * nslots ); + memcpy( newitems, items, nitems * sizeof( T * ) ); + + if ( items ) + free( items ); + + items = newitems; + } + + _addToList( item ); + if ( pos != POS_LAST ) + moveItem( nitems - 1, pos ); + + return item; + } + + // FG> avoid using this before i tested it more + void moveItem( int from, int to ) + { + if ( from == to ) + return; + + T *ptr = items[ from ]; + + if ( nitems > from + 1 ) // if moving from the end, there is nothing to shift behind our src position + //IMPORTANT note for future ports: This assumes MEMCPY accepts overlapping segments. + memcpy( &items[ from ], &items[ from + 1 ], ( nitems - from ) * sizeof( T * ) ); + + if ( to > from ) + to--; + + if ( nitems > to ) // if moving to the end, there is nothing to shift behind our target position + memcpy( &items[ to + 1 ], &items[ to ], ( nitems - to - 1 ) * sizeof( T * ) ); + + items[ to ] = ptr; + } + + // deletes first instance of a pointer in list, returns how many are left + int delItem( T *item ) + { + int c = 0; + + if ( item == NULL || items == NULL || nitems <= 0 ) + return 0; + + // count occurences of item in items, remember the first one + T **p = items; + int first = -1; + + for ( int i = 0; i < nitems; i++, p++ ) + { + if ( *p == item ) + { + if ( c++ == 0 ) + first = i; + } + } + + // if we found one, remove it + if ( c > 0 ) + { + delByPos( first ); // delByPos is faaast + c--; // decrease count + } + + return c; // returns how many occurences of this item left + } + + // removes all instances of this pointer + void delEveryItem( T *item ) { while ( delItem( item ) ); } + + // removes pointer at specified pos + void delByPos( int pos ) + { + if ( pos < 0 || pos >= nitems ) + return; //JC + + --nitems; + if ( pos == nitems ) + return; // last one? nothing to copy over + + memcpy( items + pos, items + ( pos + 1 ), sizeof( T * ) * ( nitems - pos ) ); // CT:not (nitems-(pos+1)) as nitems has been decremented earlier + } + + // removes last item, leaving pointer alone + void removeLastItem() + { + if ( nitems == 0 || items == NULL ) + return; + + nitems--; // hee hee + } + + // removes all entries, leaving pointers alone + void removeAll() + { + if ( items ) + free( items ); + + items = NULL; + nitems = 0; + nslots = 0; + } + + // removes all entries, calling FREE on the pointers + void freeAll() + { + for ( int i = 0; i < nitems; i++ ) //JC + if ( items ) + free( items[ i ] ); + + removeAll(); + } + + // removes all entries, calling delete on the pointers + void deleteAll() + { + int i; + if ( items == NULL || nitems <= 0 ) + return; //JC + + for ( i = 0; i < nitems; i++ ) + { //JC + delete items[ i ]; + } + + removeAll(); + } + + virtual int haveItem( T *item ) + {// gross-ass linear search to see if we have it + for ( int i = 0; i < nitems; i++ ) //JC + if ( items[ i ] == item ) + return 1; + + return 0; + } + + virtual int searchItem( T *item ) + { // gross-ass linear search to find index of item + for ( int i = 0; i < getNumItems(); i++ ) + if ( items[ i ] == item ) + return i; + + return -1; + } + +protected: + virtual void _addToList( T *item ) { items[ nitems++ ] = item; } + + int nitems, nslots; + T **items; +}; + +template +class PtrListBaseSorted : public PtrList +{ +public: + virtual T *findItem( char *attrib ) + { +#if 1 + if ( nitems == 0 || items == NULL ) + return NULL; + + int bot = 0, top = nitems - 1; + + for ( int c = 0; c < nitems + 16; c++ ) + { + if ( bot > top ) + return NULL; + + int mid = ( bot + top ) / 2; + int r = compareAttrib( attrib, items[ mid ] ); + + if ( r == 0 ) + return items[ mid ]; + + if ( r < 0 ) + { + top = mid - 1; + } + else + { + bot = mid + 1; + } + } + // ASSERTPR(0, "binary search fucked up"); +#else + for ( int i = 0; i < nitems; i++ ) + { + if ( compareAttrib( attrib, items[ i ] ) == 0 ) + return items[ i ]; + } +#endif + return NULL; + } + +protected: + // comparator for searching -- override + virtual int compareAttrib( char *attrib, T *item ) = 0; + + // comparator for sorting -- override , -1 p1 < p2, 0 eq, 1 p1 > p2 + virtual int compareItem( T *p1, T *p2 ) = 0; +}; + + +#if 0 +// try not to use this +template +class PtrListSortedInsertion : public PtrListBaseSorted +{ +protected: + virtual void _addToList( T *item ) + { + if ( nitems == 0 ) + { + items[ nitems++ ] = item; + + return; + } + + for ( int i = 0; i < nitems; i++ ) + { + if ( compareItem( items[ i ], item ) == 1 ) + { + T *tmp = items[ i ]; + items[ i ] = item; + for ( int j = i + 1; j < nitems; j++ ) + { + T *tmp2 = items[ j ]; + items[ j ] = tmp; + tmp = tmp2; + } + + items[ nitems++ ] = tmp; + + return; + } + } + + items[ nitems++ ] = item; + + return; + } +}; +#endif + +// a base class to defer sorting until lookup +template +class PtrListSorted : public PtrListBaseSorted +{ +public: + PtrListSorted() + { + nitems = 0; + nslots = 0; + items = NULL; + need_sorting = 0; + } + + virtual T *addItem( T *item ) + { + need_sorting = 1; + + return PtrList::addItem( item ); + } + + virtual T *findItem( char *attrib ) + { + sort(); + + return PtrListBaseSorted::findItem( attrib ); + } + + int needSort() { return need_sorting; } + + void sort() + { + if ( need_sorting ) + _sort(); + + need_sorting = 0; + } + +private: + int need_sorting; + + virtual void _sort() = 0; +}; + +template +class PtrListQuickSorted : public PtrListSorted +{ +public: + virtual void _sort() + { + if ( items == NULL || nitems <= 1 ) + return; + + Qsort( 0, nitems - 1 ); + } + + void swap( int a, int b ) + { + T *tmp = items[ a ]; + items[ a ] = items[ b ]; + items[ b ] = tmp; + } + + void Qsort( int lo0, int hi0 ) + { + int lo = lo0, hi = hi0; + if ( hi0 > lo0 ) + { + T *mid = enumItem( ( lo0 + hi0 ) / 2 ); + while ( lo <= hi ) + { + while ( ( lo < hi0 ) && ( compareItem( items[ lo ], mid ) < 0 ) ) + lo++; + + while ( ( hi > lo0 ) && ( compareItem( items[ hi ], mid ) > 0 ) ) + hi--; + + if ( lo <= hi ) + { + swap( lo, hi ); + lo++; + hi--; + } + } + + if ( lo0 < hi ) + Qsort( lo0, hi ); + + if ( lo < hi0 ) + Qsort( lo, hi0 ); + } + } +}; + +#endif diff --git a/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder.cpp b/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder.cpp new file mode 100644 index 00000000..a2a3f2fe --- /dev/null +++ b/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder.cpp @@ -0,0 +1,95 @@ +#include +#include "c_encoder.h" + +C_ENCODER::C_ENCODER(int ExtInfoSize) { + SetName("Untyped Encoder"); + ExtendedInfoPtr = (T_EncoderIOVals *)malloc(ExtInfoSize); + ExtendedInfoSize = ExtInfoSize; +} + +C_ENCODER::~C_ENCODER() { + Close(); + if(ExtendedInfoPtr && ExtendedInfoSize) free(ExtendedInfoPtr); + ExtendedInfoSize = 0; +} + +void C_ENCODER::Close() { + ClearAttribs(); +} + +void C_ENCODER::SetName(const char *name) { + if (name) lstrcpyn(Name, name, C_ENCODER_NameLen); +} + +char *C_ENCODER::GetName() { + return Name; +} + +void C_ENCODER::Reset() { +} + +void C_ENCODER::ChangeSettings(const void *Settings) { + if(ExtendedInfoPtr && ExtendedInfoSize && Settings && Settings != ExtendedInfoPtr) { + memcpy(ExtendedInfoPtr,Settings,ExtendedInfoSize); + Reset(); + } +} + +void C_ENCODER::Create(const T_EncoderIOVals *Settings, const char *name) { + if(name) SetName(name); + ChangeSettings(Settings); +} + +void C_ENCODER::ClearAttribs() { + for(int i = AttribList.size()-1; i >= 0; i--) { + T_ATTRIB *myAttrib = AttribList[i]; + if(myAttrib->OutputVals) delete[] myAttrib->OutputVals; + } + + //AttribList.deleteAll(); + for (auto attrib : AttribList) + { + delete attrib; + } + AttribList.clear(); +} + +void C_ENCODER::AddAttrib(const char *Text, const void *Attributes) { + T_ATTRIB *myAttrib = new T_ATTRIB; + if(Text!=NULL) { + ::strncpy((char *)&myAttrib->Text,Text,sizeof(myAttrib->Text)); + } else { + ::strncpy((char *)&myAttrib->Text,"",sizeof(myAttrib->Text)); + } + myAttrib->OutputVals = (T_EncoderIOVals *)Attributes; + AttribList.push_back(myAttrib); +} + +int C_ENCODER::Encode(const void *inputbuf, const unsigned int inputbufsize, void *outputbuf, const unsigned int outputbufsize, int *inputamtused) { + if((inputbuf != NULL) && (outputbuf != NULL) && (inputbufsize != 0) && (outputbufsize != 0) && (inputamtused != NULL)) { + int numitems = (inputbufsize > outputbufsize) ? outputbufsize : inputbufsize; + memcpy(outputbuf,inputbuf,numitems); + *inputamtused = numitems; + return numitems; + } + return 0; +} + +int C_ENCODER::GetNumAttribs() { + return AttribList.size(); +} + +int C_ENCODER::EnumAttrib(const int val, T_ATTRIB *buf) { + if((val < 0)||(val >= AttribList.size())||(buf==NULL)) return 0; + T_ATTRIB *myattrib = AttribList[val]; + if(myattrib==NULL) return 0; + ::memcpy(buf,myattrib,sizeof(T_ATTRIB)); + return 1; +} + +char * C_ENCODER::GetContentType() { + + //if(strcmp(this->GetName(), "MP3 Encoder") == 0) + return "audio/mpeg"; + +} \ No newline at end of file diff --git a/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder.h b/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder.h new file mode 100644 index 00000000..04ef9220 --- /dev/null +++ b/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder.h @@ -0,0 +1,53 @@ +#ifndef __C_ENCODER_H__ +#define __C_ENCODER_H__ + +#include +#include +#include +#include + +#define C_ENCODER_NameLen 1024 + +struct T_EncoderIOVals { + unsigned int output_bitRate; +}; + +struct T_ATTRIB { + char Text[256]; +void *OutputVals; +}; + +class C_ENCODER { +private: + char Name[C_ENCODER_NameLen]; + std::vector AttribList; +protected: + T_EncoderIOVals *ExtendedInfoPtr; + int ExtendedInfoSize; + void SetName(const char *name); + void ClearAttribs(); + void AddAttrib(const char *Text, const void *Attributes); +public: + C_ENCODER(int ExtInfoSize = 0); + virtual ~C_ENCODER(); + + virtual void ChangeSettings(const void *Settings); + virtual void Create(const T_EncoderIOVals *Settings, const char *name = NULL); + virtual void Close(); + virtual void Reset(); + virtual char *GetName(); + virtual void *GetExtInfo(int *ExtInfoSize = NULL) { + if(ExtInfoSize != NULL) *ExtInfoSize = ExtendedInfoSize; + return ExtendedInfoPtr; + } + + virtual char * GetContentType(); + virtual int Encode(const void *inputbuf, const unsigned int inputbufsize, void *outputbuf, const unsigned int outputbufsize, int *inputamtused); /* all values are in BYTES! */ + + virtual int GetNumAttribs(); + virtual int EnumAttrib(const int val, T_ATTRIB *buf); + + virtual bool UseNsvConfig() { return false; } +}; + +#endif /* !__C_ENCODER_H__ */ \ No newline at end of file diff --git a/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_aacp.cpp b/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_aacp.cpp new file mode 100644 index 00000000..62a09e89 --- /dev/null +++ b/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_aacp.cpp @@ -0,0 +1,84 @@ +#include "c_encoder_aacp.h" +#include "../../utils.h" + +HINSTANCE C_ENCODER_AACP::hEncoderInstance = NULL; + +C_ENCODER_AACP::C_ENCODER_AACP(HWND winamp) : C_ENCODER_NSV(sizeof(T_ENCODER_AACP_INFO)) { + SetName("AAC+ Encoder"); + winampWnd = winamp; + ConfigAudio3 = NULL; + if(hEncoderInstance == NULL) { + wchar_t dir[MAX_PATH] = {0}; + snwprintf(dir, MAX_PATH, L"%s\\enc_aacplus.dll", GetPluginDirectoryW(winamp)); + hEncoderInstance = LoadLibraryW(dir); + } + + if(hEncoderInstance) { + void * CreateAudio3=(void *)GetProcAddress(hEncoderInstance, "CreateAudio3"); + void * GetAudioTypes3=(void *)GetProcAddress(hEncoderInstance, "GetAudioTypes3"); + void * ConfigAudio3=(void *)GetProcAddress(hEncoderInstance, "ConfigAudio3"); + void * SetWinampHWND=(void *)GetProcAddress(hEncoderInstance, "SetWinampHWND"); + SetEncoder(CreateAudio3,GetAudioTypes3,ConfigAudio3,SetWinampHWND); + } + + T_ENCODER_AACP_INFO * EncInfo = (T_ENCODER_AACP_INFO *)ExtendedInfoPtr; + EncInfo->output_bitRate = AACP_DEFAULT_OUTPUTBITRATE; + EncInfo->output_channelmode = AACP_DEFAULT_OUTPUTCHANNELMODE; + EncInfo->output_quality = AACP_DEFAULT_OUTPUTQUALITY; + EncInfo->output_samplerate = AACP_DEFAULT_OUTPUTSAMPLERATE; + EncInfo->output_v2enable = AACP_DEFAULT_OUTPUTV2ENABLE; +} + +C_ENCODER_AACP::~C_ENCODER_AACP() { + C_ENCODER_NSV::~C_ENCODER_NSV(); +} + +static int cacheVal=0; +bool C_ENCODER_AACP::isPresent(HWND winamp) { + if(cacheVal!=0 && hEncoderInstance!=0) return cacheVal==2; + bool ret=false; + wchar_t dir[MAX_PATH] = {0}; + snwprintf(dir, MAX_PATH, L"%s\\enc_aacplus.dll", GetPluginDirectoryW(winamp)); + FILE * f = _wfopen(dir, L"rb"); + if (f) { + fseek(f,0,2); + if(ftell(f) > 0) ret=true; + fclose(f); + } + cacheVal=ret?2:1; + return ret; +} + +void C_ENCODER_AACP::FillAttribs() { + T_ENCODER_AACP_INFO &EncInfo = *(T_ENCODER_AACP_INFO *)ExtendedInfoPtr; + T_ENCODER_AACP_INFO *attribs = new T_ENCODER_AACP_INFO; + *attribs = EncInfo; + AddAttrib("",attribs); +} + +void C_ENCODER_AACP::FillConfFile(char * conf_file, char * section) { + if(!section) section="audio_aacplus"; + + T_ENCODER_AACP_INFO &EncInfo = *(T_ENCODER_AACP_INFO *)ExtendedInfoPtr; + + WritePrivateProfileInt("samplerate", EncInfo.output_samplerate, section, conf_file); + WritePrivateProfileInt("channelmode", EncInfo.output_channelmode, section, conf_file); + WritePrivateProfileInt("bitrate", EncInfo.output_bitRate * 1000, section, conf_file); + WritePrivateProfileInt("v2enable", EncInfo.output_v2enable, section, conf_file); +} + +void C_ENCODER_AACP::ReadConfFile(char * conf_file, char * section) { + if(!section) section="audio_aacplus"; + + T_ENCODER_AACP_INFO &EncInfo = *(T_ENCODER_AACP_INFO *)ExtendedInfoPtr; + T_ENCODER_AACP_INFO *attribs = new T_ENCODER_AACP_INFO; + *attribs = EncInfo; + + attribs->output_samplerate = GetPrivateProfileInt(section,"samplerate",AACP_DEFAULT_OUTPUTSAMPLERATE,conf_file); + attribs->output_channelmode = GetPrivateProfileInt(section,"channelmode",AACP_DEFAULT_OUTPUTCHANNELMODE,conf_file); + attribs->output_bitRate = GetPrivateProfileInt(section,"bitrate",AACP_DEFAULT_OUTPUTBITRATE,conf_file)/1000; + attribs->output_quality = GetPrivateProfileInt(section,"quality",AACP_DEFAULT_OUTPUTQUALITY,conf_file); + attribs->output_v2enable = GetPrivateProfileInt(section,"v2enable",AACP_DEFAULT_OUTPUTV2ENABLE,conf_file); + + ChangeSettings(attribs); +} \ No newline at end of file diff --git a/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_aacp.h b/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_aacp.h new file mode 100644 index 00000000..f6028169 --- /dev/null +++ b/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_aacp.h @@ -0,0 +1,37 @@ +#ifndef __C_ENCODER_AACP_H__ +#define __C_ENCODER_AACP_H__ + +#include "c_encoder_nsv.h" + +struct T_ENCODER_AACP_INFO : public T_ENCODER_NSV_INFO +{ + unsigned int output_quality; + unsigned int output_samplerate; + unsigned int output_channelmode; + unsigned int output_v2enable; +}; + +#define AACP_DEFAULT_OUTPUTCHANNELMODE 4 +#define AACP_DEFAULT_OUTPUTBITRATE 48 +#define AACP_DEFAULT_OUTPUTQUALITY 2 +#define AACP_DEFAULT_OUTPUTSAMPLERATE 44100 +#define AACP_DEFAULT_OUTPUTV2ENABLE 1 + +class C_ENCODER_AACP : public C_ENCODER_NSV { +private: + HWND winamp; +protected: + virtual void FillAttribs(); +public: + static HINSTANCE hEncoderInstance; + C_ENCODER_AACP(HWND hwnd = 0); + virtual ~C_ENCODER_AACP(); + static bool isPresent(HWND winamp); + virtual void ReadConfFile(char * conf_file, char * section=NULL); + virtual void FillConfFile(char * conf_file, char * section=NULL); + static void Unload() { if(hEncoderInstance) FreeLibrary(hEncoderInstance); hEncoderInstance=0; } + virtual char * GetContentType() { return "audio/aacp"; } + virtual HINSTANCE GetEncoderInstance() { return hEncoderInstance; } +}; + +#endif /* !__C_ENCODER_AACP_H__ */ \ No newline at end of file diff --git a/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_fhgaac.cpp b/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_fhgaac.cpp new file mode 100644 index 00000000..749988d2 --- /dev/null +++ b/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_fhgaac.cpp @@ -0,0 +1,80 @@ +#include "c_encoder_fhgaac.h" +#include "../../utils.h" + +HINSTANCE C_ENCODER_FHGAAC::hEncoderInstance = NULL; + +C_ENCODER_FHGAAC::C_ENCODER_FHGAAC(HWND winamp) : C_ENCODER_NSV(sizeof(T_ENCODER_FHGAAC_INFO)) { + SetName("Fraunhofer Encoder"); + winampWnd = winamp; + ConfigAudio3 = NULL; + if(hEncoderInstance == NULL) { + wchar_t dir[MAX_PATH] = {0}; + snwprintf(dir, MAX_PATH, L"%s\\enc_fhgaac.dll", GetPluginDirectoryW(winamp)); + hEncoderInstance = LoadLibraryW(dir); + } + + if(hEncoderInstance) { + void * CreateAudio3=(void *)GetProcAddress(hEncoderInstance, "CreateAudio3"); + void * GetAudioTypes3=(void *)GetProcAddress(hEncoderInstance, "GetAudioTypes3"); + void * ConfigAudio3=(void *)GetProcAddress(hEncoderInstance, "ConfigAudio3"); + void * SetWinampHWND=(void *)GetProcAddress(hEncoderInstance, "SetWinampHWND"); + SetEncoder(CreateAudio3,GetAudioTypes3,ConfigAudio3,SetWinampHWND,1); + } + + T_ENCODER_FHGAAC_INFO * EncInfo = (T_ENCODER_FHGAAC_INFO *)ExtendedInfoPtr; + EncInfo->output_bitRate = FHGAAC_DEFAULT_OUTPUTBITRATE; + EncInfo->output_profile = FHGAAC_DEFAULT_OUTPUTPROFILE; + EncInfo->output_surround = FHGAAC_DEFAULT_OUTPUTSURROUND; +} + +C_ENCODER_FHGAAC::~C_ENCODER_FHGAAC() { + C_ENCODER_NSV::~C_ENCODER_NSV(); +} + +static int cacheVal=0; +bool C_ENCODER_FHGAAC::isPresent(HWND winamp) { + if(cacheVal!=0 && hEncoderInstance!=0) return cacheVal==2; + bool ret=false; + wchar_t dir[MAX_PATH] = {0}; + snwprintf(dir, MAX_PATH, L"%s\\enc_fhgaac.dll", GetPluginDirectoryW(winamp)); + FILE * f = _wfopen(dir, L"rb"); + if (f) { + fseek(f,0,2); + if(ftell(f) > 0) ret=true; + fclose(f); + } + cacheVal=ret?2:1; + return ret; +} + +void C_ENCODER_FHGAAC::FillAttribs() { + T_ENCODER_FHGAAC_INFO &EncInfo = *(T_ENCODER_FHGAAC_INFO *)ExtendedInfoPtr; + T_ENCODER_FHGAAC_INFO *attribs = new T_ENCODER_FHGAAC_INFO; + *attribs = EncInfo; + AddAttrib("",attribs); +} + +void C_ENCODER_FHGAAC::FillConfFile(char * conf_file, char * section) { + if(!section) section="audio_adtsaac"; + + T_ENCODER_FHGAAC_INFO &EncInfo = *(T_ENCODER_FHGAAC_INFO *)ExtendedInfoPtr; + + WritePrivateProfileInt("profile", EncInfo.output_profile, section, conf_file); + WritePrivateProfileInt("bitrate", EncInfo.output_bitRate, section, conf_file); + WritePrivateProfileInt("surround", EncInfo.output_surround, section, conf_file); + WritePrivateProfileInt("shoutcast", 1, section, conf_file); +} + +void C_ENCODER_FHGAAC::ReadConfFile(char * conf_file, char * section) { + if(!section) section="audio_adtsaac"; + + T_ENCODER_FHGAAC_INFO &EncInfo = *(T_ENCODER_FHGAAC_INFO *)ExtendedInfoPtr; + T_ENCODER_FHGAAC_INFO *attribs = new T_ENCODER_FHGAAC_INFO; + *attribs = EncInfo; + + attribs->output_profile = GetPrivateProfileInt(section,"profile",FHGAAC_DEFAULT_OUTPUTPROFILE,conf_file); + attribs->output_bitRate = GetPrivateProfileInt(section,"bitrate",FHGAAC_DEFAULT_OUTPUTBITRATE,conf_file); + attribs->output_surround = GetPrivateProfileInt(section,"surround",FHGAAC_DEFAULT_OUTPUTSURROUND,conf_file); + + ChangeSettings(attribs); +} \ No newline at end of file diff --git a/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_fhgaac.h b/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_fhgaac.h new file mode 100644 index 00000000..67071945 --- /dev/null +++ b/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_fhgaac.h @@ -0,0 +1,33 @@ +#ifndef __C_ENCODER_FHGAAC_H__ +#define __C_ENCODER_FHGAAC_H__ + +#include "c_encoder_nsv.h" + +struct T_ENCODER_FHGAAC_INFO : public T_ENCODER_NSV_INFO +{ + unsigned int output_profile; + unsigned int output_surround; +}; + +#define FHGAAC_DEFAULT_OUTPUTBITRATE 48 +#define FHGAAC_DEFAULT_OUTPUTPROFILE 0 // automatic +#define FHGAAC_DEFAULT_OUTPUTSURROUND 0 + +class C_ENCODER_FHGAAC : public C_ENCODER_NSV { +private: + HWND winamp; +protected: + virtual void FillAttribs(); +public: + static HINSTANCE hEncoderInstance; + C_ENCODER_FHGAAC(HWND hwnd = 0); + virtual ~C_ENCODER_FHGAAC(); + static bool isPresent(HWND winamp); + virtual void ReadConfFile(char * conf_file, char * section=NULL); + virtual void FillConfFile(char * conf_file, char * section=NULL); + static void Unload() { if(hEncoderInstance) FreeLibrary(hEncoderInstance); hEncoderInstance=0; } + virtual char * GetContentType() { return "audio/aacp"; } + virtual HINSTANCE GetEncoderInstance() { return hEncoderInstance; } +}; + +#endif /* !__C_ENCODER_AACP_H__ */ \ No newline at end of file diff --git a/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_mp3dll.cpp b/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_mp3dll.cpp new file mode 100644 index 00000000..4a703b24 --- /dev/null +++ b/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_mp3dll.cpp @@ -0,0 +1,105 @@ +#include "c_encoder_mp3dll.h" +#include "../../utils.h" + +T_ENCODER_MP3_INFO formatlist[] = { + {8, 22050, 1, 44100, 2, 8}, {16, 22050, 1, 44100, 2, 8}, {24, 22050, 1, 44100, 2, 8}, + {32, 22050, 1, 44100, 2, 8}, {40, 22050, 1, 44100, 2, 8}, {48, 22050, 1, 44100, 2, 8}, + {48, 44100, 1, 44100, 2, 8}, {56, 22050, 1, 44100, 2, 8}, {56, 44100, 1, 44100, 2, 8}, + {64, 44100, 1, 44100, 2, 8}, {80, 44100, 1, 44100, 2, 8}, {96, 44100, 1, 44100, 2, 8}, + {112, 44100, 1, 44100, 2, 8}, {128, 44100, 1, 44100, 2, 8}, {40, 22050, 2, 44100, 2, 8}, + {48, 22050, 2, 44100, 2, 8}, {56, 22050, 2, 44100, 2, 8}, {64, 22050, 2, 44100, 2, 8}, + {80, 22050, 2, 44100, 2, 8}, {56, 44100, 2, 44100, 2, 8}, {64, 44100, 2, 44100, 2, 8}, + {80, 44100, 2, 44100, 2, 8}, {96, 44100, 2, 44100, 2, 8}, {112, 44100, 2, 44100, 2, 8}, + {128, 44100, 2, 44100, 2, 8}, {160, 44100, 2, 44100, 2, 8}, {192, 44100, 2, 44100, 2, 8}, + {224, 44100, 2, 44100, 2, 8}, {256, 44100, 2, 44100, 2, 8}, {320, 44100, 2, 44100, 2, 8} +}; + +static unsigned int formatlist_numEntries = sizeof(formatlist) / sizeof(T_ENCODER_MP3_INFO); + +C_ENCODER_MP3::C_ENCODER_MP3(void *init, void *params, void *encode, void *finish) : C_ENCODER(sizeof(T_ENCODER_MP3_INFO)) { //sizeof(T_ENCODER_LAMEMP3_INFO) + SetName("MP3 Encoder"); + T_ENCODER_MP3_INFO &EncInfo = *((T_ENCODER_MP3_INFO *)ExtendedInfoPtr); + Handle = NULL; + has_encoded = 0; + EncInfo = formatlist[MP3_DEFAULT_ATTRIBNUM]; + hMutex = CreateMutex(NULL,TRUE,NULL); + ReleaseMutex(hMutex); + + lame_init = (lame_t (__cdecl *)(void))init; + lame_init_params = (int (__cdecl *)(lame_global_flags *))params; + lame_encode_buffer_interleaved = (int (__cdecl *)(lame_global_flags *, short int pcm[], int num_samples, char *mp3buffer, int mp3buffer_size))encode; + lame_encode_flush = (int (__cdecl *)(lame_global_flags *, char *mp3buffer, int size))finish; +} + +C_ENCODER_MP3::~C_ENCODER_MP3() { + WaitForSingleObject(hMutex,INFINITE); + CloseHandle(hMutex); + hMutex = NULL; + C_ENCODER::~C_ENCODER(); +} + +void C_ENCODER_MP3::Close() { + C_ENCODER::Close(); + if(lame_init != NULL) { + if(has_encoded && lame_encode_flush) { + char buf[1024] = {0}; + lame_encode_flush(Handle,(char *)buf,sizeof(buf)); + } + //delete Handle; caused crash !! needs looking at + Handle = NULL; + has_encoded = 0; + } +} + +void C_ENCODER_MP3::Reset() { + T_ENCODER_MP3_INFO &EncInfo = *(T_ENCODER_MP3_INFO *)ExtendedInfoPtr; + if(WaitForSingleObject(hMutex,INFINITE) != WAIT_OBJECT_0) return; + Close(); + if(lame_init != NULL && EncInfo.input_sampleRate != 0 && EncInfo.input_numChannels != 0) { + if(EncInfo.output_sampleRate != 0 && EncInfo.output_bitRate != 0 && Handle == NULL) { + has_encoded = 0; + Handle = lame_init(); + Handle->samplerate_in = EncInfo.input_sampleRate; + Handle->num_channels = 2; // always process as 2 channels as it resolves issues with soundcard input in mono mode (which is padded to stereo) + Handle->samplerate_out = EncInfo.output_sampleRate; + Handle->mode = EncInfo.output_numChannels == 2 ? (MPEG_mode)0 : (MPEG_mode)3; + Handle->brate = EncInfo.output_bitRate; + Handle->VBR = vbr_off; + Handle->write_lame_tag = 0; + Handle->write_id3tag_automatic = 0; + Handle->quality = EncInfo.QualityMode; + if(Handle->quality < 0 || Handle->quality > 9) Handle->quality = 8; + lame_init_params(Handle); + } else { + Handle = NULL; + } + for(unsigned int i = 0; i < formatlist_numEntries; i++) { + char textbuf[256]; + formatlist[i].QualityMode = EncInfo.QualityMode; + snprintf(textbuf,sizeof(textbuf),"%dkbps, %dHz, %s",formatlist[i].output_bitRate,formatlist[i].output_sampleRate,(formatlist[i].output_numChannels == 1 ? "Mono" : "Stereo")); + T_ENCODER_MP3_INFO *attribs = new T_ENCODER_MP3_INFO; + *attribs = formatlist[i]; + AddAttrib((char *)&textbuf,attribs); + } + } + ReleaseMutex(hMutex); +} + +int C_ENCODER_MP3::Encode(const void *inputbuf, const unsigned int inputbufsize, void *outputbuf, const unsigned int outputbufsize, int *inputamtused) { + if((inputbuf != NULL) && (outputbuf != NULL) && (inputbufsize != 0) && (outputbufsize != 0) && (inputamtused != NULL) && (Handle != NULL)) { + if(WaitForSingleObject(hMutex,INFINITE) != WAIT_OBJECT_0) return 0; + int outputamtused = 0; + if(lame_encode_buffer_interleaved) { + outputamtused = lame_encode_buffer_interleaved(Handle, (short *)inputbuf, inputbufsize / (2 * sizeof(short)), (char *)outputbuf, outputbufsize); + if(outputamtused < 0) { + ReleaseMutex(hMutex); + return 0; + } + has_encoded = 1; + } + *inputamtused = inputbufsize; + ReleaseMutex(hMutex); + return outputamtused; + } + return 0; +} \ No newline at end of file diff --git a/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_mp3dll.h b/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_mp3dll.h new file mode 100644 index 00000000..05beb05e --- /dev/null +++ b/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_mp3dll.h @@ -0,0 +1,51 @@ + +#ifndef __C_ENCODER_MP3DLL_H__ +#define __C_ENCODER_MP3DLL_H__ + +#include "c_encoder.h" +#include "../lame/include/lame.h" +#include "../lame/libmp3lame/lame_global_flags.h" +#include + +// Defaults for this encoder +#define MP3_DEFAULT_INPUTSAMPLERATE 44100 +#define MP3_DEFAULT_INPUTNUMCHANNELS 2 +#define MP3_DEFAULT_OUTPUTSAMPLERATE 44100 +#define MP3_DEFAULT_OUTPUTNUMCHANNELS 2 +#define MP3_DEFAULT_OUTPUTBITRATE 96 + +#define MP3_DEFAULT_ATTRIBNUM 22 + +struct T_ENCODER_MP3_INFO { + int output_bitRate; + int output_sampleRate; + int output_numChannels; + int input_sampleRate; + int input_numChannels; + int QualityMode; +}; + +#define HBE_STREAM lame_global_flags * + +class C_ENCODER_MP3 : public C_ENCODER { +private: + HANDLE hMutex; + lame_t Handle; + int has_encoded; +protected: + lame_t (*lame_init)(void); + int (*lame_init_params)(lame_global_flags *); + int (*lame_encode_buffer_interleaved)(lame_global_flags *, short int pcm[], int num_samples, char *mp3buffer, int mp3buffer_size); + int (*lame_encode_flush)(lame_global_flags *, char *mp3buffer, int size); + +public: + C_ENCODER_MP3(void *init, void *params, void *encode, void *finish); + virtual ~C_ENCODER_MP3(); + + virtual void Close(); + virtual void Reset(); + + virtual int Encode(const void *inputbuf, const unsigned int inputbufsize, void *outputbuf, const unsigned int outputbufsize, int *inputamtused); /* all values are in BYTES! */ +}; + +#endif /* !__C_ENCODER_MP3_H__ */ \ No newline at end of file diff --git a/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_nsv.cpp b/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_nsv.cpp new file mode 100644 index 00000000..18098535 --- /dev/null +++ b/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_nsv.cpp @@ -0,0 +1,151 @@ +#include "c_encoder_nsv.h" +#include "../../utils.h" +#include +#include + +static char * configfile; +static unsigned int configfourcc; +static HWND (*ConfigAudio3)(HWND intParent, HINSTANCE hinst, unsigned int outt, char *configfile); +static HINSTANCE encoderDllInstance; +static HWND cfgwnd=NULL; +C_ENCODER_NSV::C_ENCODER_NSV(int ExtInfoSize) : C_ENCODER(ExtInfoSize) { + hMutex = CreateMutex(NULL,TRUE,NULL); + ReleaseMutex(hMutex); + CreateAudio3 = NULL; + ConfigAudio3 = NULL; + GetAudioTypes3 = NULL; + SetWinampHWND = NULL; + SetConfigItem = NULL; + GetConfigItem = NULL; + winampWnd = NULL; + fourcc = 0; + encoder = NULL; +} + +void C_ENCODER_NSV::SetEncoder(void * CreateAudio3, void * GetAudioTypes3, void * ConfigAudio3, void * SetWinampHWND, int encoderNum) { + *(void **)&(this->CreateAudio3) = CreateAudio3; + *(void **)&(this->GetAudioTypes3) = GetAudioTypes3; + *(void **)&(this->ConfigAudio3) = ConfigAudio3; + *(void **)&(this->SetWinampHWND) = SetWinampHWND; + + if(this->SetWinampHWND) { + this->SetWinampHWND(winampWnd); + } + + if(this->GetAudioTypes3) { + char name[C_ENCODER_NameLen]; + fourcc = this->GetAudioTypes3(encoderNum,name); + } +} + +C_ENCODER_NSV::~C_ENCODER_NSV() { + WaitForSingleObject(hMutex,INFINITE); + + CloseHandle(hMutex); + hMutex = NULL; + C_ENCODER::~C_ENCODER(); +} + +void C_ENCODER_NSV::Close() { + C_ENCODER::Close(); + + if(encoder) + delete encoder; + encoder = NULL; +} + +void C_ENCODER_NSV::Reset() { + if(WaitForSingleObject(hMutex,INFINITE) != WAIT_OBJECT_0) return; + + Close(); + + if(!configfile) { + configfile = GetSCIniFile(winampWnd); + } + + FillConfFile(configfile); + + int nch = ((T_ENCODER_NSV_INFO*)ExtendedInfoPtr)->input_numChannels; + int srate = ((T_ENCODER_NSV_INFO*)ExtendedInfoPtr)->input_sampleRate; + if (CreateAudio3) { + const int bps = 16; /* I think this is always the case. */ + encoder = CreateAudio3(nch,srate,bps,mmioFOURCC('P','C','M',' '),&fourcc,configfile); + } + else encoder = NULL; + + /* I think (in that I havn't found anything to the contrary) that in the CreateAudio3 call, the encoder + * reads all its settings from the conf_file and never touches them again. Hence, this is safe. Ahem. + */ + FillAttribs(); + ReleaseMutex(hMutex); +} + +int C_ENCODER_NSV::Encode(const void *inputbuf, const unsigned int inputbufsize, void *outputbuf, const unsigned int outputbufsize, int *inputamtused) { + + if(WaitForSingleObject(hMutex,INFINITE) != WAIT_OBJECT_0) return 0; + int ret=0; + if(encoder && (inputbuf != NULL) && (outputbuf != NULL) && (inputbufsize != 0) && (outputbufsize != 0) && (inputamtused != NULL)) { + ret = encoder->Encode(0,(void *)inputbuf,inputbufsize,inputamtused,outputbuf,outputbufsize); + } else + *inputamtused = inputbufsize; /* we havn't got the dll, so just say everything is ok? */ + + ReleaseMutex(hMutex); + return ret; +} + +static BOOL CALLBACK configure_dlgproc(HWND intDlg, UINT uMsg, WPARAM wParam,LPARAM lParam) { + + switch(uMsg) { + case WM_INITDIALOG: + if(configfourcc == mmioFOURCC('A','D','T','S')) { + SetWindowTextW(intDlg, LocalisedString(IDS_FHGAAC_ENCODER, NULL, 0)); + } + #ifdef USE_OGG + else if(configfourcc == mmioFOURCC('O','G','G',' ')) { + SetWindowTextW(intDlg, LocalisedString(IDS_OGG_CONFIG_TITLE, NULL, 0)); + } + #endif + + cfgwnd=ConfigAudio3(intDlg, encoderDllInstance, configfourcc, configfile); + + if(cfgwnd) { + RECT r; + GetWindowRect(GetDlgItem(intDlg,IDC_GO_HERE),&r); + ScreenToClient(intDlg,(LPPOINT)&r); + SetWindowPos(cfgwnd,NULL,r.left,r.top,0,0,SWP_NOACTIVATE|SWP_NOSIZE|SWP_NOZORDER); + ShowWindow(cfgwnd,SW_SHOWNA); + InvalidateRect(intDlg,NULL,FALSE); + } + break; + case WM_COMMAND: + if(LOWORD(wParam) == IDCANCEL) EndDialog(intDlg,0); + break; + case WM_DESTROY: + DestroyWindow(cfgwnd); + break; + } + return 0; +} + +void C_ENCODER_NSV::Configure(HWND parent, HINSTANCE hDllInstance) { + if(ConfigAudio3) { + configfourcc = fourcc; + if(!configfile) { + configfile = GetSCIniFile(winampWnd); + } + + ::ConfigAudio3 = this->ConfigAudio3; + ::encoderDllInstance = GetEncoderInstance(); + + if(WaitForSingleObject(hMutex,INFINITE) != WAIT_OBJECT_0) return; + FillConfFile(configfile); + ReleaseMutex(hMutex); + + LocalisedDialogBox(hDllInstance,IDD_NSVCONFIG,parent,::configure_dlgproc); + + if(WaitForSingleObject(hMutex,INFINITE) != WAIT_OBJECT_0) return; + ReadConfFile(configfile); + Reset(); + ReleaseMutex(hMutex); + } +} \ No newline at end of file diff --git a/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_nsv.h b/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_nsv.h new file mode 100644 index 00000000..14adca08 --- /dev/null +++ b/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_nsv.h @@ -0,0 +1,76 @@ +/* This is an abstract class to use the NSV style of encoder. + */ + +#ifndef __C_ENCODER_NSV_H__ +#define __C_ENCODER_NSV_H__ + +#include "c_encoder.h" +#include "enc_if.h" +#include +#include +#include "../../Resource/resource.h" + +struct T_ENCODER_NSV_INFO { + unsigned int output_bitRate; + unsigned int input_numChannels; + unsigned int input_sampleRate; +}; + +class C_ENCODER_NSV : public C_ENCODER { +private: + HANDLE hMutex; +protected: + // These are exported by enc_*.dll + AudioCoder* (*CreateAudio3)(int nch, int srate, int bps, unsigned int srct, unsigned int *outt, char *configfile); + int (*GetAudioTypes3)(int idx, char *desc); + HWND (*ConfigAudio3)(HWND hwndParent, HINSTANCE hinst, unsigned int outt, char *configfile); + void (*SetWinampHWND)(HWND hwnd); + int (*SetConfigItem)(unsigned int outt, char *item, char *data, char *configfile); + int (*GetConfigItem)(unsigned int outt, char *item, char *data, int len, char *configfile); + /* We don't need the rest of the exports + AudioCoder *(*FinishAudio3)(char *fn, AudioCoder *c); + void (*PrepareToFinish)(const char *filename, AudioCoder *coder); + */ + + // our encoder (the AudioCoder class is defined in enc_if.h) + AudioCoder* encoder; + + // the type of the output format + unsigned int fourcc; + + // fill up the attribute list (using AddAttrib) + virtual void FillAttribs()=0; + + // child classes MUST call this in their constructor + // note: encoderNum defaults to 0 which resolves to the first encoder + // in most enc_* but make sure to set this correctly for others + virtual void SetEncoder(void * CreateAudio3, void * GetAudioTypes3, void * ConfigAudio3, void * SetWinampHWND, int encoderNum=0); + + // this is used in Configure() + virtual HINSTANCE GetEncoderInstance()=0; + + // this is used for esternal encoders so they can be correctly localised + HWND winampWnd; + +public: + C_ENCODER_NSV(int ExtInfoSize = sizeof(T_ENCODER_NSV_INFO)); + virtual ~C_ENCODER_NSV(); + + virtual void Close(); + virtual void Reset(); + + virtual int Encode(const void *inputbuf, const unsigned int inputbufsize, void *outputbuf, const unsigned int outputbufsize, int *inputamtused); /* all values are in BYTES! */ + + // show configuration dialog + virtual void Configure(HWND parent,HINSTANCE hDllInstance); + + virtual bool UseNsvConfig() { return true; }; + + // populate the configuration file with current settings + virtual void FillConfFile(char * conf_file, char * section=NULL)=0; + + // read the configuration file and change current settings + virtual void ReadConfFile(char * conf_file, char * section=NULL)=0; +}; + +#endif /* !__C_ENCODER_NSV_H__ */ \ No newline at end of file diff --git a/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_ogg.cpp b/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_ogg.cpp new file mode 100644 index 00000000..e9420b7b --- /dev/null +++ b/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_ogg.cpp @@ -0,0 +1,117 @@ +#include "c_encoder_ogg.h" +#include "../../utils.h" + +HINSTANCE C_ENCODER_OGG::hEncoderInstance = NULL; + +C_ENCODER_OGG::C_ENCODER_OGG(HWND winamp) : C_ENCODER_NSV(sizeof(T_ENCODER_OGG_INFO)) { + SetName("OGG Vorbis Encoder"); + winampWnd = winamp; + ConfigAudio3 = NULL; + if(hEncoderInstance == NULL) { + wchar_t dir[MAX_PATH] = {0}; + snwprintf(dir, MAX_PATH, L"%s\\enc_vorbis.dll", GetPluginDirectoryW(winamp)); + hEncoderInstance = LoadLibraryW(dir); + } + + if(hEncoderInstance) { + void * CreateAudio3=(void *)GetProcAddress(hEncoderInstance, "CreateAudio3"); + void * GetAudioTypes3=(void *)GetProcAddress(hEncoderInstance, "GetAudioTypes3"); + void * ConfigAudio3=(void *)GetProcAddress(hEncoderInstance, "ConfigAudio3"); + void * SetWinampHWND=(void *)GetProcAddress(hEncoderInstance, "SetWinampHWND"); + SetEncoder(CreateAudio3,GetAudioTypes3,ConfigAudio3,SetWinampHWND); + } + + T_ENCODER_OGG_INFO * EncInfo = (T_ENCODER_OGG_INFO *)ExtendedInfoPtr; + EncInfo->output_bitRate = OGG_DEFAULT_OUTPUTBITRATE; + EncInfo->output_channelmode = OGG_DEFAULT_OUTPUTMODE; + EncInfo->output_samplerate = OGG_DEFAULT_OUTPUTSAMPLERATE; +} + +C_ENCODER_OGG::~C_ENCODER_OGG() { + C_ENCODER_NSV::~C_ENCODER_NSV(); +} + +static int cacheVal=0; +bool C_ENCODER_OGG::isPresent(HWND winamp) { + if(cacheVal!=0 && hEncoderInstance!=0) return cacheVal==2; + bool ret=false; + wchar_t dir[MAX_PATH] = {0}; + snwprintf(dir, MAX_PATH, L"%s\\enc_vorbis.dll", GetPluginDirectoryW(winamp)); + FILE * f = _wfopen(dir, L"rb"); + if (f) { + fseek(f,0,2); + if(ftell(f) > 0) ret=true; + fclose(f); + } + cacheVal=ret?2:1; + return ret; +} + +void C_ENCODER_OGG::FillAttribs() { + T_ENCODER_OGG_INFO &EncInfo = *(T_ENCODER_OGG_INFO *)ExtendedInfoPtr; + T_ENCODER_OGG_INFO *attribs = new T_ENCODER_OGG_INFO; + *attribs = EncInfo; + AddAttrib("",attribs); +} + +void C_ENCODER_OGG::FillConfFile(char * conf_file, char * section) { + if(!section) section="audio_ogg"; + + T_ENCODER_OGG_INFO &EncInfo = *(T_ENCODER_OGG_INFO *)ExtendedInfoPtr; + configtype * cfg = new configtype; + cfg->cfg_abr_use_max=0; + cfg->cfg_abr_use_min=0; + cfg->cfg_mode=0; //VBR + cfg->cfg_vbrquality=EncInfo.output_quality; + cfg->cfg_abr_nominal=EncInfo.output_bitRate; + cfg->cfg_abr_max=EncInfo.output_bitRate; + cfg->cfg_abr_min=EncInfo.output_bitRate; + + if (conf_file) WritePrivateProfileStruct(section,"conf",cfg,sizeof(configtype),conf_file); +} +int setBitrate(float ql) +{ + int br = 64; + //ql = ql*10; + // jkey: this is a pain in the ass,but the only + // way i can figure out how to get the bitrate + // outside of enc_vorbis. + // Also quality enforcement is needed to prevent the + // yp filling up with non standard bitrate streams. + // although this is vbr and will be variable bitrate anyway. + if(ql == 10 || (ql < 10 && ql > 9.5)){br=500;ql = 10.0f; return br;} + if(ql == 9.0f || (ql < 10.0f && ql > 9.0f)){br=320;ql = 9.0f;return br;} + if(ql == 8.0f || (ql < 9.0f && ql > 8.0f)){br=256;ql = 8.0f;return br;} + if(ql == 7.0f || (ql < 8.0f && ql > 7.0f)){br=224;ql = 7.0f;return br;} + if(ql == 6.0f || (ql < 7.0f && ql > 6.0f)){br=192;ql = 6.0f;return br;} + if(ql == 5.0f || (ql < 6.0f && ql > 5.0f)){br=160;ql = 5.0f;return br;} + if(ql == 4.0f || (ql < 5.0f && ql > 4.0f)){br=128;ql = 4.0f;return br;} + if(ql == 3.0f || (ql < 4.0f && ql > 3.0f)){br=112;ql = 3.0f;return br;} + if(ql == 2.0f || (ql < 3.0f && ql > 2.0f)){br=96;ql = 2.0f;return br;} + if(ql == 1.0f || (ql < 2.0f && ql > 1.0f)){br=80;ql = 1.0f;return br;} + if(ql == 0.0f || (ql < 1.0f && ql > 0.0f)){ br=64;ql = 0.0f;return br;} + if(ql == -0.5f || (ql < 0.0f && ql > -0.5f)){br=56;ql = -0.5f;return br;} + if(ql == -1.0f || ql < -0.5f){br=48;ql = -1.0f;return br;} + return br; +} +void C_ENCODER_OGG::ReadConfFile(char * conf_file, char * section) { + if(!section) section="audio_ogg"; + T_ENCODER_OGG_INFO &EncInfo = *(T_ENCODER_OGG_INFO *)ExtendedInfoPtr; + T_ENCODER_OGG_INFO *attribs = new T_ENCODER_OGG_INFO; + *attribs = EncInfo; + configtype * cfg = new configtype; + cfg->cfg_abr_use_max=0; + cfg->cfg_abr_use_min=0; + cfg->cfg_mode=0; //VBR + cfg->cfg_vbrquality=0.0f; + cfg->cfg_abr_nominal=64; + cfg->cfg_abr_max=352; + cfg->cfg_abr_min=32; + + if (conf_file) GetPrivateProfileStruct(section,"conf",cfg,sizeof(configtype),conf_file); + attribs->output_samplerate = OGG_DEFAULT_OUTPUTSAMPLERATE; + attribs->output_channelmode = cfg->cfg_mode; + attribs->output_quality = cfg->cfg_vbrquality; + attribs->output_bitRate = setBitrate(attribs->output_quality*10); + ChangeSettings(attribs); +} \ No newline at end of file diff --git a/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_ogg.h b/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_ogg.h new file mode 100644 index 00000000..be20d689 --- /dev/null +++ b/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/c_encoder_ogg.h @@ -0,0 +1,47 @@ +#ifndef __C_ENCODER_OGG_H__ +#define __C_ENCODER_OGG_H__ + +#include "c_encoder_nsv.h" +//#define round(x) ((x)>=0?(long)((x)+0.5):(long)((x)-0.5)) + +typedef struct +{ + bool cfg_abr_use_max,cfg_abr_use_min; + UINT cfg_mode; + + float cfg_vbrquality; + UINT cfg_abr_nominal; + UINT cfg_abr_max; + UINT cfg_abr_min; +} configtype; + +struct T_ENCODER_OGG_INFO : public T_ENCODER_NSV_INFO +{ + float output_quality; + unsigned int output_samplerate; + unsigned int output_channelmode; +}; + +#define OGG_DEFAULT_OUTPUTMODE 0 +#define OGG_DEFAULT_OUTPUTBITRATE 192 +#define OGG_DEFAULT_OUTPUTSAMPLERATE 44100 +#define OGG_DEFAULT_OUTPUTQUALITY 2.0f + +class C_ENCODER_OGG : public C_ENCODER_NSV { +private: + HWND winamp; +protected: + virtual void FillAttribs(); +public: + static HINSTANCE hEncoderInstance; + C_ENCODER_OGG(HWND hwnd = 0); + virtual ~C_ENCODER_OGG(); + static bool isPresent(HWND winamp); + virtual void ReadConfFile(char * conf_file, char * section=NULL); + virtual void FillConfFile(char * conf_file, char * section=NULL); + static void Unload() { if(hEncoderInstance) FreeLibrary(hEncoderInstance); hEncoderInstance=0; } + virtual char * GetContentType() { return "audio/ogg"; } + virtual HINSTANCE GetEncoderInstance() { return hEncoderInstance; } +}; + +#endif /* !__C_ENCODER_OGG_H__ */ \ No newline at end of file diff --git a/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/enc_if.h b/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/enc_if.h new file mode 100644 index 00000000..70eaff30 --- /dev/null +++ b/Src/Plugins/DSP/dsp_sc/sc2srclib/Encoders/enc_if.h @@ -0,0 +1,44 @@ +/* +** enc_if.h - common encoder interface +** +** Copyright (C) 2001-2003 Nullsoft, Inc. +** +** This software is provided 'as-is', without any express or implied warranty. +** In no event will the authors be held liable for any damages arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, including commercial +** applications, and to alter it and redistribute it freely, subject to the following restrictions: +** 1. The origin of this software must not be misrepresented; you must not claim that you wrote the +** original software. If you use this software in a product, an acknowledgment in the product +** documentation would be appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be misrepresented as +** being the original software. +** 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef _NSV_ENC_IF_H_ +#define _NSV_ENC_IF_H_ +class VideoCoder +{ + public: + VideoCoder() { } + virtual int Encode(void *in, void *out, int *iskf)=0; // returns bytes in out + virtual ~VideoCoder() { }; +}; + +class AudioCoder +{ + public: + AudioCoder() { } + virtual int Encode(int framepos, void *in, int in_avail, int *in_used, void *out, int out_avail)=0; //returns bytes in out + virtual ~AudioCoder() { }; +}; + +// unsigned int GetAudioTypes3(int idx, char *desc); +// unsigned int GetVideoTypes3(int idx, char *desc); +// AudioCoder *CreateAudio3(int nch, int srate, int bps, unsigned int srct, unsigned int *outt, char *configfile); +// VideoCoder *CreateVideo3(int w, int h, double frt, unsigned int pixt, unsigned int *outt, char *configfile); +// HWND ConfigAudio3(HWND hwndParent, HINSTANCE hinst, unsigned int outt, char *configfile); +// HWND ConfigVideo3(HWND hwndParent, HINSTANCE hinst, unsigned int outt, char *configfile); + +#endif //_NSV_ENC_IF_H_ \ No newline at end of file diff --git a/Src/Plugins/DSP/dsp_sc/sc2srclib/Include/c_jobmanager.h b/Src/Plugins/DSP/dsp_sc/sc2srclib/Include/c_jobmanager.h new file mode 100644 index 00000000..c7feacc9 --- /dev/null +++ b/Src/Plugins/DSP/dsp_sc/sc2srclib/Include/c_jobmanager.h @@ -0,0 +1,254 @@ +#ifndef __C_JOBMANAGER_H__ +#define __C_JOBMANAGER_H__ + +#include + +#ifdef _WIN32 +#include +#include // for mutex support +#define T_MUTEX HANDLE +#else // _WIN32 +#error "This won't compile under anything other than windows since I haven't implemented mutexing on anything else" +#endif // _WIN32 + +template class C_JOBMANAGER { +public: + typedef int (*T_JOBHANDLER)(int state, int last_state, T *userData); +private: + struct T_JOB { + int state; + int last_state; + int suspended; + T *userData; + }; + struct T_HANDLER { + int state; + int last_state; + T_JOBHANDLER jobHandler; + }; + std::vector JobList; + std::vector HandlerList; + T_MUTEX mutex; + +protected: + int waitForMutex() { +#ifdef _WIN32 + if(WaitForSingleObject(mutex,INFINITE) == WAIT_OBJECT_0) return 1; +#else // _WIN32 + // insert mutex magic here +#endif // _WIN32 + return 0; + } + + void releaseMutex() { +#ifdef _WIN32 + ReleaseMutex(mutex); +#else // _WIN32 + // insert mutex magic here +#endif // _WIN32 + } + +public: + C_JOBMANAGER() { +#ifdef _WIN32 + mutex = CreateMutex(NULL,TRUE,NULL); + ReleaseMutex(mutex); +#else // _WIN32 + // insert mutex magic here +#endif // _WIN32 + } + + virtual ~C_JOBMANAGER() { + waitForMutex(); +#ifdef _WIN32 + CloseHandle(mutex); + mutex = NULL; +#else // _WIN32 + // insert mutex magic here +#endif // _WIN32 + + //JobList.deleteAll(); + for (auto job : JobList) + { + delete job; + } + JobList.clear(); + + //HandlerList.deleteAll(); + for (auto handler : HandlerList) + { + delete handler; + } + HandlerList.clear(); + } + + T *operator[](int job) { + if(!waitForMutex()) return NULL; + T_JOB *j = JobList[job]; + T *val = NULL; + if(j) val = j->userData; + releaseMutex(); + return val; + } + + virtual int AddJob(int state, T *userData, int suspended = 0, int isUserUnique = 1) { + if(!waitForMutex()) return -1; + int n = JobList.size(); + if(isUserUnique && n) { + for(int i = n-1; i >= 0; i--) { + T_JOB *item = JobList[i]; + if(item) { + if(item->userData == userData) { + releaseMutex(); + return -1; + } + } + } + } + T_JOB *job = new T_JOB; + job->last_state = OUT_DISCONNECTED; + job->state = state; + job->suspended = suspended; + job->userData = userData; + JobList.push_back(job); + releaseMutex(); + return n; + } + + virtual int GetJobState(int job) { + int retval = -1; + if(waitForMutex()) { + int n = JobList.size(); + if(job < n && job >= 0) retval = JobList[job]->state; + releaseMutex(); + } + return retval; + } + + virtual void SetJobState(int job, int state) { + if(!waitForMutex()) return; + int n = JobList.size(); + if(job < n && job >= 0) JobList[job]->state = state; + releaseMutex(); + } + + virtual void SuspendJob(int job, int suspended) { + if(!waitForMutex()) return; + int n = JobList.size(); + if(job < n && job >= 0) JobList[job]->suspended = suspended; + releaseMutex(); + } + + virtual void DelJob(int job) { + if(!waitForMutex()) return; + int n = JobList.size(); + if(job < n && job >= 0) { + delete JobList[job]; + JobList.erase(JobList.begin() + job); + } + releaseMutex(); + } + + virtual void ClearJobs() { + if(!waitForMutex()) + return; + + //JobList.deleteAll(); + for (auto job : JobList) + { + delete job; + } + JobList.clear(); + + releaseMutex(); + } + + virtual void AddHandler(int state, T_JOBHANDLER jobHandler) { + if(!waitForMutex()) return; + int n = HandlerList.size(); + for(int i = n-1; i >= 0; i--) { + T_HANDLER *item = HandlerList[i]; + if(item) { + if(item->state == state) { + releaseMutex(); + return; + } + } + } + T_HANDLER *handler = new T_HANDLER; + handler->state = state; + handler->jobHandler = jobHandler; + HandlerList.push_back(handler); + releaseMutex(); + } + + virtual void DelHandler(int state) { + if(!waitForMutex()) return; + int n = HandlerList.size(); + for(int i = n-1; i >= 0; i--) { + T_HANDLER *item = HandlerList[i]; + if(item) { + if(item->state == state) { + delete HandlerList[i]; + HandlerList.erase(HandlerList.begin() + i); + releaseMutex(); + return; + } + } + } + releaseMutex(); + } + + virtual void ClearHandlers() { + if(!waitForMutex()) + return; + + //HandlerList.deleteAll(); + for (auto handler : HandlerList) + { + delete handler; + } + HandlerList.clear(); + + releaseMutex(); + } + + virtual int GetNumJobs() { + if(!waitForMutex()) return -1; + int n = JobList.size(); + releaseMutex(); + return n; + } + + virtual int GetNumHandlers() { + if(!waitForMutex()) return -1; + int n = HandlerList.size(); + releaseMutex(); + return n; + } + + virtual void Run(int job) { + if(!waitForMutex()) return; + int nJ = JobList.size(); + int nH = HandlerList.size(); + if(job < nJ && job >= 0) { + T_JOB *job_item = JobList[job]; + for(int i = nH-1; i >= 0; i--) { + T_HANDLER *handler = HandlerList[i]; + if(handler) { + if(handler->state == job_item->state) { + if(!job_item->suspended) { + int cur_state = job_item->state; + job_item->state = handler->jobHandler(job_item->state,job_item->last_state,job_item->userData); + job_item->last_state = cur_state; + } + break; + } + } + } + } + releaseMutex(); + } +}; + +#endif // !__C_JOBMANAGER_H__ \ No newline at end of file diff --git a/Src/Plugins/DSP/dsp_sc/sc2srclib/Include/c_serial_jobmanager.h b/Src/Plugins/DSP/dsp_sc/sc2srclib/Include/c_serial_jobmanager.h new file mode 100644 index 00000000..6023dd01 --- /dev/null +++ b/Src/Plugins/DSP/dsp_sc/sc2srclib/Include/c_serial_jobmanager.h @@ -0,0 +1,24 @@ +#ifndef __C_SERIAL_JOBMANAGER_H__ +#define __C_SERIAL_JOBMANAGER_H__ + +#include "c_jobmanager.h" + +template class C_SERIAL_JOBMANAGER : public C_JOBMANAGER { +private: + int currentJob; +public: + C_SERIAL_JOBMANAGER() { + currentJob = 0; + } + ~C_SERIAL_JOBMANAGER() { } + int GetCurrentJob() { return currentJob; } + virtual void Run(int passes = 1) { + int numPasses = passes; + while(numPasses-- > 0) { + C_JOBMANAGER::Run(currentJob++); + if(currentJob > GetNumJobs()) currentJob = 0; + } + } +}; + +#endif // !__C_SERIAL_JOBMANAGER_H__ \ No newline at end of file diff --git a/Src/Plugins/DSP/dsp_sc/sc2srclib/Include/c_shoutcast_2_output.h b/Src/Plugins/DSP/dsp_sc/sc2srclib/Include/c_shoutcast_2_output.h new file mode 100644 index 00000000..5b074de0 --- /dev/null +++ b/Src/Plugins/DSP/dsp_sc/sc2srclib/Include/c_shoutcast_2_output.h @@ -0,0 +1,194 @@ +#ifndef __C_SHOUTCAST_2_OUTPUT_H__ +#define __C_SHOUTCAST_2_OUTPUT_H__ + +#include +#include +#include +#include +#include "c_serial_jobmanager.h" +#include "../jnetlib/jnetlib.h" +#include "../Encoders/c_encoder.h" +#include "../Encoders/c_encoder_mp3dll.h" + +#include "../lame/include/lame.h" +#include "../lame/libmp3lame/lame_global_flags.h" +#include "../uvAuth21/uvAuth21.h" + +#ifdef USEAACP +#include "../Encoders/c_encoder_aacp.h" +#endif +struct T_OUTPUT_CONFIG { + char Name[32]; + char UserID[256]; + char Address[1024]; + u_short Port; + char StationID[8]; + char Password[256]; // 4 - 8 for 1.8.2 + char cipherkey[32];// sc2 cipherkey + int AutoRecon; + int ReconTime; + char Description[1024]; + char ServerURL[2048]; + int Genre1; + int Genre2; + char Genre3[1024]; + char ICQ[128]; + char AIM[512]; + char IRC[512]; + char content_type[11]; + int Public; + int doTitleUpdate; + int protocol; + int DoUpload; + char introfilepath[4096]; + char backupfile[4096]; +}; + +#define DEFAULT_ENCODER (C_ENCODER *)(-1) + +#define OM_ENCODE 1 +#define OM_OUTPUT 2 +#define OM_OTHER 4 +#define OM_ALL (OM_ENCODE | OM_OUTPUT | OM_OTHER) + +enum OUTPUTTYPE { + OUTTYPE_SOURCE, + OUTTYPE_TITLE, +}; + +struct T_OUTPUT_INFO { + unsigned int BytesSent; // how many bytes of content we've sent + clock_t ConnectionTime; // time a socket connection occurred + int Version; // server version + int Caps; // server capabilities + int Reconnect; // flag for the reconnection algorithm + int ReconnectTime; // value used in conjunction with the reconnection algorithm + int Succeeded; // had at least one successful connection (for reconnection alg.) -1 = password failure + int sc2Suceeded;//sc2 version + char ErrorMsg[1024]; + time_t ConnectedAt; + wchar_t Title[1024]; + wchar_t Next[1024]; + char URL[1024]; + int introuploaded; + int backupuploaded; +}; + +struct T_OUTPUT { + JNL_Connection Output; + enum OUTPUTTYPE Type; + int Bitrate; // this shouldn't be here, but it's the only way we can tell the shoutcast server the bitrate + char * ContentType; //neither should this + int SlowClose; // set to 1 to wait until all data is sent before closing the connection + T_OUTPUT_CONFIG *Config; + T_OUTPUT_INFO Info; + int m_sendmetadata; + int m_initdone; +}; + +enum OUTPUTSTATE { + OUT_ERROR, // not a true state, but is returned when GetState() is called with an invalid connection handle + OUT_IDLE, + OUT_CONNECT, + OUT_REQUEST_CIPHER, + OUT_RECV_CIPHER, + OUT_SENDAUTH, + OUT_RECVAUTHRESPONSE, + OUT_SEND_MIME, + OUT_RECV_MIME, + OUT_SEND_BITRATE, + OUT_RECV_BITRATE, + OUT_SEND_BUFSIZE, + OUT_RECV_BUFSIZE, + OUT_SEND_MAX, + OUT_RECV_MAX, + OUT_SENDYP, + OUT_RECVYP, + OUT_SEND_INITFLUSH, + OUT_RECV_INITFLUSH, + OUT_SEND_INITSTANDBY, + OUT_RECV_INITSTANDBY, + OUT_SEND_INTRO, + OUT_RECV_INTRO, + OUT_SEND_BACKUP, + OUT_RECV_BACKUP, + OUT_SENDCONTENT, + OUT_DISCONNECT, + OUT_RECONNECT, + OUT_TITLESENDUPDATE, +}; +#define OUT_DISCONNECTED OUT_IDLE + +class C_SHOUTCAST_2_OUTPUT { +private: + C_ENCODER *Encoder; + int IOwnEncoder; + C_SERIAL_JOBMANAGER OutputManager; + HANDLE mutex; + +protected: + static int Output_Idle(int state, T_OUTPUT *userData); + static int Output_Connect(int state, T_OUTPUT *userData); + static int Output_Request_Cipher(int state, T_OUTPUT *userData); + static int Output_Receive_Cipher(int state, T_OUTPUT *userData); + static int Output_SendAuth(int state, T_OUTPUT *userData); + static int Output_RecvAuthResponse(int state, T_OUTPUT *userData); + static int Output_Send_Mime(int state, T_OUTPUT *userData); + static int Output_Recv_Mime(int state, T_OUTPUT *userData); + static int Output_Send_Bitrate(int state, T_OUTPUT *userData); + static int Output_Recv_Bitrate(int state, T_OUTPUT *userData); + static int Output_Send_Buf_Size(int state, T_OUTPUT *userData); + static int Output_Recv_Buf_Size(int state, T_OUTPUT *userData); + static int Output_Send_Max_Size(int state, T_OUTPUT *userData); + static int Output_Recv_Max_Size(int state, T_OUTPUT *userData); + static int Output_DUMMY(int state, T_OUTPUT *userData); + static int Output_SendYP(int state, T_OUTPUT *userData); + static int Output_RecvYP(int state, T_OUTPUT *userData); + static int Output_Send_InitFlush(int state, T_OUTPUT *userData); + static int Output_Recv_InitFlush(int state, T_OUTPUT *userData); + static int Output_Send_InitStandby(int state, T_OUTPUT *userData); + static int Output_Recv_InitStandby(int state, T_OUTPUT *userData); + static int Output_Send_InitMeta(int state, T_OUTPUT *userData); + static int Output_Recv_InitMeta(int state, T_OUTPUT *userData); + static int Output_Send_Intro(int state, T_OUTPUT *userData); + static int Output_Recv_Intro(int state, T_OUTPUT *userData); + static int Output_Send_Backup(int state, T_OUTPUT *userData); + static int Output_Recv_Backup(int state, T_OUTPUT *userData); + + static int Output_SendContent(int state, T_OUTPUT *userData); + static int Output_Disconnect(int state, T_OUTPUT *userData); + static int Output_Reconnect(int state, T_OUTPUT *userData); + static int Output_Title_SendUpdate(int state, T_OUTPUT *userData); + static int Output_Title_SendUpdatev2(int state, T_OUTPUT *userData); + // uvox21 + static char * createUvoxFrameClasstype(std::string typeString); + static int createUvoxFrame(int length, char * payload_in,char * payload_out, char * classtype); + static int parseUvoxFrame(char * payload_in,char * payload_out); + static int checkUvoxFrameForError(char * pload_out,int state, T_OUTPUT *userData); + + void (*lame_init)(void); + void (*lame_init_params)(lame_global_flags *); + int (*lame_encode_buffer_interleaved)(lame_global_flags *,short int pcm[],int num_samples, char *mp3buffer,int mp3buffer_size); + int (*lame_encode_flush)(lame_global_flags *,char *mp3buffer, int size); + +public: + C_SHOUTCAST_2_OUTPUT(); + void SetLame(void *init, void *params, void *encode, void *finish); + ~C_SHOUTCAST_2_OUTPUT(); + int Run(int mode = 0, void *Input = NULL, int InputSize = 0); + int AddOutput(T_OUTPUT_CONFIG *Config); + void UpdateOutput(int Connection); + void RemoveOutput(int Connection); + void ConnectOutput(int Connection); + void DisconnectOutput(int Connection, int withReconnect = 0, int reconnectTime = -1); // withReconnect of -1 will use the output config's setting + void SetEncoder(C_ENCODER *encoder, int takeOwnership = 0); + void UpdateTitle(wchar_t*Title,wchar_t*Next, int Connection,int titleseq); + enum OUTPUTSTATE GetState(int Connection); + T_OUTPUT_CONFIG *operator[](int Connection); + T_OUTPUT_CONFIG *GetOutput(int Connection); + C_ENCODER *GetEncoder(); + T_OUTPUT_INFO *GetOutputInfo(int Connection); + int m_titleseq; +}; + +#endif // !__C_SHOUTCAST_2_OUTPUT_H__ \ No newline at end of file diff --git a/Src/Plugins/DSP/dsp_sc/sc2srclib/Include/shoutcast_output.h b/Src/Plugins/DSP/dsp_sc/sc2srclib/Include/shoutcast_output.h new file mode 100644 index 00000000..c6378473 --- /dev/null +++ b/Src/Plugins/DSP/dsp_sc/sc2srclib/Include/shoutcast_output.h @@ -0,0 +1,817 @@ +#ifndef __SHOUTCAST_OUTPUT_H__ +#define __SHOUTCAST_OUTPUT_H__ + +#include +#include +#include +#include +#include +#include +#include "c_serial_jobmanager.h" +#include "../Components/wac_network/wac_network_connection_api.h" +#include + +#include "../Encoders/c_encoder.h" +#include "../Encoders/c_encoder_mp3dll.h" + +#include "../lame/include/lame.h" +#include "../lame/libmp3lame/lame_global_flags.h" +#include "../uvAuth21/uvAuth21.h" + +#define UV_SYNC_BYTE 0x5A +#define UV_RESERVED 0x00 +#define UV_END 0x00 +#define UV_END_LEN 1 +#define UV_HEADER_LEN 6 +#define UV_META_LEN 6 +#define UV_FRAME_LEN 16384 +#define UV_MAX_DATA_LEN (UV_FRAME_LEN - UV_HEADER_LEN - UV_END_LEN) +#define UV_MAX_META_LEN (UV_FRAME_LEN - UV_HEADER_LEN - UV_META_LEN - UV_END_LEN) +#define UV_MAX_META_FRAGMENTS 32 +#define UV_MAX_TOTAL_META_LEN (UV_MAX_META_LEN * UV_MAX_META_FRAGMENTS) + +typedef struct { + char* name; + bool parent; + bool children; +} SCgenres; +static SCgenres genres[] = {{"Alternative", true, true}, + {"Adult Alternative", false}, + {"Britpop", false}, + {"Classic Alternative", false}, + {"College", false}, + {"Dancepunk", false}, + {"Dream Pop", false}, + {"Emo", false}, + {"Goth", false}, + {"Grunge", false}, + {"Hardcore", false}, + {"Indie Pop", false}, + {"Indie Rock", false}, + {"Industrial", false}, + {"LoFi", false}, + {"Modern Rock", false}, + {"New Wave", false}, + {"Noise Pop", false}, + {"Post Punk", false}, + {"Power Pop", false}, + {"Punk", false}, + {"Ska", false}, + {"Xtreme", false}, + + {"Blues", true, true}, + {"Acoustic Blues", false}, + {"Cajun and Zydeco", false}, + {"Chicago Blues", false}, + {"Contemporary Blues", false}, + {"Country Blues", false}, + {"Delta Blues", false}, + {"Electric Blues", false}, + + {"Classical", true, true}, + {"Baroque", false}, + {"Chamber", false}, + {"Choral", false}, + {"Classical Period", false}, + {"Early Classical", false}, + {"Impressionist", false}, + {"Modern", false}, + {"Opera", false}, + {"Piano", false}, + {"Romantic", false}, + {"Symphony", false}, + + {"Country", true, true}, + {"Alt Country", false}, + {"Americana", false}, + {"Bluegrass", false}, + {"Classic Country", false}, + {"Contemporary Bluegrass", false}, + {"Contemporary Country", false}, + {"Honky Tonk", false}, + {"Hot Country Hits", false}, + {"Western", false}, + + {"Decades", true, true}, + {"30s", false}, + {"40s", false}, + {"50s", false}, + {"60s", false}, + {"70s", false}, + {"80s", false}, + {"90s", false}, + {"00s", false}, + + {"Easy Listening", true, true}, + {"Exotica", false}, + {"Light Rock", false}, + {"Lounge", false}, + {"Orchestral Pop", false}, + {"Polka", false}, + {"Space Age Pop", false}, + + {"Electronic", true, true}, + {"Acid House", false}, + {"Ambient", false}, + {"Big Beat", false}, + {"Breakbeat", false}, + {"Dance", false}, + {"Demo", false}, + {"Disco", false}, + {"Downtempo", false}, + {"Drum and Bass", false}, + {"Dubstep", false}, + {"Electro", false}, + {"Garage", false}, + {"Hard House", false}, + {"House", false}, + {"IDM", false}, + {"Jungle", false}, + {"Progressive", false}, + {"Techno", false}, + {"Trance", false}, + {"Tribal", false}, + {"Trip Hop", false}, + + {"Folk", true, true}, + {"Alternative Folk", false}, + {"Contemporary Folk", false}, + {"Folk Rock", false}, + {"New Acoustic", false}, + {"Old Time", false}, + {"Traditional Folk", false}, + {"World Folk", false}, + + {"Inspirational", true, true}, + {"Christian", false}, + {"Christian Metal", false}, + {"Christian Rap", false}, + {"Christian Rock", false}, + {"Classic Christian", false}, + {"Contemporary Gospel", false}, + {"Gospel", false}, + {"Praise and Worship", false}, + {"Sermons and Services", false}, + {"Southern Gospel", false}, + {"Traditional Gospel", false}, + + {"International", true, true}, + {"African", false}, + {"Afrikaans", false}, + {"Arabic", false}, + {"Asian", false}, + {"Bollywood", false}, + {"Brazilian", false}, + {"Caribbean", false}, + {"Celtic", false}, + {"Creole", false}, + {"European", false}, + {"Filipino", false}, + {"French", false}, + {"German", false}, + {"Greek", false}, + {"Hawaiian and Pacific", false}, + {"Hebrew", false}, + {"Hindi", false}, + {"Indian", false}, + {"Islamic", false}, + {"Japanese", false}, + {"Klezmer", false}, + {"Korean", false}, + {"Mediterranean", false}, + {"Middle Eastern", false}, + {"North American", false}, + {"Russian", false}, + {"Soca", false}, + {"South American", false}, + {"Tamil", false}, + {"Turkish", false}, + {"Worldbeat", false}, + {"Zouk", false}, + + {"Jazz", true, true}, + {"Acid Jazz", false}, + {"Avant Garde", false}, + {"Big Band", false}, + {"Bop", false}, + {"Classic Jazz", false}, + {"Cool Jazz", false}, + {"Fusion", false}, + {"Hard Bop", false}, + {"Latin Jazz", false}, + {"Smooth Jazz", false}, + {"Swing", false}, + {"Vocal Jazz", false}, + {"World Fusion", false}, + + {"Latin", true, true}, + {"Bachata", false}, + {"Banda", false}, + {"Bossa Nova", false}, + {"Cumbia", false}, + {"Flamenco", false}, + {"Latin Dance", false}, + {"Latin Pop", false}, + {"Latin Rap and Hip Hop", false}, + {"Latin Rock", false}, + {"Mariachi", false}, + {"Merengue", false}, + {"Ranchera", false}, + {"Reggaeton", false}, + {"Regional Mexican", false}, + {"Salsa", false}, + {"Samba", false}, + {"Tango", false}, + {"Tejano", false}, + {"Tropicalia", false}, + + {"Metal", true, true}, + {"Black Metal", false}, + {"Classic Metal", false}, + {"Death Metal", false}, + {"Extreme Metal", false}, + {"Grindcore", false}, + {"Hair Metal", false}, + {"Heavy Metal", false}, + {"Metalcore", false}, + {"Power Metal", false}, + {"Progressive Metal", false}, + {"Rap Metal", false}, + {"Thrash Metal", false}, + + {"Misc", true, false}, + + {"New Age", true, true}, + {"Environmental", false}, + {"Ethnic Fusion", false}, + {"Healing", false}, + {"Meditation", false}, + {"Spiritual", false}, + + {"Pop", true, true}, + {"Adult Contemporary", false}, + {"Barbershop", false}, + {"Bubblegum Pop", false}, + {"Dance Pop", false}, + {"Idols", false}, + {"JPOP", false}, + {"KPOP", false}, + {"Oldies", false}, + {"Soft Rock", false}, + {"Teen Pop", false}, + {"Top 40", false}, + {"World Pop", false}, + + {"Public Radio", true, true}, + {"College", false}, + {"News", false}, + {"Sports", false}, + {"Talk", false}, + {"Weather", false}, + + {"R&B and Urban", true, false}, + {"Classic R&B", false}, + {"Contemporary R&B", false}, + {"Doo Wop", false}, + {"Funk", false}, + {"Motown", false}, + {"Neo Soul", false}, + {"Quiet Storm", false}, + {"Soul", false}, + {"Urban Contemporary", false}, + + {"Rap", true, true}, + {"Alternative Rap", false}, + {"Dirty South", false}, + {"East Coast Rap", false}, + {"Freestyle", false}, + {"Gangsta Rap", false}, + {"Hip Hop", false}, + {"Mixtapes", false}, + {"Old School", false}, + {"Turntablism", false}, + {"Underground Hip Hop", false}, + {"West Coast Rap", false}, + + {"Reggae", true, true}, + {"Contemporary Reggae", false}, + {"Dancehall", false}, + {"Dub", false}, + {"Pop Reggae", false}, + {"Ragga", false}, + {"Reggae Roots", false}, + {"Rock Steady", false}, + + {"Rock", true, true}, + {"Adult Album Alternative", false}, + {"British Invasion", false}, + {"Celtic Rock", false}, + {"Classic Rock", false}, + {"Garage Rock", false}, + {"Glam", false}, + {"Hard Rock", false}, + {"Jam Bands", false}, + {"JROCK", false}, + {"Piano Rock", false}, + {"Prog Rock", false}, + {"Psychedelic", false}, + {"Rock & Roll", false}, + {"Rockabilly", false}, + {"Singer and Songwriter", false}, + {"Surf", false}, + + {"Seasonal and Holiday", true, true}, + {"Anniversary", false}, + {"Birthday", false}, + {"Christmas", false}, + {"Halloween", false}, + {"Hanukkah", false}, + {"Honeymoon", false}, + {"Kwanzaa", false}, + {"Valentine", false}, + {"Wedding", false}, + {"Winter", false}, + + {"Soundtracks", true, true}, + {"Anime", false}, + {"Kids", false}, + {"Original Score", false}, + {"Showtunes", false}, + {"Video Game Music", false}, + + {"Talk", true, true}, + {"BlogTalk", false}, + {"Comedy", false}, + {"Community", false}, + {"Educational", false}, + {"Government", false}, + {"News", false}, + {"Old Time Radio", false}, + {"Other Talk", false}, + {"Political", false}, + {"Scanner", false}, + {"Spoken Word", false}, + {"Sports", false}, + {"Technology", false}, + + {"Themes", true, true}, + {"Adult", false}, + {"Best Of", false}, + {"Chill", false}, + {"Eclectic", false}, + {"Experimental", false}, + {"Female", false}, + {"Heartache", false}, + {"Instrumental", false}, + {"LGBT", false}, + {"Love and Romance", false}, + {"Party Mix", false}, + {"Patriotic", false}, + {"Rainy Day Mix", false}, + {"Reality", false}, + {"Sexy", false}, + {"Shuffle", false}, + {"Travel Mix", false}, + {"Tribute", false}, + {"Trippy", false}, + {"Work Mix", false} +}; + +// pulled from nmrCommon\intTypes.h +typedef unsigned char __uint8; +typedef unsigned short __uint16; +typedef unsigned int __uint32; +typedef unsigned long long __uint64; + +#pragma pack(push,1) + +// this structure should be 16384 bytes in total size +// and is defined in full size so that we know its ok +struct uv2xHdr +{ // uvox2 message + __uint8 sync; + __uint8 qos; + __uint16 msgType; + __uint16 msgLen; + __uint8 m_data[UV_MAX_DATA_LEN]; + __uint8 end; +}; + +struct uv2xMetadataHdr +{ /* uvox 2 metadata header */ + __uint8 sync; + __uint8 qos; + __uint16 msgType; + __uint16 msgLen; + + __uint16 id; /* ID (cookie) identifying a metadata package */ + __uint16 span; /* Span of messages in the metadata package being assembled */ + __uint16 index; /* Index of the message in the metadata package being assembled */ + + __uint8 m_data[UV_MAX_META_LEN]; + __uint8 end; +}; + +#pragma pack(pop) + +#define MSG_AUTH 0x1001 +#define MSG_BROADCAST_SETUP 0x1002 +#define MSG_NEGOTIATE_BUFFER_SIZE 0x1003 +#define MSG_STANDBY 0x1004 +#define MSG_TERMINATE 0x1005 +#define MSG_FLUSH_CACHED_METADATA 0x1006 +#define MSG_LISTENER_AUTHENTICATION 0x1007 +#define MSG_MAX_PAYLOAD_SIZE 0x1008 +#define MSG_CIPHER 0x1009 +#define MSG_MIME_TYPE 0x1040 +#define MSG_FILE_TRANSFER_BEGIN 0x1050 +#define MSG_FILE_TRANSFER_DATA 0x1051 + +#define MSG_BROADCAST_INTERRUPTION 0x2001 +#define MSG_BROADCAST_TERMINATE 0x2002 + +#define MSG_ICYNAME 0x1100 +#define MSG_ICYGENRE 0x1101 +#define MSG_ICYURL 0x1102 +#define MSG_ICYPUB 0x1103 + +#define MSG_METADATA_CONTENTINFO 0x3000 +#define MSG_METADATA_URL 0x3001 +#define MSG_METADATA_XML 0x3901 +#define MSG_METADATA_XML_NEW 0x3902 + +// only id the start of the album art type as it's variable +#define MSG_METADATA_ALBUMART 0x4000 +#define MSG_METADATA_STATION_ART 0x0000 +#define MSG_METADATA_PLAYING_ART 0x0100 +/* + 0x4 0x0xx Station logo + 0x4 0x1xx Album art + + 00 = image/jpeg + 01 = image/png + 02 = image/bmp + 03 = image/gif +*/ + +#define MSG_METADATA_TIMEREMAINING 0x5001 + +#define MP3_DATA 0x7000 +#define VLB_DATA 0x8000 +#define AAC_LC_DATA 0x8001 +#define AACP_DATA 0x8003 +#define OGG_DATA 0x8004 + +struct T_OUTPUT_CONFIG { + char Name[32]; + wchar_t DisplayName[32]; + char UserID[256]; + char Address[1024]; + u_short Port; + char StationID[12]; + char Password[256]; // 4 - 8 for 1.8.2 + char cipherkey[64]; // sc2 cipherkey + int AutoRecon; + int ReconTime; + char Description[1024]; + char ServerURL[2048]; + char Genre[256]; + char ICQ[128]; + char AIM[1024]; + char IRC[1024]; + int Public; + int doTitleUpdate; + int protocol; + int protocol_retry; + char Now[1024]; + char Next[1024]; +}; + +#define DEFAULT_ENCODER (C_ENCODER *)(-1) + +#define OM_ENCODE 1 +#define OM_OUTPUT 2 +#define OM_OTHER 4 +#define OM_ALL (OM_ENCODE | OM_OUTPUT | OM_OTHER) + +enum OUTPUTTYPE { + OUTTYPE_SOURCE, + OUTTYPE_TITLE, +}; + +typedef unsigned long ARGB32; + +struct T_OUTPUT_TITLE { + wchar_t *Title; + wchar_t *Song; + wchar_t *Album; + wchar_t *Artist; + wchar_t *Genre; + wchar_t *Comment; + wchar_t *Year; + std::vector NextList; + void *APIC[2]; + int APICLength[2]; + int APICType[2]; + + T_OUTPUT_TITLE() : Title(0), Song(0), Album(0), Artist(0), Genre(0), Comment(0), Year(0) + { + memset(APIC, 0, sizeof(void *) * 2); + memset(APICLength, 0, sizeof(int) * 2); + memset(APICType, 0, sizeof(int) * 2); + } + + + ~T_OUTPUT_TITLE() + { + if (Title) + { + free(Title); + Title = 0; + } + + if (Song) + { + free(Song); + Song = 0; + } + + if (Album) + { + free(Album); + Album = 0; + } + + if (Artist) + { + free(Artist); + Artist = 0; + } + + if (Genre) + { + free(Genre); + Genre = 0; + } + + if (Comment) + { + free(Comment); + Comment = 0; + } + + if (Year) + { + free(Year); + Year = 0; + } + } +}; + +struct T_OUTPUT_INFO { + unsigned int BytesSent; // how many bytes of content we've sent + clock_t ConnectionTime; // time a socket connection occurred + int Version; // server version + int Caps; // server capabilities + int Reconnect; // flag for the reconnection algorithm + int ReconnectTime; // value used in conjunction with the reconnection algorithm + int Succeeded; // had at least one successful connection (for reconnection alg.) -1 = password failure + int last_state; // using this as a means to allow for closing on errors but able to show a better message + int Switching; // if we're doing an automatic protocol version change (from v2 to v1) + char *ErrorMsg; + time_t ConnectedAt; + int meta_cached; + int art_cached[2]; + unsigned short art_index[2]; + unsigned short art_cached_span[2]; + int art_cached_length[2]; + + // metadata information about the stream, etc + wchar_t *Title; + std::vector NextList; + wchar_t *Song; + wchar_t *Album; + wchar_t *Artist; + wchar_t *Genre; + wchar_t *Comment; + wchar_t *Year; + void *APIC[2]; + int APICLength[2]; + int APICType[2]; + + T_OUTPUT_INFO() : BytesSent(0), ConnectionTime(0), Version(0), + Caps(0), Reconnect(0), ReconnectTime(0), + Succeeded(0), last_state(0), Switching(0), + ErrorMsg(0), ConnectedAt(0), meta_cached(0), + Title(0), Song(0), Album(0), Artist(0), + Genre(0), Comment(0), Year(0) + { + memset(art_cached, 0, sizeof(int) * 2); + memset(art_index, 0, sizeof(unsigned short) * 2); + memset(art_cached_span, 0, sizeof(unsigned short) * 2); + memset(art_cached_length, 0, sizeof(int) * 2); + + memset(APIC, 0, sizeof(void *) * 2); + memset(APICLength, 0, sizeof(int) * 2); + memset(APICType, 0, sizeof(int) * 2); + } + + ~T_OUTPUT_INFO() + { + if (Title) + { + free(Title); + Title = 0; + } + + if (Song) + { + free(Song); + Song = 0; + } + + if (Album) + { + free(Album); + Album = 0; + } + + if (Artist) + { + free(Artist); + Artist = 0; + } + + if (Genre) + { + free(Genre); + Genre = 0; + } + + if (Comment) + { + free(Comment); + Comment = 0; + } + + if (Year) + { + free(Year); + Year = 0; + } + + if (Succeeded == -2 && ErrorMsg) { + free(ErrorMsg); + ErrorMsg = 0; + } + } +}; + +struct T_OUTPUT { + int Connection; // using this for the title update callback so the correct instance is updated + void (*TitleCallback)(const int Connection, const int Mode); + api_connection *Output; + enum OUTPUTTYPE Type; + int Bitrate; // this shouldn't be here, but it's the only way we can tell the shoutcast server the bitrate + char *ContentType; // neither should this + int SlowClose; // set to 1 to wait until all data is sent before closing the connection + T_OUTPUT_CONFIG *Config; + T_OUTPUT_INFO Info; + + T_OUTPUT() : Connection(0), TitleCallback(0), Output(0), Type(OUTTYPE_SOURCE), Bitrate(0), ContentType(0), SlowClose(0), Config(0) {} +}; + +enum OUTPUTSTATE { + OUT_ERROR, // not a true state, but is returned when GetState() is called with an invalid connection handle + OUT_DISCONNECTED, + OUT_CONNECT, + OUT_REQUEST_CIPHER, + OUT_RECV_CIPHER, + OUT_SENDAUTH, + OUT_RECVAUTHRESPONSE, + OUT_SEND_MIME, + OUT_RECV_MIME, + OUT_SEND_BITRATE, + OUT_RECV_BITRATE, + OUT_SEND_BUFSIZE, + OUT_RECV_BUFSIZE, + OUT_SEND_MAX, + OUT_RECV_MAX, + OUT_SENDYP, + OUT_SEND_INITFLUSH, + OUT_RECV_INITFLUSH, + OUT_SEND_INITSTANDBY, + OUT_RECV_INITSTANDBY, + /*OUT_SEND_INTRO, + OUT_RECV_INTRO, + OUT_SEND_BACKUP, + OUT_RECV_BACKUP,*/ + OUT_SENDCONTENT, + OUT_DISCONNECT, + OUT_RECONNECT, + OUT_TITLESENDUPDATE, + OUT_FAIL_CIPHER, + OUT_SEND_METADATA, + OUT_SEND_ARTWORK, +}; +static void *mutex; + +class SHOUTCAST_OUTPUT { +private: + C_ENCODER *Encoder; + int IOwnEncoder; + C_SERIAL_JOBMANAGER OutputManager; + T_OUTPUT_TITLE metadata; + +protected: + static int Output_Disconnected(int state, int last_state, T_OUTPUT *userData); + static int Output_Connect(int state, int last_state, T_OUTPUT *userData); + static int Output_Request_Cipher(int state, int last_state, T_OUTPUT *userData); + static int Output_Receive_Cipher(int state, int last_state, T_OUTPUT *userData); + static int Output_SendAuth(int state, int last_state, T_OUTPUT *userData); + static int Output_RecvAuthResponse(int state, int last_state, T_OUTPUT *userData); + static int Output_Send_Mime(int state, int last_state, T_OUTPUT *userData); + static int Output_Recv_Mime(int state, int last_state, T_OUTPUT *userData); + static int Output_Send_Bitrate(int state, int last_state, T_OUTPUT *userData); + static int Output_Recv_Bitrate(int state, int last_state, T_OUTPUT *userData); + static int Output_Send_Buf_Size(int state, int last_state, T_OUTPUT *userData); + static int Output_Recv_Buf_Size(int state, int last_state, T_OUTPUT *userData); + static int Output_Send_Max_Size(int state, int last_state, T_OUTPUT *userData); + static int Output_Recv_Max_Size(int state, int last_state, T_OUTPUT *userData); + static int Output_DUMMY(int state, int last_state, T_OUTPUT *userData); + static int Output_SendYP(int state, int last_state, T_OUTPUT *userData); + static int Output_Send_InitFlush(int state, int last_state, T_OUTPUT *userData); + static int Output_Recv_InitFlush(int state, int last_state, T_OUTPUT *userData); + static int Output_Send_InitStandby(int state, int last_state, T_OUTPUT *userData); + static int Output_Recv_InitStandby(int state, int last_state, T_OUTPUT *userData); + /*static int Output_Send_Intro(int state, int last_state, T_OUTPUT *userData); + static int Output_Recv_Intro(int state, int last_state, T_OUTPUT *userData); + static int Output_Send_Backup(int state, int last_state, T_OUTPUT *userData); + static int Output_Recv_Backup(int state, int last_state, T_OUTPUT *userData);*/ + static int Output_SendContent(int state, int last_state, T_OUTPUT *userData); + static int Output_Disconnect(int state, int last_state, T_OUTPUT *userData); + static int Output_Reconnect(int state, int last_state, T_OUTPUT *userData); + + static int Output_Title_SendUpdate(int state, int last_state, T_OUTPUT *userData); + static int Output_Send_Metadata(int state, int last_state, T_OUTPUT *userData); + static int Output_Send_Artwork(int state, int last_state, T_OUTPUT *userData); + + // uvox21 + static void createUvoxFrame(int length, char *payload_in, int type, T_OUTPUT *userData); + static int createUvoxMetaFrame(int length, char *payload_in, int type, + T_OUTPUT *userData, unsigned short id, unsigned short span = 1); + static int parseUvoxFrame(char *payload_in, char *payload_out); + static int checkUvoxFrameForError(char *pload_out, int state, T_OUTPUT *userData); + + void (*lame_init)(void); + void (*lame_init_params)(lame_global_flags *); + int (*lame_encode_buffer_interleaved)(lame_global_flags *, short int pcm[], int num_samples, char *mp3buffer, int mp3buffer_size); + int (*lame_encode_flush)(lame_global_flags *, char *mp3buffer, int size); + + HINSTANCE libinst; + +public: + SHOUTCAST_OUTPUT(); + void SetLame(void *init, void *params, void *encode, void *finish); + ~SHOUTCAST_OUTPUT(); + int Run(int mode = 0, void *Input = NULL, int InputSize = 0, int SaveEncoder = -1); + int AddOutput(int Connection, T_OUTPUT_CONFIG *Config, void (*TitleCallback)(const int Connection, const int Mode)=0); + void UpdateOutput(int Connection); + void RemoveOutput(int Connection); + int ConnectOutput(int Connection); + int DisconnectOutput(int Connection, int withReconnect = 0, int reconnectTime = -1); // withReconnect of -1 will use the output config's setting + void SetEncoder(C_ENCODER *encoder, int takeOwnership = 0); + + // we will attempt to cache the title information to save on duplication and + // also to make it easier for the title to be re-sent on server disconnect + void UpdateTitleCache(wchar_t *Title, std::vector NextList, wchar_t *Song, + wchar_t *Album, wchar_t *Artist, wchar_t *Genre, wchar_t *Comment, + wchar_t* Year, int Connection, bool sendNext); + void UpdateTitle(wchar_t *Title, std::vector NextList, + int Connection, bool sendNext, bool UseCache = true); + + void UpdateArtwork(int Connection); + void UpdateAlbumArtCache(void* APIC, int APIClength, int APICType, int Connection); + int UpdateAlbumArt(int Connection); + + enum OUTPUTSTATE GetState(int Connection); + T_OUTPUT_CONFIG *operator[](int Connection); + T_OUTPUT_CONFIG *GetOutput(int Connection); + C_ENCODER *GetEncoder(); + T_OUTPUT_INFO *GetOutputInfo(int Connection); +}; +static unsigned short mid = 1; + +#ifdef _DEBUG +#define DEBUG_STATE OutputDebugString(__FUNCTION__); OutputDebugString("\r\n"); +#else +#define DEBUG_STATE +#endif + +#define STATE userData->Info.last_state = last_state +#define LOCK if(WaitForSingleObject(mutex,INFINITE) == WAIT_OBJECT_0) +#define UNLOCK ReleaseMutex(mutex); + +extern char sourceVersion[64]; +extern HWND hMainDLG; +#endif // !__SHOUTCAST_OUTPUT_H__ \ No newline at end of file diff --git a/Src/Plugins/DSP/dsp_sc/sc2srclib/lame/include/lame.h b/Src/Plugins/DSP/dsp_sc/sc2srclib/lame/include/lame.h new file mode 100644 index 00000000..3126cb95 --- /dev/null +++ b/Src/Plugins/DSP/dsp_sc/sc2srclib/lame/include/lame.h @@ -0,0 +1,1323 @@ +/* + * Interface to MP3 LAME encoding engine + * + * Copyright (c) 1999 Mark Taylor + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* $Id: lame.h,v 1.4 2012/03/17 00:36:51 dromagod Exp $ */ + +#ifndef LAME_LAME_H +#define LAME_LAME_H + +/* for size_t typedef */ +#include +/* for va_list typedef */ +#include +/* for FILE typedef, TODO: remove when removing lame_mp3_tags_fid */ +#include + +#if defined(__cplusplus) +extern "C" { +#endif + +typedef void (*lame_report_function)(const char *format, va_list ap); + +#if defined(WIN32) || defined(_WIN32) +#undef CDECL +#define CDECL __cdecl +#else +#define CDECL +#endif + +#define DEPRECATED_OR_OBSOLETE_CODE_REMOVED 1 + +typedef enum vbr_mode_e { + vbr_off=0, + vbr_mt, /* obsolete, same as vbr_mtrh */ + vbr_rh, + vbr_abr, + vbr_mtrh, + vbr_max_indicator, /* Don't use this! It's used for sanity checks. */ + vbr_default=vbr_mtrh /* change this to change the default VBR mode of LAME */ +} vbr_mode; + + +/* MPEG modes */ +typedef enum MPEG_mode_e { + STEREO = 0, + JOINT_STEREO, + DUAL_CHANNEL, /* LAME doesn't supports this! */ + MONO, + NOT_SET, + MAX_INDICATOR /* Don't use this! It's used for sanity checks. */ +} MPEG_mode; + +/* Padding types */ +typedef enum Padding_type_e { + PAD_NO = 0, + PAD_ALL, + PAD_ADJUST, + PAD_MAX_INDICATOR /* Don't use this! It's used for sanity checks. */ +} Padding_type; + + + +/*presets*/ +typedef enum preset_mode_e { + /*values from 8 to 320 should be reserved for abr bitrates*/ + /*for abr I'd suggest to directly use the targeted bitrate as a value*/ + ABR_8 = 8, + ABR_320 = 320, + + V9 = 410, /*Vx to match Lame and VBR_xx to match FhG*/ + VBR_10 = 410, + V8 = 420, + VBR_20 = 420, + V7 = 430, + VBR_30 = 430, + V6 = 440, + VBR_40 = 440, + V5 = 450, + VBR_50 = 450, + V4 = 460, + VBR_60 = 460, + V3 = 470, + VBR_70 = 470, + V2 = 480, + VBR_80 = 480, + V1 = 490, + VBR_90 = 490, + V0 = 500, + VBR_100 = 500, + + + + /*still there for compatibility*/ + R3MIX = 1000, + STANDARD = 1001, + EXTREME = 1002, + INSANE = 1003, + STANDARD_FAST = 1004, + EXTREME_FAST = 1005, + MEDIUM = 1006, + MEDIUM_FAST = 1007 +} preset_mode; + + +/*asm optimizations*/ +typedef enum asm_optimizations_e { + MMX = 1, + AMD_3DNOW = 2, + SSE = 3 +} asm_optimizations; + + +/* psychoacoustic model */ +typedef enum Psy_model_e { + PSY_GPSYCHO = 1, + PSY_NSPSYTUNE = 2 +} Psy_model; + + +/* buffer considerations */ +typedef enum buffer_constraint_e { + MDB_DEFAULT=0, + MDB_STRICT_ISO=1, + MDB_MAXIMUM=2 +} buffer_constraint; + + +struct lame_global_struct; +typedef struct lame_global_struct lame_global_flags; +typedef lame_global_flags *lame_t; + + + + +/*********************************************************************** + * + * The LAME API + * These functions should be called, in this order, for each + * MP3 file to be encoded. See the file "API" for more documentation + * + ***********************************************************************/ + + +/* + * REQUIRED: + * initialize the encoder. sets default for all encoder parameters, + * returns NULL if some malloc()'s failed + * otherwise returns pointer to structure needed for all future + * API calls. + */ +lame_global_flags * CDECL lame_init(void); +#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED +#else +/* obsolete version */ +int CDECL lame_init_old(lame_global_flags *); +#endif + +/* + * OPTIONAL: + * set as needed to override defaults + */ + +/******************************************************************** + * input stream description + ***********************************************************************/ +/* number of samples. default = 2^32-1 */ +int CDECL lame_set_num_samples(lame_global_flags *, unsigned long); +unsigned long CDECL lame_get_num_samples(const lame_global_flags *); + +/* input sample rate in Hz. default = 44100hz */ +int CDECL lame_set_in_samplerate(lame_global_flags *, int); +int CDECL lame_get_in_samplerate(const lame_global_flags *); + +/* number of channels in input stream. default=2 */ +int CDECL lame_set_num_channels(lame_global_flags *, int); +int CDECL lame_get_num_channels(const lame_global_flags *); + +/* + scale the input by this amount before encoding. default=1 + (not used by decoding routines) +*/ +int CDECL lame_set_scale(lame_global_flags *, float); +float CDECL lame_get_scale(const lame_global_flags *); + +/* + scale the channel 0 (left) input by this amount before encoding. default=1 + (not used by decoding routines) +*/ +int CDECL lame_set_scale_left(lame_global_flags *, float); +float CDECL lame_get_scale_left(const lame_global_flags *); + +/* + scale the channel 1 (right) input by this amount before encoding. default=1 + (not used by decoding routines) +*/ +int CDECL lame_set_scale_right(lame_global_flags *, float); +float CDECL lame_get_scale_right(const lame_global_flags *); + +/* + output sample rate in Hz. default = 0, which means LAME picks best value + based on the amount of compression. MPEG only allows: + MPEG1 32, 44.1, 48khz + MPEG2 16, 22.05, 24 + MPEG2.5 8, 11.025, 12 + (not used by decoding routines) +*/ +int CDECL lame_set_out_samplerate(lame_global_flags *, int); +int CDECL lame_get_out_samplerate(const lame_global_flags *); + + +/******************************************************************** + * general control parameters + ***********************************************************************/ +/* 1=cause LAME to collect data for an MP3 frame analyzer. default=0 */ +int CDECL lame_set_analysis(lame_global_flags *, int); +int CDECL lame_get_analysis(const lame_global_flags *); + +/* + 1 = write a Xing VBR header frame. + default = 1 + this variable must have been added by a Hungarian notation Windows programmer :-) +*/ +int CDECL lame_set_bWriteVbrTag(lame_global_flags *, int); +int CDECL lame_get_bWriteVbrTag(const lame_global_flags *); + +/* 1=decode only. use lame/mpglib to convert mp3/ogg to wav. default=0 */ +int CDECL lame_set_decode_only(lame_global_flags *, int); +int CDECL lame_get_decode_only(const lame_global_flags *); + +#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED +#else +/* 1=encode a Vorbis .ogg file. default=0 */ +/* DEPRECATED */ +int CDECL lame_set_ogg(lame_global_flags *, int); +int CDECL lame_get_ogg(const lame_global_flags *); +#endif + +/* + internal algorithm selection. True quality is determined by the bitrate + but this variable will effect quality by selecting expensive or cheap algorithms. + quality=0..9. 0=best (very slow). 9=worst. + recommended: 2 near-best quality, not too slow + 5 good quality, fast + 7 ok quality, really fast +*/ +int CDECL lame_set_quality(lame_global_flags *, int); +int CDECL lame_get_quality(const lame_global_flags *); + +/* + mode = 0,1,2,3 = stereo, jstereo, dual channel (not supported), mono + default: lame picks based on compression ration and input channels +*/ +int CDECL lame_set_mode(lame_global_flags *, MPEG_mode); +MPEG_mode CDECL lame_get_mode(const lame_global_flags *); + +#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED +#else +/* + mode_automs. Use a M/S mode with a switching threshold based on + compression ratio + DEPRECATED +*/ +int CDECL lame_set_mode_automs(lame_global_flags *, int); +int CDECL lame_get_mode_automs(const lame_global_flags *); +#endif + +/* + force_ms. Force M/S for all frames. For testing only. + default = 0 (disabled) +*/ +int CDECL lame_set_force_ms(lame_global_flags *, int); +int CDECL lame_get_force_ms(const lame_global_flags *); + +/* use free_format? default = 0 (disabled) */ +int CDECL lame_set_free_format(lame_global_flags *, int); +int CDECL lame_get_free_format(const lame_global_flags *); + +/* perform ReplayGain analysis? default = 0 (disabled) */ +int CDECL lame_set_findReplayGain(lame_global_flags *, int); +int CDECL lame_get_findReplayGain(const lame_global_flags *); + +/* decode on the fly. Search for the peak sample. If the ReplayGain + * analysis is enabled then perform the analysis on the decoded data + * stream. default = 0 (disabled) + * NOTE: if this option is set the build-in decoder should not be used */ +int CDECL lame_set_decode_on_the_fly(lame_global_flags *, int); +int CDECL lame_get_decode_on_the_fly(const lame_global_flags *); + +#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED +#else +/* DEPRECATED: now does the same as lame_set_findReplayGain() + default = 0 (disabled) */ +int CDECL lame_set_ReplayGain_input(lame_global_flags *, int); +int CDECL lame_get_ReplayGain_input(const lame_global_flags *); + +/* DEPRECATED: now does the same as + lame_set_decode_on_the_fly() && lame_set_findReplayGain() + default = 0 (disabled) */ +int CDECL lame_set_ReplayGain_decode(lame_global_flags *, int); +int CDECL lame_get_ReplayGain_decode(const lame_global_flags *); + +/* DEPRECATED: now does the same as lame_set_decode_on_the_fly() + default = 0 (disabled) */ +int CDECL lame_set_findPeakSample(lame_global_flags *, int); +int CDECL lame_get_findPeakSample(const lame_global_flags *); +#endif + +/* counters for gapless encoding */ +int CDECL lame_set_nogap_total(lame_global_flags*, int); +int CDECL lame_get_nogap_total(const lame_global_flags*); + +int CDECL lame_set_nogap_currentindex(lame_global_flags* , int); +int CDECL lame_get_nogap_currentindex(const lame_global_flags*); + + +/* + * OPTIONAL: + * Set printf like error/debug/message reporting functions. + * The second argument has to be a pointer to a function which looks like + * void my_debugf(const char *format, va_list ap) + * { + * (void) vfprintf(stdout, format, ap); + * } + * If you use NULL as the value of the pointer in the set function, the + * lame buildin function will be used (prints to stderr). + * To quiet any output you have to replace the body of the example function + * with just "return;" and use it in the set function. + */ +int CDECL lame_set_errorf(lame_global_flags *, lame_report_function); +int CDECL lame_set_debugf(lame_global_flags *, lame_report_function); +int CDECL lame_set_msgf (lame_global_flags *, lame_report_function); + + + +/* set one of brate compression ratio. default is compression ratio of 11. */ +int CDECL lame_set_brate(lame_global_flags *, int); +int CDECL lame_get_brate(const lame_global_flags *); +int CDECL lame_set_compression_ratio(lame_global_flags *, float); +float CDECL lame_get_compression_ratio(const lame_global_flags *); + + +int CDECL lame_set_preset( lame_global_flags* gfp, int ); +int CDECL lame_set_asm_optimizations( lame_global_flags* gfp, int, int ); + + + +/******************************************************************** + * frame params + ***********************************************************************/ +/* mark as copyright. default=0 */ +int CDECL lame_set_copyright(lame_global_flags *, int); +int CDECL lame_get_copyright(const lame_global_flags *); + +/* mark as original. default=1 */ +int CDECL lame_set_original(lame_global_flags *, int); +int CDECL lame_get_original(const lame_global_flags *); + +/* error_protection. Use 2 bytes from each frame for CRC checksum. default=0 */ +int CDECL lame_set_error_protection(lame_global_flags *, int); +int CDECL lame_get_error_protection(const lame_global_flags *); + +#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED +#else +/* padding_type. 0=pad no frames 1=pad all frames 2=adjust padding(default) */ +int CDECL lame_set_padding_type(lame_global_flags *, Padding_type); +Padding_type CDECL lame_get_padding_type(const lame_global_flags *); +#endif + +/* MP3 'private extension' bit Meaningless. default=0 */ +int CDECL lame_set_extension(lame_global_flags *, int); +int CDECL lame_get_extension(const lame_global_flags *); + +/* enforce strict ISO compliance. default=0 */ +int CDECL lame_set_strict_ISO(lame_global_flags *, int); +int CDECL lame_get_strict_ISO(const lame_global_flags *); + + +/******************************************************************** + * quantization/noise shaping + ***********************************************************************/ + +/* disable the bit reservoir. For testing only. default=0 */ +int CDECL lame_set_disable_reservoir(lame_global_flags *, int); +int CDECL lame_get_disable_reservoir(const lame_global_flags *); + +/* select a different "best quantization" function. default=0 */ +int CDECL lame_set_quant_comp(lame_global_flags *, int); +int CDECL lame_get_quant_comp(const lame_global_flags *); +int CDECL lame_set_quant_comp_short(lame_global_flags *, int); +int CDECL lame_get_quant_comp_short(const lame_global_flags *); + +int CDECL lame_set_experimentalX(lame_global_flags *, int); /* compatibility*/ +int CDECL lame_get_experimentalX(const lame_global_flags *); + +/* another experimental option. for testing only */ +int CDECL lame_set_experimentalY(lame_global_flags *, int); +int CDECL lame_get_experimentalY(const lame_global_flags *); + +/* another experimental option. for testing only */ +int CDECL lame_set_experimentalZ(lame_global_flags *, int); +int CDECL lame_get_experimentalZ(const lame_global_flags *); + +/* Naoki's psycho acoustic model. default=0 */ +int CDECL lame_set_exp_nspsytune(lame_global_flags *, int); +int CDECL lame_get_exp_nspsytune(const lame_global_flags *); + +void CDECL lame_set_msfix(lame_global_flags *, double); +float CDECL lame_get_msfix(const lame_global_flags *); + + +/******************************************************************** + * VBR control + ***********************************************************************/ +/* Types of VBR. default = vbr_off = CBR */ +int CDECL lame_set_VBR(lame_global_flags *, vbr_mode); +vbr_mode CDECL lame_get_VBR(const lame_global_flags *); + +/* VBR quality level. 0=highest 9=lowest */ +int CDECL lame_set_VBR_q(lame_global_flags *, int); +int CDECL lame_get_VBR_q(const lame_global_flags *); + +/* VBR quality level. 0=highest 9=lowest, Range [0,...,10[ */ +int CDECL lame_set_VBR_quality(lame_global_flags *, float); +float CDECL lame_get_VBR_quality(const lame_global_flags *); + +/* Ignored except for VBR=vbr_abr (ABR mode) */ +int CDECL lame_set_VBR_mean_bitrate_kbps(lame_global_flags *, int); +int CDECL lame_get_VBR_mean_bitrate_kbps(const lame_global_flags *); + +int CDECL lame_set_VBR_min_bitrate_kbps(lame_global_flags *, int); +int CDECL lame_get_VBR_min_bitrate_kbps(const lame_global_flags *); + +int CDECL lame_set_VBR_max_bitrate_kbps(lame_global_flags *, int); +int CDECL lame_get_VBR_max_bitrate_kbps(const lame_global_flags *); + +/* + 1=strictly enforce VBR_min_bitrate. Normally it will be violated for + analog silence +*/ +int CDECL lame_set_VBR_hard_min(lame_global_flags *, int); +int CDECL lame_get_VBR_hard_min(const lame_global_flags *); + +/* for preset */ +#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED +#else +int CDECL lame_set_preset_expopts(lame_global_flags *, int); +#endif + +/******************************************************************** + * Filtering control + ***********************************************************************/ +/* freq in Hz to apply lowpass. Default = 0 = lame chooses. -1 = disabled */ +int CDECL lame_set_lowpassfreq(lame_global_flags *, int); +int CDECL lame_get_lowpassfreq(const lame_global_flags *); +/* width of transition band, in Hz. Default = one polyphase filter band */ +int CDECL lame_set_lowpasswidth(lame_global_flags *, int); +int CDECL lame_get_lowpasswidth(const lame_global_flags *); + +/* freq in Hz to apply highpass. Default = 0 = lame chooses. -1 = disabled */ +int CDECL lame_set_highpassfreq(lame_global_flags *, int); +int CDECL lame_get_highpassfreq(const lame_global_flags *); +/* width of transition band, in Hz. Default = one polyphase filter band */ +int CDECL lame_set_highpasswidth(lame_global_flags *, int); +int CDECL lame_get_highpasswidth(const lame_global_flags *); + + +/******************************************************************** + * psycho acoustics and other arguments which you should not change + * unless you know what you are doing + ***********************************************************************/ + +/* only use ATH for masking */ +int CDECL lame_set_ATHonly(lame_global_flags *, int); +int CDECL lame_get_ATHonly(const lame_global_flags *); + +/* only use ATH for short blocks */ +int CDECL lame_set_ATHshort(lame_global_flags *, int); +int CDECL lame_get_ATHshort(const lame_global_flags *); + +/* disable ATH */ +int CDECL lame_set_noATH(lame_global_flags *, int); +int CDECL lame_get_noATH(const lame_global_flags *); + +/* select ATH formula */ +int CDECL lame_set_ATHtype(lame_global_flags *, int); +int CDECL lame_get_ATHtype(const lame_global_flags *); + +/* lower ATH by this many db */ +int CDECL lame_set_ATHlower(lame_global_flags *, float); +float CDECL lame_get_ATHlower(const lame_global_flags *); + +/* select ATH adaptive adjustment type */ +int CDECL lame_set_athaa_type( lame_global_flags *, int); +int CDECL lame_get_athaa_type( const lame_global_flags *); + +#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED +#else +/* select the loudness approximation used by the ATH adaptive auto-leveling */ +int CDECL lame_set_athaa_loudapprox( lame_global_flags *, int); +int CDECL lame_get_athaa_loudapprox( const lame_global_flags *); +#endif + +/* adjust (in dB) the point below which adaptive ATH level adjustment occurs */ +int CDECL lame_set_athaa_sensitivity( lame_global_flags *, float); +float CDECL lame_get_athaa_sensitivity( const lame_global_flags* ); + +#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED +#else +/* OBSOLETE: predictability limit (ISO tonality formula) */ +int CDECL lame_set_cwlimit(lame_global_flags *, int); +int CDECL lame_get_cwlimit(const lame_global_flags *); +#endif + +/* + allow blocktypes to differ between channels? + default: 0 for jstereo, 1 for stereo +*/ +int CDECL lame_set_allow_diff_short(lame_global_flags *, int); +int CDECL lame_get_allow_diff_short(const lame_global_flags *); + +/* use temporal masking effect (default = 1) */ +int CDECL lame_set_useTemporal(lame_global_flags *, int); +int CDECL lame_get_useTemporal(const lame_global_flags *); + +/* use temporal masking effect (default = 1) */ +int CDECL lame_set_interChRatio(lame_global_flags *, float); +float CDECL lame_get_interChRatio(const lame_global_flags *); + +/* disable short blocks */ +int CDECL lame_set_no_short_blocks(lame_global_flags *, int); +int CDECL lame_get_no_short_blocks(const lame_global_flags *); + +/* force short blocks */ +int CDECL lame_set_force_short_blocks(lame_global_flags *, int); +int CDECL lame_get_force_short_blocks(const lame_global_flags *); + +/* Input PCM is emphased PCM (for instance from one of the rarely + emphased CDs), it is STRONGLY not recommended to use this, because + psycho does not take it into account, and last but not least many decoders + ignore these bits */ +int CDECL lame_set_emphasis(lame_global_flags *, int); +int CDECL lame_get_emphasis(const lame_global_flags *); + + + +/************************************************************************/ +/* internal variables, cannot be set... */ +/* provided because they may be of use to calling application */ +/************************************************************************/ +/* version 0=MPEG-2 1=MPEG-1 (2=MPEG-2.5) */ +int CDECL lame_get_version(const lame_global_flags *); + +/* encoder delay */ +int CDECL lame_get_encoder_delay(const lame_global_flags *); + +/* + padding appended to the input to make sure decoder can fully decode + all input. Note that this value can only be calculated during the + call to lame_encoder_flush(). Before lame_encoder_flush() has + been called, the value of encoder_padding = 0. +*/ +int CDECL lame_get_encoder_padding(const lame_global_flags *); + +/* size of MPEG frame */ +int CDECL lame_get_framesize(const lame_global_flags *); + +/* number of PCM samples buffered, but not yet encoded to mp3 data. */ +int CDECL lame_get_mf_samples_to_encode( const lame_global_flags* gfp ); + +/* + size (bytes) of mp3 data buffered, but not yet encoded. + this is the number of bytes which would be output by a call to + lame_encode_flush_nogap. NOTE: lame_encode_flush() will return + more bytes than this because it will encode the reamining buffered + PCM samples before flushing the mp3 buffers. +*/ +int CDECL lame_get_size_mp3buffer( const lame_global_flags* gfp ); + +/* number of frames encoded so far */ +int CDECL lame_get_frameNum(const lame_global_flags *); + +/* + lame's estimate of the total number of frames to be encoded + only valid if calling program set num_samples +*/ +int CDECL lame_get_totalframes(const lame_global_flags *); + +/* RadioGain value. Multiplied by 10 and rounded to the nearest. */ +int CDECL lame_get_RadioGain(const lame_global_flags *); + +/* AudiophileGain value. Multipled by 10 and rounded to the nearest. */ +int CDECL lame_get_AudiophileGain(const lame_global_flags *); + +/* the peak sample */ +float CDECL lame_get_PeakSample(const lame_global_flags *); + +/* Gain change required for preventing clipping. The value is correct only if + peak sample searching was enabled. If negative then the waveform + already does not clip. The value is multiplied by 10 and rounded up. */ +int CDECL lame_get_noclipGainChange(const lame_global_flags *); + +/* user-specified scale factor required for preventing clipping. Value is + correct only if peak sample searching was enabled and no user-specified + scaling was performed. If negative then either the waveform already does + not clip or the value cannot be determined */ +float CDECL lame_get_noclipScale(const lame_global_flags *); + + + + + + + +/* + * REQUIRED: + * sets more internal configuration based on data provided above. + * returns -1 if something failed. + */ +int CDECL lame_init_params(lame_global_flags *); + + +/* + * OPTIONAL: + * get the version number, in a string. of the form: + * "3.63 (beta)" or just "3.63". + */ +const char* CDECL get_lame_version ( void ); +const char* CDECL get_lame_short_version ( void ); +const char* CDECL get_lame_very_short_version ( void ); +const char* CDECL get_psy_version ( void ); +const char* CDECL get_lame_url ( void ); +const char* CDECL get_lame_os_bitness ( void ); + +/* + * OPTIONAL: + * get the version numbers in numerical form. + */ +typedef struct { + /* generic LAME version */ + int major; + int minor; + int alpha; /* 0 if not an alpha version */ + int beta; /* 0 if not a beta version */ + + /* version of the psy model */ + int psy_major; + int psy_minor; + int psy_alpha; /* 0 if not an alpha version */ + int psy_beta; /* 0 if not a beta version */ + + /* compile time features */ + const char *features; /* Don't make assumptions about the contents! */ +} lame_version_t; +void CDECL get_lame_version_numerical(lame_version_t *); + + +/* + * OPTIONAL: + * print internal lame configuration to message handler + */ +void CDECL lame_print_config(const lame_global_flags* gfp); + +void CDECL lame_print_internals( const lame_global_flags *gfp); + + +/* + * input pcm data, output (maybe) mp3 frames. + * This routine handles all buffering, resampling and filtering for you. + * + * return code number of bytes output in mp3buf. Can be 0 + * -1: mp3buf was too small + * -2: malloc() problem + * -3: lame_init_params() not called + * -4: psycho acoustic problems + * + * The required mp3buf_size can be computed from num_samples, + * samplerate and encoding rate, but here is a worst case estimate: + * + * mp3buf_size in bytes = 1.25*num_samples + 7200 + * + * I think a tighter bound could be: (mt, March 2000) + * MPEG1: + * num_samples*(bitrate/8)/samplerate + 4*1152*(bitrate/8)/samplerate + 512 + * MPEG2: + * num_samples*(bitrate/8)/samplerate + 4*576*(bitrate/8)/samplerate + 256 + * + * but test first if you use that! + * + * set mp3buf_size = 0 and LAME will not check if mp3buf_size is + * large enough. + * + * NOTE: + * if gfp->num_channels=2, but gfp->mode = 3 (mono), the L & R channels + * will be averaged into the L channel before encoding only the L channel + * This will overwrite the data in buffer_l[] and buffer_r[]. + * +*/ +int CDECL lame_encode_buffer ( + lame_global_flags* gfp, /* global context handle */ + const short int buffer_l [], /* PCM data for left channel */ + const short int buffer_r [], /* PCM data for right channel */ + const int nsamples, /* number of samples per channel */ + unsigned char* mp3buf, /* pointer to encoded MP3 stream */ + const int mp3buf_size ); /* number of valid octets in this + stream */ + +/* + * as above, but input has L & R channel data interleaved. + * NOTE: + * num_samples = number of samples in the L (or R) + * channel, not the total number of samples in pcm[] + */ +int CDECL lame_encode_buffer_interleaved( + lame_global_flags* gfp, /* global context handlei */ + short int pcm[], /* PCM data for left and right + channel, interleaved */ + int num_samples, /* number of samples per channel, + _not_ number of samples in + pcm[] */ + unsigned char* mp3buf, /* pointer to encoded MP3 stream */ + int mp3buf_size ); /* number of valid octets in this + stream */ + + +/* as lame_encode_buffer, but for 'float's. + * !! NOTE: !! data must still be scaled to be in the same range as + * short int, +/- 32768 + */ +int CDECL lame_encode_buffer_float( + lame_global_flags* gfp, /* global context handle */ + const float pcm_l [], /* PCM data for left channel */ + const float pcm_r [], /* PCM data for right channel */ + const int nsamples, /* number of samples per channel */ + unsigned char* mp3buf, /* pointer to encoded MP3 stream */ + const int mp3buf_size ); /* number of valid octets in this + stream */ + +/* as lame_encode_buffer, but for 'float's. + * !! NOTE: !! data must be scaled to +/- 1 full scale + */ +int CDECL lame_encode_buffer_ieee_float( + lame_t gfp, + const float pcm_l [], /* PCM data for left channel */ + const float pcm_r [], /* PCM data for right channel */ + const int nsamples, + unsigned char * mp3buf, + const int mp3buf_size); +int CDECL lame_encode_buffer_interleaved_ieee_float( + lame_t gfp, + const float pcm[], /* PCM data for left and right + channel, interleaved */ + const int nsamples, + unsigned char * mp3buf, + const int mp3buf_size); + +/* as lame_encode_buffer, but for 'double's. + * !! NOTE: !! data must be scaled to +/- 1 full scale + */ +int CDECL lame_encode_buffer_ieee_double( + lame_t gfp, + const double pcm_l [], /* PCM data for left channel */ + const double pcm_r [], /* PCM data for right channel */ + const int nsamples, + unsigned char * mp3buf, + const int mp3buf_size); +int CDECL lame_encode_buffer_interleaved_ieee_double( + lame_t gfp, + const double pcm[], /* PCM data for left and right + channel, interleaved */ + const int nsamples, + unsigned char * mp3buf, + const int mp3buf_size); + +/* as lame_encode_buffer, but for long's + * !! NOTE: !! data must still be scaled to be in the same range as + * short int, +/- 32768 + * + * This scaling was a mistake (doesn't allow one to exploit full + * precision of type 'long'. Use lame_encode_buffer_long2() instead. + * + */ +int CDECL lame_encode_buffer_long( + lame_global_flags* gfp, /* global context handle */ + const long buffer_l [], /* PCM data for left channel */ + const long buffer_r [], /* PCM data for right channel */ + const int nsamples, /* number of samples per channel */ + unsigned char* mp3buf, /* pointer to encoded MP3 stream */ + const int mp3buf_size ); /* number of valid octets in this + stream */ + +/* Same as lame_encode_buffer_long(), but with correct scaling. + * !! NOTE: !! data must still be scaled to be in the same range as + * type 'long'. Data should be in the range: +/- 2^(8*size(long)-1) + * + */ +int CDECL lame_encode_buffer_long2( + lame_global_flags* gfp, /* global context handle */ + const long buffer_l [], /* PCM data for left channel */ + const long buffer_r [], /* PCM data for right channel */ + const int nsamples, /* number of samples per channel */ + unsigned char* mp3buf, /* pointer to encoded MP3 stream */ + const int mp3buf_size ); /* number of valid octets in this + stream */ + +/* as lame_encode_buffer, but for int's + * !! NOTE: !! input should be scaled to the maximum range of 'int' + * If int is 4 bytes, then the values should range from + * +/- 2147483648. + * + * This routine does not (and cannot, without loosing precision) use + * the same scaling as the rest of the lame_encode_buffer() routines. + * + */ +int CDECL lame_encode_buffer_int( + lame_global_flags* gfp, /* global context handle */ + const int buffer_l [], /* PCM data for left channel */ + const int buffer_r [], /* PCM data for right channel */ + const int nsamples, /* number of samples per channel */ + unsigned char* mp3buf, /* pointer to encoded MP3 stream */ + const int mp3buf_size ); /* number of valid octets in this + stream */ + + + + + +/* + * REQUIRED: + * lame_encode_flush will flush the intenal PCM buffers, padding with + * 0's to make sure the final frame is complete, and then flush + * the internal MP3 buffers, and thus may return a + * final few mp3 frames. 'mp3buf' should be at least 7200 bytes long + * to hold all possible emitted data. + * + * will also write id3v1 tags (if any) into the bitstream + * + * return code = number of bytes output to mp3buf. Can be 0 + */ +int CDECL lame_encode_flush( + lame_global_flags * gfp, /* global context handle */ + unsigned char* mp3buf, /* pointer to encoded MP3 stream */ + int size); /* number of valid octets in this stream */ + +/* + * OPTIONAL: + * lame_encode_flush_nogap will flush the internal mp3 buffers and pad + * the last frame with ancillary data so it is a complete mp3 frame. + * + * 'mp3buf' should be at least 7200 bytes long + * to hold all possible emitted data. + * + * After a call to this routine, the outputed mp3 data is complete, but + * you may continue to encode new PCM samples and write future mp3 data + * to a different file. The two mp3 files will play back with no gaps + * if they are concatenated together. + * + * This routine will NOT write id3v1 tags into the bitstream. + * + * return code = number of bytes output to mp3buf. Can be 0 + */ +int CDECL lame_encode_flush_nogap( + lame_global_flags * gfp, /* global context handle */ + unsigned char* mp3buf, /* pointer to encoded MP3 stream */ + int size); /* number of valid octets in this stream */ + +/* + * OPTIONAL: + * Normally, this is called by lame_init_params(). It writes id3v2 and + * Xing headers into the front of the bitstream, and sets frame counters + * and bitrate histogram data to 0. You can also call this after + * lame_encode_flush_nogap(). + */ +int CDECL lame_init_bitstream( + lame_global_flags * gfp); /* global context handle */ + + + +/* + * OPTIONAL: some simple statistics + * a bitrate histogram to visualize the distribution of used frame sizes + * a stereo mode histogram to visualize the distribution of used stereo + * modes, useful in joint-stereo mode only + * 0: LR left-right encoded + * 1: LR-I left-right and intensity encoded (currently not supported) + * 2: MS mid-side encoded + * 3: MS-I mid-side and intensity encoded (currently not supported) + * + * attention: don't call them after lame_encode_finish + * suggested: lame_encode_flush -> lame_*_hist -> lame_close + */ + +void CDECL lame_bitrate_hist( + const lame_global_flags * gfp, + int bitrate_count[14] ); +void CDECL lame_bitrate_kbps( + const lame_global_flags * gfp, + int bitrate_kbps [14] ); +void CDECL lame_stereo_mode_hist( + const lame_global_flags * gfp, + int stereo_mode_count[4] ); + +void CDECL lame_bitrate_stereo_mode_hist ( + const lame_global_flags * gfp, + int bitrate_stmode_count[14][4] ); + +void CDECL lame_block_type_hist ( + const lame_global_flags * gfp, + int btype_count[6] ); + +void CDECL lame_bitrate_block_type_hist ( + const lame_global_flags * gfp, + int bitrate_btype_count[14][6] ); + +#if (DEPRECATED_OR_OBSOLETE_CODE_REMOVED && 0) +#else +/* + * OPTIONAL: + * lame_mp3_tags_fid will rewrite a Xing VBR tag to the mp3 file with file + * pointer fid. These calls perform forward and backwards seeks, so make + * sure fid is a real file. Make sure lame_encode_flush has been called, + * and all mp3 data has been written to the file before calling this + * function. + * NOTE: + * if VBR tags are turned off by the user, or turned off by LAME because + * the output is not a regular file, this call does nothing + * NOTE: + * LAME wants to read from the file to skip an optional ID3v2 tag, so + * make sure you opened the file for writing and reading. + * NOTE: + * You can call lame_get_lametag_frame instead, if you want to insert + * the lametag yourself. +*/ +void CDECL lame_mp3_tags_fid(lame_global_flags *, FILE* fid); +#endif + +/* + * OPTIONAL: + * lame_get_lametag_frame copies the final LAME-tag into 'buffer'. + * The function returns the number of bytes copied into buffer, or + * the required buffer size, if the provided buffer is too small. + * Function failed, if the return value is larger than 'size'! + * Make sure lame_encode flush has been called before calling this function. + * NOTE: + * if VBR tags are turned off by the user, or turned off by LAME, + * this call does nothing and returns 0. + * NOTE: + * LAME inserted an empty frame in the beginning of mp3 audio data, + * which you have to replace by the final LAME-tag frame after encoding. + * In case there is no ID3v2 tag, usually this frame will be the very first + * data in your mp3 file. If you put some other leading data into your + * file, you'll have to do some bookkeeping about where to write this buffer. + */ +size_t CDECL lame_get_lametag_frame( + const lame_global_flags *, unsigned char* buffer, size_t size); + +/* + * REQUIRED: + * final call to free all remaining buffers + */ +int CDECL lame_close (lame_global_flags *); + +#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED +#else +/* + * OBSOLETE: + * lame_encode_finish combines lame_encode_flush() and lame_close() in + * one call. However, once this call is made, the statistics routines + * will no longer work because the data will have been cleared, and + * lame_mp3_tags_fid() cannot be called to add data to the VBR header + */ +int CDECL lame_encode_finish( + lame_global_flags* gfp, + unsigned char* mp3buf, + int size ); +#endif + + + + + + +/********************************************************************* + * + * decoding + * + * a simple interface to mpglib, part of mpg123, is also included if + * libmp3lame is compiled with HAVE_MPGLIB + * + *********************************************************************/ + +struct hip_global_struct; +typedef struct hip_global_struct hip_global_flags; +typedef hip_global_flags *hip_t; + + +typedef struct { + int header_parsed; /* 1 if header was parsed and following data was + computed */ + int stereo; /* number of channels */ + int samplerate; /* sample rate */ + int bitrate; /* bitrate */ + int mode; /* mp3 frame type */ + int mode_ext; /* mp3 frame type */ + int framesize; /* number of samples per mp3 frame */ + + /* this data is only computed if mpglib detects a Xing VBR header */ + unsigned long nsamp; /* number of samples in mp3 file. */ + int totalframes; /* total number of frames in mp3 file */ + + /* this data is not currently computed by the mpglib routines */ + int framenum; /* frames decoded counter */ +} mp3data_struct; + +/* required call to initialize decoder */ +hip_t CDECL hip_decode_init(void); + +/* cleanup call to exit decoder */ +int CDECL hip_decode_exit(hip_t gfp); + +/* HIP reporting functions */ +void CDECL hip_set_errorf(hip_t gfp, lame_report_function f); +void CDECL hip_set_debugf(hip_t gfp, lame_report_function f); +void CDECL hip_set_msgf (hip_t gfp, lame_report_function f); + +/********************************************************************* + * input 1 mp3 frame, output (maybe) pcm data. + * + * nout = hip_decode(hip, mp3buf,len,pcm_l,pcm_r); + * + * input: + * len : number of bytes of mp3 data in mp3buf + * mp3buf[len] : mp3 data to be decoded + * + * output: + * nout: -1 : decoding error + * 0 : need more data before we can complete the decode + * >0 : returned 'nout' samples worth of data in pcm_l,pcm_r + * pcm_l[nout] : left channel data + * pcm_r[nout] : right channel data + * + *********************************************************************/ +int CDECL hip_decode( hip_t gfp + , unsigned char * mp3buf + , size_t len + , short pcm_l[] + , short pcm_r[] + ); + +/* same as hip_decode, and also returns mp3 header data */ +int CDECL hip_decode_headers( hip_t gfp + , unsigned char* mp3buf + , size_t len + , short pcm_l[] + , short pcm_r[] + , mp3data_struct* mp3data + ); + +/* same as hip_decode, but returns at most one frame */ +int CDECL hip_decode1( hip_t gfp + , unsigned char* mp3buf + , size_t len + , short pcm_l[] + , short pcm_r[] + ); + +/* same as hip_decode1, but returns at most one frame and mp3 header data */ +int CDECL hip_decode1_headers( hip_t gfp + , unsigned char* mp3buf + , size_t len + , short pcm_l[] + , short pcm_r[] + , mp3data_struct* mp3data + ); + +/* same as hip_decode1_headers, but also returns enc_delay and enc_padding + from VBR Info tag, (-1 if no info tag was found) */ +int CDECL hip_decode1_headersB( hip_t gfp + , unsigned char* mp3buf + , size_t len + , short pcm_l[] + , short pcm_r[] + , mp3data_struct* mp3data + , int *enc_delay + , int *enc_padding + ); + + + +/* OBSOLETE: + * lame_decode... functions are there to keep old code working + * but it is strongly recommended to replace calls by hip_decode... + * function calls, see above. + */ +#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED +#else +int CDECL lame_decode_init(void); +int CDECL lame_decode( + unsigned char * mp3buf, + int len, + short pcm_l[], + short pcm_r[] ); +int CDECL lame_decode_headers( + unsigned char* mp3buf, + int len, + short pcm_l[], + short pcm_r[], + mp3data_struct* mp3data ); +int CDECL lame_decode1( + unsigned char* mp3buf, + int len, + short pcm_l[], + short pcm_r[] ); +int CDECL lame_decode1_headers( + unsigned char* mp3buf, + int len, + short pcm_l[], + short pcm_r[], + mp3data_struct* mp3data ); +int CDECL lame_decode1_headersB( + unsigned char* mp3buf, + int len, + short pcm_l[], + short pcm_r[], + mp3data_struct* mp3data, + int *enc_delay, + int *enc_padding ); +int CDECL lame_decode_exit(void); + +#endif /* obsolete lame_decode API calls */ + + +/********************************************************************* + * + * id3tag stuff + * + *********************************************************************/ + +/* + * id3tag.h -- Interface to write ID3 version 1 and 2 tags. + * + * Copyright (C) 2000 Don Melton. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. + */ + +/* utility to obtain alphabetically sorted list of genre names with numbers */ +void CDECL id3tag_genre_list( + void (*handler)(int, const char *, void *), + void* cookie); + +void CDECL id3tag_init (lame_t gfp); + +/* force addition of version 2 tag */ +void CDECL id3tag_add_v2 (lame_t gfp); + +/* add only a version 1 tag */ +void CDECL id3tag_v1_only (lame_t gfp); + +/* add only a version 2 tag */ +void CDECL id3tag_v2_only (lame_t gfp); + +/* pad version 1 tag with spaces instead of nulls */ +void CDECL id3tag_space_v1 (lame_t gfp); + +/* pad version 2 tag with extra 128 bytes */ +void CDECL id3tag_pad_v2 (lame_t gfp); + +/* pad version 2 tag with extra n bytes */ +void CDECL id3tag_set_pad (lame_t gfp, size_t n); + +void CDECL id3tag_set_title(lame_t gfp, const char* title); +void CDECL id3tag_set_artist(lame_t gfp, const char* artist); +void CDECL id3tag_set_album(lame_t gfp, const char* album); +void CDECL id3tag_set_year(lame_t gfp, const char* year); +void CDECL id3tag_set_comment(lame_t gfp, const char* comment); + +/* return -1 result if track number is out of ID3v1 range + and ignored for ID3v1 */ +int CDECL id3tag_set_track(lame_t gfp, const char* track); + +/* return non-zero result if genre name or number is invalid + result 0: OK + result -1: genre number out of range + result -2: no valid ID3v1 genre name, mapped to ID3v1 'Other' + but taken as-is for ID3v2 genre tag */ +int CDECL id3tag_set_genre(lame_t gfp, const char* genre); + +/* return non-zero result if field name is invalid */ +int CDECL id3tag_set_fieldvalue(lame_t gfp, const char* fieldvalue); + +/* return non-zero result if image type is invalid */ +int CDECL id3tag_set_albumart(lame_t gfp, const char* image, size_t size); + +/* lame_get_id3v1_tag copies ID3v1 tag into buffer. + * Function returns number of bytes copied into buffer, or number + * of bytes rquired if buffer 'size' is too small. + * Function fails, if returned value is larger than 'size'. + * NOTE: + * This functions does nothing, if user/LAME disabled ID3v1 tag. + */ +size_t CDECL lame_get_id3v1_tag(lame_t gfp, unsigned char* buffer, size_t size); + +/* lame_get_id3v2_tag copies ID3v2 tag into buffer. + * Function returns number of bytes copied into buffer, or number + * of bytes rquired if buffer 'size' is too small. + * Function fails, if returned value is larger than 'size'. + * NOTE: + * This functions does nothing, if user/LAME disabled ID3v2 tag. + */ +size_t CDECL lame_get_id3v2_tag(lame_t gfp, unsigned char* buffer, size_t size); + +/* normaly lame_init_param writes ID3v2 tags into the audio stream + * Call lame_set_write_id3tag_automatic(gfp, 0) before lame_init_param + * to turn off this behaviour and get ID3v2 tag with above function + * write it yourself into your file. + */ +void CDECL lame_set_write_id3tag_automatic(lame_global_flags * gfp, int); +int CDECL lame_get_write_id3tag_automatic(lame_global_flags const* gfp); + +/* experimental */ +int CDECL id3tag_set_textinfo_latin1(lame_t gfp, char const *id, char const *text); + +/* experimental */ +int CDECL id3tag_set_comment_latin1(lame_t gfp, char const *lang, char const *desc, char const *text); + +#if DEPRECATED_OR_OBSOLETE_CODE_REMOVED +#else +/* experimental */ +int CDECL id3tag_set_textinfo_ucs2(lame_t gfp, char const *id, unsigned short const *text); + +/* experimental */ +int CDECL id3tag_set_comment_ucs2(lame_t gfp, char const *lang, + unsigned short const *desc, unsigned short const *text); + +/* experimental */ +int CDECL id3tag_set_fieldvalue_ucs2(lame_t gfp, const unsigned short *fieldvalue); +#endif + +/* experimental */ +int CDECL id3tag_set_fieldvalue_utf16(lame_t gfp, const unsigned short *fieldvalue); + +/* experimental */ +int CDECL id3tag_set_textinfo_utf16(lame_t gfp, char const *id, unsigned short const *text); + +/* experimental */ +int CDECL id3tag_set_comment_utf16(lame_t gfp, char const *lang, unsigned short const *desc, unsigned short const *text); + + +/*********************************************************************** +* +* list of valid bitrates [kbps] & sample frequencies [Hz]. +* first index: 0: MPEG-2 values (sample frequencies 16...24 kHz) +* 1: MPEG-1 values (sample frequencies 32...48 kHz) +* 2: MPEG-2.5 values (sample frequencies 8...12 kHz) +***********************************************************************/ + +extern const int bitrate_table [3][16]; +extern const int samplerate_table [3][ 4]; + +/* access functions for use in DLL, global vars are not exported */ +int CDECL lame_get_bitrate(int mpeg_version, int table_index); +int CDECL lame_get_samplerate(int mpeg_version, int table_index); + + +/* maximum size of albumart image (128KB), which affects LAME_MAXMP3BUFFER + as well since lame_encode_buffer() also returns ID3v2 tag data */ +#define LAME_MAXALBUMART (128 * 1024) + +/* maximum size of mp3buffer needed if you encode at most 1152 samples for + each call to lame_encode_buffer. see lame_encode_buffer() below + (LAME_MAXMP3BUFFER is now obsolete) */ +#define LAME_MAXMP3BUFFER (16384 + LAME_MAXALBUMART) + + +typedef enum { + LAME_OKAY = 0, + LAME_NOERROR = 0, + LAME_GENERICERROR = -1, + LAME_NOMEM = -10, + LAME_BADBITRATE = -11, + LAME_BADSAMPFREQ = -12, + LAME_INTERNALERROR = -13, + + FRONTEND_READERROR = -80, + FRONTEND_WRITEERROR = -81, + FRONTEND_FILETOOLARGE = -82 + +} lame_errorcodes_t; + +#if defined(__cplusplus) +} +#endif +#endif /* LAME_LAME_H */ + diff --git a/Src/Plugins/DSP/dsp_sc/sc2srclib/lame/libmp3lame/lame_global_flags.h b/Src/Plugins/DSP/dsp_sc/sc2srclib/lame/libmp3lame/lame_global_flags.h new file mode 100644 index 00000000..ad9e677d --- /dev/null +++ b/Src/Plugins/DSP/dsp_sc/sc2srclib/lame/libmp3lame/lame_global_flags.h @@ -0,0 +1,184 @@ +#ifndef LAME_GLOBAL_FLAGS_H +#define LAME_GLOBAL_FLAGS_H + +#ifndef lame_internal_flags_defined +#define lame_internal_flags_defined +struct lame_internal_flags; +typedef struct lame_internal_flags lame_internal_flags; +#endif + + +typedef enum short_block_e { + short_block_not_set = -1, /* allow LAME to decide */ + short_block_allowed = 0, /* LAME may use them, even different block types for L/R */ + short_block_coupled, /* LAME may use them, but always same block types in L/R */ + short_block_dispensed, /* LAME will not use short blocks, long blocks only */ + short_block_forced /* LAME will not use long blocks, short blocks only */ +} short_block_t; + +/*********************************************************************** +* +* Control Parameters set by User. These parameters are here for +* backwards compatibility with the old, non-shared lib API. +* Please use the lame_set_variablename() functions below +* +* +***********************************************************************/ +struct lame_global_struct { + unsigned int class_id; + + /* input description */ + unsigned long num_samples; /* number of samples. default=2^32-1 */ + int num_channels; /* input number of channels. default=2 */ + int samplerate_in; /* input_samp_rate in Hz. default=44.1 kHz */ + int samplerate_out; /* output_samp_rate. + default: LAME picks best value + at least not used for MP3 decoding: + Remember 44.1 kHz MP3s and AC97 */ + float scale; /* scale input by this amount before encoding + at least not used for MP3 decoding */ + float scale_left; /* scale input of channel 0 (left) by this + amount before encoding */ + float scale_right; /* scale input of channel 1 (right) by this + amount before encoding */ + + /* general control params */ + int analysis; /* collect data for a MP3 frame analyzer? */ + int write_lame_tag; /* add Xing VBR tag? */ + int decode_only; /* use lame/mpglib to convert mp3 to wav */ + int quality; /* quality setting 0=best, 9=worst default=5 */ + MPEG_mode mode; /* see enum in lame.h + default = LAME picks best value */ + int force_ms; /* force M/S mode. requires mode=1 */ + int free_format; /* use free format? default=0 */ + int findReplayGain; /* find the RG value? default=0 */ + int decode_on_the_fly; /* decode on the fly? default=0 */ + int write_id3tag_automatic; /* 1 (default) writes ID3 tags, 0 not */ + + int nogap_total; + int nogap_current; + + int substep_shaping; + int noise_shaping; + int subblock_gain; /* 0 = no, 1 = yes */ + int use_best_huffman; /* 0 = no. 1=outside loop 2=inside loop(slow) */ + + /* + * set either brate>0 or compression_ratio>0, LAME will compute + * the value of the variable not set. + * Default is compression_ratio = 11.025 + */ + int brate; /* bitrate */ + float compression_ratio; /* sizeof(wav file)/sizeof(mp3 file) */ + + + /* frame params */ + int copyright; /* mark as copyright. default=0 */ + int original; /* mark as original. default=1 */ + int extension; /* the MP3 'private extension' bit. + Meaningless */ + int emphasis; /* Input PCM is emphased PCM (for + instance from one of the rarely + emphased CDs), it is STRONGLY not + recommended to use this, because + psycho does not take it into account, + and last but not least many decoders + don't care about these bits */ + int error_protection; /* use 2 bytes per frame for a CRC + checksum. default=0 */ + int strict_ISO; /* enforce ISO spec as much as possible */ + + int disable_reservoir; /* use bit reservoir? */ + + /* quantization/noise shaping */ + int quant_comp; + int quant_comp_short; + int experimentalY; + int experimentalZ; + int exp_nspsytune; + + int preset; + + /* VBR control */ + vbr_mode VBR; + float VBR_q_frac; /* Range [0,...,1[ */ + int VBR_q; /* Range [0,...,9] */ + int VBR_mean_bitrate_kbps; + int VBR_min_bitrate_kbps; + int VBR_max_bitrate_kbps; + int VBR_hard_min; /* strictly enforce VBR_min_bitrate + normaly, it will be violated for analog + silence */ + + + /* resampling and filtering */ + int lowpassfreq; /* freq in Hz. 0=lame choses. + -1=no filter */ + int highpassfreq; /* freq in Hz. 0=lame choses. + -1=no filter */ + int lowpasswidth; /* freq width of filter, in Hz + (default=15%) */ + int highpasswidth; /* freq width of filter, in Hz + (default=15%) */ + + + + /* + * psycho acoustics and other arguments which you should not change + * unless you know what you are doing + */ + float maskingadjust; + float maskingadjust_short; + int ATHonly; /* only use ATH */ + int ATHshort; /* only use ATH for short blocks */ + int noATH; /* disable ATH */ + int ATHtype; /* select ATH formula */ + float ATHcurve; /* change ATH formula 4 shape */ + float ATH_lower_db; /* lower ATH by this many db */ + int athaa_type; /* select ATH auto-adjust scheme */ + float athaa_sensitivity; /* dB, tune active region of auto-level */ + short_block_t short_blocks; + int useTemporal; /* use temporal masking effect */ + float interChRatio; + float msfix; /* Naoki's adjustment of Mid/Side maskings */ + + int tune; /* 0 off, 1 on */ + float tune_value_a; /* used to pass values for debugging and stuff */ + + float attackthre; /* attack threshold for L/R/M channel */ + float attackthre_s; /* attack threshold for S channel */ + + + struct { + void (*msgf) (const char *format, va_list ap); + void (*debugf) (const char *format, va_list ap); + void (*errorf) (const char *format, va_list ap); + } report; + + /************************************************************************/ + /* internal variables, do not set... */ + /* provided because they may be of use to calling application */ + /************************************************************************/ + + int lame_allocated_gfp; /* is this struct owned by calling + program or lame? */ + + + + /**************************************************************************/ + /* more internal variables are stored in this structure: */ + /**************************************************************************/ + lame_internal_flags *internal_flags; + + + struct { + int mmx; + int amd3dnow; + int sse; + + } asm_optimizations; +}; + +int is_lame_global_flags_valid(const lame_global_flags * gfp); + +#endif /* LAME_GLOBAL_FLAGS_H */ diff --git a/Src/Plugins/DSP/dsp_sc/sc2srclib/shoutcast_output.cpp b/Src/Plugins/DSP/dsp_sc/sc2srclib/shoutcast_output.cpp new file mode 100644 index 00000000..17e05b8b --- /dev/null +++ b/Src/Plugins/DSP/dsp_sc/sc2srclib/shoutcast_output.cpp @@ -0,0 +1,1636 @@ +#include +#include +#include "../api.h" +#include "Include/shoutcast_output.h" +#include "../../sc_serv3/nmrCommon/stl/stringUtils.h" +#include "../utils.h" +#include "../nu/ServiceBuilder.h" +#include + +#pragma intrinsic(memcpy, memset) + +static char buf[1024]; +static char out[1024]; + +extern int iscompatibility; +extern winampDSPModule module; +extern api_service *WASABI_API_SVC; + +static api_connection *CreateConnection(const char *url) +{ + api_connection *conn = 0; + if (WASABI_API_SVC/*module.service*/) + { + if (!_strnicmp(url, "https://", 8)) + { + ServiceBuild(WASABI_API_SVC/*module.service*/, conn, sslConnectionFactoryGUID); + } + else + { + ServiceBuild(WASABI_API_SVC/*module.service*/, conn, connectionFactoryGUID); + } + } + + return conn; +} + +static void ReleaseConnection(api_connection *&conn, const char *url) +{ + if (!conn) { + return; + } + + waServiceFactory *connectionFactory = 0; + if (WASABI_API_SVC/*mod.service*/) + { + if (!_strnicmp(url, "https://", 8)) { + connectionFactory = WASABI_API_SVC/*mod.service*/->service_getServiceByGuid(sslConnectionFactoryGUID); + } else { + connectionFactory = WASABI_API_SVC/*mod.service*/->service_getServiceByGuid(connectionFactoryGUID); + } + } + + if (connectionFactory) { + connectionFactory->releaseInterface(conn); + } + + conn = 0; +} + +int resolvenow(const char *hostname, unsigned short port, addrinfo **addr, int sockettype, char **saddress) +{ + addrinfo hints; + memset(&hints,0,sizeof(hints)); + hints.ai_family = PF_UNSPEC; + if (hostname) + hints.ai_flags = AI_NUMERICHOST; + else + hints.ai_flags = AI_NUMERICHOST | AI_PASSIVE; + hints.ai_socktype = sockettype; + + char portString[32] = {0}; + sprintf(portString, "%u", (unsigned int)port); + + if (getaddrinfo(hostname, portString, &hints, addr) == 0) + { + return 0; + } + else + { + hints.ai_flags = 0; + if (getaddrinfo(hostname, portString, &hints, addr) == 0) + { + // need to make sure we're not using 0.0.0.0 + // as that will break us when using localhost + bool found = false; + struct addrinfo *ptr = NULL; + for (ptr = *addr; ptr != NULL; ptr = ptr->ai_next) + { + char* address = ::inet_ntoa(((sockaddr_in*)(ptr->ai_addr))->sin_addr); + if (strcmp(address, "0.0.0.0")) + { + *saddress = address; + *addr = ptr; + return 0; + } + } + } + } + return -1; +} + +void compatible_connect(T_OUTPUT *Output, int port) { + + // due to a bug in re-using the connection + // with Winamp's jnetlib implementation we + // need to do a force drop and make a new + // instance so all is initialised properly + if (Output->Output && iscompatibility) { + Output->Output->Close(); + Output->Output->Release(); + Output->Output = NULL; + } + + // create the connection only when it's needed + if (!Output->Output) { + Output->Output = CreateConnection(""); + if (Output->Output) { + Output->Output->Open(API_DNS_AUTODNS, 16384, 16384); + } + } + + if (Output->Output) { + if (iscompatibility && !strnicmp(Output->Config->Address, "localhost", 9)) { + addrinfo *saddr = 0; + char *address = 0; + if (!resolvenow(Output->Config->Address, (Output->Config->Port) + (LOBYTE(Output->Config->protocol) == 1), &saddr, SOCK_STREAM, &address) && address) { + Output->Output->connect(address, (Output->Config->Port) + (LOBYTE(Output->Config->protocol) == 1)); + } else { + Output->Output->connect(Output->Config->Address, (Output->Config->Port) + (LOBYTE(Output->Config->protocol) == 1)); + } + } else { + Output->Output->connect(Output->Config->Address, port); + } + } +} + +SHOUTCAST_OUTPUT::SHOUTCAST_OUTPUT() : lame_init(0), lame_init_params(0), lame_encode_buffer_interleaved(0), lame_encode_flush(0), libinst(NULL) { + IOwnEncoder = 0; + SetEncoder(DEFAULT_ENCODER); + OutputManager.AddHandler(OUT_DISCONNECTED, Output_Disconnected); + OutputManager.AddHandler(OUT_CONNECT, Output_Connect); + OutputManager.AddHandler(OUT_REQUEST_CIPHER, Output_Request_Cipher);//cipher request + OutputManager.AddHandler(OUT_RECV_CIPHER, Output_Receive_Cipher);// and process cipher + OutputManager.AddHandler(OUT_SENDAUTH, Output_SendAuth); + OutputManager.AddHandler(OUT_RECVAUTHRESPONSE, Output_RecvAuthResponse); + OutputManager.AddHandler(OUT_SEND_MIME, Output_Send_Mime); + OutputManager.AddHandler(OUT_RECV_MIME, Output_Recv_Mime); + OutputManager.AddHandler(OUT_SEND_BITRATE, Output_Send_Bitrate); + OutputManager.AddHandler(OUT_RECV_BITRATE, Output_Recv_Bitrate); + OutputManager.AddHandler(OUT_SEND_BUFSIZE, Output_Send_Buf_Size); + OutputManager.AddHandler(OUT_RECV_BUFSIZE, Output_Recv_Buf_Size); + OutputManager.AddHandler(OUT_SEND_MAX, Output_Send_Max_Size); + OutputManager.AddHandler(OUT_RECV_MAX, Output_Recv_Max_Size); + OutputManager.AddHandler(OUT_SENDYP, Output_SendYP); + OutputManager.AddHandler(OUT_SEND_INITFLUSH, Output_Send_InitFlush); + OutputManager.AddHandler(OUT_RECV_INITFLUSH, Output_Recv_InitFlush); + OutputManager.AddHandler(OUT_SEND_INITSTANDBY, Output_Send_InitStandby); + OutputManager.AddHandler(OUT_RECV_INITSTANDBY, Output_Recv_InitStandby); + //OutputManager.AddHandler(OUT_SEND_INTRO, Output_Send_Intro); + //OutputManager.AddHandler(OUT_RECV_INTRO, Output_Recv_Intro); + //OutputManager.AddHandler(OUT_SEND_BACKUP, Output_Send_Backup); + //OutputManager.AddHandler(OUT_RECV_BACKUP, Output_Recv_Backup); + OutputManager.AddHandler(OUT_SENDCONTENT, Output_SendContent); + OutputManager.AddHandler(OUT_DISCONNECT, Output_Disconnect); + OutputManager.AddHandler(OUT_RECONNECT, Output_Reconnect); + OutputManager.AddHandler(OUT_TITLESENDUPDATE, Output_Title_SendUpdate); + OutputManager.AddHandler(OUT_SEND_METADATA, Output_Send_Metadata); + OutputManager.AddHandler(OUT_SEND_ARTWORK, Output_Send_Artwork); + ReleaseMutex((mutex = CreateMutex(NULL, TRUE, NULL))); +} + +void SHOUTCAST_OUTPUT::SetLame(void *init, void *params, void *encode, void *finish) { + LOCK{ + lame_init = (void (__cdecl *)(void))init; + lame_init_params = (void (__cdecl *)(lame_global_flags *))params; + lame_encode_buffer_interleaved = (int (__cdecl *)(lame_global_flags *, short int pcm[], int num_samples, char *mp3buffer, int mp3buffer_size))encode; + lame_encode_flush = (int (__cdecl *)(lame_global_flags *, char *mp3buffer, int size))finish; + }UNLOCK +} + +SHOUTCAST_OUTPUT::~SHOUTCAST_OUTPUT() { + LOCK + CloseHandle(mutex); +} + +/* +Create a Uvox frame +*/ + +void SHOUTCAST_OUTPUT::createUvoxFrame(int length, char *payload_in, int type, T_OUTPUT *userData) { + int len = min(length, UV_MAX_DATA_LEN); + uv2xHdr hdr2 = { UV_SYNC_BYTE, UV_RESERVED, htons(type), htons(len), {UV_END}, UV_END }; + if (payload_in && len > 0 && len <= UV_MAX_DATA_LEN) { + memcpy(hdr2.m_data, payload_in, len); + } + int sent = UV_HEADER_LEN + len + UV_END_LEN; + userData->Output->send(&hdr2, sent); + userData->Info.BytesSent += sent; +} + +int SHOUTCAST_OUTPUT::createUvoxMetaFrame(int length, char *payload_in, int type, + T_OUTPUT *userData, unsigned short id, + unsigned short span) { + // check for the class type and abort if it's not right - will typically be 0x0000 or 0xXXFF + if ((type >= 0x3000) && (type < 0x5000)) { + int artType = ((type & 0xFF00) == (MSG_METADATA_ALBUMART|MSG_METADATA_PLAYING_ART)); + int len = min(length, UV_MAX_META_LEN); + uv2xMetadataHdr metaHdr = { UV_SYNC_BYTE, UV_RESERVED, htons(type), htons(len + UV_META_LEN), + htons(id), htons(span), htons((span > 1 ? userData->Info.art_index[artType] : 0x1)), + {UV_END}, UV_END }; + + if (payload_in && len > 0 && len <= UV_MAX_META_LEN) { + memcpy(metaHdr.m_data, payload_in, len); + } + + int sent = UV_HEADER_LEN + len + UV_META_LEN + UV_END_LEN; + if(userData->Output->send(&metaHdr, sent) != -1) + { + userData->Info.BytesSent += sent; + } + else + { + // if the send buffer is still full then we need to nudge + // it all along so that we can send the metadata as not + // checking for this will cause the loss of meta frames + while(userData->Output->GetSendBytesAvailable()) + { + Sleep(10); // sleep a bit and try again + userData->Output->run(); + if(userData->Output->send(&metaHdr, sent) != -1) + { + userData->Info.BytesSent += sent; + break; + } + } + } + return id+1; + } + return id; +} + +/* +Parse a Uvox Frame +*/ +int SHOUTCAST_OUTPUT::parseUvoxFrame(char *payload_in, char *payload_out) { + LOCK{ + uv2xHdr *hdr2 = (uv2xHdr *)payload_in; + int len = min(ntohs(hdr2->msgLen), UV_MAX_DATA_LEN); + memmove(payload_out, hdr2->m_data, len); + // just doing this to ensure its terminated correctly + hdr2->m_data[len] = UV_END; + }UNLOCK + return 1; +} + +/* +Check for known Uvox Error responses +*/ +int SHOUTCAST_OUTPUT::checkUvoxFrameForError(char *pload_out, int state, T_OUTPUT *userData) { + LOCK{ + if (userData->Info.Succeeded == -2 && + userData->Info.ErrorMsg) { + free(userData->Info.ErrorMsg); + } + userData->Info.Succeeded = 1; + userData->Info.ErrorMsg = 0; + + if (!strcmp("NAK:2.1:Deny", pload_out) || !strcmp("NAK:Deny", pload_out)) { + userData->Info.Succeeded = -1; + userData->Info.ErrorMsg = "NAK:Deny"; + UNLOCK + return userData->Info.Succeeded; + } + + if (!strcmp("NAK:2.1:Stream ID Error", pload_out)) { + userData->Info.Succeeded = -1; + userData->Info.ErrorMsg = "StreamID"; + UNLOCK + return userData->Info.Succeeded; + } + + if (!strcmp("NAK:2.1:Stream Moved", pload_out)) { + userData->Info.Succeeded = -1; + userData->Info.ErrorMsg = "StreamMoved"; + UNLOCK + return userData->Info.Succeeded; + } + + if (!strcmp("NAK:Bit Rate Error", pload_out)) { + userData->Info.Succeeded = -1; + userData->Info.ErrorMsg = "BitrateError"; + UNLOCK + return userData->Info.Succeeded; + } + + if (!strcmp("NAK:2.1:Version Error", pload_out)) { + userData->Info.Succeeded = -1; + userData->Info.ErrorMsg = "VersionError"; + UNLOCK + return userData->Info.Succeeded; + } + + if (!strcmp("NAK:2.1:Parse Error", pload_out) || !strcmp("NAK:Parse Error", pload_out)) { + userData->Info.Succeeded = -1; + userData->Info.ErrorMsg = "ParseError"; + UNLOCK + return userData->Info.Succeeded; + } + + if (!strcmp("NAK:Stream In Use", pload_out)) { + userData->Info.Succeeded = -1; + userData->Info.ErrorMsg = "InUse"; + UNLOCK + return userData->Info.Succeeded; + } + + if (!strcmp("NAK", pload_out)) { + userData->Info.Succeeded = -2; + // TODO + userData->Info.ErrorMsg = _strdup(pload_out); + UNLOCK + return userData->Info.Succeeded; + } + + if (!strcmp("ACK", pload_out)) { + userData->Info.Succeeded = 1; + UNLOCK + return userData->Info.Succeeded; + } + + }UNLOCK + return userData->Info.Succeeded; +} + +int SHOUTCAST_OUTPUT::Run(int mode, void *Input, int InputSize, int SaveEncoder) { + int in_used = 0; + LOCK{ + int bad = 1; + + if (mode & OM_OTHER) OutputManager.Run(OutputManager.GetNumJobs()); + + for (int i = OutputManager.GetNumJobs()-1; i >= 0; i--) { + T_OUTPUT *Output = OutputManager[i]; + if (mode & OM_OUTPUT && Output->Output) { + Output->Output->run(); + } + + int state = OutputManager.GetJobState(i); + if (state != OUT_DISCONNECTED) { + int outstate = (Output->Output ? Output->Output->get_state() : CONNECTION_STATE_ERROR); + if ((outstate == CONNECTION_STATE_ERROR || outstate == CONNECTION_STATE_CLOSED) && + state != OUT_DISCONNECT && state != OUT_RECONNECT) { + if (Output->Type == OUTTYPE_SOURCE) { + Output->Info.ConnectionTime = clock(); + Output->SlowClose = 0; + OutputManager.SetJobState(i, state = OUT_DISCONNECT); // kill the connection + } else if (Output->Type == OUTTYPE_TITLE) { + delete OutputManager[i]; + OutputManager.DelJob(i); // title update failed, remove it + } + } + if (state == OUT_SENDCONTENT) bad = 0; + } else { + if (Output->Type == OUTTYPE_TITLE) { + #ifndef _DEBUG + delete OutputManager[i]; + #endif + OutputManager.DelJob(i); // title update succeeded, remove it + } + } + }// for num jobs + if (!bad && Input && (InputSize > 0) && Encoder && (mode & OM_ENCODE)) { + while (in_used < InputSize && InputSize > 0) { + int inused = 0; + char EncodedData[32768] = {0}; + int out_used = Encoder->Encode(((char *)Input+in_used), (InputSize-in_used), EncodedData, sizeof(EncodedData), &inused); + in_used += inused; + + for (int i = OutputManager.GetNumJobs()-1; i >= 0; i--) { + T_OUTPUT *Output = OutputManager[i]; + int state = OutputManager.GetJobState(i); + if (out_used > 0 && state == OUT_SENDCONTENT) { + int sofar = 0, type = 0; + + // when enabled this will save the encoded output to a local file for DJ backup, etc + if (SaveEncoder != -1) { + WriteSaveEncoded(SaveEncoder, EncodedData, out_used); + } + + if (strcmp("audio/aacp", Output->ContentType)==0) { type = AACP_DATA; } // aacp + if (strcmp("audio/aac", Output->ContentType)==0) { type = AAC_LC_DATA; } // aac lc + if (strcmp("audio/mpeg", Output->ContentType)==0) { type = MP3_DATA; } // mp3 + if (strcmp("audio/ogg", Output->ContentType)==0) { type = OGG_DATA; } // ogg + + // make sure that this doesn't go negative otherwise things will go wrong + while (out_used > 0) { + int delta = Output->Output->GetSendBytesAvailable(); + + if (LOBYTE(Output->Config->protocol) != 1) { + // try to clamp things so that it's within the packet size limits + delta = min(delta, UV_MAX_DATA_LEN); + } + + delta = min(delta, out_used); + if (delta > 7) { + if (LOBYTE(Output->Config->protocol) != 1) { + // send sc2 data frames + createUvoxFrame(delta, EncodedData+sofar, type, Output); + } else { + // send sc1 data + Output->Info.BytesSent += delta; + Output->Output->send(EncodedData+sofar, delta); + } + } else { + // check for the connection having dropped and we're just spinning + // and if we are then we need to abort from here else we lock up + int outstate = Output->Output->get_state(); + if (outstate == CONNECTION_STATE_CLOSED || outstate == CONNECTION_STATE_ERROR) { + break; + } + Sleep(10); // sleep a bit and try again + Output->Output->run(); + } + sofar+=delta; + out_used-=delta; + } + } + } + if (InputSize <= 0) break; + } + } + }UNLOCK + return in_used; +} + +int SHOUTCAST_OUTPUT::AddOutput(int Connection, T_OUTPUT_CONFIG *Config, void (*TitleCallback)(const int Connection, const int Mode)) { + if (Connection >= 0 && Connection < 5) { + T_OUTPUT *job = new T_OUTPUT; + job->Bitrate = 0; + job->ContentType = "audio/mpeg"; + if (Encoder) { + int infosize = sizeof(T_EncoderIOVals); + T_EncoderIOVals *EncSettings = (T_EncoderIOVals *)Encoder->GetExtInfo(&infosize); + if (EncSettings && infosize) job->Bitrate = EncSettings->output_bitRate; + job->ContentType = Encoder->GetContentType(); + } + job->Connection = Connection; + job->TitleCallback = TitleCallback; + job->Config = Config; + job->Info.Reconnect = Config->AutoRecon; + job->Info.ReconnectTime = Config->ReconTime; + job->Type = OUTTYPE_SOURCE; + job->SlowClose = 0; + // trigger a title update on start as artwork + // is done later so does not need to set here + job->Info.meta_cached = 1; + + int retval = -1; + LOCK{ + retval = OutputManager.AddJob(OUT_DISCONNECTED, job); + }UNLOCK + return retval; + } + return -1; +} + +void SHOUTCAST_OUTPUT::UpdateOutput(int Connection) { + if (Connection >= 0 && Connection < 5) { + LOCK{ + T_OUTPUT *Output = OutputManager[Connection]; + if (Output) { + Output->Info.Reconnect = Output->Config->AutoRecon; + Output->Info.ReconnectTime = Output->Config->ReconTime; + } + }UNLOCK + } +} + +void SHOUTCAST_OUTPUT::RemoveOutput(int Connection) { + if (Connection >= 0 && Connection < 5) { + LOCK{ + T_OUTPUT *Output = OutputManager[Connection]; + if (Output) { + OutputManager.DelJob(Connection); + delete Output; + } + }UNLOCK + } +} + +int SHOUTCAST_OUTPUT::ConnectOutput(int Connection) { + if (Connection >= 0 && Connection < 5) { + LOCK{ + T_OUTPUT *Output = OutputManager[Connection]; + if (Output && Encoder) { + UpdateOutput(Connection); + + compatible_connect(Output, (Output->Config->Port) + (LOBYTE(Output->Config->protocol) == 1)); + + Output->Bitrate = 0; + Output->ContentType = "audio/mpeg"; + + if (Encoder) { + int infosize = sizeof(T_EncoderIOVals); + T_ENCODER_MP3_INFO *EncSettings = (T_ENCODER_MP3_INFO *)Encoder->GetExtInfo(&infosize); + if (EncSettings && infosize) Output->Bitrate = EncSettings->output_bitRate; + Output->ContentType = Encoder->GetContentType(); + } + OutputManager.SetJobState(Connection, OUT_CONNECT); + } + }UNLOCK + } + return 1; +} + +int SHOUTCAST_OUTPUT::DisconnectOutput(int Connection, int withReconnect, int reconnectTime) { + if (Connection >= 0 && Connection < 5) { + LOCK{ + T_OUTPUT *Output = OutputManager[Connection]; + if (Output) { + int state = OutputManager.GetJobState(Connection); + if (state != OUT_DISCONNECTED) { + Output->Info.Reconnect = withReconnect >= 0 ? withReconnect : Output->Config->AutoRecon; + Output->Info.ReconnectTime = reconnectTime >= 0 ? reconnectTime : Output->Config->ReconTime; + Output->SlowClose = 0; + if (Encoder) { + int infosize = sizeof(T_EncoderIOVals); + T_ENCODER_MP3_INFO *EncSettings = (T_ENCODER_MP3_INFO *)Encoder->GetExtInfo(&infosize); + if (EncSettings && infosize) Output->Bitrate = EncSettings->output_bitRate; + Output->ContentType = Encoder->GetContentType(); + } + OutputManager.SetJobState(Connection, OUT_DISCONNECT); + } + } + }UNLOCK + } + return 1; +} + +void SHOUTCAST_OUTPUT::SetEncoder(C_ENCODER *encoder, int takeOwnership) { + LOCK{ + if (IOwnEncoder && Encoder && Encoder != DEFAULT_ENCODER) + delete Encoder; + if (encoder != DEFAULT_ENCODER) { + IOwnEncoder = takeOwnership; + Encoder = encoder; + } else { + try { + Encoder = new C_ENCODER_MP3(lame_init, lame_init_params, lame_encode_buffer_interleaved, lame_encode_flush); + IOwnEncoder = 1; + } catch(...) { + Encoder = NULL; + } + } + }UNLOCK +} + +C_ENCODER *SHOUTCAST_OUTPUT::GetEncoder() { + C_ENCODER *Enc = NULL; + LOCK{ + Enc = Encoder; + }UNLOCK + return Enc; +} + +bool validateTitle(wchar_t **dest, const wchar_t* src) +{ + bool allowed = true; + int src_len = lstrlenW(src); + char *str = (char*)calloc(src_len, sizeof(char)); + if (str) + { + WideCharToMultiByte(CP_ACP, 0, src, -1, str, src_len, 0, 0); + + std::string updinfoSong(str); + if (!stringUtil::stripAlphaDigit(updinfoSong).empty()) + { + // work on lowercase comparison as well as doing a check to see if + // after removing white space + punctuation we have a valid title. + std::string m_checkUpdinfoSong = stringUtil::toLower(updinfoSong); + + // exclude weird title updates from being accepted + // as no point in giving junk to the user later on + if (m_checkUpdinfoSong.find("!doctype") != string::npos || + m_checkUpdinfoSong.find(" NextList, wchar_t *Song, + wchar_t *Album, wchar_t *Artist, wchar_t *Genre, wchar_t *Comment, + wchar_t* Year, int Connection, bool sendNext) { + int update = 0; + + if(!metadata.Title || wcscmp(metadata.Title, Title)) { + if (metadata.Title) free(metadata.Title); + metadata.Title = _wcsdup(Title); + update++; + } + + metadata.NextList.resize(0); + if (!NextList.empty()) { + metadata.NextList = NextList; + update++; + } + + if(!metadata.Song || wcscmp(metadata.Song, (Song ? Song : L""))) { + if (validateTitle(&metadata.Song, (Song ? Song : L""))) { + update++; + } + } + + if(!metadata.Album || wcscmp(metadata.Album, (Album ? Album : L""))) { + if (validateTitle(&metadata.Album, (Album ? Album : L""))) { + update++; + } + } + + if(!metadata.Artist || wcscmp(metadata.Artist, (Artist ? Artist : L""))) { + if (validateTitle(&metadata.Artist, (Artist ? Artist : L""))) { + update++; + } + } + + if(!metadata.Genre || wcscmp(metadata.Genre, (Genre ? Genre : L""))) { + if (validateTitle(&metadata.Genre, (Genre ? Genre : L""))) { + update++; + } + } + + if(!metadata.Comment || wcscmp(metadata.Comment, (Comment ? Comment : L""))) { + if (validateTitle(&metadata.Comment, (Comment ? Comment : L""))) { + update++; + } + } + + if(!metadata.Year || wcscmp(metadata.Year, (Year ? Year : L""))) { + if (validateTitle(&metadata.Year, (Year ? Year : L""))) { + update++; + } + } + + UpdateTitle(0, NextList, Connection, sendNext); +} + +void SHOUTCAST_OUTPUT::UpdateAlbumArtCache(void* APIC, int APICLength, int APICType, int Connection) { + int artType = ((APICType & 0xFF00) == (MSG_METADATA_ALBUMART|MSG_METADATA_PLAYING_ART)); + // make sure we're within the metadata limits + // which is 32 * max metadata payload (523872) + // and if not then we discard the artwork + int prevAPICLength = metadata.APICLength[artType]; + if (APICLength > UV_MAX_TOTAL_META_LEN) { + metadata.APICLength[artType] = 0; + metadata.APIC[artType] = 0; + } else { + metadata.APIC[artType] = APIC; + metadata.APICLength[artType] = APICLength; + } + metadata.APICType[artType] = APICType; + + // update the metadata cache usage so we can work + if (Connection >= 0 && Connection < 5) { + LOCK{ + T_OUTPUT *Output = OutputManager[Connection]; + if (Output) { + // only update the flag if it's new stream art or is playing and is different from before + if (!(!prevAPICLength && artType && (prevAPICLength == metadata.APICLength[artType]))) { + Output->Info.meta_cached |= (!artType ? 2 : 4); + } + } + }UNLOCK + + UpdateArtwork(Connection); + } +} + +void SHOUTCAST_OUTPUT::UpdateTitle(wchar_t *Title, std::vector NextList, + int Connection, bool sendNext, bool UseCache) { + //LOCK{ + if (Connection >= 0 && Connection < 5) { + T_OUTPUT *Output = OutputManager[Connection]; + if (Output && Output->Type == OUTTYPE_SOURCE && + (OutputManager.GetJobState(Connection) == OUT_SENDCONTENT || + OutputManager.GetJobState(Connection) == OUT_SEND_METADATA)) { + + // clear the metadata send flag + if (Output->Info.meta_cached & 1) { + Output->Info.meta_cached -= 1; + } + + if (LOBYTE(Output->Config->protocol) == 1 && Output->Config->doTitleUpdate) { + T_OUTPUT *job = new T_OUTPUT; + job->Bitrate = Output->Bitrate; + job->ContentType = Encoder->GetContentType(); + job->Config = Output->Config; + job->Type = OUTTYPE_TITLE; + job->SlowClose = 0; + compatible_connect(job, job->Config->Port); + + wchar_t *title = (UseCache?metadata.Title:Title); + if (title) { + validateTitle(&job->Info.Title, title); + validateTitle(&Output->Info.Title, title); + } else { + validateTitle(&job->Info.Title, Output->Info.Title); + } + + OutputManager.AddJob(OUT_CONNECT, job); + } else if (LOBYTE(Output->Config->protocol) != 1) { + validateTitle(&Output->Info.Title, (UseCache?metadata.Title:Title)); + + //#ifndef _DEBUG + if (UseCache) { + if (!metadata.NextList.empty()) { + Output->Info.NextList = metadata.NextList; + } + } else { + if (!NextList.empty()) { + Output->Info.NextList = NextList; + } + } + //#endif + + validateTitle(&Output->Info.Song, (metadata.Song && UseCache ? metadata.Song : L"")); + validateTitle(&Output->Info.Album, (metadata.Album && UseCache ? metadata.Album : L"")); + validateTitle(&Output->Info.Artist, (metadata.Artist && UseCache ? metadata.Artist : L"")); + validateTitle(&Output->Info.Genre, (metadata.Genre ? metadata.Genre : L"")); + validateTitle(&Output->Info.Comment, (metadata.Comment && UseCache ? metadata.Comment : L"")); + validateTitle(&Output->Info.Year, (metadata.Year && UseCache ? metadata.Year : L"")); + + if (Output->Config->doTitleUpdate) { + // only output friendlier line breaks when debugging is needed + #ifdef _DEBUG + #define XML_DEBUG + #endif + + #ifdef XML_DEBUG + #define EOL "\n" + #define TAB "\t" + #else + #define EOL "" + #define TAB "" + #endif + stringstream s; + s << "\n" EOL; + + wchar_t * title = (Output->Info.Song && *Output->Info.Song ? Output->Info.Song : Output->Info.Title); + if (validateTitle(0, title)) { + s << "" << ConvertToUTF8Escaped(title) << "" EOL; + } + + if (Output->Info.Album && Output->Info.Album[0]) s << "" << ConvertToUTF8Escaped(Output->Info.Album) << "" EOL; + if (Output->Info.Artist && Output->Info.Artist[0]) s << "" << ConvertToUTF8Escaped(Output->Info.Artist) << "" EOL; + if (Output->Info.Year && Output->Info.Year[0]) s << "" << ConvertToUTF8Escaped(Output->Info.Year) << "" EOL; + if (Output->Info.Comment && Output->Info.Comment[0]) s << "" << ConvertToUTF8Escaped(Output->Info.Comment) << "" EOL; + if (Output->Info.Genre && Output->Info.Genre[0]) s << "" << ConvertToUTF8Escaped(Output->Info.Genre) << "" EOL; + + s << "SHOUTcast Source DSP v" << sourceVersion << "" EOL; + + if (Output->Config->Description[0]) s << "" << escapeXML(Output->Config->Description) << "" EOL; + + s << "" << escapeXML(Output->Config->ServerURL && Output->Config->ServerURL[0] ? Output->Config->ServerURL : "http://www.shoutcast.com") << "" EOL; + + if (validateTitle(0, title)) { + s << "" EOL TAB "" << ConvertToUTF8Escaped(title) << "" EOL; + + if (!Output->Info.NextList.empty() && sendNext) { + s << TAB; + std::string soon; + for (size_t idx = 0, seq = 0; idx < Output->Info.NextList.size(); idx++) { + if (validateTitle(0, Output->Info.NextList[idx].c_str())) + { + std::string next = ConvertToUTF8Escaped(Output->Info.NextList[idx].c_str()); + s << "" << next << "" EOL TAB; + // store the first item as that is used for the 'soon' item + if (seq == 0) soon = next; + seq++; + } + } + if (!soon.empty()) { + s << "" << soon << "" EOL; + } + } + s << "" EOL; + } + s << ""; + + // TODO make sure this will split up > UV_MAX_META_FRAME_LEN blocks + mid = createUvoxMetaFrame(s.str().length(),(char*)s.str().data(), MSG_METADATA_XML_NEW, Output, mid); + } + } + } + } +} + +void SHOUTCAST_OUTPUT::UpdateArtwork(int Connection) { + if (Connection >= 0 && Connection < 5) { + LOCK{ + T_OUTPUT *Output = OutputManager[Connection]; + if (Output && Output->Type == OUTTYPE_SOURCE) { + + if (LOBYTE(Output->Config->protocol) != 1) { + Output->Info.APIC[0] = metadata.APIC[0]; + Output->Info.APICLength[0] = metadata.APICLength[0]; + Output->Info.APICType[0] = metadata.APICType[0]; + + Output->Info.APIC[1] = metadata.APIC[1]; + Output->Info.APICLength[1] = metadata.APICLength[1]; + Output->Info.APICType[1] = metadata.APICType[1]; + + // calculate the album art span inorder to use it once we've sent the title in following callbacks + for (int i = 0; i < 2; i++) { + int AlbumArtSpan = (metadata.APICLength[i] / UV_MAX_DATA_LEN) + ((metadata.APICLength[i] % UV_MAX_DATA_LEN) < UV_MAX_DATA_LEN ? 1 : 0); + // if over the limit then abort trying to send this + if (AlbumArtSpan > UV_MAX_META_FRAGMENTS) { + AlbumArtSpan = 1; + Output->Info.APIC[i] = 0; + } + + Output->Info.art_cached_span[i] = Output->Info.art_cached[i] = AlbumArtSpan; + Output->Info.art_index[i] = 1; + Output->Info.art_cached_length[i] = Output->Info.APICLength[i]; + } + } + } + }UNLOCK + } +} + +int SHOUTCAST_OUTPUT::UpdateAlbumArt(int Connection) { + if (Connection >= 0 && Connection < 5) { + LOCK{ + T_OUTPUT *Output = OutputManager[Connection]; + if (Output && Output->Type == OUTTYPE_SOURCE && + OutputManager.GetJobState(Connection) == OUT_SEND_ARTWORK) { + int artType = (Output->Info.meta_cached & 4 ? 1 : (Output->Info.meta_cached & 2 ? 0 : -1)); + if (artType != -1 && LOBYTE(Output->Config->protocol) != 1 && Output->Info.art_cached[artType] > 0) { + int type = Output->Info.APICType[artType]; + int length = Output->Info.art_cached_length[artType]; + int count = Output->Info.art_cached_span[artType] - Output->Info.art_cached[artType]; + + // attempt to process as a multi-part message as APIC most likely won't fit in just one uvox packet + if (length >= UV_MAX_META_LEN) { + createUvoxMetaFrame(UV_MAX_META_LEN, + (char*)Output->Info.APIC[artType]+(count * UV_MAX_META_LEN), + type, Output, mid, Output->Info.art_cached_span[artType]); + Output->Info.art_cached_length[artType] -= UV_MAX_META_LEN; + } else { + createUvoxMetaFrame(length, + (char*)Output->Info.APIC[artType]+(count * UV_MAX_META_LEN), + type, Output, mid, Output->Info.art_cached_span[artType]); + Output->Info.art_cached_length[artType] = 0; + } + Output->Info.art_index[artType] += 1; + + if (Output->Info.art_cached[artType] == 1) { + mid+=1; + Output->Info.meta_cached -= (artType ? 4 : 2); + // reset the values so we can re-send on disconnect, etc + Output->Info.art_cached[artType] = Output->Info.art_cached_span[artType]; + Output->Info.art_index[artType] = 1; + Output->Info.art_cached_length[artType] = Output->Info.APICLength[artType]; + } + } + } + }UNLOCK + } + return 1; +} + +enum OUTPUTSTATE SHOUTCAST_OUTPUT::GetState(int Connection) { + int retval = -1; + if (Connection >= 0 && Connection < 5) { + LOCK{ + retval = OutputManager.GetJobState(Connection); + }UNLOCK + } + return retval == -1 ? OUT_ERROR : (enum OUTPUTSTATE)retval; +} + +T_OUTPUT_CONFIG *SHOUTCAST_OUTPUT::operator[](int Connection) { + return GetOutput(Connection); +} + +T_OUTPUT_CONFIG *SHOUTCAST_OUTPUT::GetOutput(int Connection) { + T_OUTPUT_CONFIG *Config = NULL; + if (Connection >= 0 && Connection < 5) { + LOCK{ + T_OUTPUT *Output = OutputManager[Connection]; + if (Output) Config = Output->Config; + }UNLOCK + } + return Config; +} + +T_OUTPUT_INFO *SHOUTCAST_OUTPUT::GetOutputInfo(int Connection) { + T_OUTPUT_INFO *Info = NULL; + if (Connection >= 0 && Connection < 5) { + LOCK{ + T_OUTPUT *Output = OutputManager[Connection]; + if (Output) Info = &Output->Info; + }UNLOCK + } + return Info; +} + +// Protected methods + +int SHOUTCAST_OUTPUT::Output_Disconnected(int state, int last_state, T_OUTPUT *userData) { + STATE; + LOCK{ + userData->Config->protocol_retry = 0; + }UNLOCK + return state; // sit and spin +} + +int SHOUTCAST_OUTPUT::Output_Connect(int state, int last_state, T_OUTPUT *userData) { + DEBUG_STATE + STATE; + LOCK{ + userData->Config->protocol_retry = MAKEWORD(0, HIBYTE(userData->Config->protocol_retry)); + userData->Info.Succeeded = 1; + if (userData->Output) { + userData->Output->FlushSend(); + } + + if ((clock() - userData->Info.ConnectionTime) / CLOCKS_PER_SEC >= 1/*userData->Info.ReconnectTime*/) { + if (userData->Output && userData->Output->get_state() == CONNECTION_STATE_CONNECTED) { // are we connected yet? + userData->Info.ConnectionTime = clock(); + if (userData->Type == OUTTYPE_SOURCE) { + userData->Info.Reconnect = userData->Config->AutoRecon; + userData->Info.ReconnectTime = userData->Config->ReconTime; + userData->Info.ConnectedAt = time(NULL); + UNLOCK + return OUT_REQUEST_CIPHER; // connected, go authenticate + } else if (userData->Type == OUTTYPE_TITLE) { + UNLOCK + return OUT_TITLESENDUPDATE; // connected, go update the title + } + } + } + }UNLOCK + return state; // sit and spin +} + +int SHOUTCAST_OUTPUT::Output_SendContent(int state, int last_state, T_OUTPUT *userData) { + DEBUG_STATE + STATE; + if (userData->Output->get_state() == CONNECTION_STATE_ERROR) { // connection error? + return OUT_DISCONNECT; // error happened, go shut down + } + + // check if we have any metadata or artwork to dispatch (depends on protocol) + if (userData->Info.meta_cached & 1) { + userData->Info.meta_cached -= 1; + return OUT_SEND_METADATA; + } else if (LOBYTE(userData->Config->protocol) != 1 && + (userData->Info.meta_cached & 2 || userData->Info.meta_cached & 4) && + userData->Info.art_cached > 0) { + return OUT_SEND_ARTWORK; + } + + return state; // sit and spin +} + +int SHOUTCAST_OUTPUT::Output_Disconnect(int state, int last_state, T_OUTPUT *userData) { + DEBUG_STATE + //STATE; + LOCK{ + // reset the metadata flags so it will trigger on a connection coming back + // with usage of what was already cached to determine what we will re-send + userData->Info.meta_cached = 1|(userData->Info.art_cached_length[0] ? 2 : 0)|(userData->Info.art_cached_length[1] ? 4 : 0); + + int outstate = (userData->Output ? userData->Output->get_state() : CONNECTION_STATE_ERROR); + if (outstate == CONNECTION_STATE_CONNECTING) { // are we connecting? if so then just abort + UNLOCK + return OUT_DISCONNECTED; + } + + if (outstate == CONNECTION_STATE_CONNECTED) { // are we connected yet? + userData->Info.ConnectionTime = clock(); + userData->Info.ConnectedAt = 0; + userData->Info.BytesSent = 0; + if (LOBYTE(userData->Config->protocol) != 1) { + createUvoxFrame(1, "0\0", MSG_TERMINATE, userData); + if (userData->Info.Succeeded != -1) { + Output_DUMMY(state, last_state, userData); + } + } + userData->Output->Close(!userData->SlowClose); + } + + if (outstate == CONNECTION_STATE_CLOSING) { + userData->Info.ConnectionTime = clock(); // update the clock with the current time so that we don't get some weird crap for reconnections. + } + + if (outstate == CONNECTION_STATE_CLOSED || outstate == CONNECTION_STATE_ERROR) { + userData->Info.Switching = 0; + if (last_state == OUT_SENDYP) { + userData->Info.Succeeded = -1; + userData->Info.ErrorMsg = "Blocked"; + } else if (userData->Type == OUTTYPE_SOURCE && + (last_state == OUT_CONNECT || last_state == OUT_REQUEST_CIPHER || + last_state == OUT_SENDAUTH || last_state == OUT_RECV_CIPHER || + last_state == OUT_DISCONNECT)) { + // if using automatic mode then switch between v1 and v2 modes as needed + if (HIBYTE(userData->Config->protocol) && ((LOBYTE(userData->Config->protocol_retry) > 5) || + (last_state == OUT_CONNECT && HIBYTE(userData->Config->protocol_retry) < 1))) { + int protocol = (2 - (LOBYTE(userData->Config->protocol) != 1)); + userData->Config->protocol_retry = MAKEWORD(0, HIBYTE(userData->Config->protocol_retry)+1); + userData->Info.Switching = (LOBYTE(userData->Config->protocol) != 1 ? 2 : 1); + userData->Config->protocol = MAKEWORD(protocol, 1); + userData->Info.ConnectionTime = clock(); + UNLOCK + return OUT_RECONNECT; + } + } + + if (userData->Info.Reconnect && userData->Type == OUTTYPE_SOURCE) { + // if using automatic mode then switch between v1 and v2 modes as needed + if (HIBYTE(userData->Config->protocol)) { + // TODO need to make this into a function and have it show the switching message + int protocol = (2 - (LOBYTE(userData->Config->protocol) != 1)); + userData->Config->protocol_retry = 0; + userData->Info.Switching = (LOBYTE(userData->Config->protocol) != 1 ? 2 : 1); + userData->Config->protocol = MAKEWORD(protocol, 1); + userData->Info.ConnectionTime = clock(); + } + UNLOCK + return OUT_RECONNECT; + } else { + UNLOCK + return OUT_DISCONNECTED; + } + } + }UNLOCK + return state; // sit and spin +} + +int SHOUTCAST_OUTPUT::Output_Reconnect(int state, int last_state, T_OUTPUT *userData) { + DEBUG_STATE + STATE; + LOCK{ + if ((clock() - userData->Info.ConnectionTime) / CLOCKS_PER_SEC >= userData->Info.ReconnectTime) { + if (userData->Output) { + compatible_connect(userData, userData->Config->Port + (LOBYTE(userData->Config->protocol) == 1)); + } + UNLOCK + return OUT_CONNECT; + } + }UNLOCK + return state; // sit and spin +} + +void FixString(const char *in, char *out) { + while (in && *in) { + if ((*in >= 'A' && *in <= 'Z') || + (*in >= 'a' && *in <= 'z') || + (*in >= '0' && *in <= '9') || + (*in == '-') || + (*in == '_') || + (*in == '.') || + (*in == '!') || + (*in == '~') || + (*in == '*') || + (*in == '\'') || + (*in == '(') || + (*in == '[') || + (*in == ']') || + (*in == ')')) { + *out++=*in++; + } else if (*in >= 0 && *in < 32 && *in != 9 && *in != 10 && *in != 13) { + // strip out characters which aren't supported by the DNAS + // (only allow backspace, linefeed and carriage return) + in++; + } else { + unsigned int i=*(unsigned char *)in; + const char *t=in; + in++; + if (in == t + 1) { + if (i == '\'') i = '`'; + else if (i == '<') i = '('; + else if (i == '>') i = ')'; + else if (i == '\"') i='`'; + else if (i == '[') i='`'; + else if (i == ']') i='`'; + snprintf(out, 3, "%%%02X", i); + out += 3; + } else if (in == t + 2) { // multibyte + } + } + } + *out=0; +} + +/* SHOUTcast 1 title updates */ +int SHOUTCAST_OUTPUT::Output_Title_SendUpdate(int state, int last_state, T_OUTPUT *userData) { + DEBUG_STATE + STATE; + LOCK{ + char utf8title[1024] = {0}; + char fixedtitle[3072] = {0}; // if every character is URL-encoded, we would get a maximum of 3 times the space needed + char buffer[2048] = {0}; + + WideCharToMultiByte(CP_ACP, 0, userData->Info.Title, -1, utf8title, sizeof(utf8title), 0, 0); + FixString(utf8title, fixedtitle); + fixedtitle[1023] = 0; // DNAS doesn't like titles bigger than 1k + + // send the correct password based on dj name, etc + std::string t, u, p; + u = userData->Config->UserID; + p = userData->Config->Password; + if (!u.empty()) t = u + ":" + p; + else t = p; + + snprintf(buffer, sizeof(buffer), "GET /admin.cgi?pass=%s&mode=updinfo&song=%s&dj=%s HTTP/1.0\n", (char *)t.data(), fixedtitle, userData->Config->UserID); + userData->Output->SendString(buffer); + + stringstream s; + s << "User-Agent: SHOUTcast Source DSP v" << sourceVersion << " Title Update (Mozilla)\n\n"; + userData->Output->SendString((char*)s.str().data()); + + userData->SlowClose = 1; + userData->Info.meta_cached = 1; + }UNLOCK + return OUT_DISCONNECT; +} + +/* SHOUTcast 2 in-stream metada */ +int SHOUTCAST_OUTPUT::Output_Send_Metadata(int state, int last_state, T_OUTPUT *userData) { + DEBUG_STATE + STATE; + LOCK{ + if (userData->TitleCallback) { + if (userData->Connection >= 0 && userData->Connection < 5) { + userData->TitleCallback(userData->Connection, 0); + } + } + }UNLOCK + return OUT_SENDCONTENT; +} + +/* SHOUTcast 2 in-stream metada */ +int SHOUTCAST_OUTPUT::Output_Send_Artwork(int state, int last_state, T_OUTPUT *userData) { + DEBUG_STATE + STATE; + LOCK{ + int artType = (userData->Info.meta_cached & 4 ? 1 : (userData->Info.meta_cached & 2 ? 0 : -1)); + if (userData->TitleCallback) { + if (userData->Connection >= 0 && userData->Connection < 5) { + if (artType != -1) { + userData->TitleCallback(userData->Connection, 1); + // decrement counts to move to next part of image packets if needed + if (userData->Info.meta_cached & (artType ? 4 : 2)) { + userData->Info.art_cached[artType]--; + if (userData->Info.art_cached[artType] < 0) userData->Info.art_cached[artType] = 0; + } + } + } + } else { + if (artType != -1) { + userData->Info.art_cached[artType] = 0; + } + } + }UNLOCK + return OUT_SENDCONTENT; +} + +int SHOUTCAST_OUTPUT::Output_Request_Cipher(int state, int last_state, T_OUTPUT *userData) { + DEBUG_STATE + STATE; + LOCK{ + if (LOBYTE(userData->Config->protocol) != 1) { + userData->Output->FlushSend(); + createUvoxFrame(3, "2.1\0", MSG_CIPHER, userData); + UNLOCK + return OUT_RECV_CIPHER; + } else { + // shoutcast 1 + UNLOCK + return OUT_SENDAUTH; + } + }UNLOCK + return state; +} + +int SHOUTCAST_OUTPUT::Output_Receive_Cipher(int state, int last_state, T_OUTPUT *userData) { + DEBUG_STATE + STATE; + LOCK{ + if (userData->Output->GetReceiveBytesAvailable() > 0) { + memset(buf, '\0', sizeof(buf)); + userData->Output->ReceiveLine(buf, sizeof(buf)); + parseUvoxFrame(buf, out); + if (checkUvoxFrameForError(out, state, userData) == -1) { + UNLOCK + return OUT_DISCONNECT; + } else { + // copy the cipher key now we have it. + strcpy_s(userData->Config->cipherkey, sizeof(userData->Config->cipherkey)-1, out+4); + UNLOCK + userData->Config->protocol_retry = 0; + return OUT_SENDAUTH; + } + } + + userData->Config->protocol_retry = MAKEWORD(LOBYTE(userData->Config->protocol_retry)+1, HIBYTE(userData->Config->protocol_retry)); + + // this handles automatic mode being enabled + if (!HIBYTE(userData->Config->protocol) && + (LOBYTE(userData->Config->protocol_retry) > 5)) { + userData->Output->Close(!userData->SlowClose); + UNLOCK + return OUT_SENDAUTH; + } + + }UNLOCK + + return (LOBYTE(userData->Config->protocol_retry) > 5 ? OUT_DISCONNECT : state); +} + +int SHOUTCAST_OUTPUT::Output_SendAuth(int state, int last_state, T_OUTPUT *userData) { + DEBUG_STATE + STATE; + LOCK{ + userData->Output->FlushSend(); + userData->Info.Succeeded = 1; + std::string s, + u = userData->Config->UserID, + p = userData->Config->Password; + + if (LOBYTE(userData->Config->protocol) != 1) { + uvAuth21 * auth = new uvAuth21(); + std::string k; + k = userData->Config->cipherkey; + s = "2.1:"; + s += (!userData->Config->StationID[0] ? "1" : userData->Config->StationID); + s += ":"; + s += auth->encrypt(u, p, k); + createUvoxFrame(s.length(), (char *)s.data(), MSG_AUTH, userData); + delete auth; + } else { + if (!u.empty()) s = u + ":" + p; + else s = p; + userData->Output->SendString((char *)s.data()); + userData->Output->SendString("\n"); + } + UNLOCK + return OUT_RECVAUTHRESPONSE; + }UNLOCK + return state; +} + +int SHOUTCAST_OUTPUT::Output_RecvAuthResponse(int state, int last_state, T_OUTPUT *userData) { + DEBUG_STATE + STATE; + LOCK{ + if (LOBYTE(userData->Config->protocol) != 1) { + if (userData->Output->GetReceiveBytesAvailable() > 0) { + memset(buf, '\0', sizeof(buf)); + userData->Info.Succeeded = 1; + userData->Output->recv_bytes(buf, sizeof(buf)); + parseUvoxFrame(buf, out); + int err = checkUvoxFrameForError(out, state, userData); + if (err == -1) { + UNLOCK + return OUT_DISCONNECT; + } else { + // test for a cipher failure + if (strncmp(buf, "ACK:", 4) == 0) { + userData->Info.Succeeded = -1; + userData->Info.ErrorMsg = "CipherFail"; + UNLOCK + return OUT_FAIL_CIPHER; + } + UNLOCK + return OUT_SEND_MIME; + } + } + } else { + // auth shoutcast 1 + if (userData->Output->GetReceiveBytesAvailable() > 0) { + userData->Output->ReceiveLine(buf, sizeof(buf)); + char *ok = strstr(buf, "OK"); + if (ok) { // we got OK response... shoutcast v1 + userData->Info.Succeeded = 1; + UNLOCK + return OUT_SENDYP; // send the sc v1 YP stuff, now + } else { + userData->Info.Succeeded = 0; + if (strcmpi(buf, "invalid password") == 0) { + userData->Info.Succeeded = -1; + userData->Info.ErrorMsg = "NAK:Deny"; + } else if (strcmpi(buf, "stream moved") == 0) { + userData->Info.Succeeded = -1; + userData->Info.ErrorMsg = "StreamMoved"; + } + UNLOCK + return OUT_DISCONNECT; + } + } + } + }UNLOCK + return state; +} + +int SHOUTCAST_OUTPUT::Output_Send_Mime(int state, int last_state, T_OUTPUT *userData) { + DEBUG_STATE + STATE; + LOCK{ + std::string s = userData->ContentType; + userData->Output->FlushSend(); + createUvoxFrame(s.length(), (char *)s.data(), MSG_MIME_TYPE, userData); + }UNLOCK + return OUT_RECV_MIME; +} + +int SHOUTCAST_OUTPUT::Output_Recv_Mime(int state, int last_state, T_OUTPUT *userData) { + DEBUG_STATE + STATE; + LOCK{ + if (userData->Output->GetReceiveBytesAvailable() > 0) { + memset(buf, '\0', sizeof(buf)); + userData->Output->recv_bytes(buf, sizeof(buf)); + parseUvoxFrame(buf, out); + UNLOCK + return OUT_SEND_BITRATE; + } + }UNLOCK + return state; +} + +int SHOUTCAST_OUTPUT::Output_Send_Bitrate(int state, int last_state, T_OUTPUT *userData) { + DEBUG_STATE + STATE; + LOCK{ + char bitrate[128] = {0}; + std::string s; + snprintf(bitrate, sizeof(bitrate), "%d:%d", userData->Bitrate*1000, userData->Bitrate*1000); + s = bitrate; + userData->Output->FlushSend(); + createUvoxFrame(s.length(), (char *)s.data(), MSG_BROADCAST_SETUP, userData); + }UNLOCK + return OUT_RECV_BITRATE; +} + +int SHOUTCAST_OUTPUT::Output_Recv_Bitrate(int state, int last_state, T_OUTPUT *userData) { + DEBUG_STATE + STATE; + LOCK{ + if (userData->Output->GetReceiveBytesAvailable() > 0) { + memset(buf, '\0', sizeof(buf)); + userData->Output->recv_bytes(buf, sizeof(buf)); + parseUvoxFrame(buf, out); + int err = checkUvoxFrameForError(out, state, userData); + if (err == -1) { + UNLOCK + return OUT_DISCONNECT; + } else { + UNLOCK + return OUT_SEND_BUFSIZE; + } + } + }UNLOCK + return state; +} + +int SHOUTCAST_OUTPUT::Output_Send_Buf_Size(int state, int last_state, T_OUTPUT *userData) { + DEBUG_STATE + STATE; + LOCK{ + char bsize[32] = {0}; + int toSend = (userData->Bitrate * 20)/8; + snprintf(bsize, sizeof(bsize), "%d:0", toSend); + std::string s = bsize; + userData->Output->FlushSend(); + createUvoxFrame(s.length(), (char *)s.data(), MSG_NEGOTIATE_BUFFER_SIZE, userData); + }UNLOCK + return OUT_RECV_BUFSIZE; +} + +int SHOUTCAST_OUTPUT::Output_Recv_Buf_Size(int state, int last_state, T_OUTPUT *userData) { + DEBUG_STATE + STATE; + LOCK{ + if (userData->Output->GetReceiveBytesAvailable() > 0) { + memset(buf, '\0', sizeof(buf)); + userData->Output->recv_bytes(buf, sizeof(buf)); + UNLOCK + return OUT_SEND_MAX; + } + }UNLOCK + return state; +} + +int SHOUTCAST_OUTPUT::Output_Send_Max_Size(int state, int last_state, T_OUTPUT *userData) { + DEBUG_STATE + STATE; + LOCK{ + userData->Output->FlushSend(); + createUvoxFrame(7, "16377:0\0", MSG_MAX_PAYLOAD_SIZE, userData); + }UNLOCK + return OUT_RECV_MAX; +} + +int SHOUTCAST_OUTPUT::Output_Recv_Max_Size(int state, int last_state, T_OUTPUT *userData) { + DEBUG_STATE + STATE; + LOCK{ + if (userData->Output->GetReceiveBytesAvailable() > 0) { + memset(buf, '\0', sizeof(buf)); + userData->Output->recv_bytes(buf, sizeof(buf)); + parseUvoxFrame(buf, out); + UNLOCK + return OUT_SENDYP; + } + }UNLOCK + return state; +} + +int SHOUTCAST_OUTPUT::Output_DUMMY(int state, int last_state, T_OUTPUT *userData) { + DEBUG_STATE + STATE; + LOCK{ + if (userData->Output->GetReceiveBytesAvailable() > 0) { + memset(buf, '\0', sizeof(buf)); + userData->Output->recv_bytes(buf, sizeof(buf)); + parseUvoxFrame(buf, out); + } + }UNLOCK + return state; +} + +int SHOUTCAST_OUTPUT::Output_SendYP(int state, int last_state, T_OUTPUT *userData) { + DEBUG_STATE + STATE; + LOCK{ + if (LOBYTE(userData->Config->protocol) != 1) { + //icyname + std::string s; + s = userData->Config->Description; + createUvoxFrame(s.length(), (char *)s.data(), MSG_ICYNAME, userData); + Output_DUMMY(state, last_state, userData); + + //icygenre + s = userData->Config->Genre; + createUvoxFrame(s.length(), (char *)s.data(), MSG_ICYGENRE, userData); + Output_DUMMY(state, last_state, userData); + + //icyurl + s = userData->Config->ServerURL; + createUvoxFrame(s.length(), (char *)s.data(), MSG_ICYURL, userData); + Output_DUMMY(state, last_state, userData); + + //icypub + char pub[2] = {0}; + snprintf(pub, sizeof(pub), "%d", !!userData->Config->Public); + createUvoxFrame(1, pub, MSG_ICYPUB, userData); + Output_DUMMY(state, last_state, userData); + UNLOCK + return OUT_SEND_INITFLUSH; + } else { + char ypbuf[4096] = {0}; + char ypbuf2[4096] = {0}; + // send Description + snprintf(ypbuf, sizeof(ypbuf), "icy-name:%s\n", userData->Config->Description); + userData->Output->SendString(ypbuf); + // send Genre + ypbuf2[0] = 0; + if (strlen(userData->Config->Genre) > 0) { + snprintf((char *)&ypbuf2+strlen(ypbuf2), sizeof(ypbuf2), "%s", userData->Config->Genre); + } + snprintf(ypbuf, sizeof(ypbuf), "icy-genre:%s\n", ypbuf2); + userData->Output->SendString(ypbuf); + // send URL + snprintf(ypbuf, sizeof(ypbuf), "icy-url:%s\n", userData->Config->ServerURL); + userData->Output->SendString(ypbuf); + // send IRC + snprintf(ypbuf, sizeof(ypbuf), "icy-irc:%s\n", userData->Config->IRC); + userData->Output->SendString(ypbuf); + // send ICQ + snprintf(ypbuf, sizeof(ypbuf), "icy-icq:%s\n", userData->Config->ICQ); + userData->Output->SendString(ypbuf); + // send AIM + snprintf(ypbuf, sizeof(ypbuf), "icy-aim:%s\n", userData->Config->AIM); + userData->Output->SendString(ypbuf); + // send publicity + snprintf(ypbuf, sizeof(ypbuf), "icy-pub:%u\n", userData->Config->Public ? 1 : 0); + userData->Output->SendString(ypbuf); + // send bitrate (this is a huge bad big ugly hack... needs to be fixed, but this works so far) + snprintf(ypbuf, sizeof(ypbuf), "icy-br:%d\n", userData->Bitrate); + userData->Output->SendString(ypbuf); + // send content type (shouldn't be here as with the bitrate, but it works) + snprintf(ypbuf, sizeof(ypbuf), "content-type:%s\n", userData->ContentType); + userData->Output->SendString(ypbuf); + // end our list of configurations + userData->Output->SendString("\n"); + UNLOCK + return OUT_SENDCONTENT; + } + }UNLOCK + return state; +} + +int SHOUTCAST_OUTPUT::Output_Send_InitFlush(int state, int last_state, T_OUTPUT *userData) { + DEBUG_STATE + STATE; + LOCK{ + userData->Output->FlushSend(); + createUvoxFrame(1, "0\0", MSG_FLUSH_CACHED_METADATA, userData); + }UNLOCK + return OUT_RECV_INITFLUSH; +} + +int SHOUTCAST_OUTPUT::Output_Recv_InitFlush(int state, int last_state, T_OUTPUT *userData) { + DEBUG_STATE + STATE; + LOCK{ + Output_DUMMY(state, last_state, userData); + }UNLOCK + return OUT_SEND_INITSTANDBY; +} + +int SHOUTCAST_OUTPUT::Output_Send_InitStandby(int state, int last_state, T_OUTPUT *userData) { + DEBUG_STATE + STATE; + LOCK{ + userData->Output->FlushSend(); + createUvoxFrame(1, "0\0", MSG_STANDBY, userData); + }UNLOCK + return OUT_RECV_INITSTANDBY;//OUT_SENDCONTENT; +} + +int SHOUTCAST_OUTPUT::Output_Recv_InitStandby(int state, int last_state, T_OUTPUT *userData) { + DEBUG_STATE + STATE; + LOCK{ + if (userData->Output->GetReceiveBytesAvailable() > 0) { + memset(buf, '\0', sizeof(buf)); + userData->Output->ReceiveLine(buf, sizeof(buf)); + parseUvoxFrame(buf, out); + if (checkUvoxFrameForError(out, state, userData) == -1) { + UNLOCK + return OUT_DISCONNECT; + } else { + UNLOCK + return OUT_SENDCONTENT;//OUT_SEND_INTRO; + } + } + }UNLOCK + return OUT_SENDCONTENT;//OUT_SEND_INTRO; +} + +/*int SHOUTCAST_OUTPUT::Output_Send_Intro(int state, T_OUTPUT *userData) { + LOCK{ + }UNLOCK + return OUT_RECV_INTRO; +} + +int SHOUTCAST_OUTPUT::Output_Recv_Intro(int state, T_OUTPUT *userData) { + LOCK{ + }UNLOCK + return OUT_SEND_BACKUP; +} + +int SHOUTCAST_OUTPUT::Output_Send_Backup(int state, T_OUTPUT *userData) { + LOCK{ + }UNLOCK + return OUT_RECV_BACKUP; +} + +int SHOUTCAST_OUTPUT::Output_Recv_Backup(int state, T_OUTPUT *userData) { + LOCK{ + }UNLOCK + return OUT_SENDCONTENT; +}*/ \ No newline at end of file diff --git a/Src/Plugins/DSP/dsp_sc/sc2srclib/uvAuth21/uvAuth21.cpp b/Src/Plugins/DSP/dsp_sc/sc2srclib/uvAuth21/uvAuth21.cpp new file mode 100644 index 00000000..763507e9 --- /dev/null +++ b/Src/Plugins/DSP/dsp_sc/sc2srclib/uvAuth21/uvAuth21.cpp @@ -0,0 +1,90 @@ +#include "uvAuth21.h" + +uvAuth21::uvAuth21(void) { +} + +uvAuth21::~uvAuth21(void) { +} + +string uvAuth21::encrypt(string user,string pass,string key) { + string blob; + uint8_t* username = (uint8_t*)user.data(); + uint8_t* password = (uint8_t*)pass.data(); + uint8_t* cipherkey = (uint8_t*)key.data(); + blob = XTEA_encipher(username,user.length(),cipherkey,key.length()); + blob += ':'; + blob += XTEA_encipher(password,pass.length(),cipherkey,key.length()); + return blob; +} + +const char * uvAuth21::encrypt(char * user,char * pass,char * key) { + static string blob; + uint8_t* username = (uint8_t*)user; + uint8_t* password = (uint8_t*)pass; + uint8_t* cipherkey = (uint8_t*)key; + blob = XTEA_encipher(username,sizeof(user),cipherkey,sizeof(key)); + blob += ':'; + blob += XTEA_encipher(password,sizeof(pass),cipherkey,sizeof(key)); + return blob.c_str(); +} + +// from wikipedia. Slightly modified to be 32/64 bit clean +void uvAuth21::XTEA_encipher(__uint32* v, __uint32* k) { + unsigned int num_rounds = 32; + __uint32 v0=v[0], v1=v[1], i; + __uint32 sum=0, delta=0x9E3779B9; + for(i=0; i> 5)) + v1) ^ (sum + k[sum & 3]); + sum += delta; + v1 += (((v0 << 4) ^ (v0 >> 5)) + v0) ^ (sum + k[(sum>>11) & 3]); + } + v[0]=v0; v[1]=v1; +} + +__uint32 uvAuth21::fourCharsToLong(__uint8 *s) { + __uint32 l = 0; + l |= s[0]; l <<= 8; + l |= s[1]; l <<= 8; + l |= s[2]; l <<= 8; + l |= s[3]; + return l; +} + + std::vector &uvAuth21::split(const std::string &s, char delim, std::vector &elems) { + std::stringstream ss(s); + std::string item; + while(std::getline(ss, item, delim)) { + elems.push_back(item); + } + return elems; +} + +std::vector uvAuth21::dosplit(const std::string &s, char delim) { + std::vector elems; + return split(s, delim, elems); +} + +std::string uvAuth21::XTEA_encipher(const __uint8* c_data,size_t c_data_cnt, const __uint8* c_key,size_t c_key_cnt) { + std::ostringstream oss; + std::vector<__uint8> key(c_key,c_key + c_key_cnt); + std::vector<__uint8> data(c_data,c_data + c_data_cnt); + + // key is always 128 bits + while(key.size() < 16) key.push_back(XTEA_KEY_PAD); // pad key with zero + __uint32 k[4] = { fourCharsToLong(&key[0]),fourCharsToLong(&key[4]),fourCharsToLong(&key[8]),fourCharsToLong(&key[12])}; + + // data is multiple of 64 bits + size_t siz = data.size(); + while(siz % 8) { siz+=1; data.push_back(XTEA_DATA_PAD);} // pad data with zero + + for(size_t x = 0; x < siz; x+=8) { + __uint32 v[2]; + v[0] = fourCharsToLong(&data[x]); + v[1] = fourCharsToLong(&data[x+4]); + XTEA_encipher(v,k); + oss << setw(8) << setfill('0') << hex << v[0]; + oss << setw(8) << setfill('0') << hex << v[1]; // hex values. uvox uses colon as seperator so we can't use chars for + // fear of collision + } + return oss.str(); +} \ No newline at end of file diff --git a/Src/Plugins/DSP/dsp_sc/sc2srclib/uvAuth21/uvAuth21.h b/Src/Plugins/DSP/dsp_sc/sc2srclib/uvAuth21/uvAuth21.h new file mode 100644 index 00000000..971ce02a --- /dev/null +++ b/Src/Plugins/DSP/dsp_sc/sc2srclib/uvAuth21/uvAuth21.h @@ -0,0 +1,34 @@ +#pragma once + +#include +#include "../replicant\foundation\win-x86\types.h" +#include +#include +#include +#include +#include +#include +#include + +#define XTEA_KEY_PAD 0 +#define XTEA_DATA_PAD 0 +#define __uint32 uint32_t +#define __uint8 uint8_t + +using namespace std; + +class uvAuth21 { +public: + uvAuth21(void); + string encrypt(string user, string password, string key); + const char * encrypt(char * user,char * password,char * key); + ~uvAuth21(void); +private: + +protected: + static void XTEA_encipher(__uint32* v, __uint32* k); + static __uint32 fourCharsToLong(__uint8 *s); + static string XTEA_encipher(const __uint8* c_data, size_t c_data_cnt, const __uint8* c_key, size_t c_key_cnt); + static std::vector dosplit(const std::string &s, char delim); + static std::vector &split(const std::string &s, char delim, std::vector &elems); +}; \ No newline at end of file diff --git a/Src/Plugins/DSP/dsp_sc/utils.cpp b/Src/Plugins/DSP/dsp_sc/utils.cpp new file mode 100644 index 00000000..809234df --- /dev/null +++ b/Src/Plugins/DSP/dsp_sc/utils.cpp @@ -0,0 +1,653 @@ +#include +#include +#include "utils.h" +#include "api.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "resource/resource.h" + +extern winampDSPModule module; + +// Config file +char IniName[MAX_PATH] = {0}, + IniEncName[MAX_PATH] = {0}, + IniDir[MAX_PATH] = {0}, + PluginDir[MAX_PATH] = {0}; +wchar_t IniDirW[MAX_PATH] = {0}, + PluginDirW[MAX_PATH] = {0}, + SharedDirW[MAX_PATH] = {0}; +HANDLE NextTracks[NUM_OUTPUTS] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE}; +HANDLE SaveEncoded[NUM_OUTPUTS] = {INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE}; + +static bool IsVista = false, + checked = false; + +bool IsVistaUp() { + if (checked == false) { + OSVERSIONINFO version = {0}; + version.dwOSVersionInfoSize = sizeof(version); + if (!GetVersionEx(&version)) ZeroMemory(&version, sizeof(OSVERSIONINFO)); + IsVista = (version.dwMajorVersion >= 6); + checked = true; + } + return IsVista; +} + +UINT ver = -1; +UINT GetWinampVersion(HWND winamp) +{ + if(ver == -1) + { + return (ver = SendMessage(winamp, WM_WA_IPC, 0, IPC_GETVERSION)); + } + return ver; +} + +char* LocalisedStringA(UINT uID, char *str, size_t maxlen) { + if (WASABI_API_LNG) { + if (!str) { + return WASABI_API_LNGSTRING(uID); + } else { + return WASABI_API_LNGSTRING_BUF(uID, str, maxlen); + } + } else { + __declspec(thread) static char *tmp; + char* strtmp = 0; + if (!str) { + if (!tmp) tmp = (char *)malloc(1024*sizeof(char)); + strtmp = tmp; + maxlen = 1024; + } else { + strtmp = str; + } + LoadStringA(module.hDllInstance, uID, strtmp, maxlen); + return strtmp; + } +} + +wchar_t* LocalisedString(UINT uID, wchar_t *str, size_t maxlen) { + if (WASABI_API_LNG) { + if (!str) { + return WASABI_API_LNGSTRINGW(uID); + } else { + return WASABI_API_LNGSTRINGW_BUF(uID, str, maxlen); + } + } else { + __declspec(thread) static wchar_t *tmp; + wchar_t* strtmp = 0; + if (!str) { + if (!tmp) tmp = (wchar_t *)malloc(1024*sizeof(wchar_t)); + strtmp = tmp; + maxlen = 1024; + } else { + strtmp = str; + } + LoadStringW(module.hDllInstance, uID, strtmp, maxlen); + return strtmp; + } +} + +HWND LocalisedCreateDialog(HINSTANCE instance, UINT dialog_id, HWND hWndParent, DLGPROC DlgProc, LPARAM user_id) { + if (WASABI_API_LNG) { + return WASABI_API_CREATEDIALOGPARAMW(dialog_id, hWndParent, DlgProc, user_id); + } else { + return CreateDialogParamW(instance, MAKEINTRESOURCEW(dialog_id), hWndParent, DlgProc, user_id); + } +} + +INT_PTR LocalisedDialogBox(HINSTANCE hDllInstance, UINT dialog_id, HWND hWndParent, DLGPROC lpDialogFunc) { + if (WASABI_API_LNG) { + return WASABI_API_DIALOGBOXW(dialog_id, hWndParent, lpDialogFunc); + } else { + return DialogBoxW(hDllInstance, MAKEINTRESOURCEW(dialog_id), hWndParent, lpDialogFunc); + } +} + +// about the most reliable way i can find to get the Winamp window as it could +// have been started with the /CLASS= parameter which then means it won't be +// 'Winamp v1.x' so instead go for a fixed child window which will always be +// there (and deals with other apps who create a 'fake' Winamp window (like AIMP) +// and there are two versions to cope with classic or modern skins being used. +HWND hwndWinamp = 0; +BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam) { + char name[24]; + GetClassName(hwnd,name,24); + // this check will only work for classic skins + if (!strnicmp(name, "Winamp PE", 24)) { + HWND child = GetWindow(GetWindow(hwnd, GW_CHILD), GW_CHILD); + GetClassName(child, name, 24); + // this check improves reliability of this check against players + // like KMPlayer which also create a fake playlist editor window + if (!strnicmp(name, "WinampVis", 24) || strnicmp(name, "TSkinPanel", 24)) { + hwndWinamp = GetWindow(hwnd, GW_OWNER); + return FALSE; + } + } else if (!strnicmp(name, "BaseWindow_RootWnd", 24)) { + // this check will only work for modern skins + HWND child = GetWindow(GetWindow(hwnd, GW_CHILD), GW_CHILD); + GetClassName(child, name, 24); + if (!strnicmp(name, "Winamp PE", 24)) { + hwndWinamp = GetWindow(hwnd, GW_OWNER); + return FALSE; + } + } else if (!strnicmp(name, "Winamp v1.x", 24)) { + // this check will fail if /CLASS= was used on Winamp + HWND child = GetWindow(hwnd, GW_CHILD); + GetClassName(child, name, 24); + if (!strnicmp(name, "WinampVis", 24)) { + hwndWinamp = hwnd; + return FALSE; + } + } + return TRUE; +} + +HWND GetWinampHWND(HWND winamp) { + // if no HWND is passed then attemp to find it + if (!IsWindow(winamp)) { + // but only do the enumeration again if we have an invalid HWND cached + if (!IsWindow(hwndWinamp)) { + hwndWinamp = 0; + EnumThreadWindows(GetCurrentThreadId(), EnumWindowsProc, 0); + } + return hwndWinamp; + } else { + return (hwndWinamp = winamp); + } +} + +HINSTANCE GetMyInstance() { + MEMORY_BASIC_INFORMATION mbi = {0}; + if (VirtualQuery(GetMyInstance, &mbi, sizeof(mbi))) { + return (HINSTANCE)mbi.AllocationBase; + } + return NULL; +} + +char* GetIniDirectory(HWND winamp) { + if (!IniDir[0]) { + // this gets the string of the full ini file path + strncpy(IniDir, (char*)SendMessage(winamp, WM_WA_IPC, 0, IPC_GETINIDIRECTORY), MAX_PATH); + } + return IniDir; +} + +wchar_t* GetIniDirectoryW(HWND winamp) { + if (!IniDirW[0]) { + // this gets the string of the full ini file path + wcsncpy(IniDirW, (wchar_t*)SendMessage(winamp, WM_WA_IPC, 0, IPC_GETINIDIRECTORYW), MAX_PATH); + } + return IniDirW; +} + +char* GetPluginDirectory(HWND winamp) { + // this gets the string of the full plug-in folder path + strncpy(PluginDir, (char*)SendMessage(winamp, WM_WA_IPC, 0, IPC_GETPLUGINDIRECTORY), MAX_PATH); + return PluginDir; +} + +wchar_t* GetPluginDirectoryW(HWND winamp) { + // this gets the string of the full plug-in folder path + wcsncpy(PluginDirW, (wchar_t*)SendMessage(winamp, WM_WA_IPC, 0, IPC_GETPLUGINDIRECTORYW), MAX_PATH); + return PluginDirW; +} + +wchar_t* GetSharedDirectoryW(HWND winamp) { + // this gets the string of the full shared dll folder path + wchar_t* str = (wchar_t*)SendMessage(winamp, WM_WA_IPC, 0, IPC_GETSHAREDDLLDIRECTORYW); + if (str > (wchar_t*)65536) { + wcsncpy(SharedDirW, str, MAX_PATH); + } else { + // and on older versions of Winamp we revert to the plug-ins folder path + wcsncpy(SharedDirW, GetPluginDirectoryW(winamp), MAX_PATH); + } + return SharedDirW; +} + +void GetDefaultNextTracksLogFile(HWND winamp, int bufferLen, wchar_t* buffer, int index) { + snwprintf(buffer, bufferLen, L"%s\\Plugins\\dsp_sc_nexttracks_%d.log", GetIniDirectoryW(winamp), index+1); +} + +char* GetSCIniFile(HWND winamp) { + if (!IniName[0]) { + + // allows support for multiple instances of the dsp_sc.dll + // without the settings being saved into the same section + char dll_name[MAX_PATH] = {"dsp_sc"}; + if (GetModuleFileName(module.hDllInstance, dll_name, MAX_PATH)) { + PathStripPath(dll_name); + PathRemoveExtension(dll_name); + } + snprintf(IniName, MAX_PATH, "%s\\Plugins\\%s.ini", GetIniDirectory(winamp), dll_name); + } + return IniName; +} + +wchar_t* GetSCLogFile(HWND winamp, int bufferLen, wchar_t* logFile, int index) { + snwprintf(logFile, bufferLen, L"%s\\Plugins\\dsp_sc_%d.log", GetIniDirectoryW(winamp), index + 1); + return logFile; +} + +char* CreateLogFileMessage(char* buffer, wchar_t* message, int* len) { + SYSTEMTIME sysTime; + GetLocalTime(&sysTime); + char d[100], t[100], msg[1024]; + GetDateFormat(LOCALE_SYSTEM_DEFAULT, 0, &sysTime, "yyyy'-'MM'-'dd", d, 99); + GetTimeFormat(LOCALE_SYSTEM_DEFAULT, 0, &sysTime, "HH':'mm':'ss", t, 99); + + std::string utf8 = ConvertToUTF8(message); + char* m = (char*)utf8.c_str(); + char* n = msg; + while (m && *m) { + if (m && *m && *m == '\n') { + *n = ' '; + } else if (m) { + if (n) *n = *m; + } + m = CharNext(m); + n = CharNext(n); + } + *n = 0; + + *len = snprintf(buffer, 1024, "%s %s\t%s\r\n", d, t, msg); + return buffer; +} + +void StartNextTracks(int index, wchar_t* file) { + NextTracks[index] = CreateFileW(file, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_ALWAYS, 0, 0); + if (NextTracks[index] != INVALID_HANDLE_VALUE) { + // reset the file on loading things + SetFilePointer(NextTracks[index], 0, NULL, FILE_BEGIN); + SetEndOfFile(NextTracks[index]); + } +} + +void WriteNextTracks(int index, HWND winamp, std::vector nextListIdx, std::vector nextList, bool xml) { + if (NextTracks[index] != INVALID_HANDLE_VALUE) { + DWORD written; + + // reset the file so if there are no tracks then that'll be set + SetFilePointer(NextTracks[index], 0, NULL, FILE_BEGIN); + SetEndOfFile(NextTracks[index]); + + std::stringstream s; + if (xml) { + s << "\n\n"; + } + + if (!nextList.empty()) { + std::vector::const_iterator i = nextList.begin(); + std::vector::const_iterator idx = nextListIdx.begin(); + for (int count = 1; i != nextList.end(); ++i, ++idx, count++) { + wchar_t *file=(wchar_t*)SendMessage(module.hwndParent, WM_WA_IPC, (*idx), IPC_GETPLAYLISTFILEW); + if (xml) { + std::string filepath = ConvertToUTF8Escaped(file); + s << "\t" << filepath << "\n\t"; + std::string next = ConvertToUTF8Escaped((*i).c_str()); + s << "" << next << "\n"; + } else { + std::string rawfilepath = ConvertToUTF8(file); + WriteFile(NextTracks[index], rawfilepath.c_str(), rawfilepath.length(), &written, 0); + WriteFile(NextTracks[index], "\r\n", 2, &written, 0); + } + } + } + + if (xml) { + s << "\n"; + WriteFile(NextTracks[index], s.str().data(), s.str().length(), &written, 0); + } + } +} + +void StopNextTracks(int index) { + if (NextTracks[index] != INVALID_HANDLE_VALUE) { + CloseHandle(NextTracks[index]); + NextTracks[index] = INVALID_HANDLE_VALUE; + } +} + +void StartSaveEncoded(int index, wchar_t* file) { + SaveEncoded[index] = CreateFileW(file, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_ALWAYS, 0, 0); + if (SaveEncoded[index] != INVALID_HANDLE_VALUE) { + // reset the file on loading things + SetFilePointer(SaveEncoded[index], 0, NULL, FILE_BEGIN); + SetEndOfFile(SaveEncoded[index]); + } +} + +void WriteSaveEncoded(int index, LPCVOID buffer, int bufferLen) { + if (SaveEncoded[index] != INVALID_HANDLE_VALUE) { + DWORD written; + WriteFile(SaveEncoded[index], buffer, bufferLen, &written, 0); + } +} + +void StopSaveEncoded(int index) { + if (SaveEncoded[index] != INVALID_HANDLE_VALUE) { + CloseHandle(SaveEncoded[index]); + SaveEncoded[index] = INVALID_HANDLE_VALUE; + } +} + +void StartLogging(int index, int clearOnStart) { + wchar_t name[MAX_PATH]; + logFiles[index] = CreateFileW(GetSCLogFile(module.hwndParent, ARRAYSIZE(name), name, index), GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_ALWAYS, 0, 0); + if (logFiles[index] != INVALID_HANDLE_VALUE) { + // clear the file when started + if (clearOnStart) { + SetFilePointer(logFiles[index], 0, NULL, FILE_BEGIN); + SetEndOfFile(logFiles[index]); + } else { + SetFilePointer(logFiles[index], 0, NULL, FILE_END); + } + + int len = 0; + DWORD written; + char buf[1024]; + CreateLogFileMessage(buf, L"Logging starting", &len); + WriteFile(logFiles[index], buf, len, &written, 0); + } +} + +void StopLogging(int index) { + if (logFiles[index] != INVALID_HANDLE_VALUE) { + int len = 0; + DWORD written; + char buf[1024]; + CreateLogFileMessage(buf, L"Logging stopping\r\n", &len); + WriteFile(logFiles[index], buf, len, &written, 0); + CloseHandle(logFiles[index]); + } + logFiles[index] = INVALID_HANDLE_VALUE; +} + +BOOL IsDirectMouseWheelMessage(const UINT uMsg) { + static UINT WINAMP_WM_DIRECT_MOUSE_WHEEL = WM_NULL; + + if (WM_NULL == WINAMP_WM_DIRECT_MOUSE_WHEEL) { + WINAMP_WM_DIRECT_MOUSE_WHEEL = RegisterWindowMessageW(L"WINAMP_WM_DIRECT_MOUSE_WHEEL"); + if (WM_NULL == WINAMP_WM_DIRECT_MOUSE_WHEEL) + return FALSE; + } + + return (WINAMP_WM_DIRECT_MOUSE_WHEEL == uMsg); +} + +HWND ActiveChildWindowFromPoint(HWND hwnd, POINTS cursor_s, const int *controls, size_t controlsCount) { + POINT pt = {0}; + RECT controlRect = {0}; + POINTSTOPOINT(pt, cursor_s); + + while (controlsCount--) { + HWND controlWindow = GetDlgItem(hwnd, controls[controlsCount]); + if (NULL != controlWindow && + FALSE != GetClientRect(controlWindow, &controlRect)) { + MapWindowPoints(controlWindow, HWND_DESKTOP, (POINT*)&controlRect, 2); + if (FALSE != PtInRect(&controlRect, pt)) { + unsigned long windowStyle; + windowStyle = (unsigned long)GetWindowLongPtrW(controlWindow, GWL_STYLE); + if (WS_VISIBLE == ((WS_VISIBLE | WS_DISABLED) & windowStyle)) + return controlWindow; + break; + } + } + } + return NULL; +} + +BOOL DirectMouseWheel_ProcessDialogMessage(HWND hwnd, unsigned int uMsg, WPARAM wParam, LPARAM lParam) { + if (FALSE != IsDirectMouseWheelMessage(uMsg)) { + const int controls[] = { + IDC_MUSSLIDER, + IDC_MUS2SLIDER, + IDC_MICSLIDER, + IDC_FADESLIDER, + IDC_MICFADESLIDER, + }; + HWND targetWindow = ActiveChildWindowFromPoint(hwnd, MAKEPOINTS(lParam), controls, ARRAYSIZE(controls)); + if (NULL != targetWindow) { + SendMessage(targetWindow, WM_MOUSEWHEEL, wParam, lParam); + SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, (long)TRUE); + return TRUE; + } + } + return FALSE; +} + + +static HCURSOR link_hand_cursor; +LRESULT link_handlecursor(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { + LRESULT ret = CallWindowProcW((WNDPROC)GetPropW(hwndDlg, L"link_proc"), hwndDlg, uMsg, wParam, lParam); + // override the normal cursor behaviour so we have a hand to show it is a link + if (uMsg == WM_SETCURSOR) { + if ((HWND)wParam == hwndDlg) { + if (!link_hand_cursor) { + link_hand_cursor = LoadCursor(NULL, IDC_HAND); + } + SetCursor(link_hand_cursor); + return TRUE; + } + } + return ret; +} + +void link_startsubclass(HWND hwndDlg, UINT id) { + HWND ctrl = GetDlgItem(hwndDlg, id); + if (!GetPropW(ctrl, L"link_proc")) { + SetPropW(ctrl, L"link_proc", (HANDLE)SetWindowLongPtrW(ctrl, GWLP_WNDPROC, (LONG_PTR)link_handlecursor)); + } +} + +BOOL link_handledraw(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { + if (uMsg == WM_DRAWITEM) { + DRAWITEMSTRUCT *di = (DRAWITEMSTRUCT *)lParam; + if (di->CtlType == ODT_BUTTON) { + wchar_t wt[123]; + int y; + RECT r; + + // due to the fun of theming and owner drawing we have to get the background colour + if (isthemethere){ + HTHEME hTheme = OpenThemeData(hwndDlg, L"Tab"); + if (hTheme) { + DrawThemeParentBackground(di->hwndItem, di->hDC, &di->rcItem); + CloseThemeData(hTheme); + } + } + + HPEN hPen, hOldPen; + GetDlgItemTextW(hwndDlg, wParam, wt, ARRAYSIZE(wt)); + + // draw text + SetTextColor(di->hDC, (di->itemState & ODS_SELECTED) ? RGB(220, 0, 0) : RGB(0, 0, 220)); + r = di->rcItem; + r.left += 2; + DrawTextW(di->hDC, wt, -1, &r, DT_VCENTER | DT_SINGLELINE); + + memset(&r, 0, sizeof(r)); + DrawTextW(di->hDC, wt, -1, &r, DT_SINGLELINE | DT_CALCRECT); + + // draw underline + y = di->rcItem.bottom - ((di->rcItem.bottom - di->rcItem.top) - (r.bottom - r.top)) / 2 - 1; + hPen = CreatePen(PS_SOLID, 0, (di->itemState & ODS_SELECTED) ? RGB(220, 0, 0) : RGB(0, 0, 220)); + hOldPen = (HPEN) SelectObject(di->hDC, hPen); + MoveToEx(di->hDC, di->rcItem.left + 2, y, NULL); + LineTo(di->hDC, di->rcItem.right + 2 - ((di->rcItem.right - di->rcItem.left) - (r.right - r.left)), y); + SelectObject(di->hDC, hOldPen); + DeleteObject(hPen); + return TRUE; + } + } + return FALSE; +} + +#include +class xmlEscapes: public std::map +{ +public: + xmlEscapes() { + (*this)['<'] = "<"; + (*this)['>'] = ">"; + (*this)['&'] = "&"; + (*this)['\''] = "'"; + (*this)['"'] = """; + } +}; + +static const xmlEscapes gsXmlEscapes; + +// this will only be receiving an already converted +// string so no need to do the commented part again +char* escapeXML(const char* s) { + static char result[2048] = {0}; + memset(&result, 0, 2048); + int len = strlen(s); + for (int x = 0, y = 0; x < len; x++) + { + xmlEscapes::const_iterator i = gsXmlEscapes.find(s[x]); + if (i != gsXmlEscapes.end()) { + strcat(&result[y-1], (*i).second.c_str()); + y += (*i).second.size(); + } else if (s[x] >= 0 && s[x] <= 31 && s[x] != 9 && s[x] != 10 && s[x] != 13) { + // strip out characters which aren't supported by the DNAS + // (only allow backspace, linefeed and carriage return) + #ifdef DEBUG + result[y] = '\xEF'; + y++; + result[y] = '\xBF'; + y++; + result[y] = '\xBD'; + y++; + #endif + } else if ((x < len - 2) && s[x] == '\xEF' && s[x+1] == '\xBF' && s[x+2] == '\xBF') { + // and any UTF-8 boms which are in there (seen it happen!) + x+=2; + #ifdef DEBUG + result[y] = '\xEF'; + y++; + result[y] = '\xBF'; + y++; + result[y] = '\xBD'; + y++; + #endif + } else { + result[y] = s[x]; + y++; + } + } + return result; +} + +char* ConvertToUTF8Escaped(const wchar_t *str) { + static char utf8tmp[1024] = {0}; + memset(&utf8tmp, 0, sizeof(utf8tmp)); + WideCharToMultiByte(CP_UTF8, 0, str, -1, utf8tmp, sizeof(utf8tmp), 0, 0); + return escapeXML(utf8tmp); +} + +char* ConvertToUTF8(const wchar_t *str) { + static char utf8tmp2[1024] = {0}; + memset(&utf8tmp2, 0, sizeof(utf8tmp2)); + WideCharToMultiByte(CP_UTF8, 0, str, -1, utf8tmp2, sizeof(utf8tmp2), 0, 0); + return utf8tmp2; +} + +int ConvertFromUTF8(const char *src, wchar_t *dest, int destlen) { + if (destlen == 0) + return MultiByteToWideChar(CP_UTF8, 0, src, -1, dest, destlen); + int converted = MultiByteToWideChar(CP_UTF8, 0, src, -1, dest, destlen-1); + if (!converted) + return 0; + dest[converted]=0; + return converted+1; +} + +DWORD GetPrivateProfileStringUTF8(LPCSTR lpAppName, LPCSTR lpKeyName, LPCSTR lpDefault, LPWSTR lpReturnedString, DWORD nSize, LPCSTR lpFileName) { + char tmp[MAX_PATH] = {0}; + GetPrivateProfileString(lpAppName, lpKeyName, lpDefault, tmp, nSize, lpFileName); + return ConvertFromUTF8(tmp, lpReturnedString, nSize); +} + + +void ShowWindowDlgItem(HWND hDlg, int nIDDlgItem, int nCmdShow) { + ShowWindow(GetDlgItem(hDlg, nIDDlgItem), nCmdShow); +} + +void EnableWindowDlgItem(HWND hDlg, int nIDDlgItem, BOOL bEnable) { + EnableWindow(GetDlgItem(hDlg, nIDDlgItem), bEnable); +} + +template +std::vector tokenizer_if(const S &ins,F isdelimiter) throw() +{ + std::vector result; + S accum; + + for(typename S::const_iterator i = ins.begin(); i != ins.end(); ++i) + { + if (!isdelimiter(*i)) + { + accum.push_back(*i);// was += + } + else + { + if (!accum.empty()) + { + result.push_back(accum); + accum = S(); + } + } + } + + if (!accum.empty()) + result.push_back(accum); + return result; +} + +template +inline std::vector tokenizer(const S &ins,typename S::value_type delim) throw() + { return tokenizer_if(ins,bind1st(std::equal_to(),delim)); } + +extern char sourceVersion[64]; +bool CompareVersions(char *verStr) +{ + bool needsUpdating = false; + if (verStr && *verStr) + { + std::vector newVerStr = tokenizer(std::string(verStr), '.'); + std::vector curVerStr = tokenizer(std::string(sourceVersion), '.'); + int newVer[] = {::atoi(newVerStr[0].c_str()), ::atoi(newVerStr[1].c_str()), ::atoi(newVerStr[2].c_str()), ::atoi(newVerStr[3].c_str())}, + curVer[] = {::atoi(curVerStr[0].c_str()), ::atoi(curVerStr[1].c_str()), ::atoi(curVerStr[2].c_str()), ::atoi(curVerStr[3].c_str())}; + + // look to compare from major to minor parts of the version strings + // 2.x.x.x vs 3.x.x.x + if (newVer[0] > curVer[0]) { + needsUpdating = true; + } + // 2.0.x.x vs 2.2.x.x + else if((newVer[0] == curVer[0]) && (newVer[1] > curVer[1])) { + needsUpdating = true; + } + // 2.0.0.x vs 2.0.1.x + else if((newVer[0] == curVer[0]) && (newVer[1] == curVer[1]) && (newVer[2] > curVer[2])) { + needsUpdating = true; + } + // 2.0.0.29 vs 2.0.0.30 + else if((newVer[0] == curVer[0]) && (newVer[1] == curVer[1]) && (newVer[2] == curVer[2]) && (newVer[3] > curVer[3])) { + needsUpdating = true; + } + } + return needsUpdating; +} \ No newline at end of file diff --git a/Src/Plugins/DSP/dsp_sc/utils.h b/Src/Plugins/DSP/dsp_sc/utils.h new file mode 100644 index 00000000..3e816142 --- /dev/null +++ b/Src/Plugins/DSP/dsp_sc/utils.h @@ -0,0 +1,67 @@ +#ifndef __UTILS_H +#define __UTILS_H + +#include +#include + +#ifdef WIN32 +#define snprintf _snprintf +#define snwprintf _snwprintf +#endif + +extern char IniName[MAX_PATH], + IniEncName[MAX_PATH], + IniDir[MAX_PATH]; + +wchar_t* GetSharedDirectoryW(HWND winamp); +wchar_t* GetPluginDirectoryW(HWND winamp); +char* GetIniDirectory(HWND winamp); +char* GetSCIniFile(HWND winamp); +void GetDefaultNextTracksLogFile(HWND winamp, int bufferLen, wchar_t* buffer, int index); + +#define NUM_OUTPUTS 5 +extern HANDLE logFiles[NUM_OUTPUTS]; +wchar_t* GetSCLogFile(HWND winamp, int bufferLen, wchar_t* logFile, int index); +char* CreateLogFileMessage(char* buffer, wchar_t* message, int* len); +void StartLogging(int index, int clearOnStart); +void StopLogging(int index); + +void StartNextTracks(int index, wchar_t* file); +void WriteNextTracks(int index, HWND winamp, std::vector nextListIdx, std::vector nextList, bool xml); +void StopNextTracks(int index); + +void StartSaveEncoded(int index, wchar_t* file); +void WriteSaveEncoded(int index, LPCVOID buffer, int bufferLen); +void StopSaveEncoded(int index); + +INT_PTR LocalisedDialogBox(HINSTANCE hDllInstance, UINT dialog_id, HWND hWndParent, DLGPROC lpDialogFunc); +HWND LocalisedCreateDialog(HINSTANCE instance, UINT dialog_id, HWND hWndParent, DLGPROC DlgProc, LPARAM user_id); +char* LocalisedStringA(UINT uID, char *str, size_t maxlen); +wchar_t* LocalisedString(UINT uID, wchar_t *str, size_t maxlen); + +UINT GetWinampVersion(HWND winamp); + +bool IsVistaUp(); +HINSTANCE GetMyInstance(); +HWND GetWinampHWND(HWND winamp); + +extern int isthemethere; +BOOL link_handledraw(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); +void link_startsubclass(HWND hwndDlg, UINT id); +BOOL DirectMouseWheel_ProcessDialogMessage(HWND hwnd, unsigned int uMsg, WPARAM wParam, LPARAM lParam); + +char* escapeXML(const char* s); +char* ConvertToUTF8Escaped(const wchar_t *str); +char* ConvertToUTF8(const wchar_t *str); +int ConvertFromUTF8(const char *src, wchar_t *dest, int destlen); +// reads a unicode string stored in utf8 from an ini file +DWORD GetPrivateProfileStringUTF8(LPCSTR lpAppName, LPCSTR lpKeyName, LPCSTR lpDefault, LPWSTR lpReturnedString, DWORD nSize, LPCSTR lpFileName); + +void WritePrivateProfileInt(LPCSTR lpKeyName, int value, LPCSTR lpAppName, LPCSTR lpFileName); + +void ShowWindowDlgItem(HWND hDlg, int nIDDlgItem, int nCmdShow); +void EnableWindowDlgItem(HWND hDlg, int nIDDlgItem, BOOL bEnable); + +bool CompareVersions(char *verStr); + +#endif \ No newline at end of file diff --git a/Src/Plugins/DSP/dsp_sps/api__dsp_sps.h b/Src/Plugins/DSP/dsp_sps/api__dsp_sps.h new file mode 100644 index 00000000..3aeb4332 --- /dev/null +++ b/Src/Plugins/DSP/dsp_sps/api__dsp_sps.h @@ -0,0 +1,9 @@ +#ifndef NULLSOFT_API_H +#define NULLSOFT_API_H + +#include "api/service/api_service.h" +#include "api/service/waServiceFactory.h" + +#include "../Agave/Language/api_language.h" + +#endif \ No newline at end of file diff --git a/Src/Plugins/DSP/dsp_sps/constant.bin b/Src/Plugins/DSP/dsp_sps/constant.bin new file mode 100644 index 00000000..f0efef8b Binary files /dev/null and b/Src/Plugins/DSP/dsp_sps/constant.bin differ diff --git a/Src/Plugins/DSP/dsp_sps/dsp_sps.cpp b/Src/Plugins/DSP/dsp_sps/dsp_sps.cpp new file mode 100644 index 00000000..3c9323d8 --- /dev/null +++ b/Src/Plugins/DSP/dsp_sps/dsp_sps.cpp @@ -0,0 +1,294 @@ +#include +#include +#include "../winamp/dsp.h" +#include "resource.h" +#include "sps_common.h" +#include "../winamp/wa_ipc.h" +#include "../../General/gen_hotkeys/wa_hotkeys.h" +#define SPS_HOTKEY_ID "dsp_sps sc" +#include "sps_configdlg.h" + +//#define PLUGIN_NAME "Nullsoft Signal Processing Studio DSP v1.0b" +#define PLUGIN_VERSION "v1.0b" + + // config, winamp specific shit here: +struct +{ + int showeditor; //def 0 + int visible; //def 1 +} g_config; + +SPSEffectContext g_wacontext; +HWND g_configwindow; +HWND helpWnd; +int helpWndOpenHack = 0; +char *INI_FILE; +static int loaded_once = 0; +char g_path[MAX_PATH]; + +// wasabi based services for localisation support +api_service *WASABI_API_SVC = 0; +api_language *WASABI_API_LNG = 0; +HINSTANCE WASABI_API_LNG_HINST = 0, WASABI_API_ORIG_HINST = 0; + +// module getter. +winampDSPModule *getModule(int which); + +void config(struct winampDSPModule *this_mod); +int init(struct winampDSPModule *this_mod); +void quit(struct winampDSPModule *this_mod); +int modify_samples(struct winampDSPModule *this_mod, short int *samples, int numsamples, int bps, int nch, int srate); + +// Module header, includes version, description, and address of the module retriever function +typedef struct +{ + int version; // DSP_HDRVER + char *description; // description of library + winampDSPModule* (*getModule)(int); // module retrieval function + int (*sf)(int); +} winampDSPHeaderEx; + +static int sf(int v) +{ + int res; + res = v * (unsigned long)1103515245; + res += (unsigned long)13293; + res &= (unsigned long)0x7FFFFFFF; + res ^= v; + return res; +} + +winampDSPHeaderEx hdr = { DSP_HDRVER+1, 0, getModule, sf }; + +// first module +winampDSPModule mod = +{ + 0,//"Signal Processing Studio", + NULL, // hwndParent + NULL, // hDllInstance + config, + init, + modify_samples, + quit +}; + +extern "C" +{ + static HINSTANCE GetMyInstance() + { + MEMORY_BASIC_INFORMATION mbi = {0}; + + if(VirtualQuery(GetMyInstance, &mbi, sizeof(mbi))) + return (HINSTANCE)mbi.AllocationBase; + + return NULL; + } + + __declspec( dllexport ) winampDSPHeaderEx *winampDSPGetHeader2(HWND hwndParent) + { + if(IsWindow(hwndParent)) + { + if(!WASABI_API_LNG_HINST) + { + // loader so that we can get the localisation service api for use + WASABI_API_SVC = (api_service*)SendMessageW(hwndParent, WM_WA_IPC, 0, IPC_GET_API_SERVICE); + + if (WASABI_API_SVC == (api_service*)1) + WASABI_API_SVC = NULL; + + waServiceFactory *sf = WASABI_API_SVC->service_getServiceByGuid(languageApiGUID); + + if (sf) + WASABI_API_LNG = reinterpret_cast(sf->getInterface()); + + // need to have this initialised before we try to do anything with localisation features + WASABI_API_START_LANG(GetMyInstance(),DspSpsLangGUID); + } + + static char szDescription[256]; + if(!szDescription[0]) + { + char temp[256]; + wsprintfA(szDescription,"%s %s",WASABI_API_LNGSTRING_BUF(IDS_SPS_TITLE,temp,256), PLUGIN_VERSION ); + hdr.description = szDescription; + } + + static char szDescription2[256]; + if(!szDescription2[0]) + { + mod.description = WASABI_API_LNGSTRING_BUF(IDS_SPS_MODULE_TITLE,szDescription2,256); + } + } + return &hdr; + } +} + +// getmodule routine from the main header. Returns NULL if an invalid module was requested, +// otherwise returns either mod1 or mod2 depending on 'which'. +winampDSPModule *getModule(int which) +{ + switch (which) + { + case 0: return &mod; + default:return NULL; + } +} + +void config(struct winampDSPModule *this_mod) +{ + // show config + if (g_configwindow && IsWindow(g_configwindow)) + { + g_config.visible=1; + ShowWindow(g_configwindow,SW_SHOW); + } +} + +static void WriteInt(char *section, char *name,int value, char *fn) +{ + char str[128]; + wsprintf(str,"%d",value); + WritePrivateProfileString(section,name,str,fn); +} + +static char ghkStr[64]; +static genHotkeysAddStruct sps_ghas_showconfig = { + ghkStr, + 0, + WM_USER+0x80, + 0, + 0, + SPS_HOTKEY_ID, + 0, +}; +static int m_genhotkeys_add_ipc; + +int init(struct winampDSPModule *this_mod) +{ + wsprintf(g_path,"%s\\dsp_sps",(char*)SendMessageW(this_mod->hwndParent,WM_WA_IPC,0,IPC_GETPLUGINDIRECTORY)); + CreateDirectory(g_path,NULL); + + // loader so that we can get the localisation service api for use + WASABI_API_SVC = (api_service*)SendMessageW(this_mod->hwndParent, WM_WA_IPC, 0, IPC_GET_API_SERVICE); + if (WASABI_API_SVC == (api_service*)1) + WASABI_API_SVC = NULL; + + waServiceFactory *sf = WASABI_API_SVC->service_getServiceByGuid(languageApiGUID); + if (sf) + WASABI_API_LNG = reinterpret_cast(sf->getInterface()); + + // need to have this initialised before we try to do anything with localisation features + WASABI_API_START_LANG(this_mod->hDllInstance,DspSpsLangGUID); + + SPS_initapp(); + + SPS_initcontext(&g_wacontext); + + /* read config */ + INI_FILE = (char*)SendMessageW(this_mod->hwndParent,WM_WA_IPC,0,IPC_GETINIFILE); + g_config.showeditor=GetPrivateProfileInt("DSP_SPS","showeditor",0,INI_FILE); + g_config.visible=GetPrivateProfileInt("DSP_SPS","visible",1,INI_FILE); + + g_wacontext.bypass=GetPrivateProfileInt("DSP_SPS","bypass",1,INI_FILE); + SPS_load_preset(&g_wacontext,INI_FILE,"DSP_SPS"); + GetPrivateProfileString("DSP_SPS","last_preset","",g_wacontext.curpreset_name,sizeof(g_wacontext.curpreset_name),INI_FILE); + + g_configwindow=WASABI_API_CREATEDIALOGPARAM(IDD_DIALOG1,this_mod->hwndParent,SPS_configWindowProc,(LPARAM)&g_wacontext); + + // we are starting minimised so process as needed (keep our window hidden) + if(!loaded_once) + { + loaded_once = 1; + if (g_config.visible) + ShowWindow(g_configwindow,!GetPrivateProfileInt("Winamp","minimized",1,INI_FILE)?SW_SHOWNA:SW_SHOWMINNOACTIVE); + } + else{ + if (g_config.visible) + ShowWindow(g_configwindow,SW_SHOWNA); + } + + m_genhotkeys_add_ipc=SendMessageW(this_mod->hwndParent,WM_WA_IPC,(WPARAM)&"GenHotkeysAdd",IPC_REGISTER_WINAMP_IPCMESSAGE); + + WASABI_API_LNGSTRING_BUF(IDS_SPS_SHOW_CONFIG,ghkStr,64); + sps_ghas_showconfig.flags &= ~HKF_DISABLED; + sps_ghas_showconfig.wnd = g_configwindow; + + if (m_genhotkeys_add_ipc > 65536) + PostMessageW(this_mod->hwndParent,WM_WA_IPC,(WPARAM)&sps_ghas_showconfig,m_genhotkeys_add_ipc); //post so gen_hotkeys will catch it if not inited yet + + return 0; +} + +void quit(struct winampDSPModule *this_mod) +{ + if(IsWindow(helpWnd)) + { + DestroyWindow(helpWnd); + helpWndOpenHack = 1; + } + + helpWnd = 0; + + if(IsWindow(g_configwindow)) + { + DestroyWindow(g_configwindow); + } + + g_configwindow=0; + + /* write config */ + WriteInt("DSP_SPS","showeditor",g_config.showeditor,INI_FILE); + WriteInt("DSP_SPS","visible",g_config.visible,INI_FILE); + + WritePrivateProfileString("DSP_SPS","last_preset",g_wacontext.curpreset_name,INI_FILE); + WriteInt("DSP_SPS","bypass",g_wacontext.bypass,INI_FILE); + SPS_save_preset(&g_wacontext,INI_FILE,"DSP_SPS"); + + SPS_quitcontext(&g_wacontext); + SPS_quitapp(); + + sps_ghas_showconfig.flags |= HKF_DISABLED; + sps_ghas_showconfig.wnd=0; + + if (m_genhotkeys_add_ipc > 65536) + { + DWORD d; + SendMessageTimeout(this_mod->hwndParent,WM_WA_IPC,(WPARAM)&sps_ghas_showconfig,m_genhotkeys_add_ipc,SMTO_BLOCK|SMTO_ABORTIFHUNG,500,&d); + } + + // helps to work around a crash on close issue when the help dialog is open + if(helpWndOpenHack) + { + char buf[MAX_PATH]; + GetModuleFileName(this_mod->hDllInstance, buf, MAX_PATH); + LoadLibrary(buf); + } +} + +int modify_samples(struct winampDSPModule *this_mod, short int *samples, int numsamples, int bps, int nch, int srate) +{ + return SPS_process_samples(&g_wacontext,samples,numsamples,0,bps,nch,srate,numsamples*2,1); +} + +BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID) +{ + if (fdwReason == DLL_PROCESS_ATTACH) + { + DisableThreadLibraryCalls(hinstDLL); + } + + return TRUE; +} + +extern "C" __declspec( dllexport ) int winampUninstallPlugin(HINSTANCE hDllInst, HWND hwndDlg, int param) +{ + // force plugin to be uninstalled from a restart so that we can deal with the case of the help dialog being open + return helpWndOpenHack; +} + +//////////// common dialog stuff + +#define SPS_CONFIGDLG_IMPL +#define SPS_CONFIGDLG_ON_WM_CLOSE { ShowWindow(hwndDlg,SW_HIDE); g_config.visible=0; } +#define SPS_CONFIGDLG_HIDEABLE_EDITOR g_config.showeditor +#include "sps_configdlg.h" \ No newline at end of file diff --git a/Src/Plugins/DSP/dsp_sps/dsp_sps.dsp b/Src/Plugins/DSP/dsp_sps/dsp_sps.dsp new file mode 100644 index 00000000..3ae414e2 --- /dev/null +++ b/Src/Plugins/DSP/dsp_sps/dsp_sps.dsp @@ -0,0 +1,250 @@ +# Microsoft Developer Studio Project File - Name="dsp_sps" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=dsp_sps - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "dsp_sps.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "dsp_sps.mak" CFG="dsp_sps - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "dsp_sps - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "dsp_sps - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "dsp_sps - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 1 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "DSP_SPS_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "../Wasabi" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "DSP_SPS_EXPORTS" /D "NSEEL_LOOPFUNC_SUPPORT" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /base:"0x8000000" /entry:"DllMain" /dll /map /machine:I386 /out:"C:/progra~1/winamp/plugins/dsp_sps.dll" /opt:nowin98 +# SUBTRACT LINK32 /pdb:none /debug + +!ELSEIF "$(CFG)" == "dsp_sps - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "DSP_SPS_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../Wasabi" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "DSP_SPS_EXPORTS" /D "NSEEL_LOOPFUNC_SUPPORT" /YX /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /out:"C:/progra~1/winamp/plugins/dsp_sps.dll" /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "dsp_sps - Win32 Release" +# Name "dsp_sps - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Group "eel" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE="..\ns-eel\megabuf.c" + +!IF "$(CFG)" == "dsp_sps - Win32 Release" + +# ADD CPP /O1 + +!ELSEIF "$(CFG)" == "dsp_sps - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE="..\ns-eel\ns-eel-addfuncs.h" +# End Source File +# Begin Source File + +SOURCE="..\ns-eel\ns-eel-int.h" +# End Source File +# Begin Source File + +SOURCE="..\ns-eel\ns-eel.h" +# End Source File +# Begin Source File + +SOURCE="..\ns-eel\nseel-caltab.c" +# End Source File +# Begin Source File + +SOURCE="..\ns-eel\nseel-cfunc.c" + +!IF "$(CFG)" == "dsp_sps - Win32 Release" + +# ADD CPP /O1 + +!ELSEIF "$(CFG)" == "dsp_sps - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE="..\ns-eel\nseel-compiler.c" +# End Source File +# Begin Source File + +SOURCE="..\ns-eel\nseel-eval.c" +# End Source File +# Begin Source File + +SOURCE="..\ns-eel\nseel-lextab.c" +# End Source File +# Begin Source File + +SOURCE="..\ns-eel\nseel-yylex.c" +# End Source File +# End Group +# Begin Source File + +SOURCE=.\dsp_sps.cpp + +!IF "$(CFG)" == "dsp_sps - Win32 Release" + +# SUBTRACT CPP /Z + +!ELSEIF "$(CFG)" == "dsp_sps - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=.\sps_common.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Group "Support Files" + +# PROP Default_Filter "*.h" +# Begin Source File + +SOURCE=..\Winamp\api_language.h +# End Source File +# Begin Source File + +SOURCE=..\Wasabi\api\service\api_service.h +# End Source File +# Begin Source File + +SOURCE=..\Winamp\lang.h +# End Source File +# Begin Source File + +SOURCE=..\gen_hotkeys\wa_hotkeys.h +# End Source File +# Begin Source File + +SOURCE=..\Winamp\wa_ipc.h +# End Source File +# Begin Source File + +SOURCE=..\Wasabi\api\service\waservicefactory.h +# End Source File +# End Group +# Begin Source File + +SOURCE=.\api.h +# End Source File +# Begin Source File + +SOURCE=..\Winamp\DSP.H +# End Source File +# Begin Source File + +SOURCE=.\resource.h +# End Source File +# Begin Source File + +SOURCE=.\sps_common.h +# End Source File +# Begin Source File + +SOURCE=.\sps_configdlg.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# Begin Source File + +SOURCE=.\constant.bin +# End Source File +# Begin Source File + +SOURCE=.\function.bin +# End Source File +# Begin Source File + +SOURCE=.\general.bin +# End Source File +# Begin Source File + +SOURCE=.\operator.bin +# End Source File +# Begin Source File + +SOURCE=.\res.rc +# End Source File +# End Group +# End Target +# End Project diff --git a/Src/Plugins/DSP/dsp_sps/dsp_sps.dsw b/Src/Plugins/DSP/dsp_sps/dsp_sps.dsw new file mode 100644 index 00000000..cf887680 --- /dev/null +++ b/Src/Plugins/DSP/dsp_sps/dsp_sps.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "dsp_sps"=.\dsp_sps.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/Src/Plugins/DSP/dsp_sps/dsp_sps.vcxproj b/Src/Plugins/DSP/dsp_sps/dsp_sps.vcxproj new file mode 100644 index 00000000..897568f0 --- /dev/null +++ b/Src/Plugins/DSP/dsp_sps/dsp_sps.vcxproj @@ -0,0 +1,346 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + 17.0 + + + {434C583B-DC45-400E-952F-D11755828553} + 10.0.19041.0 + + + + DynamicLibrary + v142 + false + MultiByte + + + DynamicLibrary + v142 + false + MultiByte + + + DynamicLibrary + v142 + false + MultiByte + + + DynamicLibrary + v142 + false + MultiByte + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>17.0.32505.173 + + + $(PlatformShortName)_$(Configuration)\ + $(PlatformShortName)_$(Configuration)\ + false + + + false + + + $(PlatformShortName)_$(Configuration)\ + $(PlatformShortName)_$(Configuration)\ + false + + + false + + + false + + + Debug + x86-windows-static-md + + + Debug + x86-windows-static-md + + + x86-windows-static-md + + + x86-windows-static-md + + + + Disabled + WIN32;_WINDOWS;_USRDLL;DSP_SPS_EXPORTS;NSEEL_LOOPFUNC_SUPPORT;_DEBUG;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebugDLL + + $(IntDir)$(TargetName).pch + $(IntDir) + $(IntDir) + $(IntDir)$(TargetName).pdb + Level3 + true + ProgramDatabase + ..\..\..\Wasabi;%(AdditionalIncludeDirectories) + true + + + $(OutDir)$(TargetName).dll + true + true + $(IntDir)$(TargetName).pdb + $(IntDir)$(TargetName).lib + MachineX86 + false + + + _DEBUG;%(PreprocessorDefinitions) + true + true + Win32 + $(IntDir)$(TargetName).tlb + + + + _DEBUG;%(PreprocessorDefinitions) + 0x0409 + + + xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ +xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ + Post build event: 'xcopy /Y /D $(OutDir)*.dll ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\' + + + + + Disabled + WIN32;_WINDOWS;_USRDLL;DSP_SPS_EXPORTS;NSEEL_LOOPFUNC_SUPPORT;_DEBUG;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebugDLL + + + $(IntDir)$(TargetName).pch + $(IntDir) + $(IntDir) + $(IntDir)$(TargetName).pdb + Level3 + true + ProgramDatabase + ..\..\..\Wasabi;%(AdditionalIncludeDirectories) + true + + + $(OutDir)$(TargetName).dll + true + true + $(IntDir)$(TargetName).pdb + $(IntDir)$(TargetName).lib + false + + + _DEBUG;%(PreprocessorDefinitions) + true + true + $(IntDir)$(TargetName).tlb + + + + + _DEBUG;%(PreprocessorDefinitions) + 0x0409 + + + xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ +xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ + Post build event: 'xcopy /Y /D $(OutDir)*.dll ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\' + + + + + MaxSpeed + OnlyExplicitInline + Speed + true + ..\..\..\Wasabi;%(AdditionalIncludeDirectories) + WIN32;_WINDOWS;_USRDLL;DSP_SPS_EXPORTS;NSEEL_LOOPFUNC_SUPPORT;NDEBUG;%(PreprocessorDefinitions) + true + Sync + MultiThreadedDLL + true + false + true + + $(IntDir)$(TargetName).pch + $(IntDir) + $(IntDir) + $(IntDir)$(TargetName).pdb + Level3 + true + None + true + + + $(OutDir)$(TargetName).dll + true + $(IntDir)$(TargetName).pdb + true + $(IntDir)$(TargetName).lib + MachineX86 + false + + + NDEBUG;%(PreprocessorDefinitions) + true + true + Win32 + $(IntDir)$(TargetName).tlb + + + + NDEBUG;%(PreprocessorDefinitions) + 0x0409 + + + xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ + Post build event: 'xcopy /Y /D $(OutDir)*.dll ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\' + + + + + MaxSpeed + OnlyExplicitInline + Speed + true + ..\..\..\Wasabi;%(AdditionalIncludeDirectories) + WIN32;_WINDOWS;_USRDLL;DSP_SPS_EXPORTS;NSEEL_LOOPFUNC_SUPPORT;NDEBUG;%(PreprocessorDefinitions) + true + Sync + MultiThreadedDLL + true + false + true + + + $(IntDir)$(TargetName).pch + $(IntDir) + $(IntDir) + $(IntDir)$(TargetName).pdb + Level3 + true + None + true + + + $(OutDir)$(TargetName).dll + true + $(IntDir)$(TargetName).pdb + true + $(IntDir)$(TargetName).lib + false + + + NDEBUG;%(PreprocessorDefinitions) + true + true + $(IntDir)$(TargetName).tlb + + + + + NDEBUG;%(PreprocessorDefinitions) + 0x0409 + + + xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ + Post build event: 'xcopy /Y /D $(OutDir)*.dll ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\' + + + + + + + + + + Disabled + Disabled + WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;DSP_SPS_EXPORTS;NSEEL_LOOPFUNC_SUPPORT + WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;DSP_SPS_EXPORTS;NSEEL_LOOPFUNC_SUPPORT + EnableFastChecks + EnableFastChecks + MaxSpeed + MaxSpeed + WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;DSP_SPS_EXPORTS;NSEEL_LOOPFUNC_SUPPORT + WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;DSP_SPS_EXPORTS;NSEEL_LOOPFUNC_SUPPORT + + + Disabled + Disabled + WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;DSP_SPS_EXPORTS;NSEEL_LOOPFUNC_SUPPORT + WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;DSP_SPS_EXPORTS;NSEEL_LOOPFUNC_SUPPORT + EnableFastChecks + EnableFastChecks + MaxSpeed + MaxSpeed + WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;DSP_SPS_EXPORTS;NSEEL_LOOPFUNC_SUPPORT + WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;DSP_SPS_EXPORTS;NSEEL_LOOPFUNC_SUPPORT + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Src/Plugins/DSP/dsp_sps/dsp_sps.vcxproj.filters b/Src/Plugins/DSP/dsp_sps/dsp_sps.vcxproj.filters new file mode 100644 index 00000000..7f746be3 --- /dev/null +++ b/Src/Plugins/DSP/dsp_sps/dsp_sps.vcxproj.filters @@ -0,0 +1,92 @@ + + + + + {5493b195-56dc-432a-ae15-7e7d896e38bf} + cpp;c;cxx;rc;def;r;odl;idl;hpj;bat + + + {02e8ad96-519d-4f05-a8f7-7b6c37776a0e} + h;hpp;hxx;hm;inl + + + {0796236c-1cfe-4820-bfbf-fcc2167a1af2} + ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe + + + {67403946-6e24-444a-a76d-d03f25c59a93} + + + + + Source Files + + + Source Files + + + Source Files\ns-eel2 + + + Source Files\ns-eel2 + + + Source Files\ns-eel2 + + + Source Files\ns-eel2 + + + Source Files\ns-eel2 + + + + + Header Files + + + Header Files + + + Header Files + + + Source Files\ns-eel2 + + + Source Files\ns-eel2 + + + Source Files\ns-eel2 + + + Source Files\ns-eel2 + + + Source Files\ns-eel2 + + + Source Files\ns-eel2 + + + Source Files\ns-eel2 + + + Source Files\ns-eel2 + + + Source Files\ns-eel2 + + + Source Files\ns-eel2 + + + Header Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/Src/Plugins/DSP/dsp_sps/dxi/AudioPlugIn.cpp b/Src/Plugins/DSP/dsp_sps/dxi/AudioPlugIn.cpp new file mode 100644 index 00000000..f2ebc687 --- /dev/null +++ b/Src/Plugins/DSP/dsp_sps/dxi/AudioPlugIn.cpp @@ -0,0 +1,283 @@ +// AudioPlugIn.cpp: implementation of the CAudioPlugIn class. +// +////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "AudioPlugIn.h" + +// Note: see AudioPlugIn.h to redefine PROCESS_IN_PLACE +#if PROCESS_IN_PLACE +#pragma message("***** Compiling an IN-PLACE audio plug-in *****") +#else +#pragma message("***** Compiling an NON-IN-PLACE audio plug-in *****") +#endif + +#include "resource.h" +#include + +char g_path[MAX_PATH]; + +#define SPS_CONFIGDLG_IMPL +#define SPS_CONFIGDLG_ON_WM_CLOSE { ShowWindow(hwndDlg,SW_HIDE); /*g_config.visible=0;*/ } +//#define SPS_CONFIGDLG_HIDEABLE_EDITOR 0 +#include "../sps_common.h" +#include "../sps_configdlg.h" + +////////////////////////////////////////////////////////////////////// +// Ctors + +CAudioPlugIn::CAudioPlugIn( HRESULT* phr ) +{ + // TODO: put all initialization code in Initialize(), below. +} + +CAudioPlugIn::~CAudioPlugIn() +{ + SPS_quitapp(); +} + +//////////////////////////////////////////////////////////////////////////////// + +HRESULT CAudioPlugIn::Initialize() +{ + SPS_initapp(); + strcpy(g_path,"c:\\progra~1\\winamp\\plugins\\dsp_sps"); //FUCKO + return S_OK; +} + +//////////////////////////////////////////////////////////////////////////////// + +HRESULT CAudioPlugIn::IsValidInputFormat( const WAVEFORMATEX* pwfx ) const +{ + // The plug-in base class will have already validated pwfx to ensure that + // it is 16-bit PCM or 32-bit float, 1 or 2 channels. + + // TODO: Add any additional checks here, such as sample rate, etc. + + // By default, only 32-bit float buffers are supported. + if (WAVE_FORMAT_IEEE_FLOAT != pwfx->wFormatTag) + return VFW_E_TYPE_NOT_ACCEPTED; + + return S_OK; +} + +//////////////////////////////////////////////////////////////////////////////// + +HRESULT CAudioPlugIn::IsValidOutputFormat( const WAVEFORMATEX* pwfx ) const +{ + // The plug-in base class will have already validated pwfx to ensure that + // it is 16-bit PCM or 32-bit float, 1 or 2 channels. + + // TODO: Add any additional checks here, such as sample rate, etc. + + // By default, only 32-bit float buffers are supported. + if (WAVE_FORMAT_IEEE_FLOAT != pwfx->wFormatTag) + return VFW_E_TYPE_NOT_ACCEPTED; + + return S_OK; +} + +//////////////////////////////////////////////////////////////////////////////// + +HRESULT CAudioPlugIn::IsValidTransform( const WAVEFORMATEX* pwfxIn, const WAVEFORMATEX* pwfxOut ) const +{ + // The plug-in base class will have already validated pwfxIn/pwfxOut to ensure that + // it is 16-bit PCM or 32-bit float, 1 or 2 channels, and that both have the same + // sample rate. + + // TODO: Add any additional checks here, such as sample rate, etc. + + // By default, only 32-bit float buffers are supported. + if (WAVE_FORMAT_IEEE_FLOAT != pwfxIn->wFormatTag) + return VFW_E_TYPE_NOT_ACCEPTED; + if (WAVE_FORMAT_IEEE_FLOAT != pwfxOut->wFormatTag) + return VFW_E_TYPE_NOT_ACCEPTED; + + return S_OK; +} + +//////////////////////////////////////////////////////////////////////////////// + +HRESULT CAudioPlugIn::SuggestOutputFormat( WAVEFORMATEX* pwfx ) const +{ + // The plug-in base class will have already validated pwfx to ensure that + // it is 16-bit PCM or 32-bit float, 1 or 2 channels, and that both have the same + // sample rate. + // TODO: Add any additional checks here, such as sample rate, etc. + + // pwfx is initially set to the input format. If your plug-in doesn't need + // to change the output format, simply return S_OK. + // TODO: change pwfx if necessary. + + return S_OK; +} + +//////////////////////////////////////////////////////////////////////////////// + +HRESULT CAudioPlugIn::Process( LONGLONG llSampAudioTimestamp, + AudioBuffer* pbufIn, + AudioBuffer* pbufOut ) +{ + BOOL const bGenerateTail = (NULL == pbufIn); + BOOL const bIsInPlace = (pbufIn == pbufOut); + + // Note about deferred zero filling: + // + // AudioBuffer will automatically take advantage of IDeferZeroFill, + // if the host app supports it. To avoid unnecessary and wasteful buffer + // fills, always check the 'bZero' flag in AudioBuffer before calling + // the GetPointer() method. This is because calling GetPointer() will + // trigger a zero-fill if the underlying data buffer was marked as "defer + // zero fill." + // + // Similarly, to allow downstream filters to benefit from deferred + // zero filling, be sure to set the 'bZero' flag in an AudioBuffer, if + // your DSP code is producing a completely silent buffer. + + if (bGenerateTail) + { + // TODO: Add code to generate a tail if required by your plug-in. + // Return S_OK if more effect tail data remains. Return S_FALSE + // if no more tail data remains. + + // Default implementation generates no tail + return S_FALSE; + } + + // TODO: Put your DSP code here + float *in=pbufIn->GetPointer(); + float *out=pbufOut->GetPointer(); + int of=(int)pbufIn->lOffset; + int size=(int)pbufIn->pms->GetSize(); + int nbsamp=size/sizeof(float); + +/* // If we're bypassed, copy input to output without processing + float fEnabled = GetParamValue( PARAM_ENABLE ); + if (fEnabled < 0.5f) + { + memcpy (out, in, pbufIn->cSamp * m_wfxIn.nBlockAlign ); + return S_OK; + }*/ + + + const WAVEFORMATEX *inpformat=GetInputFormat(); + int nch=inpformat->nChannels; + int srate=inpformat->nSamplesPerSec; + + if(0) + { + char tmp[512]; + int size2=(int)pbufOut->pms->GetSize(); + wsprintf(tmp,"%d %d %d %d %d %d\n",of,size,size2,nbsamp,nch,srate); + OutputDebugString(tmp); + } + + memcpy(out,in,size); + extern SPSEffectContext *g_fucko_ctx; + SPS_process_samples(g_fucko_ctx, + (void *)out, nbsamp/nch, 1, 32, nch, srate, + nbsamp, nbsamp); + + return S_OK; +} + +//////////////////////////////////////////////////////////////////////////////// + +HRESULT CAudioPlugIn::AllocateResources() +{ + // TODO: add code to here to prepare the for the start of streaming + + return S_OK; +} + +//////////////////////////////////////////////////////////////////////////////// + +HRESULT CAudioPlugIn::FreeResources() +{ + // TODO: add code to here to clean up after streaming + + return S_OK; +} + +//////////////////////////////////////////////////////////////////////////////// + +int CAudioPlugIn::PersistGetSize() const +{ + int const cb + = sizeof(DWORD) // # of persisted parameters + + NUM_PARAMS * (sizeof(DWORD) + sizeof(float)); // (index,value), for each parameter + + return cb; +} + +//////////////////////////////////////////////////////////////////////////////// + +HRESULT CAudioPlugIn::PersistLoad( IStream* pStream ) +{ + ULONG cb = 0; + HRESULT hr = S_OK; + + // Get the number of persisted parameters + DWORD cParams = 0; + hr = pStream->Read( &cParams, sizeof(cParams), &cb ); + if (FAILED( hr ) || cb != sizeof(cParams)) + return E_FAIL; + + // Restore each parameter + for (DWORD ix = 0; ix < cParams; ix++) + { + // Get the parameter index + DWORD dwParam = 0; + hr = pStream->Read( &dwParam, sizeof(dwParam), &cb ); + if (FAILED( hr ) || cb != sizeof(dwParam)) + return E_FAIL; + + // Get the parameter value + float fValue = 0; + hr = pStream->Read( &fValue, sizeof(fValue), &cb ); + if (FAILED( hr ) || cb != sizeof(fValue)) + return E_FAIL; + + // Set the parameter value + if (m_pMediaParams) + m_pMediaParams->SetParam( dwParam, fValue ); + } + + return S_OK; +} + +//////////////////////////////////////////////////////////////////////////////// + +HRESULT CAudioPlugIn::PersistSave( IStream* pStream ) +{ + ULONG cb = 0; + HRESULT hr = S_OK; + + // Put the number of persisted parameters + DWORD cParams = NUM_PARAMS; + hr = pStream->Write( &cParams, sizeof(cParams), &cb ); + if (FAILED( hr ) || cb != sizeof(cParams)) + return E_FAIL; + + // Save each parameter + for (DWORD dwParam = 0; dwParam < cParams; dwParam++) + { + float fValue = 0; + + // Get the parameter value + if (m_pMediaParams) + m_pMediaParams->GetParam( dwParam, &fValue ); + + // Write the parameter index + hr = pStream->Write( &dwParam, sizeof(dwParam), &cb ); + if (FAILED( hr ) || cb != sizeof(dwParam)) + return E_FAIL; + + // Write the parameter value + hr = pStream->Write( &fValue, sizeof(fValue), &cb ); + if (FAILED( hr ) || cb != sizeof(fValue)) + return E_FAIL; + } + + return S_OK; +} diff --git a/Src/Plugins/DSP/dsp_sps/dxi/AudioPlugIn.def b/Src/Plugins/DSP/dsp_sps/dxi/AudioPlugIn.def new file mode 100644 index 00000000..aa233f15 --- /dev/null +++ b/Src/Plugins/DSP/dsp_sps/dxi/AudioPlugIn.def @@ -0,0 +1,7 @@ +LIBRARY AudioPlugIn.dll +DESCRIPTION 'AudioPlugIn DXi' +EXPORTS + DllGetClassObject PRIVATE + DllCanUnloadNow PRIVATE + DllRegisterServer PRIVATE + DllUnregisterServer PRIVATE diff --git a/Src/Plugins/DSP/dsp_sps/dxi/AudioPlugIn.dep b/Src/Plugins/DSP/dsp_sps/dxi/AudioPlugIn.dep new file mode 100644 index 00000000..50ef98b0 --- /dev/null +++ b/Src/Plugins/DSP/dsp_sps/dxi/AudioPlugIn.dep @@ -0,0 +1,69 @@ +# Microsoft Developer Studio Generated Dependency File, included by AudioPlugIn.mak + +.\AudioPlugIn.cpp : \ + ".\AudioPlugIn.h"\ + ".\include\CakeMedParam.h"\ + ".\include\DeferZeroFill.h"\ + ".\include\DXi.h"\ + ".\MediaParams.h"\ + ".\ParamEnvelope.h"\ + ".\Parameters.h"\ + ".\StdAfx.h"\ + + +.\AudioPlugIn.rc : \ + ".\res\AudioPlugIn.rc2"\ + + +.\AudioPlugInPropPage.cpp : \ + ".\AudioPlugInPropPage.h"\ + ".\include\CakeMedParam.h"\ + ".\Parameters.h"\ + ".\StdAfx.h"\ + + +.\Filter.cpp : \ + ".\AudioPlugIn.h"\ + ".\AudioPlugInPropPage.h"\ + ".\Filter.h"\ + ".\include\CakeMedParam.h"\ + ".\include\DeferZeroFill.h"\ + ".\include\DXi.h"\ + ".\MediaParams.h"\ + ".\ParamEnvelope.h"\ + ".\Parameters.h"\ + ".\PlugInGUIDs.h"\ + ".\StdAfx.h"\ + + +.\MediaParams.cpp : \ + ".\AudioPlugIn.h"\ + ".\include\CakeMedParam.h"\ + ".\include\CakeMedParam_i.c"\ + ".\include\DeferZeroFill.h"\ + ".\include\DXi.h"\ + ".\MediaParams.h"\ + ".\ParamEnvelope.h"\ + ".\Parameters.h"\ + ".\StdAfx.h"\ + + +.\ParamEnvelope.cpp : \ + ".\AudioPlugIn.h"\ + ".\include\CakeMedParam.h"\ + ".\include\DeferZeroFill.h"\ + ".\include\DXi.h"\ + ".\MediaParams.h"\ + ".\ParamEnvelope.h"\ + ".\Parameters.h"\ + ".\StdAfx.h"\ + + +.\PlugInApp.cpp : \ + ".\PlugInApp.h"\ + ".\StdAfx.h"\ + + +.\StdAfx.cpp : \ + ".\StdAfx.h"\ + diff --git a/Src/Plugins/DSP/dsp_sps/dxi/AudioPlugIn.dsp b/Src/Plugins/DSP/dsp_sps/dxi/AudioPlugIn.dsp new file mode 100644 index 00000000..e35a6a76 --- /dev/null +++ b/Src/Plugins/DSP/dsp_sps/dxi/AudioPlugIn.dsp @@ -0,0 +1,271 @@ +# Microsoft Developer Studio Project File - Name="AudioPlugIn" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=AudioPlugIn - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "AudioPlugIn.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "AudioPlugIn.mak" CFG="AudioPlugIn - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "AudioPlugIn - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "AudioPlugIn - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "AudioPlugIn - Win32 Release" + +# PROP BASE Use_MFC 2 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 2 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MD /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_WINDLL" /D "_AFXDLL" /Yu"stdafx.h" /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /I "." /I "include" /I "..\..\dshow\include" /I "..\..\dshow\dshow" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_WINDLL" /D "_MBCS" /D "_USRDLL" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "NDEBUG" /d "_AFXDLL" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 /nologo /subsystem:windows /dll /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib advapi32.lib ole32.lib oleaut32.lib DMOGUIDS.LIB winmm.lib comdlg32.lib /nologo /entry:"DllEntryPoint@12" /subsystem:windows /dll /machine:I386 /nodefaultlib:"libcmt" /nodefaultlib:"libcmtd" /libpath:"../../dshow" /opt:nowin98 +# SUBTRACT LINK32 /pdb:none +# Begin Custom Build - Custom Build Steps +OutDir=.\Release +TargetName=AudioPlugIn +InputPath=.\Release\AudioPlugIn.dll +SOURCE="$(InputPath)" + +"custom.bld" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + regsvr32.exe /s $(OutDir)\$(TargetName).DLL + echo >custom.bld + +# End Custom Build + +!ELSEIF "$(CFG)" == "AudioPlugIn - Win32 Debug" + +# PROP BASE Use_MFC 2 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 2 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_WINDLL" /D "_AFXDLL" /Yu"stdafx.h" /FD /GZ /c +# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "." /I "include" /I "..\..\dshow\include" /I "..\..\dshow\dshow" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_WINDLL" /D "_MBCS" /D "_USRDLL" /YX /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x409 /d "_DEBUG" /d "_AFXDLL" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 /nologo /subsystem:windows /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib advapi32.lib ole32.lib oleaut32.lib DMOGUIDS.LIB winmm.lib comdlg32.lib /nologo /entry:"DllEntryPoint@12" /subsystem:windows /dll /debug /machine:I386 /nodefaultlib:"libcmt" /nodefaultlib:"libcmtd" /pdbtype:sept /libpath:"../../dshow" +# Begin Custom Build - Custom Build Steps +OutDir=.\Debug +TargetName=AudioPlugIn +InputPath=.\Debug\AudioPlugIn.dll +SOURCE="$(InputPath)" + +"custom.bld" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + regsvr32.exe /s $(OutDir)\$(TargetName).DLL + echo >custom.bld + +# End Custom Build + +!ENDIF + +# Begin Target + +# Name "AudioPlugIn - Win32 Release" +# Name "AudioPlugIn - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Group "dx shit" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=.\AudioPlugIn.cpp +# End Source File +# Begin Source File + +SOURCE=.\AudioPlugIn.def +# End Source File +# Begin Source File + +SOURCE=.\AudioPlugIn.rc +# End Source File +# Begin Source File + +SOURCE=.\AudioPlugInPropPage.cpp +# End Source File +# Begin Source File + +SOURCE=.\Filter.cpp +# End Source File +# Begin Source File + +SOURCE=.\MediaParams.cpp +# End Source File +# Begin Source File + +SOURCE=.\ParamEnvelope.cpp +# End Source File +# Begin Source File + +SOURCE=.\PlugInApp.cpp +# End Source File +# Begin Source File + +SOURCE=.\StdAfx.cpp +# ADD CPP /Yc"stdafx.h" +# End Source File +# End Group +# Begin Group "ns-eel" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE="..\..\ns-eel\megabuf.c" +# End Source File +# Begin Source File + +SOURCE="..\..\ns-eel\ns-eel-addfuncs.h" +# End Source File +# Begin Source File + +SOURCE="..\..\ns-eel\ns-eel-int.h" +# End Source File +# Begin Source File + +SOURCE="..\..\ns-eel\ns-eel.h" +# End Source File +# Begin Source File + +SOURCE="..\..\ns-eel\nseel-caltab.c" +# End Source File +# Begin Source File + +SOURCE="..\..\ns-eel\nseel-cfunc.c" +# End Source File +# Begin Source File + +SOURCE="..\..\ns-eel\nseel-compiler.c" +# End Source File +# Begin Source File + +SOURCE="..\..\ns-eel\nseel-eval.c" +# End Source File +# Begin Source File + +SOURCE="..\..\ns-eel\nseel-lextab.c" +# End Source File +# Begin Source File + +SOURCE="..\..\ns-eel\nseel-yylex.c" +# End Source File +# End Group +# Begin Group "sps" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\sps_common.cpp +# End Source File +# End Group +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\AudioPlugIn.h +# End Source File +# Begin Source File + +SOURCE=.\AudioPlugInPropPage.h +# End Source File +# Begin Source File + +SOURCE=.\include\DXi.h +# End Source File +# Begin Source File + +SOURCE=.\Filter.h +# End Source File +# Begin Source File + +SOURCE=.\MediaParams.h +# End Source File +# Begin Source File + +SOURCE=.\ParamEnvelope.h +# End Source File +# Begin Source File + +SOURCE=.\Parameters.h +# End Source File +# Begin Source File + +SOURCE=.\PlugInApp.h +# End Source File +# Begin Source File + +SOURCE=.\PlugInGUIDs.h +# End Source File +# Begin Source File + +SOURCE=.\resource.h +# End Source File +# Begin Source File + +SOURCE=.\StdAfx.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# Begin Source File + +SOURCE=.\res\AudioPlugIn.rc2 +# End Source File +# End Group +# Begin Source File + +SOURCE=.\ReadMe.txt +# End Source File +# End Target +# End Project diff --git a/Src/Plugins/DSP/dsp_sps/dxi/AudioPlugIn.dsw b/Src/Plugins/DSP/dsp_sps/dxi/AudioPlugIn.dsw new file mode 100644 index 00000000..c697ed2e --- /dev/null +++ b/Src/Plugins/DSP/dsp_sps/dxi/AudioPlugIn.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "AudioPlugIn"=.\AudioPlugIn.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/Src/Plugins/DSP/dsp_sps/dxi/AudioPlugIn.h b/Src/Plugins/DSP/dsp_sps/dxi/AudioPlugIn.h new file mode 100644 index 00000000..14446d14 --- /dev/null +++ b/Src/Plugins/DSP/dsp_sps/dxi/AudioPlugIn.h @@ -0,0 +1,47 @@ +// AudioPlugIn.h: interface for the CAudioPlugIn class. +// +////////////////////////////////////////////////////////////////////// + +#if !defined(AFX_AUDIOPLUGIN_H__D9177ACC_DFF4_4C13_8FB9_F949C35BFEF0__INCLUDED_) +#define AFX_AUDIOPLUGIN_H__D9177ACC_DFF4_4C13_8FB9_F949C35BFEF0__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +struct DXiEvent; +struct MfxEvent; + +#include "DXi.h" + +// TODO: #define PROCESS_IN_PLACE to FALSE if necessary, i.e., for plug-ins +// which convert mono to stereo. +#define PROCESS_IN_PLACE (TRUE) + +class CAudioPlugIn : + public CDXi +{ +public: + CAudioPlugIn( HRESULT* phr ); + virtual ~CAudioPlugIn(); + + HRESULT Initialize(); + + HRESULT IsValidInputFormat( const WAVEFORMATEX* pwfx ) const; + HRESULT IsValidOutputFormat( const WAVEFORMATEX* pwfx ) const; + HRESULT IsValidTransform( const WAVEFORMATEX* pwfxIn, const WAVEFORMATEX* pwfxOut ) const; + HRESULT SuggestOutputFormat( WAVEFORMATEX* pwfx ) const; + + HRESULT Process( LONGLONG llSampAudioTimestamp, + AudioBuffer* pbufIn, + AudioBuffer* pbufOut ); + + HRESULT AllocateResources(); + HRESULT FreeResources(); + + int PersistGetSize() const; + HRESULT PersistLoad( IStream* pStream ); + HRESULT PersistSave( IStream* pStream ); +}; + +#endif // !defined(AFX_AUDIOPLUGIN_H__D9177ACC_DFF4_4C13_8FB9_F949C35BFEF0__INCLUDED_) diff --git a/Src/Plugins/DSP/dsp_sps/dxi/AudioPlugIn.mak b/Src/Plugins/DSP/dsp_sps/dxi/AudioPlugIn.mak new file mode 100644 index 00000000..09d2403e --- /dev/null +++ b/Src/Plugins/DSP/dsp_sps/dxi/AudioPlugIn.mak @@ -0,0 +1,312 @@ +# Microsoft Developer Studio Generated NMAKE File, Based on AudioPlugIn.dsp +!IF "$(CFG)" == "" +CFG=AudioPlugIn - Win32 Debug +!MESSAGE No configuration specified. Defaulting to AudioPlugIn - Win32 Debug. +!ENDIF + +!IF "$(CFG)" != "AudioPlugIn - Win32 Release" && "$(CFG)" != "AudioPlugIn - Win32 Debug" +!MESSAGE Invalid configuration "$(CFG)" specified. +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "AudioPlugIn.mak" CFG="AudioPlugIn - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "AudioPlugIn - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "AudioPlugIn - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE +!ERROR An invalid configuration is specified. +!ENDIF + +!IF "$(OS)" == "Windows_NT" +NULL= +!ELSE +NULL=nul +!ENDIF + +!IF "$(CFG)" == "AudioPlugIn - Win32 Release" + +OUTDIR=.\Release +INTDIR=.\Release +# Begin Custom Macros +OutDir=.\Release +# End Custom Macros + +ALL : "$(OUTDIR)\AudioPlugIn.dll" ".\custom.bld" + + +CLEAN : + -@erase "$(INTDIR)\AudioPlugIn.obj" + -@erase "$(INTDIR)\AudioPlugIn.pch" + -@erase "$(INTDIR)\AudioPlugIn.res" + -@erase "$(INTDIR)\AudioPlugInPropPage.obj" + -@erase "$(INTDIR)\Filter.obj" + -@erase "$(INTDIR)\MediaParams.obj" + -@erase "$(INTDIR)\ParamEnvelope.obj" + -@erase "$(INTDIR)\PlugInApp.obj" + -@erase "$(INTDIR)\StdAfx.obj" + -@erase "$(INTDIR)\vc60.idb" + -@erase "$(OUTDIR)\AudioPlugIn.dll" + -@erase "$(OUTDIR)\AudioPlugIn.exp" + -@erase "$(OUTDIR)\AudioPlugIn.lib" + -@erase "custom.bld" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /MD /W3 /GX /O2 /I "." /I "include" /I "$(MSSDK)\include" /I "$(MSSDK)\Samples\Multimedia\DirectShow\BaseClasses" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_WINDLL" /D "_MBCS" /D "_USRDLL" /Fp"$(INTDIR)\AudioPlugIn.pch" /Yu"stdafx.h" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c + +.c{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.c{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +MTL=midl.exe +MTL_PROJ=/nologo /D "NDEBUG" /mktyplib203 /win32 +RSC=rc.exe +RSC_PROJ=/l 0x409 /fo"$(INTDIR)\AudioPlugIn.res" /d "NDEBUG" +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\AudioPlugIn.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib advapi32.lib ole32.lib oleaut32.lib \build\sdks\dxmedia\lib\DMOGUIDS.LIB winmm.lib /nologo /entry:"DllEntryPoint@12" /subsystem:windows /dll /incremental:no /pdb:"$(OUTDIR)\AudioPlugIn.pdb" /machine:I386 /nodefaultlib:"libcmt" /nodefaultlib:"libcmtd" /def:".\AudioPlugIn.def" /out:"$(OUTDIR)\AudioPlugIn.dll" /implib:"$(OUTDIR)\AudioPlugIn.lib" +DEF_FILE= \ + ".\AudioPlugIn.def" +LINK32_OBJS= \ + "$(INTDIR)\AudioPlugIn.obj" \ + "$(INTDIR)\AudioPlugInPropPage.obj" \ + "$(INTDIR)\Filter.obj" \ + "$(INTDIR)\MediaParams.obj" \ + "$(INTDIR)\ParamEnvelope.obj" \ + "$(INTDIR)\PlugInApp.obj" \ + "$(INTDIR)\StdAfx.obj" \ + "$(INTDIR)\AudioPlugIn.res" + +"$(OUTDIR)\AudioPlugIn.dll" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +OutDir=.\Release +TargetName=AudioPlugIn +InputPath=.\Release\AudioPlugIn.dll +SOURCE="$(InputPath)" + +".\custom.bld" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + <custom.bld +<< + + +!ELSEIF "$(CFG)" == "AudioPlugIn - Win32 Debug" + +OUTDIR=.\Debug +INTDIR=.\Debug +# Begin Custom Macros +OutDir=.\Debug +# End Custom Macros + +ALL : "$(OUTDIR)\AudioPlugIn.dll" ".\custom.bld" + + +CLEAN : + -@erase "$(INTDIR)\AudioPlugIn.obj" + -@erase "$(INTDIR)\AudioPlugIn.pch" + -@erase "$(INTDIR)\AudioPlugIn.res" + -@erase "$(INTDIR)\AudioPlugInPropPage.obj" + -@erase "$(INTDIR)\Filter.obj" + -@erase "$(INTDIR)\MediaParams.obj" + -@erase "$(INTDIR)\ParamEnvelope.obj" + -@erase "$(INTDIR)\PlugInApp.obj" + -@erase "$(INTDIR)\StdAfx.obj" + -@erase "$(INTDIR)\vc60.idb" + -@erase "$(INTDIR)\vc60.pdb" + -@erase "$(OUTDIR)\AudioPlugIn.dll" + -@erase "$(OUTDIR)\AudioPlugIn.exp" + -@erase "$(OUTDIR)\AudioPlugIn.ilk" + -@erase "$(OUTDIR)\AudioPlugIn.lib" + -@erase "$(OUTDIR)\AudioPlugIn.pdb" + -@erase "custom.bld" + +"$(OUTDIR)" : + if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" + +CPP=cl.exe +CPP_PROJ=/nologo /MDd /W3 /Gm /GX /ZI /Od /I "." /I "include" /I "$(MSSDK)\include" /I "$(MSSDK)\Samples\Multimedia\DirectShow\BaseClasses" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_WINDLL" /D "_MBCS" /D "_USRDLL" /Fp"$(INTDIR)\AudioPlugIn.pch" /Yu"stdafx.h" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /GZ /c + +.c{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.obj:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.c{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cpp{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +.cxx{$(INTDIR)}.sbr:: + $(CPP) @<< + $(CPP_PROJ) $< +<< + +MTL=midl.exe +MTL_PROJ=/nologo /D "_DEBUG" /mktyplib203 /win32 +RSC=rc.exe +RSC_PROJ=/l 0x409 /fo"$(INTDIR)\AudioPlugIn.res" /d "_DEBUG" +BSC32=bscmake.exe +BSC32_FLAGS=/nologo /o"$(OUTDIR)\AudioPlugIn.bsc" +BSC32_SBRS= \ + +LINK32=link.exe +LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib advapi32.lib ole32.lib oleaut32.lib \build\sdks\dxmedia\lib\DMOGUIDS.LIB winmm.lib /nologo /entry:"DllEntryPoint@12" /subsystem:windows /dll /incremental:yes /pdb:"$(OUTDIR)\AudioPlugIn.pdb" /debug /machine:I386 /nodefaultlib:"libcmt" /nodefaultlib:"libcmtd" /def:".\AudioPlugIn.def" /out:"$(OUTDIR)\AudioPlugIn.dll" /implib:"$(OUTDIR)\AudioPlugIn.lib" /pdbtype:sept +DEF_FILE= \ + ".\AudioPlugIn.def" +LINK32_OBJS= \ + "$(INTDIR)\AudioPlugIn.obj" \ + "$(INTDIR)\AudioPlugInPropPage.obj" \ + "$(INTDIR)\Filter.obj" \ + "$(INTDIR)\MediaParams.obj" \ + "$(INTDIR)\ParamEnvelope.obj" \ + "$(INTDIR)\PlugInApp.obj" \ + "$(INTDIR)\StdAfx.obj" \ + "$(INTDIR)\AudioPlugIn.res" + +"$(OUTDIR)\AudioPlugIn.dll" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) + $(LINK32) @<< + $(LINK32_FLAGS) $(LINK32_OBJS) +<< + +OutDir=.\Debug +TargetName=AudioPlugIn +InputPath=.\Debug\AudioPlugIn.dll +SOURCE="$(InputPath)" + +".\custom.bld" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" + <custom.bld +<< + + +!ENDIF + + +!IF "$(NO_EXTERNAL_DEPS)" != "1" +!IF EXISTS("AudioPlugIn.dep") +!INCLUDE "AudioPlugIn.dep" +!ELSE +!MESSAGE Warning: cannot find "AudioPlugIn.dep" +!ENDIF +!ENDIF + + +!IF "$(CFG)" == "AudioPlugIn - Win32 Release" || "$(CFG)" == "AudioPlugIn - Win32 Debug" +SOURCE=.\AudioPlugIn.cpp + +"$(INTDIR)\AudioPlugIn.obj" : $(SOURCE) "$(INTDIR)" "$(INTDIR)\AudioPlugIn.pch" + + +SOURCE=.\AudioPlugIn.rc + +"$(INTDIR)\AudioPlugIn.res" : $(SOURCE) "$(INTDIR)" + $(RSC) $(RSC_PROJ) $(SOURCE) + + +SOURCE=.\AudioPlugInPropPage.cpp + +"$(INTDIR)\AudioPlugInPropPage.obj" : $(SOURCE) "$(INTDIR)" "$(INTDIR)\AudioPlugIn.pch" + + +SOURCE=.\Filter.cpp + +"$(INTDIR)\Filter.obj" : $(SOURCE) "$(INTDIR)" "$(INTDIR)\AudioPlugIn.pch" + + +SOURCE=.\MediaParams.cpp + +"$(INTDIR)\MediaParams.obj" : $(SOURCE) "$(INTDIR)" "$(INTDIR)\AudioPlugIn.pch" + + +SOURCE=.\ParamEnvelope.cpp + +"$(INTDIR)\ParamEnvelope.obj" : $(SOURCE) "$(INTDIR)" "$(INTDIR)\AudioPlugIn.pch" + + +SOURCE=.\PlugInApp.cpp + +"$(INTDIR)\PlugInApp.obj" : $(SOURCE) "$(INTDIR)" "$(INTDIR)\AudioPlugIn.pch" + + +SOURCE=.\StdAfx.cpp + +!IF "$(CFG)" == "AudioPlugIn - Win32 Release" + +CPP_SWITCHES=/nologo /MD /W3 /GX /O2 /I "." /I "include" /I "$(MSSDK)\include" /I "$(MSSDK)\Samples\Multimedia\DirectShow\BaseClasses" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_WINDLL" /D "_MBCS" /D "_USRDLL" /Fp"$(INTDIR)\AudioPlugIn.pch" /Yc"stdafx.h" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c + +"$(INTDIR)\StdAfx.obj" "$(INTDIR)\AudioPlugIn.pch" : $(SOURCE) "$(INTDIR)" + $(CPP) @<< + $(CPP_SWITCHES) $(SOURCE) +<< + + +!ELSEIF "$(CFG)" == "AudioPlugIn - Win32 Debug" + +CPP_SWITCHES=/nologo /MDd /W3 /Gm /GX /ZI /Od /I "." /I "include" /I "$(MSSDK)\include" /I "$(MSSDK)\Samples\Multimedia\DirectShow\BaseClasses" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_WINDLL" /D "_MBCS" /D "_USRDLL" /Fp"$(INTDIR)\AudioPlugIn.pch" /Yc"stdafx.h" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /GZ /c + +"$(INTDIR)\StdAfx.obj" "$(INTDIR)\AudioPlugIn.pch" : $(SOURCE) "$(INTDIR)" + $(CPP) @<< + $(CPP_SWITCHES) $(SOURCE) +<< + + +!ENDIF + + +!ENDIF + diff --git a/Src/Plugins/DSP/dsp_sps/dxi/AudioPlugIn.rc b/Src/Plugins/DSP/dsp_sps/dxi/AudioPlugIn.rc new file mode 100644 index 00000000..38ba0bd7 --- /dev/null +++ b/Src/Plugins/DSP/dsp_sps/dxi/AudioPlugIn.rc @@ -0,0 +1,202 @@ +//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include \r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)\r\n" + "#ifdef _WIN32\r\n" + "LANGUAGE 9, 1\r\n" + "#pragma code_page(1252)\r\n" + "#endif\r\n" + "#include ""res\\AudioPlugIn.rc2"" // non-Microsoft Visual C++ edited resources\r\n" + "#endif\0" +END + +#endif // APSTUDIO_INVOKED + + +#ifndef _MAC +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,0,1 + PRODUCTVERSION 1,0,0,1 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "Comments", "\0" + VALUE "CompanyName", "\0" + VALUE "FileDescription", "AudioPlugIn DLL\0" + VALUE "FileVersion", "1, 0, 0, 1\0" + VALUE "InternalName", "AudioPlugIn\0" + VALUE "LegalCopyright", "Copyright © 1997\0" + VALUE "LegalTrademarks", "\0" + VALUE "OriginalFilename", "AudioPlugIn.DLL\0" + VALUE "PrivateBuild", "\0" + VALUE "ProductName", "AudioPlugIn Dynamic Link Library\0" + VALUE "ProductVersion", "1, 0, 0, 1\0" + VALUE "SpecialBuild", "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + +#endif // !_MAC + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_DIALOG1 DIALOGEX 0, 0, 343, 222 +STYLE DS_CONTROL | DS_CENTER | WS_CHILD +EXSTYLE WS_EX_TOOLWINDOW +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "Slider1",IDC_SLIDER1,"msctls_trackbar32",TBS_AUTOTICKS | + TBS_VERT | WS_TABSTOP,11,70,11,116 + CONTROL "Slider1",IDC_SLIDER2,"msctls_trackbar32",TBS_AUTOTICKS | + TBS_VERT | WS_TABSTOP,51,70,11,116 + CONTROL "Slider1",IDC_SLIDER3,"msctls_trackbar32",TBS_AUTOTICKS | + TBS_VERT | WS_TABSTOP,91,70,11,116 + EDITTEXT IDC_SLIDER1_LABEL2,24,74,26,13,ES_AUTOHSCROLL + EDITTEXT IDC_SLIDER1_LABEL3,24,170,26,13,ES_AUTOHSCROLL + EDITTEXT IDC_SLIDER1_LABEL1,12,189,38,13,ES_AUTOHSCROLL + EDITTEXT IDC_SLIDER2_LABEL2,64,74,26,13,ES_AUTOHSCROLL + EDITTEXT IDC_SLIDER2_LABEL3,64,170,26,13,ES_AUTOHSCROLL + EDITTEXT IDC_SLIDER2_LABEL1,52,189,38,13,ES_AUTOHSCROLL + EDITTEXT IDC_SLIDER3_LABEL2,104,74,26,13,ES_AUTOHSCROLL + EDITTEXT IDC_SLIDER3_LABEL3,104,170,26,13,ES_AUTOHSCROLL + EDITTEXT IDC_SLIDER3_LABEL1,92,189,38,13,ES_AUTOHSCROLL + PUSHBUTTON "Save",IDC_SAVE,185,4,50,14 + PUSHBUTTON "Help",IDC_SHOWHELP,289,4,50,14 + EDITTEXT IDC_INIT,183,32,155,43,ES_MULTILINE | ES_AUTOVSCROLL | + ES_AUTOHSCROLL | ES_WANTRETURN | WS_VSCROLL + EDITTEXT IDC_ONSLIDERCHANGE,183,87,155,41,ES_MULTILINE | + ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN | + WS_VSCROLL + EDITTEXT IDC_PERSAMPLE,183,141,155,80,ES_MULTILINE | + ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN | + WS_VSCROLL + GROUPBOX "Signal Processing Controls",IDC_STATIC,3,61,176,159 + LTEXT "Initialization/format change:",IDC_STATIC,183,22,88,8 + LTEXT "Per sample (or sample-pair):",IDC_STATIC,183,131,87,8 + LTEXT "Slider change/initialization:",IDC_STATIC,183,77,85,8 + LTEXT "",IDC_PRESET,3,32,175,10,SS_SUNKEN + LTEXT "Current preset:",IDC_STATIC,3,21,47,8 + PUSHBUTTON "Load",IDC_LOAD,3,4,41,14 + PUSHBUTTON "New",IDC_NEW,50,4,41,14 + PUSHBUTTON "",IDC_EDIT,133,4,46,14 + CONTROL "Enable processing",IDC_BYPASS,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,3,47,74,10 + CONTROL "Slider1",IDC_SLIDER4,"msctls_trackbar32",TBS_AUTOTICKS | + TBS_VERT | WS_TABSTOP,130,70,11,116 + EDITTEXT IDC_SLIDER4_LABEL2,144,74,26,13,ES_AUTOHSCROLL + EDITTEXT IDC_SLIDER4_LABEL3,144,170,26,13,ES_AUTOHSCROLL + EDITTEXT IDC_SLIDER4_LABEL1,132,189,38,13,ES_AUTOHSCROLL + PUSHBUTTON "Trigger 1",IDC_TRIGGER1,12,204,38,11 + PUSHBUTTON "Trigger 2",IDC_TRIGGER2,52,204,38,11 + PUSHBUTTON "Trigger 3",IDC_TRIGGER3,92,204,38,11 + PUSHBUTTON "Trigger 4",IDC_TRIGGER4,132,204,38,11 +END + +IDD_EVAL_HELP DIALOG DISCARDABLE 0, 0, 311, 231 +STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "SPS Expression Help" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "Close",IDOK,7,210,50,14 + EDITTEXT IDC_EDIT1,12,25,286,177,ES_MULTILINE | ES_READONLY | + WS_VSCROLL + CONTROL "Tab1",IDC_TAB1,"SysTabControl32",0x0,7,7,297,200 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE DISCARDABLE +BEGIN + IDS_NAME_PLUGIN "Nullsoft SPS DX plugin" + IDS_DESC_PLUGIN "SPS" + IDS_SETTINGS "Settings" + IDS_HELPFILE_PLUGIN "AudioPlugIn.hlp" +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE 9, 1 +#pragma code_page(1252) +#endif +#include "res\AudioPlugIn.rc2" // non-Microsoft Visual C++ edited resources +#endif +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/Src/Plugins/DSP/dsp_sps/dxi/AudioPlugInPropPage.cpp b/Src/Plugins/DSP/dsp_sps/dxi/AudioPlugInPropPage.cpp new file mode 100644 index 00000000..5fbf0bd8 --- /dev/null +++ b/Src/Plugins/DSP/dsp_sps/dxi/AudioPlugInPropPage.cpp @@ -0,0 +1,326 @@ +// AudioPlugInPropPage.cpp : implementation file +// + +#include "stdafx.h" +#include "resource.h" +#include "AudioPlugInPropPage.h" + +#include +#include "CakeMedParam.h" +#include "Parameters.h" + +///////////////////////////////////////////////////////////////////////////// +// CAudioPlugInPropPage property page + +extern HMODULE g_hInst; + +SPSEffectContext *g_fucko_ctx; + +CAudioPlugInPropPage::CAudioPlugInPropPage( IUnknown* pUnk, HRESULT* phr ) : + CUnknown( "AudioPlugInPropPage", pUnk ), + m_hWnd( NULL ), + m_pMediaParams( NULL ), + m_pUICallback( NULL ), + m_pPageSite( NULL ), + m_bDirty( FALSE ) +{ + SPS_initcontext(&m_ctx); //FUCKO + g_fucko_ctx=&m_ctx; +} + +///////////////////////////////////////////////////////////////////////////// + +CUnknown * WINAPI CAudioPlugInPropPage::CreateInstance(LPUNKNOWN lpunk, HRESULT *phr) +{ + return new CAudioPlugInPropPage( lpunk, phr ); +} + +///////////////////////////////////////////////////////////////////////////// + +CAudioPlugInPropPage::~CAudioPlugInPropPage() +{ + if (m_pMediaParams) + m_pMediaParams->Release(); + m_pMediaParams = NULL; + + if (m_pUICallback) + m_pUICallback->Release(); + m_pUICallback = NULL; +} + +///////////////////////////////////////////////////////////////////////////// +// CUnknown + +HRESULT CAudioPlugInPropPage::NonDelegatingQueryInterface( REFIID riid, void** ppv ) +{ + if (IID_IUnknown == riid) + return GetInterface( (IUnknown*)this, ppv ); + else if (IID_IPropertyPage == riid) + return GetInterface( (IPropertyPage*)this, ppv ); + else + { + *ppv = NULL; + return E_NOINTERFACE; + } +} + +//////////////////////////////////////////////////////////////////////////////// +// IPropertyPage + +HRESULT CAudioPlugInPropPage::GetPageInfo( LPPROPPAGEINFO pPageInfo ) +{ + IMalloc* pIMalloc; + if (FAILED( CoGetMalloc( MEMCTX_TASK, &pIMalloc ) )) + return E_FAIL; + + pPageInfo->pszTitle = (LPOLESTR)pIMalloc->Alloc( 256 ); + + pIMalloc->Release(); + + if (!pPageInfo->pszTitle) + return E_OUTOFMEMORY; + + static const char szTitle[] = "AudioPlugIn"; + mbstowcs( pPageInfo->pszTitle, szTitle, strlen( szTitle ) ); + + pPageInfo->size.cx = 100; + pPageInfo->size.cy = 100; + pPageInfo->pszDocString = NULL; + pPageInfo->pszHelpFile = NULL; + pPageInfo->dwHelpContext= 0; + + // Create the property page in order to determine its size + HWND const hWnd = ::CreateDialogParam( g_hInst, MAKEINTRESOURCE( IDD_DIALOG1 ), GetDesktopWindow(), (DLGPROC)StaticDialogProc, 0 ); + if (hWnd) + { + // Get the dialog size and destroy the window + RECT rc; + GetWindowRect( hWnd, &rc ); + pPageInfo->size.cx = rc.right - rc.left; + pPageInfo->size.cy = rc.bottom - rc.top; + DestroyWindow( hWnd ); + } + + return S_OK; +} + +//////////////////////////////////////////////////////////////////////////////// + +HRESULT CAudioPlugInPropPage::SetObjects( ULONG cObjects, LPUNKNOWN* ppUnk ) +{ + // Release old interfaces + if (m_pMediaParams) + m_pMediaParams->Release(); + m_pMediaParams = NULL; + if (m_pUICallback) + m_pUICallback->Release(); + m_pUICallback = NULL; + + // Look for a new IFilter + ULONG cObj = 0; + for (ULONG i = 0; i < cObjects; ++i) + { + if (S_OK == ppUnk[i]->QueryInterface( IID_IMediaParams, (void**)&m_pMediaParams )) + { + ppUnk[i]->QueryInterface( IID_IMediaParamsUICallback, (void**)&m_pUICallback ); + break; + } + } + + // Update controls if we've got a new object and we're activated + if (m_pMediaParams && ::IsWindow( m_hWnd )) + UpdateControls(); + + return S_OK; +} + +//////////////////////////////////////////////////////////////////////////////// + +extern BOOL CALLBACK SPS_configWindowProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam); + +BOOL CALLBACK CAudioPlugInPropPage::StaticDialogProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) +{ +#if 0 + CAudioPlugInPropPage* pPage; + + if (WM_INITDIALOG == uMsg) + { + SetWindowLong( hwnd, DWL_USER, lParam ); + pPage = reinterpret_cast(lParam); + if (!pPage) + return TRUE; + } + + pPage = reinterpret_cast(GetWindowLong( hwnd, DWL_USER )); + if (!pPage) + return TRUE; + + return pPage->DialogProc( hwnd, uMsg, wParam, lParam ); +#endif + return SPS_configWindowProc(hwnd, uMsg, wParam,lParam); +} + +//////////////////////////////////////////////////////////////////////////////// + +BOOL CAudioPlugInPropPage::DialogProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) +{ + //return SPS_configWindowProc(hwnd, uMsg, wParam,lParam); + return 0; + +/* switch( uMsg ) + { + case WM_INITDIALOG: + m_hWnd = hwnd; + break; + + default: + return FALSE; + } + + return TRUE;*/ +} + +//////////////////////////////////////////////////////////////////////////////// + +void CAudioPlugInPropPage::UpdateControls() +{ + // TODO: update all UI elements to reflect new control state +} + +//////////////////////////////////////////////////////////////////////////////// + +HRESULT CAudioPlugInPropPage::Activate( HWND hwndParent, LPCRECT pRect, BOOL fModal ) +{ + if (!pRect) + return E_POINTER; + if (NULL != m_hWnd) + return E_UNEXPECTED; // already active! + + m_hWnd = CreateDialogParam( g_hInst, MAKEINTRESOURCE( IDD_DIALOG1 ), hwndParent, (DLGPROC)StaticDialogProc, (LPARAM)&m_ctx ); + if (!m_hWnd) + return E_OUTOFMEMORY; + + // Refresh the property page controls + UpdateControls(); + + // Move page into position and show it + Move( pRect ); + Show( SW_SHOWNORMAL ); + + return S_OK; +} + +//////////////////////////////////////////////////////////////////////////////// + +HRESULT CAudioPlugInPropPage::Move( LPCRECT pRect ) +{ + if (!pRect) + return E_POINTER; + if (NULL == m_hWnd) + E_UNEXPECTED; + + MoveWindow( m_hWnd, pRect->left, pRect->top, pRect->right - pRect->left, pRect->bottom - pRect->top, TRUE ); + + return S_OK; +} + +//////////////////////////////////////////////////////////////////////////////// + +HRESULT CAudioPlugInPropPage::Show( UINT nCmdShow ) +{ + if (NULL == m_hWnd) + E_UNEXPECTED; + // Ignore wrong show flags + if (nCmdShow != SW_SHOW && nCmdShow != SW_SHOWNORMAL && nCmdShow != SW_HIDE) + return E_INVALIDARG; + + ShowWindow( m_hWnd, nCmdShow ); + + if (SW_SHOWNORMAL == nCmdShow || SW_SHOW == nCmdShow) + { + // TODO: set the focus to which control needs it + } + + return S_OK; +} + +//////////////////////////////////////////////////////////////////////////////// + +HRESULT CAudioPlugInPropPage::Deactivate() +{ + if (NULL == m_hWnd) + return E_UNEXPECTED; + + DestroyWindow( m_hWnd ); + m_hWnd = NULL; + return S_OK; +} + +//////////////////////////////////////////////////////////////////////////////// + +HRESULT CAudioPlugInPropPage::SetPageSite( LPPROPERTYPAGESITE pPageSite ) +{ + if (pPageSite) + { + if (m_pPageSite) + return E_UNEXPECTED; + m_pPageSite = pPageSite; + m_pPageSite->AddRef(); + } + else + { + if (m_pPageSite == NULL) + return E_UNEXPECTED; + m_pPageSite->Release(); + m_pPageSite = NULL; + } + return S_OK; +} + +//////////////////////////////////////////////////////////////////////////////// + +HRESULT CAudioPlugInPropPage::Apply() +{ + // Take no action except clearing the dirty flag. + // So that the property page may be used in realtime, all user interface + // changes are immediately passed to the filter. I.e. there is no Cancel. + m_bDirty = FALSE; + return S_OK; +} + +//////////////////////////////////////////////////////////////////////////////// + +HRESULT CAudioPlugInPropPage::IsPageDirty( void ) +{ + return m_bDirty ? S_OK : S_FALSE; +} + +///////////////////////////////////////////////////////////////////////////// + +HRESULT CAudioPlugInPropPage::Help( LPCWSTR lpszHelpDir ) +{ + // Get location of DLL + char szDLL[ _MAX_PATH ]; + if (0 == ::GetModuleFileName( g_hInst, szDLL, sizeof szDLL )) + return E_FAIL; + + // Convert to location of .HLP file + char szHelp[ _MAX_PATH ]; + ::strncpy( szHelp, szDLL, ::strlen( szDLL ) - 3 ); + ::strcat( szHelp, "HLP" ); + + // Call help + if (::WinHelp( m_hWnd, szHelp, HELP_CONTENTS, NULL )) + return S_OK; + + return E_FAIL; +} + +///////////////////////////////////////////////////////////////////////////// + +HRESULT CAudioPlugInPropPage::TranslateAccelerator( LPMSG lpMsg ) +{ + return E_NOTIMPL; +} + +///////////////////////////////////////////////////////////////////////////// diff --git a/Src/Plugins/DSP/dsp_sps/dxi/AudioPlugInPropPage.h b/Src/Plugins/DSP/dsp_sps/dxi/AudioPlugInPropPage.h new file mode 100644 index 00000000..10485990 --- /dev/null +++ b/Src/Plugins/DSP/dsp_sps/dxi/AudioPlugInPropPage.h @@ -0,0 +1,68 @@ +#ifndef _PLUGIN_PROP_PAGE_H_ +#define _PLUGIN_PROP_PAGE_H_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#include "../sps_common.h" + +struct IMediaParams; +struct IMediaParamsUICallback; + +///////////////////////////////////////////////////////////////////////////// +// CAudioPlugInPropPage dialog + +class CAudioPlugInPropPage : + public CUnknown, + public IPropertyPage +{ +// Construction +public: + CAudioPlugInPropPage( IUnknown* pUnk, HRESULT* phr ); + virtual ~CAudioPlugInPropPage(); + + // CUnknown + DECLARE_IUNKNOWN; + STDMETHODIMP NonDelegatingQueryInterface(REFIID riid,void **ppv); + + // *** IPropertyPage methods *** + STDMETHODIMP_(HRESULT) SetPageSite(LPPROPERTYPAGESITE pPageSite); + STDMETHODIMP_(HRESULT) Activate(HWND hwndParent, LPCRECT prect, BOOL fModal); + STDMETHODIMP_(HRESULT) Deactivate(void); + STDMETHODIMP_(HRESULT) GetPageInfo(LPPROPPAGEINFO pPageInfo); + STDMETHODIMP_(HRESULT) SetObjects(ULONG cObjects, LPUNKNOWN *ppUnk); + STDMETHODIMP_(HRESULT) Show(UINT nCmdShow); + STDMETHODIMP_(HRESULT) Move(LPCRECT prect); + STDMETHODIMP_(HRESULT) IsPageDirty(void); + STDMETHODIMP_(HRESULT) Apply(void); + STDMETHODIMP_(HRESULT) Help(LPCWSTR lpszHelpDir); + STDMETHODIMP_(HRESULT) TranslateAccelerator(LPMSG lpMsg); + +public: + + static CUnknown * WINAPI CreateInstance(LPUNKNOWN lpunk, HRESULT *phr); + +// Implementation +protected: + + void UpdateControls(); + BOOL DialogProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ); + + static BOOL CALLBACK StaticDialogProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ); + +private: + + HWND m_hWnd; + BOOL m_bDirty; + IPropertyPageSite* m_pPageSite; + IMediaParams* m_pMediaParams; + IMediaParamsUICallback* m_pUICallback; + + SPSEffectContext m_ctx; +}; + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // _PLUGIN_PROP_PAGE_H_ diff --git a/Src/Plugins/DSP/dsp_sps/dxi/Filter.cpp b/Src/Plugins/DSP/dsp_sps/dxi/Filter.cpp new file mode 100644 index 00000000..35fef3a7 --- /dev/null +++ b/Src/Plugins/DSP/dsp_sps/dxi/Filter.cpp @@ -0,0 +1,1139 @@ +// Filter.cpp : implementation file +// + +#include "stdafx.h" +#include "resource.h" +#include +#include +#include +#include +#include "Filter.h" +#include "AudioPlugInPropPage.h" + +#ifdef _DEBUG +#pragma comment(lib, "strmbasd.lib") +#pragma comment(lib, "asynbased.lib") +#else +#pragma comment(lib, "strmbase.lib") +#endif + +#include "PlugInGUIDs.h" + +#define PLUGTITLE L"Nullsoft SPS Plug-In" + +///////////////////////////////////////////////////////////////////////////// +// Setup data + +static AMOVIESETUP_MEDIATYPE sudPinTypes = +{ + &MEDIATYPE_Audio, // Major CLSID + &MEDIASUBTYPE_NULL // Minor type +}; + +static AMOVIESETUP_PIN psudPins[] = +{ + { L"Input", // Pin's string name + FALSE, // Is it rendered + FALSE, // Is it an output + FALSE, // Allowed none + FALSE, // Allowed many + &CLSID_NULL, // Connects to filter + L"Output", // Connects to pin + 1, // Number of types + &sudPinTypes }, // Pin information + { L"Output", // Pin's string name + FALSE, // Is it rendered + TRUE, // Is it an output + FALSE, // Allowed none + FALSE, // Allowed many + &CLSID_NULL, // Connects to filter + L"Input", // Connects to pin + 1, // Number of types + &sudPinTypes } // Pin information +}; + +static AMOVIESETUP_FILTER sudFilter = +{ + &CLSID_Filter, // CLSID of filter + PLUGTITLE, // Filter's name + MERIT_DO_NOT_USE, // Filter merit + 2, // Number of pins + psudPins // Pin information +}; + +///////////////////////////////////////////////////////////////////////////// + +CFactoryTemplate g_Templates[ 2 ] = +{ + { PLUGTITLE + , &CLSID_Filter + , CFilter::CreateInstance + , NULL + , &sudFilter } + , + { L"AudioPlugIn Properties" + , &CLSID_FilterPropPage + , CAudioPlugInPropPage::CreateInstance + , NULL + , NULL } +}; + +int g_cTemplates = sizeof(g_Templates) / sizeof(g_Templates[0]); + +//-------------------------------------------------------------------------------- +// Helper function to notify the graph about pin changes + +static void notifyGraphChanged( IFilterGraph* pGraph ) +{ + if (pGraph) + { + IMediaEventSink* pSink = NULL; + if (S_OK == pGraph->QueryInterface( IID_IMediaEventSink, (void**)&pSink )) + { + pSink->Notify( EC_GRAPH_CHANGED, 0, 0 ); + pSink->Release(); + } + } +} + + +//////////////////////////////////////////////////////////////////////////////// +// CFilter +//////////////////////////////////////////////////////////////////////////////// + +//-------------------------------------------------------------------------------- +// Ctor + +CFilter::CFilter( TCHAR* pName, LPUNKNOWN pUnk, HRESULT* phr ) : + CBaseFilter( pName, pUnk, this, CLSID_Filter, phr ), + CPersistStream( pUnk, phr ), + CAudioPlugIn( phr ), + m_pinInput( NAME("Input"), this, phr, L"Input" ), + m_pinOutput( NAME("Output"), this, phr, L"Output" ), + m_llSamplePosition( 0 ) +{ + if (SUCCEEDED( *phr )) + { + // Create our DirectX automation helper object + *phr = CMediaParams::Create( &m_pMediaParams, (IUnknown*)(IBaseFilter*)this ); + } + + if (SUCCEEDED( *phr )) + { + // Initialize the plug-in + *phr = Initialize(); + } +} + + +//------------------------------------------------------------------------------ +// Factory-style construction + +CUnknown * WINAPI CFilter::CreateInstance( LPUNKNOWN pUnk, HRESULT* phr ) +{ + return new CFilter( NAME("Filter"), pUnk, phr ); +} + + +//-------------------------------------------------------------------------------- +// Expose setup data for registration + +LPAMOVIESETUP_FILTER CFilter::GetSetupData() +{ + return &sudFilter; +} + + +//-------------------------------------------------------------------------------- +// Expose other interfaces to the world + +STDMETHODIMP CFilter::NonDelegatingQueryInterface( REFIID riid, void** ppv ) +{ + if (riid == IID_IDispatch) + return GetInterface((IDispatch*)this, ppv); + else if (riid == IID_ISpecifyPropertyPages) + return GetInterface((ISpecifyPropertyPages*)this, ppv); + else if (riid == IID_IPersistStream) + return GetInterface((IPersistStream*)this, ppv); + else if (NULL != m_pMediaParams) + { + if (riid == IID_IMediaParams) + return GetInterface((IMediaParams*)m_pMediaParams, ppv); + else if (riid == IID_IMediaParamInfo) + return GetInterface((IMediaParamInfo*)m_pMediaParams, ppv); + else if (riid == IID_IMediaParamsSetUICallback) + return GetInterface((IMediaParamsSetUICallback*)m_pMediaParams, ppv); + else if (riid == IID_IMediaParamsUICallback) + return GetInterface((IMediaParamsUICallback*)m_pMediaParams, ppv); + } + + return CBaseFilter::NonDelegatingQueryInterface(riid, ppv); +} + + +//-------------------------------------------------------------------------------- +// CFilter dtor + +CFilter::~CFilter() +{ + if (m_pMediaParams) + m_pMediaParams->Release(), m_pMediaParams = NULL; +} + + +//-------------------------------------------------------------------------------- +// Get the total number of pins in our filter + +int CFilter::GetPinCount() +{ + return 2; +} + + +//-------------------------------------------------------------------------------- +// Get the Nth pin from our filter (CBaseFilter pure virtual override) +// Pin 0 is the input pin and all others are output pins + +CBasePin* CFilter::GetPin( int n ) +{ + if (0 == n) + return &m_pinInput; + else if (1 == n) + return &m_pinOutput; + else + return NULL; +} + + +//-------------------------------------------------------------------------------- +// Helper for checking media types + +HRESULT CFilter::CheckMediaType( PIN_DIRECTION pinDir, const CMediaType* pmt ) +{ + if (NULL == pmt) + return E_POINTER; + + // Must be audio + if (pmt->majortype != MEDIATYPE_Audio) + return VFW_E_TYPE_NOT_ACCEPTED; + + // Must not be compressed + if (pmt->bTemporalCompression) + return VFW_E_TYPE_NOT_ACCEPTED; + + // Must be WAVEFORMATEX + if (pmt->cbFormat < sizeof(WAVEFORMATEX)) + return VFW_E_TYPE_NOT_ACCEPTED; + + WAVEFORMATEX* pwfx = reinterpret_cast( pmt->Format() ); + + // Must be 16-bit PCM or 32-bit float + BOOL const bInt16 = + (WAVE_FORMAT_PCM == pwfx->wFormatTag && 16 == pwfx->wBitsPerSample) || + (WAVE_FORMAT_EXTENSIBLE == pwfx->wFormatTag && 16 == pwfx->wBitsPerSample); + BOOL const bFloat32 = + (WAVE_FORMAT_IEEE_FLOAT == pwfx->wFormatTag && 32 == pwfx->wBitsPerSample); + if (!bInt16 && !bFloat32) + return VFW_E_TYPE_NOT_ACCEPTED; + + // Must be mono or stereo + if (1 != pwfx->nChannels && 2 != pwfx->nChannels) + return VFW_E_TYPE_NOT_ACCEPTED; + + // Let the plug-in decide the rest + if (PINDIR_INPUT == pinDir) + return IsValidInputFormat( pwfx ); + else + return IsValidOutputFormat( pwfx ); +} + + +//-------------------------------------------------------------------------------- +// CheckTransform + +HRESULT CFilter::CheckTransform( const CMediaType* pmtIn, const CMediaType* pmtOut ) +{ + if (NULL == pmtIn) + return E_POINTER; + if (NULL == pmtOut) + return E_POINTER; + + // Make sure input/output types are valid + HRESULT hr = S_OK; + hr = CheckMediaType( PINDIR_INPUT, pmtIn ); + if (FAILED( hr )) + return hr; + hr = CheckMediaType( PINDIR_OUTPUT, pmtOut ); + if (FAILED( hr )) + return hr; + + // Make sure sample rates are the same + WAVEFORMATEX* pwfxIn = reinterpret_cast( pmtIn->Format() ); + WAVEFORMATEX* pwfxOut = reinterpret_cast( pmtOut->Format() ); + if (pwfxIn->nSamplesPerSec != pwfxOut->nSamplesPerSec) + return VFW_E_TYPE_NOT_ACCEPTED; + + // Let the plug-in decide for sure + return IsValidTransform( pwfxIn, pwfxOut ); +} + + +//-------------------------------------------------------------------------------- +// GetMediaType + +HRESULT CFilter::GetMediaType( int iPosition, CMediaType* pmt ) +{ + if (!m_pinInput.IsConnected()) + return E_UNEXPECTED; + + if (iPosition < 0) + return E_INVALIDARG; + + if (iPosition > 0) + return VFW_S_NO_MORE_ITEMS; + + // Get pointers to input/ouput formats + WAVEFORMATEX* pwfxIn = reinterpret_cast( m_pinInput.CurrentMediaType().Format() ); + if (NULL == pwfxIn) + return E_FAIL; + WAVEFORMATEX* pwfxOut = reinterpret_cast( pmt->Format() ); + if (NULL == pwfxOut) + return E_FAIL; + + // Assume output format is the same as input format + *pwfxOut = *pwfxIn; + + // let the plug-in suggest something different + return SuggestOutputFormat( pwfxOut ); +} + + +//-------------------------------------------------------------------------------- +// SetMediaType + +HRESULT CFilter::SetMediaType( PIN_DIRECTION pindir, const CMediaType* pmt ) +{ + CAutoLock autoLock(m_pLock); + HRESULT hr = NOERROR; + + // Make sure it's a valid audio type + hr = CheckMediaType( pindir, pmt ); + if (S_OK != hr) + return hr; + + if (PINDIR_INPUT == pindir) + m_wfxIn = *reinterpret_cast( pmt->Format() ); + else if (PINDIR_OUTPUT == pindir) + m_wfxOut = *reinterpret_cast( pmt->Format() ); + + m_pMediaParams->SetSampleRate( m_wfxIn.nSamplesPerSec ); + + return NOERROR; +} + + +//-------------------------------------------------------------------------------- +// Run: Overriden to handle no input connections, and to notify the plug-in + +STDMETHODIMP CFilter::Run( REFERENCE_TIME tStart ) +{ + CAutoLock cObjectLock(m_pLock); + + CBaseFilter::Run( tStart ); + if (!m_pinInput.IsConnected()) + m_pinInput.EndOfStream(); + + return NOERROR; +} + + +//-------------------------------------------------------------------------------- +// Pause: Overriden to handle no input connections, and to notify the plug-in + +STDMETHODIMP CFilter::Pause() +{ + CAutoLock cObjectLock(m_pLock); + + if (State_Paused == m_State) + return S_OK; // nothing to do + + BOOL const bPreRoll = (State_Stopped == m_State); + + // Set this to something in case we never see a media sample timestamp + m_llSamplePosition = 0; + + // Let the base-class do the real pausing + HRESULT hr = CBaseFilter::Pause(); + + // Deal with unexpected disconnect + if (!m_pinInput.IsConnected()) + m_pinInput.EndOfStream(); + + if (bPreRoll && SUCCEEDED( hr )) + hr = AllocateResources(); + + return hr; +} + + +//-------------------------------------------------------------------------------- +// Stop: Overriden to handle no input connections, and to notify the plug-in + +STDMETHODIMP CFilter::Stop() +{ + CAutoLock cObjectLock(m_pLock); + + if (State_Stopped == m_State) + return S_OK; // nothing to do + + // Let the base-class do the real stopping + HRESULT hr = CBaseFilter::Stop(); + if (SUCCEEDED( hr )) + hr = FreeResources(); + + return hr; +} + + +//-------------------------------------------------------------------------------- +// Initialize an AudioBuffer and IMediaSample for an output pin. + +HRESULT CFilter::getOutputBuffer( CFilterOutputPin* pOutputPin, + AudioBuffer* pbufOut, + REFERENCE_TIME* prtStart, + BOOL bSyncPoint, BOOL bDiscontinuity, BOOL bPreroll ) +{ + if (NULL == pOutputPin) + return E_POINTER; + if (NULL == pbufOut) + return E_POINTER; + if (!pOutputPin->IsConnected()) + return E_UNEXPECTED; + + // Ask our output pin to allocate an output media sample + HRESULT hr = pOutputPin->GetDeliveryBuffer( &pbufOut->pms, NULL, NULL, 0 ); + if (FAILED( hr )) + return hr; + + // Set media sample properties + pbufOut->pms->SetTime( prtStart, NULL ); + pbufOut->pms->SetSyncPoint( bSyncPoint ); + pbufOut->pms->SetDiscontinuity( bDiscontinuity ); + pbufOut->pms->SetPreroll( bPreroll ); + + // Set the output size + pbufOut->cSamp = pbufOut->pms->GetSize() / m_wfxOut.nBlockAlign; + pbufOut->lOffset = 0; + + // Tag the output buffer as being all zeros + pbufOut->SetZerofill( TRUE ); + + return S_OK; +} + + +//-------------------------------------------------------------------------------- +// Deliver results and cleanup + +HRESULT CFilter::deliverOutputBuffer( CFilterOutputPin* pOutputPin, + AudioBuffer* pbufOut, + HRESULT hrProcess, BOOL bCleanup ) +{ + if (NULL == pbufOut) + return E_POINTER; + if (NULL == pbufOut->pms) + return E_POINTER; + if (!pOutputPin->IsConnected()) + return E_UNEXPECTED; + + HRESULT hr = S_OK; + + // Deliver only if we need to + if (S_OK == hrProcess) + { + if (pbufOut->GetZerofill()) + { + // If the output buffer support IDeferZeroFill, there is nothing more + // that we need to do to zerofill it: the zerofill will occur when some + // downstream filter attempts to get the media sample's buffer pointer. + // buffer fill if possible. + IDeferZeroFill* pdzf = NULL; + if (S_OK == pbufOut->pms->QueryInterface( IID_IDeferZeroFill, (void**)&pdzf )) + { + pdzf->Release(); + } + else + { + // AudioBuffer::GetPointer will handle any zerofill for us. + pbufOut->GetPointer(); + } + } + + // Set the final output sample size + hr = pbufOut->pms->SetActualDataLength( pbufOut->cSamp * m_wfxOut.nBlockAlign ); + if (SUCCEEDED( hr )) + { + // Tell the current output pin to deliver this sample + hr = pOutputPin->Deliver( pbufOut->pms ); + } + } + else if (S_FALSE == hrProcess) + { + hr = pOutputPin->DeliverEndOfStream(); + } + + // Release the media sample once we're done with it + if (bCleanup) + { + pbufOut->pms->Release(); + pbufOut->pms = NULL; + } + + return hr; +} + + +//-------------------------------------------------------------------------------- +// ISpecifyPropertyPages + +STDMETHODIMP CFilter::GetPages(CAUUID *pPages) +{ + if (NULL == pPages) + return E_POINTER; + + pPages->cElems = 1; + pPages->pElems= (GUID*) CoTaskMemAlloc( sizeof(GUID) ); + + if (pPages->pElems==NULL) + return E_OUTOFMEMORY; + + pPages->pElems[0] = CLSID_FilterPropPage; + + return NOERROR; +} + + +//-------------------------------------------------------------------------------- +// CPersistStream + +STDMETHODIMP CFilter::GetClassID(CLSID* pClsid) +{ + if (NULL == pClsid) + return E_POINTER; + *pClsid = CLSID_Filter; + return NOERROR; +} + +int CFilter::SizeMax() +{ + return PersistGetSize(); +} + +HRESULT CFilter::WriteToStream(IStream* pStream) +{ + CAutoLock cObjectLock(m_pLock); + + return PersistSave( pStream ); +} + +HRESULT CFilter::ReadFromStream(IStream* pStream) +{ + CAutoLock cObjectLock(m_pLock); + + return PersistLoad( pStream ); +} + + +//-------------------------------------------------------------------------------- +// IDispatch is exposed only so MFC's COlePropertyPage can connect; ever single +// method is not implemented. + +HRESULT CFilter::GetTypeInfoCount( UINT* ) +{ + return E_NOTIMPL; +} + +HRESULT CFilter::GetTypeInfo( UINT, LCID, ITypeInfo** ) +{ + return E_NOTIMPL; +} + +HRESULT CFilter::GetIDsOfNames( REFIID, OLECHAR**, UINT, LCID, DISPID* ) +{ + return E_NOTIMPL; +} + +HRESULT CFilter::Invoke( DISPID, REFIID, LCID, WORD, DISPPARAMS*, VARIANT*, EXCEPINFO*, UINT* ) +{ + return E_NOTIMPL; +} + + +////////////////////////////////////////////////////////////////////////////////// +// CFilterInputPin +////////////////////////////////////////////////////////////////////////////////// + +//-------------------------------------------------------------------------------- +// Ctor + +CFilterInputPin::CFilterInputPin( TCHAR* pObjDesc, CFilter* pFilter, HRESULT* phr, LPCWSTR pPinName ) : + CBaseInputPin( pObjDesc, pFilter, pFilter, phr, pPinName ), + m_pFilter( pFilter ), + m_bInsideCheckMediaType( FALSE ) +{ + ASSERT( pFilter ); +} + + +//-------------------------------------------------------------------------------- +// CheckMediaType + +HRESULT CFilterInputPin::CheckMediaType( const CMediaType* pmt ) +{ + CAutoLock autoLock(m_pLock); + HRESULT hr = NOERROR; + + // If we are already inside checkmedia type for this pin, return NOERROR. + // It is possble to hookup two of the these filters and some other filter + // like the video effects sample to get into this situation. If we don't + // detect this situation, we will carry on looping till we blow the stack. + + if (m_bInsideCheckMediaType) + return NOERROR; + + m_bInsideCheckMediaType = TRUE; + + // See if the wave format is supported + hr = m_pFilter->CheckMediaType( PINDIR_INPUT, pmt ); + if (S_OK != hr) + { + m_bInsideCheckMediaType = FALSE; + return hr; + } + + // Make sure the output pin agrees on the type + if (m_pFilter->m_pinOutput.IsConnected()) + { + // See if we can transform from input to the current output type + const CMediaType& mtOut = m_pFilter->m_pinOutput.CurrentMediaType(); + hr = m_pFilter->CheckTransform( pmt, &mtOut ); + if (FAILED( hr )) + { + m_bInsideCheckMediaType = FALSE; + return VFW_E_TYPE_NOT_ACCEPTED; + } + } + + // Either all the downstream pins have accepted or there are none. + m_bInsideCheckMediaType = FALSE; + return NOERROR; +} + + +//-------------------------------------------------------------------------------- +// Set the media type for this connection + +HRESULT CFilterInputPin::SetMediaType( const CMediaType* pmtIn ) +{ + // Set the base class media type (should always succeed) + HRESULT hr = CBaseInputPin::SetMediaType( pmtIn ); + ASSERT( SUCCEEDED( hr ) ); + + // Check the transform can be done (should always succeed) + hr = m_pFilter->CheckMediaType( PINDIR_INPUT, pmtIn ); + ASSERT( SUCCEEDED( hr ) ); + + return m_pFilter->SetMediaType( PINDIR_INPUT, pmtIn ); +} + + +//-------------------------------------------------------------------------------- +// CompleteConnect: Negotiate possible reconnection if types don't match + +HRESULT CFilterInputPin::CompleteConnect( IPin* pRecievePin ) +{ + HRESULT hr = S_OK; + + const CMediaType& mtIn = m_pFilter->m_pinInput.CurrentMediaType(); + + if (m_pFilter->m_pinOutput.IsConnected()) + { + const CMediaType& mtOut = m_pFilter->m_pinOutput.CurrentMediaType(); + hr = m_pFilter->CheckTransform( &mtIn, &mtOut ); + if (FAILED( hr )) + { + if (NULL == m_pFilter->m_pGraph) + hr = VFW_E_NOT_IN_GRAPH; + else + hr = m_pFilter->m_pGraph->Reconnect( &m_pFilter->m_pinOutput ); + } + } + + return hr; +} + + +//-------------------------------------------------------------------------------- +// Receive: override to send message to all downstream pins + +HRESULT CFilterInputPin::Receive( IMediaSample* pms ) +{ + CAutoLock autoLock(m_pLock); + HRESULT hr = NOERROR; + + ASSERT( pms ); + + // Grab the time stamp on discontinuity samples. We'll synthesize + // time stamps for all the rest. + REFERENCE_TIME rtStart = 0; + REFERENCE_TIME* prtStart = NULL; + if (S_OK == pms->IsDiscontinuity()) + { + REFERENCE_TIME rtEnd; + HRESULT hrGetTime = pms->GetTime( &rtStart, &rtEnd ); + if (S_OK == hrGetTime || VFW_S_NO_STOP_TIME == hrGetTime) + { + m_pFilter->m_llSamplePosition = (rtStart * m_pFilter->m_wfxIn.nSamplesPerSec) / UNITS; + prtStart = &rtStart; + } + } + + // Determine the total number of samples to be processed + long const cSampTotal = pms->GetActualDataLength() / m_pFilter->m_wfxIn.nBlockAlign; + + // Set up an input AudioBuffer + AudioBuffer bufIn; + bufIn.cSamp = cSampTotal; + bufIn.pms = pms; + + // Check to see if the input buffer is defer zero fill + IDeferZeroFill* pdzf = NULL; + if (S_OK == pms->QueryInterface( IID_IDeferZeroFill, (void**)&pdzf )) + { + bufIn.SetZerofill( pdzf->get_NeedsZerofill() ); + pdzf->Release(); + } + else + bufIn.SetZerofill( FALSE ); + + // Determine the set of decimation points for processing + long const lFs = m_pFilter->m_wfxIn.nSamplesPerSec; + int const cbSampIn = m_pFilter->m_wfxIn.nBlockAlign; + int const cbSampOut = m_pFilter->m_wfxOut.nBlockAlign; + std::vector samplePos; + hr = m_pFilter->m_pMediaParams->GetDecimationTimes( m_pFilter->m_llSamplePosition, m_pFilter->m_llSamplePosition + bufIn.cSamp, &samplePos ); + if (FAILED( hr )) + return hr; + + AudioBuffer* pbufOut; + +#if PROCESS_IN_PLACE + + // Make sure we don't double-release the shared media sample + bufIn.pms->AddRef(); + + // Point to the same input buffer + pbufOut = &bufIn; + +#else // !PROCESS_IN_PLACE + + AudioBuffer bufOut; + + // Get a brand new output buffer + hr = m_pFilter->getOutputBuffer( &m_pFilter->m_pinOutput, &bufOut, prtStart, + S_OK == pms->IsSyncPoint(), + S_OK == pms->IsDiscontinuity(), + S_OK == pms->IsPreroll() ); + if (FAILED( hr )) + return hr; + + // Set output buffer size = input buffer size + bufOut.cSamp = cSampTotal; + + // Point to this brand new output buffer + pbufOut = &bufOut; + +#endif // PROCESS_IN_PLACE + + HRESULT hrProcess = S_OK; + LONGLONG llPrevPos = m_pFilter->m_llSamplePosition; + LONGLONG llCurrPos = llPrevPos; + long cSampDone = 0; + for (std::vector::iterator it = samplePos.begin(); it != samplePos.end(); it++) + { + // Get the next "landmark", i.e., the next point in time where either a + // shape starts or ends, or where we periodically decimate. + llCurrPos = *it; + + // Position all automation parameters + hr = m_pFilter->m_pMediaParams->UpdateValuesForSample( llPrevPos ); + if (FAILED( hr )) + return hr; + + // Don't process if we haven't changed position + if (llCurrPos == llPrevPos) + continue; + + // Determine the number of samples processed in this iteration + long const cSampIter = long( min( llCurrPos - llPrevPos, cSampTotal - cSampDone ) ); + if (0 == cSampIter) + continue; // nothing to do this iteration + + // Set the buffer sizes and offsets + bufIn.cSamp = cSampIter; + +#if !PROCESS_IN_PLACE + pbufOut->cSamp = bufIn.cSamp; +#endif + + // Process the audio + hrProcess = m_pFilter->Process( llPrevPos, &bufIn, pbufOut ); + if (FAILED( hrProcess )) + break; + + // Update buffer offsets + bufIn.lOffset += (cSampIter * cbSampIn); + ASSERT( bufIn.lOffset <= (cSampTotal * cbSampIn) ); + +#if !PROCESS_IN_PLACE + pbufOut->lOffset += (cSampIter * cbSampOut); + ASSERT( pbufOut->lOffset <= (cSampTotal * cbSampOut) ); +#endif + + // On to the next... + llPrevPos = llCurrPos; + cSampDone += cSampIter; + } + + // Process the final buffer + if (SUCCEEDED( hrProcess ) && cSampDone < cSampTotal) + { + // Position all automation parameters + hr = m_pFilter->m_pMediaParams->UpdateValuesForSample( llPrevPos ); + if (FAILED( hr )) + return hr; + + // Set the final buffer size + bufIn.cSamp = cSampTotal - cSampDone; + +#if !PROCESS_IN_PLACE + pbufOut->cSamp = bufIn.cSamp; +#endif + + // Process the audio + hrProcess = m_pFilter->Process( llPrevPos, &bufIn, pbufOut ); + } + + // Deliver results and cleanup + pbufOut->lOffset = 0; + pbufOut->cSamp = cSampTotal; + hr = m_pFilter->deliverOutputBuffer( &m_pFilter->m_pinOutput, pbufOut, hrProcess, TRUE ); + + // Propagate any failures in processing + if (FAILED( hrProcess )) + hr = hrProcess; + + // Update our position + m_pFilter->m_llSamplePosition += cSampTotal; + + return NOERROR; +} + + +//-------------------------------------------------------------------------------- +// EndOfStream: override to send message to all downstream pins + +HRESULT CFilterInputPin::EndOfStream() +{ + CAutoLock autoLock(m_pLock); + HRESULT hr = NOERROR; + + // Get an output buffer + AudioBuffer bufOut; + hr = m_pFilter->getOutputBuffer( &m_pFilter->m_pinOutput, &bufOut, + NULL, FALSE, FALSE, FALSE ); + if (FAILED( hr )) + return hr; + + int const cSamp = bufOut.cSamp; + + // Deliver buffers until the entire flush is complete + for (;;) + { + // Process the audio + HRESULT hrProcess = m_pFilter->Process( m_pFilter->m_llSamplePosition, NULL, &bufOut ); + + // Deliver results, but don't cleanup yet + hr = m_pFilter->deliverOutputBuffer( &m_pFilter->m_pinOutput, &bufOut, hrProcess, FALSE ); + + // Propagate any failures in processing + if (FAILED( hrProcess )) + hr = hrProcess; + + // Stop at end-of-stream or failure + if (S_FALSE == hrProcess || FAILED( hr )) + break; + + // Update our position + m_pFilter->m_llSamplePosition += cSamp; + } + + // Clean up output buffer memory + m_pFilter->deliverOutputBuffer( &m_pFilter->m_pinOutput, &bufOut, S_FALSE, TRUE ); + + return hr; +} + + +//-------------------------------------------------------------------------------- +// BeginFlush: override to send message to all downstream pins + +HRESULT CFilterInputPin::BeginFlush() +{ + CAutoLock autoLock(m_pLock); + HRESULT hr = NOERROR; + + if (m_pFilter->m_pinOutput.IsConnected()) + { + hr = m_pFilter->m_pinOutput.DeliverBeginFlush(); + if (FAILED( hr )) + return hr; + } + + return CBaseInputPin::BeginFlush(); +} + + +//-------------------------------------------------------------------------------- +// EndFlush: override to send message to all downstream pins + +HRESULT CFilterInputPin::EndFlush() +{ + CAutoLock autoLock(m_pLock); + HRESULT hr = NOERROR; + + if (m_pFilter->m_pinOutput.IsConnected()) + { + hr = m_pFilter->m_pinOutput.DeliverEndFlush(); + if (FAILED( hr )) + return hr; + } + + return CBaseInputPin::EndFlush(); +} + + +//-------------------------------------------------------------------------------- +// NewSegment: override to send message to all downstream pins + +HRESULT CFilterInputPin::NewSegment( REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate ) +{ + CAutoLock autoLock(m_pLock); + HRESULT hr = NOERROR; + + if (m_pFilter->m_pinOutput.IsConnected()) + { + hr = m_pFilter->m_pinOutput.DeliverNewSegment( tStart, tStop, dRate ); + if (FAILED( hr )) + return hr; + } + + return CBaseInputPin::NewSegment( tStart, tStop, dRate ); +} + + + +////////////////////////////////////////////////////////////////////////////////// +// CFilterOutputPin +////////////////////////////////////////////////////////////////////////////////// + +DWORD CFilterOutputPin::m_idNext = 1; + +//-------------------------------------------------------------------------------- +// Ctor + +CFilterOutputPin::CFilterOutputPin( TCHAR* pObjDesc, + CFilter* pFilter, + HRESULT* phr, + LPCWSTR pPinName ) : + CBaseOutputPin( pObjDesc, pFilter, pFilter, phr, pPinName ), + m_pFilter( pFilter ), + m_bInsideCheckMediaType( FALSE ), + m_id( 0 ), + m_pPosition( NULL ) +{ + m_id = m_idNext; + m_idNext++; +} + +CFilterOutputPin::~CFilterOutputPin() +{ + if (m_pPosition) + m_pPosition->Release(); + m_pPosition = NULL; +} + + +//-------------------------------------------------------------------------------- +// NonDelegatingQueryInterface - Overriden to expose IMediaPosition and +// IMediaSeeking control interfaces + +STDMETHODIMP CFilterOutputPin::NonDelegatingQueryInterface( REFIID riid, void**ppv ) +{ + CheckPointer( ppv, E_POINTER ); + ValidateReadWritePtr( ppv, sizeof(PVOID) ); + *ppv = NULL; + + if (riid == IID_IMediaPosition || riid == IID_IMediaSeeking) + { + // Create the interface now if we haven't yet + if (m_pPosition == NULL) + { + HRESULT hr = CreatePosPassThru( GetOwner(), FALSE, &m_pFilter->m_pinInput, &m_pPosition ); + if (FAILED( hr )) + return hr; + } + return m_pPosition->QueryInterface( riid, ppv ); + } + else + return CBaseOutputPin::NonDelegatingQueryInterface(riid, ppv); +} + + +//-------------------------------------------------------------------------------- +// DecideBufferSize +// +// Determine allocator properties for our output pin. +// This has to be present to override the PURE virtual class base function + +HRESULT CFilterOutputPin::DecideBufferSize( IMemAllocator* pAllocator, + ALLOCATOR_PROPERTIES* pProp ) +{ + HRESULT hr = S_OK; + + // Get properties from the input pin, if possible + if (m_pFilter->m_pinInput.IsConnected()) + { + IMemAllocator* pAlloc = NULL; + if (SUCCEEDED( m_pFilter->m_pinInput.GetAllocator( &pAlloc ) )) + { + pAlloc->GetProperties( pProp ); + pAlloc->Release(); + } + + // Ask for a larger buffer size if necessary + if (1 == m_pFilter->m_wfxIn.nChannels && 2 == m_pFilter->m_wfxOut.nChannels) + pProp->cbBuffer *= 2; + } + + // Set the allocator's properties + ALLOCATOR_PROPERTIES propActual; + hr = pAllocator->SetProperties( pProp, &propActual ); + if (SUCCEEDED( hr )) + *pProp = propActual; + + return hr; +} + + +//-------------------------------------------------------------------------------- +// EnumMediaTypes + +STDMETHODIMP CFilterOutputPin::EnumMediaTypes( IEnumMediaTypes** ppEnum ) +{ + CAutoLock autoLock(m_pLock); + ASSERT( ppEnum ); + + // Make sure that we are connected + if (!m_pFilter->m_pinInput.IsConnected()) + return VFW_E_NOT_CONNECTED; + + // We will simply return the enumerator of our input pin's peer + return m_pFilter->m_pinInput.m_Connected->EnumMediaTypes(ppEnum); +} + + +//-------------------------------------------------------------------------------- +// CheckMediaType + +HRESULT CFilterOutputPin::CheckMediaType( const CMediaType* pmt ) +{ + CAutoLock autoLock(m_pLock); + + // If we are already inside checkmedia type for this pin, return NOERROR + // It is possble to hookup two of these filters and some other filter + // like the video effects sample to get into this situation. If we + // do not detect this, we will loop till we blow the stack + + if (m_bInsideCheckMediaType) + return NOERROR; + + m_bInsideCheckMediaType = TRUE; + + HRESULT hr = NOERROR; + + // See if the wave format is supported + hr = m_pFilter->CheckMediaType( PINDIR_OUTPUT, pmt ); + if (S_OK != hr) + { + m_bInsideCheckMediaType = FALSE; + return hr; + } + + // The input needs to have been connected first + if (!m_pFilter->m_pinInput.IsConnected()) + { + m_bInsideCheckMediaType = FALSE; + return VFW_E_NOT_CONNECTED; + } + + // Make sure we can transform from one type to the other + const CMediaType& mtIn = m_pFilter->m_pinInput.CurrentMediaType(); + hr = m_pFilter->CheckTransform( &mtIn, pmt ); + if (FAILED( hr )) + { + m_bInsideCheckMediaType = FALSE; + return VFW_E_TYPE_NOT_ACCEPTED; + } + + m_bInsideCheckMediaType = FALSE; + return NOERROR; +} + + +//-------------------------------------------------------------------------------- +// GetMediaType - get the media type supported by this pin + +HRESULT CFilterOutputPin::GetMediaType( int iPosition, CMediaType* pmt ) +{ + // We don't have any media types if our input is not connected + if (m_pFilter->m_pinInput.IsConnected()) + { + return m_pFilter->GetMediaType( iPosition, pmt ); + } + else + { + return VFW_S_NO_MORE_ITEMS; + } +} + + +//-------------------------------------------------------------------------------- +// SetMediaType + +HRESULT CFilterOutputPin::SetMediaType( const CMediaType* pmt ) +{ + CAutoLock autoLock(m_pLock); + + // Make sure that we have an input connected + if (!m_pFilter->m_pinInput.IsConnected()) + return VFW_E_NOT_CONNECTED; + + // Make sure that the base class likes it + HRESULT hr = CBaseOutputPin::SetMediaType( pmt ); + if (FAILED( hr )) + return hr; + + // Make sure the plug-in likes it + return m_pFilter->SetMediaType( PINDIR_OUTPUT, pmt ); +} diff --git a/Src/Plugins/DSP/dsp_sps/dxi/Filter.h b/Src/Plugins/DSP/dsp_sps/dxi/Filter.h new file mode 100644 index 00000000..a101742b --- /dev/null +++ b/Src/Plugins/DSP/dsp_sps/dxi/Filter.h @@ -0,0 +1,176 @@ +#ifndef _FILTER_H_ +#define _FILTER_H_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#include "DXi.h" +#include "AudioPlugIn.h" + +class CFilter; +class CFilterOutputPin; +class CFilterInputPin; + +//-------------------------------------------------------------------------------- + +class CFilterInputPin : public CBaseInputPin +{ + friend class CFilter; + friend class CFilterOutputPin; + +public: + // Ctor + CFilterInputPin( TCHAR* pObjDesc, CFilter* pFilter, HRESULT* phr, LPCWSTR pPinName ); + + // Check that we can support this output type + HRESULT CheckMediaType( const CMediaType* pmtIn ); + + // Set the connection media type + HRESULT SetMediaType( const CMediaType* pmt ); + + // Negotiate possible reconnection if types don't match + HRESULT CompleteConnect( IPin* pRecievePin ); + + // What is our media type? + CMediaType& CurrentMediaType() { return m_mt; }; + + // Pass through calls downstream + STDMETHODIMP EndOfStream(); + STDMETHODIMP BeginFlush(); + STDMETHODIMP EndFlush(); + STDMETHODIMP NewSegment( REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate ); + + // Handles the next block of data from the stream + STDMETHODIMP Receive( IMediaSample *pms ); + +// Implementation +private: + CFilter* m_pFilter; // the filter which owns us + BOOL m_bInsideCheckMediaType; // re-entrancy control +}; + +//-------------------------------------------------------------------------------- + +class CFilterOutputPin : public CBaseOutputPin +{ + friend class CFilter; + friend class CFilterInputPin; + +public: + + // Ctors + CFilterOutputPin( TCHAR* pObjDesc, CFilter* pFilter, HRESULT* phr, LPCWSTR pPinName ); + virtual ~CFilterOutputPin(); + + DECLARE_IUNKNOWN; + + // Override to expose IMediaPosition + STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void **ppv); + + // Override to enumerate media types + STDMETHODIMP EnumMediaTypes( IEnumMediaTypes** ppEnum ); + + // Check that we can support an output type + HRESULT CheckMediaType( const CMediaType* pmt ); + HRESULT SetMediaType( const CMediaType* pmt ); + HRESULT GetMediaType( int iPosition, CMediaType* pmt ); + + // What is our media type? + CMediaType& CurrentMediaType() { return m_mt; }; + + // Negotiation to use our input pins allocator + HRESULT DecideBufferSize( IMemAllocator* pAllocator, ALLOCATOR_PROPERTIES* pProp ); + + // Get the unique identifier for this output pin + DWORD GetId() const { return m_id; } + +// Implementation +private: + CFilter* m_pFilter; // the filter which owns us + BOOL m_bInsideCheckMediaType; // re-entrancy control + DWORD m_id; // unique identifier + IUnknown* m_pPosition; + + static DWORD m_idNext; +}; + +//-------------------------------------------------------------------------------- + +class CFilter : + public CAudioPlugIn, + + public CBaseFilter, + public ISpecifyPropertyPages, + public IDispatch, + public CPersistStream +{ + friend class CFilterInputPin; + friend class CFilterOutputPin; + +public: + + // Ctors + CFilter( TCHAR *pName, LPUNKNOWN pUnk, HRESULT* phr ); + ~CFilter(); + + // Function needed for the class factory + static CUnknown * WINAPI CreateInstance( LPUNKNOWN pUnk, HRESULT* phr ); + + DECLARE_IUNKNOWN; + + STDMETHODIMP NonDelegatingQueryInterface( REFIID riid, void** ppv ); + + LPAMOVIESETUP_FILTER GetSetupData(); + + // CBaseFilter pure virtual overrides + CBasePin* GetPin( int n ); + int GetPinCount(); + + // Overrides to deal with not having input connections + STDMETHODIMP Run( REFERENCE_TIME tStart ); + STDMETHODIMP Pause(); + STDMETHODIMP Stop(); + + // Helpers for media type checking + HRESULT CheckMediaType( PIN_DIRECTION direction, const CMediaType* pmt ); + HRESULT CheckTransform( const CMediaType* pmtIn, const CMediaType* pmtOut ); + HRESULT GetMediaType( int iPosition, CMediaType* pmt ); + HRESULT SetMediaType( PIN_DIRECTION direction, const CMediaType* pmt ); + + // ISpecifyPropertyPages + STDMETHODIMP GetPages( CAUUID* pPages ); + + // CPersistStream + STDMETHODIMP GetClassID(CLSID* pClsid); + int SizeMax(); + HRESULT WriteToStream(IStream* pStream); + HRESULT ReadFromStream(IStream* pStream); + + // IDispatch + STDMETHODIMP GetTypeInfoCount( UINT* ); + STDMETHODIMP GetTypeInfo( UINT, LCID, ITypeInfo** ); + STDMETHODIMP GetIDsOfNames( REFIID, OLECHAR**, UINT, LCID, DISPID* ); + STDMETHODIMP Invoke( DISPID, REFIID, LCID, WORD, DISPPARAMS*, VARIANT*, EXCEPINFO*, UINT* ); + +// Implementation +private: + + HRESULT getOutputBuffer( CFilterOutputPin* pPin, + AudioBuffer* pbufOut, + REFERENCE_TIME* prtStart, + BOOL bSyncPoint, BOOL bDiscontuity, BOOL bPreroll ); + HRESULT deliverOutputBuffer( CFilterOutputPin* pPin, + AudioBuffer* abufOut, + HRESULT hrProcess, BOOL bCleanup ); + + HRESULT audioPortsChangeBegin( int nNewPinCount ); + HRESULT audioPortsChangeEnd( int nNewPinCount ); + + CFilterInputPin m_pinInput; + CFilterOutputPin m_pinOutput; + IMemAllocator* m_pAllocator; + LONGLONG m_llSamplePosition; +}; + +#endif // _FILTER_H_ diff --git a/Src/Plugins/DSP/dsp_sps/dxi/MediaParams.cpp b/Src/Plugins/DSP/dsp_sps/dxi/MediaParams.cpp new file mode 100644 index 00000000..43fc4743 --- /dev/null +++ b/Src/Plugins/DSP/dsp_sps/dxi/MediaParams.cpp @@ -0,0 +1,465 @@ +// MediaParams.cpp: implementation of the CMediaParams class. +// +////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "AudioPlugIn.h" +#include "MediaParams.h" +#include "ParamEnvelope.h" + +#include "CakeMedParam_i.c" + +#define DEFINE_PARAM_INFO +#include "Parameters.h" + +////////////////////////////////////////////////////////////////////// +// Ctors + +CMediaParams::CMediaParams( IUnknown* pUnkOuter ) : m_pUnkOuter(pUnkOuter) +{ + m_pCallback = NULL; + m_cRef = 0; + m_aEnv = NULL; + m_dDecimationInterval = 20.0 / 1000.0; // 20 msec + m_lFs = 44100; +} + +CMediaParams::~CMediaParams() +{ + ASSERT( 0 == m_cRef ); + if (m_pCallback) + m_pCallback->Release(); + m_pCallback = NULL; + m_pUnkOuter = NULL; + + delete [] m_aEnv; + m_aEnv = NULL; +} + + +////////////////////////////////////////////////////////////////////// +// Factory-style construction + +HRESULT CMediaParams::Create( CMediaParams** ppObj, IUnknown* pUnkOuter ) +{ + if (NULL == ppObj) + return E_POINTER; + if (NULL == pUnkOuter) + return E_POINTER; + + // Construct the CMediaParams object + CMediaParams* pNew = new CMediaParams( pUnkOuter ); + if (NULL == pNew) + return E_OUTOFMEMORY; + + // Construct and initialize its parameters + pNew->m_aEnv = new CParamEnvelope [ NUM_PARAMS ]; + if (NULL == pNew->m_aEnv) + return E_OUTOFMEMORY; + for (ULONG ix = 0; ix < NUM_PARAMS; ++ix) + pNew->m_aEnv[ ix ].SetParamInfo( m_aParamInfo[ ix ] ); + + pNew->AddRef(); + *ppObj = pNew; + return S_OK; +} + + +//////////////////////////////////////////////////////////////////////////////// +// Given a sample range, fills pTimes with sample positions where we need +// to recompute one or more automated parameter values. Positions are always +// added periodically at the decimation interval; positions are also added +// for every segment boundary among all of the parameters. + +HRESULT CMediaParams::GetDecimationTimes( LONGLONG llSampStart, + LONGLONG llSampEnd, + std::vector* pTimes ) +{ + LONGLONG const llInterval = static_cast( GetDecimationInterval() * GetSampleRate() ); + double const dSamplesPerRefTime = static_cast( GetSampleRate() ) / UNITS; + REFERENCE_TIME const rtStart = REFERENCE_TIME( llSampStart / dSamplesPerRefTime + 0.5 ); + + // Make an worst-case guess at how many decimation points we'll need + ULONG uPoints = 0; + for (DWORD dwParam = 0; dwParam < NUM_AUTOMATED_PARAMS; dwParam++) + { + const CParamEnvelope& env = m_aEnv[ dwParam ]; + uPoints += env.GetCount() * 2; + } + + // If there is no automation, then there is no need to decimate + if (0 == uPoints) + return S_OK; + + // Account for points that are added due to periodic decimation + uPoints += ULONG( ((llSampEnd - llSampStart) / llInterval) + 1 ); + + // Reserve some memory for landmark points + pTimes->reserve( uPoints ); + + // Add periodic landmarks at the decimation interval + LONGLONG llSamp = (llSampStart / llInterval) * llInterval; + if (llSamp < llSampStart) + llSamp += llInterval; + while (llSamp < llSampEnd) + { + pTimes->push_back( llSamp ); + llSamp += llInterval; + } + + // Add landmarks for each shape boundary + for (dwParam = 0; dwParam < NUM_AUTOMATED_PARAMS; dwParam++) + { + const CParamEnvelope& env = m_aEnv[ dwParam ]; + unsigned const nCount = env.GetCount(); + + // Add each shape endpoint that falls in our time range + for (unsigned ix = 0; ix < nCount; ix++) + { + const MP_ENVELOPE_SEGMENT& seg = env.GetAt( ix ); + LONGLONG const llEnvStart = static_cast( seg.rtStart * dSamplesPerRefTime + 0.5 ); + LONGLONG const llEnvEnd = static_cast( seg.rtEnd * dSamplesPerRefTime + 0.5 ); + if (llSampStart <= llEnvStart && llEnvStart < llSampEnd) + pTimes->push_back( llEnvStart ); + if (llSampStart <= llEnvEnd && llEnvEnd < llSampEnd) + pTimes->push_back( llEnvEnd ); + } + } + + // Sort result + std::sort( pTimes->begin(), pTimes->end() ); + + return S_OK; +} + +//////////////////////////////////////////////////////////////////////////////// +// Set the current position among all parameters, updating current envelope +// value and deltas. This method is called repeatedly by the streaming code, +// to update parameter values as they evolve along the duration of the envelope. + +HRESULT CMediaParams::UpdateValuesForSample( LONGLONG llSamp ) +{ + double const dSamplesPerRefTime = static_cast( GetSampleRate() ) / UNITS; + REFERENCE_TIME const rt = REFERENCE_TIME( llSamp / dSamplesPerRefTime + 0.5 ); + + HRESULT hr = S_OK; + for (DWORD dwParam = 0; dwParam < NUM_AUTOMATED_PARAMS; dwParam++) + { + hr = m_aEnv[ dwParam ].UpdateValuesForRefTime( rt, GetSampleRate() ); + if (FAILED( hr )) + break; + } + + return hr; +} + + +//////////////////////////////////////////////////////////////////////////////// +// IUnknown + +HRESULT CMediaParams::QueryInterface( REFIID riid, void** ppv ) +{ + if (NULL == ppv) + return E_POINTER; + + if (riid == IID_IUnknown) + { + *ppv = static_cast( static_cast( this ) ); + m_pUnkOuter->AddRef(); + return S_OK; + } + else + { + return m_pUnkOuter->QueryInterface( riid, ppv ); + } +} + +ULONG CMediaParams::AddRef() +{ + return InterlockedIncrement( &m_cRef ); +} + +ULONG CMediaParams::Release() +{ + ASSERT( m_cRef > 0 ); + ULONG ul = InterlockedDecrement( &m_cRef ); + if (0 == ul) + { + delete this; + return 0; + } + else + return ul; +} + + +//////////////////////////////////////////////////////////////////////////////// +// IMediaParams + +HRESULT CMediaParams::GetParam(ULONG dwParamIndex, FLOAT* pValue) +{ + if (dwParamIndex >= NUM_PARAMS) + return E_INVALIDARG; + if (NULL == pValue) + return E_POINTER; + + return m_aEnv[ dwParamIndex ].GetParam( pValue ); +} + +//////////////////////////////////////////////////////////////////////////////// + +HRESULT CMediaParams::SetParam(ULONG dwParamIndex, FLOAT value) +{ + if (dwParamIndex >= NUM_PARAMS) + return E_INVALIDARG; + + return m_aEnv[ dwParamIndex ].SetParam( value ); +} + +//////////////////////////////////////////////////////////////////////////////// + +HRESULT CMediaParams::AddEnvelope(ULONG dwParamIndex, ULONG cSegments, MP_ENVELOPE_SEGMENT* pmpes) +{ + if (dwParamIndex >= NUM_AUTOMATED_PARAMS && dwParamIndex != DWORD_ALLPARAMS) + return E_INVALIDARG; + if (0 == cSegments) + return S_OK; + if (IsBadReadPtr( pmpes, cSegments * sizeof(MP_ENVELOPE_SEGMENT) )) + return E_POINTER; + + double const dSamplesPerRefTime = static_cast( GetSampleRate() ) / UNITS; + + if (dwParamIndex == DWORD_ALLPARAMS) + { + for (ULONG ix = 0; ix < NUM_AUTOMATED_PARAMS; ix++) + { + HRESULT hr = m_aEnv[ ix ].AddEnvelope( cSegments, pmpes, dSamplesPerRefTime ); + if (FAILED( hr )) + return hr; + } + return S_OK; + } + else + { + return m_aEnv[ dwParamIndex ].AddEnvelope( cSegments, pmpes, dSamplesPerRefTime ); + } +} + +//////////////////////////////////////////////////////////////////////////////// + +HRESULT CMediaParams::FlushEnvelope(ULONG dwParamIndex, REFERENCE_TIME rtStart, REFERENCE_TIME rtEnd) +{ + if (dwParamIndex >= NUM_AUTOMATED_PARAMS && dwParamIndex != DWORD_ALLPARAMS) + return E_INVALIDARG; + if (rtStart > rtEnd) + return E_INVALIDARG; + + double const dSamplesPerRefTime = static_cast( GetSampleRate() ) / UNITS; + + if (dwParamIndex == DWORD_ALLPARAMS) + { + for (ULONG ix = 0; ix < NUM_AUTOMATED_PARAMS; ix++) + { + HRESULT hr = m_aEnv[ ix ].FlushEnvelope( rtStart, rtEnd, dSamplesPerRefTime ); + if (FAILED( hr )) + return hr; + } + return S_OK; + } + else + { + HRESULT hr = m_aEnv[ dwParamIndex ].FlushEnvelope( rtStart, rtEnd, dSamplesPerRefTime ); + if (FAILED( hr )) + return hr; + } + + return S_OK; +} + +//////////////////////////////////////////////////////////////////////////////// + +HRESULT CMediaParams::SetTimeFormat(GUID guidTimeFormat, ULONG mpTimeData) +{ + if (guidTimeFormat != GUID_TIME_REFERENCE) + return E_INVALIDARG; + return S_OK; +} + + +//////////////////////////////////////////////////////////////////////////////// +// IMediaParamInfo + +HRESULT CMediaParams::GetParamCount(ULONG* pdwParams) +{ + if (NULL == pdwParams) + return E_POINTER; + + *pdwParams = NUM_AUTOMATED_PARAMS; + + return S_OK; +} + +//////////////////////////////////////////////////////////////////////////////// + +HRESULT CMediaParams::GetParamInfo(ULONG dwParamIndex, MP_PARAMINFO* pInfo) +{ + if (dwParamIndex >= NUM_AUTOMATED_PARAMS) + return E_INVALIDARG; + if (IsBadWritePtr( pInfo, sizeof(MP_PARAMINFO) )) + return E_POINTER; + + *pInfo = m_aParamInfo[ dwParamIndex ].mppi; + + return S_OK; +} + +//////////////////////////////////////////////////////////////////////////////// + +HRESULT CMediaParams::GetParamText(ULONG dwParamIndex, WCHAR** ppwchText) +{ + if (dwParamIndex >= NUM_AUTOMATED_PARAMS) + return E_INVALIDARG; + if (NULL == ppwchText) + return E_POINTER; + + const ParamInfo& info = m_aParamInfo[ dwParamIndex ]; + const MP_PARAMINFO& mppi = info.mppi; + + // Count up lengths of label and unit strings, plus null terminators + int cch = wcslen(mppi.szLabel) + wcslen(mppi.szUnitText) + 3; + + // Add in length of the enum. text if any was supplied + if (NULL != info.pwszEnumText) + cch += wcslen(info.pwszEnumText) + 1; + + // Allocate memory for the returned string + *ppwchText = (WCHAR*)CoTaskMemAlloc( sizeof(WCHAR) * cch ); + if (NULL == *ppwchText) + return E_OUTOFMEMORY; + + // Text format is "Name\0Units\0Enum1\0Enum2\0...EnumN\0\0" + WCHAR* pwsz = *ppwchText; + + // [1] Copy in the name + wcscpy( pwsz, mppi.szLabel ); + pwsz += wcslen(mppi.szLabel) + 1; + + // [2] Copy in the units + wcscpy( pwsz, mppi.szUnitText ); + pwsz += wcslen(mppi.szUnitText) + 1; + + // [3] Copy in the enum. text, if any was supplied + if (NULL != info.pwszEnumText) + { + wcscpy( pwsz, info.pwszEnumText ); + + // Replace commas with nulls, to conform to DX8 string format spec + while (*pwsz) + { + if (*pwsz == L',') + *pwsz = 0; + pwsz++; + } + } + + return S_OK; +} + +//////////////////////////////////////////////////////////////////////////////// + +HRESULT CMediaParams::GetNumTimeFormats(ULONG* pdwNumTimeFormats) +{ + if (NULL == pdwNumTimeFormats) + return E_POINTER; + *pdwNumTimeFormats = 1; + return S_OK; +} + +//////////////////////////////////////////////////////////////////////////////// + +HRESULT CMediaParams::GetSupportedTimeFormat(ULONG dwFormatIndex, GUID* pguidTimeFormat) +{ + if (NULL == pguidTimeFormat) + return E_POINTER; + if (0 != dwFormatIndex) + return E_INVALIDARG; + *pguidTimeFormat = GUID_TIME_REFERENCE; + return S_OK; +} + +//////////////////////////////////////////////////////////////////////////////// + +HRESULT CMediaParams::GetCurrentTimeFormat(GUID* pguidTimeFormat, ULONG*) +{ + if (NULL == pguidTimeFormat) + return E_POINTER; + *pguidTimeFormat = GUID_TIME_REFERENCE; + return S_OK; +} + + +//////////////////////////////////////////////////////////////////////////////// +// IMediaParamsSetUICallback + +HRESULT CMediaParams::SetUICallback(IMediaParamsUICallback* pICallback) +{ + if (pICallback) + pICallback->AddRef(); + if (m_pCallback) + m_pCallback->Release(); + m_pCallback = pICallback; + return S_OK; +} + + +//////////////////////////////////////////////////////////////////////////////// +// IMediaParamsUICallback + +HRESULT CMediaParams::ParamsBeginCapture(DWORD *aIndex, DWORD cPoints) +{ + HRESULT hr = S_OK; + + // Inform each parameter that capture has begun + for (DWORD ix = 0; ix < cPoints; ix++) + m_aEnv[ aIndex[ ix ] ].BeginCapture(); + + if (m_pCallback) + hr = m_pCallback->ParamsBeginCapture( aIndex, cPoints ); + + return hr; +} + +HRESULT CMediaParams::ParamsChanged(DWORD *aIndex, DWORD cPoints, MP_DATA *paData) +{ + HRESULT hr = S_OK; + + // Send the parameter change to each parameter + for (DWORD ix = 0; ix < cPoints; ix++) + { + hr = SetParam( aIndex[ ix ], paData[ ix ] ); + if (FAILED( hr )) + return hr; + } + + // Send the parameter change to our callback + if (m_pCallback) + hr = m_pCallback->ParamsChanged( aIndex, cPoints, paData ); + + return hr; +} + +HRESULT CMediaParams::ParamsEndCapture(DWORD *aIndex, DWORD cPoints) +{ + HRESULT hr = S_OK; + + // Inform each parameter that capture has ended + for (DWORD ix = 0; ix < cPoints; ix++) + m_aEnv[ aIndex[ ix ] ].EndCapture(); + + if (m_pCallback) + hr = m_pCallback->ParamsEndCapture( aIndex, cPoints ); + + return hr; +} + +//////////////////////////////////////////////////////////////////////////////// diff --git a/Src/Plugins/DSP/dsp_sps/dxi/MediaParams.h b/Src/Plugins/DSP/dsp_sps/dxi/MediaParams.h new file mode 100644 index 00000000..947399f6 --- /dev/null +++ b/Src/Plugins/DSP/dsp_sps/dxi/MediaParams.h @@ -0,0 +1,99 @@ +// MediaParams.h: interface for the CMediaParams class. +// +////////////////////////////////////////////////////////////////////// + +#if !defined(AFX_MEDIAPARAMS_H__3DB99F00_3887_4C35_BBA8_C47835777A69__INCLUDED_) +#define AFX_MEDIAPARAMS_H__3DB99F00_3887_4C35_BBA8_C47835777A69__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#include // DX8 automation +#include "CakeMedParam.h" // DX8 automation + +#include "ParamEnvelope.h" +#include "Parameters.h" + +//////////////////////////////////////////////////////////////////////////////// + +class CMediaParams : + public IMediaParams, + public IMediaParamInfo, + public IMediaParamsSetUICallback, + public IMediaParamsUICallback +{ +public: + + static HRESULT Create( CMediaParams** ppObj, IUnknown* pUnkOuter ); + +public: + + // IUnknown + STDMETHOD(QueryInterface)( REFIID riid, void** ppv ); + STDMETHODIMP_(ULONG) AddRef(); + STDMETHODIMP_(ULONG) Release(); + + // IMediaParams + STDMETHOD(GetParam)(ULONG dwParamIndex, FLOAT* pValue); + STDMETHOD(SetParam)(ULONG dwParamIndex, FLOAT value); + STDMETHOD(AddEnvelope)(ULONG dwParamIndex, ULONG cSegments, MP_ENVELOPE_SEGMENT* pEnvelopeSegments); + STDMETHOD(FlushEnvelope)(ULONG dwParamIndex, REFERENCE_TIME refTimeStart, REFERENCE_TIME refTimeEnd); + STDMETHOD(SetTimeFormat)(GUID guidTimeFormat, ULONG mpTimeData); + + // IMediaParamInfo + STDMETHOD(GetParamCount)(ULONG* pdwParams); + STDMETHOD(GetParamInfo)(ULONG dwParamIndex, MP_PARAMINFO* pInfo); + STDMETHOD(GetParamText)(ULONG dwParamIndex, WCHAR** ppwchText); + STDMETHOD(GetNumTimeFormats)(ULONG* pdwNumTimeFormats); + STDMETHOD(GetSupportedTimeFormat)(ULONG dwFormatIndex, GUID* pguidTimeFormat); + STDMETHOD(GetCurrentTimeFormat)(GUID* pguidTimeFormat, ULONG* pTimeData); + + // IMediaParamsSetUICallback + STDMETHOD(SetUICallback)(IMediaParamsUICallback* pICallback); + + // IMediaParamsUICallback + STDMETHOD(ParamsBeginCapture)(DWORD *aIndex, DWORD cPoints); + STDMETHOD(ParamsChanged)(DWORD *aIndex, DWORD cPoints, MP_DATA *paData); + STDMETHOD(ParamsEndCapture)(DWORD *aIndex, DWORD cPoints); + + // Helpers for setting the current sample rate + void SetSampleRate( long lFs ) { m_lFs = lFs; } + long GetSampleRate() const { return m_lFs; } + + // Helpers to decimate shapes into smaller chunks + HRESULT GetDecimationTimes( LONGLONG llSampStart, LONGLONG llSampEnd, std::vector* pTimes ); + void SetDecimationInterval( double d ) { m_dDecimationInterval = d; } + double GetDecimationInterval() const { return m_dDecimationInterval; } + + // Set our position among all parameter segments, updating current values, and + // flushing any out-of-date segments. + HRESULT UpdateValuesForSample( LONGLONG llSamp ); + + // Get the envelope for a given parameter + const CParamEnvelope& GetParamEnvelope( DWORD ix ) + { + ASSERT( ix >= 0 && ix < NUM_PARAMS ); + return m_aEnv[ ix ]; + } + +private: + + IUnknown* m_pUnkOuter; + IMediaParamsUICallback* m_pCallback; + LONG m_cRef; + CParamEnvelope* m_aEnv; + double m_dDecimationInterval; + long m_lFs; + +private: + + static const ParamInfo m_aParamInfo[ NUM_PARAMS ]; + +private: + + CMediaParams( IUnknown* pUnkOuter ); + virtual ~CMediaParams(); +}; + +#endif // !defined(AFX_MEDIAPARAMS_H__3DB99F00_3887_4C35_BBA8_C47835777A69__INCLUDED_) diff --git a/Src/Plugins/DSP/dsp_sps/dxi/ParamEnvelope.cpp b/Src/Plugins/DSP/dsp_sps/dxi/ParamEnvelope.cpp new file mode 100644 index 00000000..923496bb --- /dev/null +++ b/Src/Plugins/DSP/dsp_sps/dxi/ParamEnvelope.cpp @@ -0,0 +1,403 @@ +// ParamEnvelope.cpp: implementation of the CParamEnvelope class. +// +////////////////////////////////////////////////////////////////////// + +#include "stdafx.h" +#include "AudioPlugIn.h" +#include "ParamEnvelope.h" + +#include + +//////////////////////////////////////////////////////////////////////////////// +// ParamInfo +//////////////////////////////////////////////////////////////////////////////// + +float ParamInfo::MapToInternal( float fValue ) const +{ + // Convert a user-supplied parameter value to one that is for internal + // use by the plug-in's processing code. + + if (MPT_FLOAT == mppi.mpType) + { + // Map floats to the internal range, using a linear mapping + double dDelta = (fValue - mppi.mpdMinValue) / (mppi.mpdMaxValue - mppi.mpdMinValue); + return float( fInternalMin + dDelta * (fInternalMax - fInternalMin) ); + } + else if (MPT_BOOL == mppi.mpType) + { + // Map booleans to 0.0 or 1.0 + return float( (fValue < 0.5) ? MPBOOL_FALSE : MPBOOL_TRUE ); + } + else // (MPT_ENUM == mppi.mpType || MPT_INT == mppi.mpType) + { + // Map integers to the internal range, using a linear mapping, and then + // round to the nearest value. + double dDelta = (fValue - mppi.mpdMinValue) / (mppi.mpdMaxValue - mppi.mpdMinValue); + double dMapped = fInternalMin + dDelta * (fInternalMax - fInternalMin); + return static_cast( floor( dMapped + 0.5 ) ); + } +} + +//////////////////////////////////////////////////////////////////////////////// + +float ParamInfo::MapToExternal( float fValue ) const +{ + // Convert an internal processing value to value in the user's input range + + if (MPT_FLOAT == mppi.mpType) + { + // Map floats to the external range, using a linear mapping + double dDelta = (fValue - fInternalMin) / (fInternalMax - fInternalMin); + return float( mppi.mpdMinValue + dDelta * (mppi.mpdMaxValue - mppi.mpdMinValue) ); + } + else if (MPT_BOOL == mppi.mpType) + { + // Booleans are already in a suitable range; no mapping required. + return fValue; + } + else // (MPT_ENUM == mppi.mpType || MPT_INT == mppi.mpType) + { + // Map integers to the external range, using a linear mapping + double dDelta = (fValue - fInternalMin) / (fInternalMax - fInternalMin); + return float( mppi.mpdMinValue + dDelta * (mppi.mpdMaxValue - mppi.mpdMinValue) ); + } +} + +//////////////////////////////////////////////////////////////////////////////// +// CParamEnvelope +//////////////////////////////////////////////////////////////////////////////// + +//------------------------------------------------------------------------------ +// Ctors + +CParamEnvelope::CParamEnvelope() : + m_bOverride( FALSE ), + m_bCaptured( FALSE ), + m_fOverrideValue( 0 ), + m_fEnvelopeValue( 0 ), + m_dEnvelopeDelta1( 0 ), + m_dEnvelopeDelta2( 0 ), + m_bValidDeltas( TRUE ), + m_rtRendered( 0 ) +{ +} + +CParamEnvelope::~CParamEnvelope() +{ +} + + +//---------------------------------------------------------------------------- +// Phase 2 of construction + +void CParamEnvelope::SetParamInfo( const ParamInfo& info ) +{ + m_info = info; + m_fEnvelopeValue = m_info.MapToInternal( m_info.mppi.mpdNeutralValue ); + cleanup(); +} + + +//---------------------------------------------------------------------------- +// Find the index for data on or after rt + +int CParamEnvelope::IndexForRefTime( REFERENCE_TIME rt ) const +{ + CAutoLock lock( const_cast( this ) ); + + int const nLength = GetCount(); + + // Fail gracefully if the list is empty + if (0 == nLength) + return -1; + + // Special case for position after the last segment + if (rt >= m_envSegs[ nLength - 1 ].rtEnd) + return nLength - 1; + + int ixMin = 0; + int ixMax = nLength; + int ix = ( ixMin + ixMax ) / 2; + + // Binary search for the shape which starts on or before the given time + do + { + REFERENCE_TIME rtShape = m_envSegs[ ix ].rtStart; + + // We've made an exact match + if (rtShape == rt) + return ix; + + // No match was found, so update search indices + else if (rt < rtShape) + ixMax = ix; + else if (rt > rtShape) + ixMin = ix; + ix = (ixMin + ixMax) / 2; + } + while (ix != ixMin); + + // The search may have left us at a shape after the desired time, so + // scan back if necessary + while (ix >= 0 && m_envSegs[ ix ].rtStart > rt) + --ix; + + return ix; +} + + +//------------------------------------------------------------------------------ +// Set the current position, updating current envelope value and deltas. This +// method is called repeatedly by the streaming code, to update parameter values +// as they evolve along the duration of the envelope. + +HRESULT CParamEnvelope::UpdateValuesForRefTime( REFERENCE_TIME rt, long lSampleRate ) +{ + CAutoLock lock( this ); + + int const nLength = GetCount(); + if (0 == nLength) + return S_OK; // nothing to do + + int const ix = IndexForRefTime( rt ); + const MP_ENVELOPE_SEGMENT* pmpseg = (ix < 0 || ix >= nLength) ? NULL : &m_envSegs[ ix ]; + + // Assume deltas are valid. We'll make them invalid if we encounter a SIN curve. + m_bValidDeltas = TRUE; + + if (NULL == pmpseg || rt < pmpseg->rtStart || rt > pmpseg->rtEnd) + { + // The seek position is between 2 segments, so do not modify the current envelope + // value. The envelope will either latch the previous value, or continue to obey + // any intervening override value, until we hit the next segment boundary. + if (NULL != pmpseg) + m_fEnvelopeValue = m_info.MapToInternal( pmpseg->valEnd ); + m_dEnvelopeDelta1 = m_dEnvelopeDelta2 = 0; + } + else + { + // We're dealing with point directly over a shape. Stop any override value. + stopOverride(); + + // Compute the time delta between this vector and the next, as value + // between 0..1. We use to interpolate between points. + double dx = double(pmpseg->rtEnd - pmpseg->rtStart) / UNITS; + double y0 = m_info.MapToInternal( pmpseg->valStart ); + double y1 = m_info.MapToInternal( pmpseg->valEnd ); + double dy = y1 - y0; + double x = (double(rt - pmpseg->rtStart) / UNITS) / dx; + + // Convert dx to units per sample, before computing deltas + dx = dx * lSampleRate; + + // Interpolate between times + if (MP_CURVE_JUMP == pmpseg->iCurve) + { + m_dEnvelopeDelta2 = 0; + m_dEnvelopeDelta1 = 0; + m_fEnvelopeValue = static_cast( y0 ); + } + else if (MP_CURVE_LINEAR == pmpseg->iCurve) + { + m_dEnvelopeDelta2 = 0; + m_dEnvelopeDelta1 = dy / dx; + m_fEnvelopeValue = static_cast( y0 + dy * x ); + } + else if (MP_CURVE_SQUARE == pmpseg->iCurve || MP_CURVE_INVSQUARE == pmpseg->iCurve) + { + double A; + double B; + if (MP_CURVE_SQUARE == pmpseg->iCurve) + { + A = y0; + B = dy; + } + else + { + x = x - 1; + A = y1; + B = -dy; + } + + m_dEnvelopeDelta2 = 2.0 * B / (dx * dx); + m_dEnvelopeDelta1 = (B / dx) * (2.0 * x + (1.0 / dx)); + m_fEnvelopeValue = static_cast( A + B * x * x ); + } + else if (MP_CURVE_SINE) + { + static const double dPI = 3.14159265358979323846264338327950288419716939937510; + double dTheta = dPI * (x - 0.5); + m_bValidDeltas = FALSE; + m_fEnvelopeValue = float( dy * ( sin( dTheta ) + 0.5 ) ); + } + } + + // Keep track of the latest time rendered so far + m_rtRendered = max( m_rtRendered, rt ); + + return S_OK; +} + + +//------------------------------------------------------------------------------ +// If the list is empty, make sure we get an override value. + +void CParamEnvelope::cleanup() +{ + if (0 == GetCount() && !IsOverrideActive()) + { + m_fOverrideValue = m_fEnvelopeValue; + m_bOverride = TRUE; + } +} + + +//--------------------------------------------------------------------------- +// Set the value of our parameter, overriding any segment in effect. + +HRESULT CParamEnvelope::SetParam( float fValue ) +{ + m_bOverride = TRUE; + m_fOverrideValue = m_info.MapToInternal( fValue ); + m_fEnvelopeValue = m_fOverrideValue; + m_dEnvelopeDelta1 = m_dEnvelopeDelta2 = 0; + return S_OK; +} + + +//------------------------------------------------------------------------------ +// Get the value of the parameter, either overriden on an a segment + +HRESULT CParamEnvelope::GetParam( float* pfValue ) +{ + if (NULL == pfValue) + return E_POINTER; + *pfValue = m_info.MapToExternal( GetCurrentValue() ); + return S_OK; +} + + +//------------------------------------------------------------------------------ +// Add segments to this envelope + +static bool compareEnvSeg( const MP_ENVELOPE_SEGMENT& a, const MP_ENVELOPE_SEGMENT& b ) +{ + return a.rtStart < b.rtStart; +} + +static bool operator==( const MP_ENVELOPE_SEGMENT& a, const MP_ENVELOPE_SEGMENT& b ) +{ + return 0 == memicmp( &a, &b, sizeof(MP_ENVELOPE_SEGMENT) ); +} + +HRESULT CParamEnvelope::AddEnvelope( DWORD cSegments, MP_ENVELOPE_SEGMENT* pmpes, double dSamplesPerRefTime ) +{ + CAutoLock lock( this ); + + // Make room for what we are going to add + m_envSegs.reserve( m_envSegs.size() + cSegments ); + + // Add each segment, noting which one is earliest in time + REFERENCE_TIME rtMin = _I64_MAX; + for (int ix = 0; ix < cSegments; ix++) + { + // Round reference times to sample boundaries + MP_ENVELOPE_SEGMENT mpes = pmpes[ ix ]; + mpes.rtStart = REFERENCE_TIME(mpes.rtStart * dSamplesPerRefTime) / dSamplesPerRefTime + 0.5; + mpes.rtEnd = REFERENCE_TIME(mpes.rtEnd * dSamplesPerRefTime) / dSamplesPerRefTime + 0.5; + m_envSegs.push_back( pmpes[ ix ] ); + if (mpes.rtStart < rtMin) + rtMin = mpes.rtStart; + } + + // Flush all segments prior to the first newly added one + ix = IndexForRefTime( rtMin ); + if (ix > 0 && rtMin < m_rtRendered) + m_envSegs.erase( m_envSegs.begin(), m_envSegs.begin() + ix ); + + // Sort them + std::sort( m_envSegs.begin(), m_envSegs.end(), compareEnvSeg ); + + // Remove duplicates + EnvelopeSegs::iterator it = m_envSegs.begin(); + while (it != m_envSegs.end()) + { + EnvelopeSegs::iterator itBegin = it + 1; + EnvelopeSegs::iterator itEnd = itBegin; + while (itEnd != m_envSegs.end() && *itEnd == *it) + itEnd++; + if (itEnd != itBegin) + it = m_envSegs.erase( itBegin, itEnd ); + else + it++; + + } + + return S_OK; +} + + +//------------------------------------------------------------------------------ +// Flush segments within the specified time range. The rules for flushing are +// described as follows in the documentation for IMediaParams: +// +// If the time span specified by refTimeStart and refTimeEnd overlaps an envelope +// segment, the entire segment is flushed. On the other hand, if it falls on +// the boundary of an envelope segment, the entire segment is retained. Thus: +// +// [] If the start time falls inside an envelope segment, the segment is flushed. +// [] If the end time falls inside an envelope segment, the segment is flushed. +// [] If the start time equals the end time of an envelope segment, the segment is retained. +// [] If the end time equals the start time of an envelope segment, the segment is retained. + +HRESULT CParamEnvelope::FlushEnvelope( REFERENCE_TIME rtStart, REFERENCE_TIME rtEnd, double dSamplesPerRefTime ) +{ + CAutoLock lock( this ); + + // Round reference times to sample boundaries + if (rtStart != _I64_MIN && rtStart != _I64_MAX) + rtStart = REFERENCE_TIME( REFERENCE_TIME(rtStart / dSamplesPerRefTime) * dSamplesPerRefTime + 0.5 ); + if (rtEnd != _I64_MIN && rtEnd != _I64_MAX) + rtEnd = REFERENCE_TIME( REFERENCE_TIME(rtEnd / dSamplesPerRefTime) * dSamplesPerRefTime + 0.5 ); + + EnvelopeSegs::iterator it = m_envSegs.begin(); + while (it != m_envSegs.end()) + { + if (!(rtStart >= it->rtEnd || rtEnd <= it->rtStart)) + it = m_envSegs.erase( it ); + else + it++; + } + + // Once envelopes get thrown away, we need to redetermine our max render time + m_rtRendered = 0; + + cleanup(); + + return S_OK; +} + + +//------------------------------------------------------------------------------ +// The parameter pcSegments passes values both ways. The caller needs to pass in the +// size of the segment array. GetEnvelope() then uses pcSegments to return the number +// of segments it has placed in the array. + +HRESULT CParamEnvelope::GetEnvelope( DWORD *pcSegments, MP_ENVELOPE_SEGMENT *pmpes ) +{ + CAutoLock lock( this ); + + ASSERT( pcSegments ); + + DWORD ix = 0; + for (EnvelopeSegs::iterator it = m_envSegs.begin(); + it != m_envSegs.end() && ix < *pcSegments; + it++, ix++) + { + pmpes[ ix ] = *it; + } + + *pcSegments = ix; + + return S_OK; +} diff --git a/Src/Plugins/DSP/dsp_sps/dxi/ParamEnvelope.h b/Src/Plugins/DSP/dsp_sps/dxi/ParamEnvelope.h new file mode 100644 index 00000000..56bff5d5 --- /dev/null +++ b/Src/Plugins/DSP/dsp_sps/dxi/ParamEnvelope.h @@ -0,0 +1,124 @@ +// ParamEnvelope.h: interface for the CParamEnvelope class. +// +////////////////////////////////////////////////////////////////////// + +#if !defined(AFX_PARAMENVELOPE_H__F04D0178_4674_45AF_9F7A_5C8206DE4CF6__INCLUDED_) +#define AFX_PARAMENVELOPE_H__F04D0178_4674_45AF_9F7A_5C8206DE4CF6__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +//////////////////////////////////////////////////////////////////////////////// + +typedef std::vector EnvelopeSegs; + +//////////////////////////////////////////////////////////////////////////////// + +struct ParamInfo +{ + MP_PARAMINFO mppi; // external parameter info, as presented to the user + float fInternalMin; // minimum value used by internal processing code + float fInternalMax; // maximum value used by internal processing code + const WCHAR* pwszEnumText; // text for enumerations + + float MapToInternal( float fValue ) const; + float MapToExternal( float fValue ) const; +}; + +//////////////////////////////////////////////////////////////////////////////// + +class CParamEnvelope : public CCritSec +{ +public: + CParamEnvelope(); + virtual ~CParamEnvelope(); + +// Attributes +public: + + unsigned GetCount() const + { + return m_envSegs.size(); + } + + const MP_ENVELOPE_SEGMENT& GetAt( unsigned ix ) const + { + return m_envSegs[ ix ]; + } + + // Tell the envelope about the parameter being controlled + void SetParamInfo( const ParamInfo& info ); + + // These methods are called by CMediaParams to manipulate segments in an envelope + HRESULT AddEnvelope( DWORD cSegments, MP_ENVELOPE_SEGMENT* pEnvelopeSegments, double dSamplesPerRefTime ); + HRESULT FlushEnvelope( REFERENCE_TIME refTimeStart, REFERENCE_TIME refTimeEnd, double dSamplesPerRefTime ); + HRESULT GetEnvelope( DWORD* cSegments, MP_ENVELOPE_SEGMENT* pEnvelopeSegments ); + HRESULT SetParam( float fValue ); + HRESULT GetParam( float* pfValue ); + + // Manage UI capture and release + void BeginCapture() { m_bCaptured = TRUE; } + void EndCapture() { m_bCaptured = FALSE; } + + // Set the current position, updating current envelope value and deltas + HRESULT UpdateValuesForRefTime( REFERENCE_TIME rt, long lSampleRate ); + + // Check if automation envelopes have been overriden with a specific value + BOOL IsOverrideActive() const { return m_bOverride || m_bCaptured; } + + // Get the current automation data point + float GetCurrentValue() const + { + if (IsOverrideActive()) + return m_fOverrideValue; + else + return m_fEnvelopeValue; + } + + // Get the current automation deltas (for per-sample rendering) + HRESULT GetCurrentDeltas( double* pdDelta1, double* pdDelta2 ) const + { + if (!m_bValidDeltas) + return E_FAIL; + *pdDelta1 = m_dEnvelopeDelta1; + *pdDelta2 = m_dEnvelopeDelta2; + return S_OK; + } + + int IndexForRefTime( REFERENCE_TIME rt ) const; + +private: + + // Stop overriding data + void stopOverride() + { + if (IsOverrideActive() && GetCount() > 0) + m_bOverride = FALSE; + } + + // Make sure the automation track is in a state suitable for playback + void cleanup(); + +private: + + ParamInfo m_info; // information about this parameter + EnvelopeSegs m_envSegs; // the list of envelope segments + float m_fEnvelopeValue; // our evolving dynamic value + double m_dEnvelopeDelta1; // 1st delta of current envelope (w.r.t. seconds) + double m_dEnvelopeDelta2; // 2nd delta of current envelope (w.r.t. seconds) + BOOL m_bValidDeltas; // TRUE when deltas can be used (e.g. except for sin) + BOOL m_bOverride; // TRUE while automation point value is overridden + BOOL m_bCaptured; // TRUE while the captured by the UI + float m_fOverrideValue; // our override value + REFERENCE_TIME m_rtRendered; // latest time rendered so far + +private: + + CParamEnvelope( const CParamEnvelope& ); + CParamEnvelope& operator=( const CParamEnvelope& ); +}; + +//////////////////////////////////////////////////////////////////////////////// + +#endif // !defined(AFX_PARAMENVELOPE_H__F04D0178_4674_45AF_9F7A_5C8206DE4CF6__INCLUDED_) diff --git a/Src/Plugins/DSP/dsp_sps/dxi/Parameters.h b/Src/Plugins/DSP/dsp_sps/dxi/Parameters.h new file mode 100644 index 00000000..f0bbfb3c --- /dev/null +++ b/Src/Plugins/DSP/dsp_sps/dxi/Parameters.h @@ -0,0 +1,47 @@ +// Declarations of automated parameters used by the plug-in + +//////////////////////////////////////////////////////////////////////////////// +#ifndef DEFINE_PARAM_INFO +//////////////////////////////////////////////////////////////////////////////// + +enum +{ + PARAM_ENABLE, + + // TODO: Add new automated parameter IDs here + + NUM_AUTOMATED_PARAMS, + + // TODO: Add new internal parameter IDs here. Make sure to assign the + // first value to NUM_AUTOMATED_PARAMS, i.e., + // + // _PARAM_INTERNAL1 = NUM_AUTOMATED_PARAMS, + // _PARAM_INTERNAL2, + // ... + + NUM_PARAMS +}; + +//////////////////////////////////////////////////////////////////////////////// +#else +//////////////////////////////////////////////////////////////////////////////// + +#define MP_NONE (0) +#define MP_JUMP (MP_CURVE_JUMP) +#define MP_LINES (MP_CURVE_JUMP|MP_CURVE_LINEAR) +#define MP_QUADS (MP_CURVE_JUMP|MP_CURVE_LINEAR|MP_CURVE_SQUARE|MP_CURVE_INVSQUARE) +#define MP_ALL (MP_CURVE_JUMP|MP_CURVE_LINEAR|MP_CURVE_SQUARE|MP_CURVE_INVSQUARE|MP_CURVE_SINE) + +const ParamInfo CMediaParams::m_aParamInfo[ NUM_PARAMS ] = +{ +// MP_TYPE MP_CAPS min max def units label int.min int.max "Enum1,Enum2,.." +// ------- ------- --- --- --- ----- ----- ------- ------- --------------- +{ MPT_BOOL, MP_QUADS, 0, 1, 1, L"", L"Enabled", 0, 1, NULL }, + +// TODO: Add entries for additional parameters here + +}; + +//////////////////////////////////////////////////////////////////////////////// +#endif // DEFINE_PARAM_INFO +//////////////////////////////////////////////////////////////////////////////// diff --git a/Src/Plugins/DSP/dsp_sps/dxi/PlugInApp.cpp b/Src/Plugins/DSP/dsp_sps/dxi/PlugInApp.cpp new file mode 100644 index 00000000..be1fdfbf --- /dev/null +++ b/Src/Plugins/DSP/dsp_sps/dxi/PlugInApp.cpp @@ -0,0 +1,192 @@ +// PlugInApp.cpp : Defines the initialization routines for the DLL. +// + +#include "stdafx.h" +#include "PlugInApp.h" + +//////////////////////////////////////////////////////////////////////////////// + +extern "C" BOOL WINAPI DllEntryPoint(HINSTANCE, ULONG, LPVOID); + +BOOL WINAPI DllMain(HINSTANCE hInstance, ULONG ulReason, LPVOID pv) +{ + return DllEntryPoint( hInstance, ulReason, pv ); +} + +//////////////////////////////////////////////////////////////////////////////// + +LONG recursiveDeleteKey( HKEY hKeyParent, // Parent of key to delete + const char* lpszKeyChild ) // Key to delete +{ + // Open the child. + HKEY hKeyChild ; + LONG lRes = RegOpenKeyEx( hKeyParent, lpszKeyChild, 0, KEY_ALL_ACCESS, &hKeyChild ); + if (lRes != ERROR_SUCCESS) + { + return lRes; + } + + // Enumerate all of the decendents of this child. + FILETIME time; + char szBuffer[ 256 ]; + DWORD dwSize = 256; + while (RegEnumKeyEx( hKeyChild, 0, szBuffer, &dwSize, NULL, NULL, NULL, &time ) == S_OK) + { + // Delete the decendents of this child. + lRes = recursiveDeleteKey( hKeyChild, szBuffer ); + if (lRes != ERROR_SUCCESS) + { + // Cleanup before exiting. + RegCloseKey( hKeyChild ); + return lRes; + } + dwSize = 256; + } + + // Close the child. + RegCloseKey( hKeyChild ); + + // Delete this child. + return RegDeleteKey( hKeyParent, lpszKeyChild ); +} + +//////////////////////////////////////////////////////////////////////////////// + +static const char* s_pszReg = "CakewalkPlugIns\\"; + +extern CFactoryTemplate g_Templates[]; +extern int g_cTemplates; + +//////////////////////////////////////////////////////////////////////////////// + +STDAPI DllRegisterServer() +{ + HKEY hKey = 0; + char sz[ _MAX_PATH ]; + OLECHAR wsz[ _MAX_PATH ]; + char szCLSID[ 64 ]; + ITypeLib* pTypeLib = 0; + int i = 0; + HRESULT hr = E_FAIL; + + // Do DirectShow registration + hr = AMovieDllRegisterServer2( TRUE ); + if (FAILED( hr )) + goto DONE; + + // Get our full pathname, converting to multibyte + GetModuleFileName( g_hInst, sz, sizeof sz ); + if (0 == MultiByteToWideChar( CP_ACP, 0, sz, _MAX_PATH, wsz, _MAX_PATH )) + goto DONE; + + // Iterate over all exported CLSIDs + for (i = 0; i < g_cTemplates; i++) + { + CFactoryTemplate* pT = &g_Templates[ i ]; + + if (NULL != pT->m_pAMovieSetup_Filter) + { + // For backwards compatability, instantiate all servers and get hold of + // IAMovieSetup (if implemented) and call IAMovieSetup.Register() method + if (NULL != pT->m_lpfnNew) + { + IAMovieSetup* pSetup = 0; + if (SUCCEEDED( CoCreateInstance( *(pT->m_ClsID), 0, CLSCTX_INPROC_SERVER, + IID_IAMovieSetup, (void**)&pSetup ) )) + { + pSetup->Register(); + pSetup->Release(); + } + } + + // Convert the CLSID to an ANSI string + StringFromGUID2( *(pT->m_ClsID), wsz, sizeof wsz ); + if (0 == WideCharToMultiByte( CP_ACP, 0, wsz, -1, szCLSID, sizeof szCLSID, NULL, NULL )) + goto DONE; + + // Add {...} to HKEY_CLASSES_ROOT\ + strcpy( sz, s_pszReg ); + strcat( sz, szCLSID ); + if (ERROR_SUCCESS != RegCreateKey( HKEY_CLASSES_ROOT, sz, &hKey )) + goto DONE; + + // {...}\Description = + if (0 == WideCharToMultiByte( CP_ACP, 0, pT->m_Name, -1, sz, sizeof sz, NULL, NULL )) + goto DONE; + RegSetValueEx( hKey, "Description", 0, REG_SZ, (BYTE*)sz, strlen(sz) ); + + // Written for backwards compatability with SONAR 1.x and Pro Audio: + // {...}\HelpFilePath = "" + // {...}\HelpFileTopic = "" + *sz = 0; + RegSetValueEx( hKey, "HelpFilePath", 0, REG_SZ, (BYTE*)sz, 1 ); + RegSetValueEx( hKey, "HelpFileTopic", 0, REG_SZ, (BYTE*)sz, 1 ); + + RegCloseKey( hKey ); + hKey = 0; + } + } + + hr = S_OK; + +DONE: + + + if (hKey) + RegCloseKey( hKey ); + + return hr; +} + +//////////////////////////////////////////////////////////////////////////////// + +STDAPI DllUnregisterServer() +{ + char sz[ _MAX_PATH ]; + OLECHAR wsz[ _MAX_PATH ]; + char szCLSID[ 64 ]; + int i = 0; + HRESULT hr = E_FAIL; + + // Do DirectShow unregistration + hr = AMovieDllRegisterServer2( FALSE ); + if (FAILED( hr )) + goto DONE; + + // Iterate over all exported CLSIDs + for (i = 0; i < g_cTemplates; i++) + { + CFactoryTemplate* pT = &g_Templates[ i ]; + + // For backwards compatability, instantiate all servers and get hold of + // IAMovieSetup (if implemented) and call IAMovieSetup.Register() method + if (NULL != pT->m_lpfnNew) + { + IAMovieSetup* pSetup = 0; + if (SUCCEEDED( CoCreateInstance( *(pT->m_ClsID), 0, CLSCTX_INPROC_SERVER, + IID_IAMovieSetup, (void**)&pSetup ) )) + { + pSetup->Unregister(); + pSetup->Release(); + } + } + + // Convert the CLSID to an ANSI string + StringFromGUID2( *(pT->m_ClsID), wsz, sizeof wsz ); + if (0 == WideCharToMultiByte( CP_ACP, 0, wsz, -1, szCLSID, sizeof szCLSID, NULL, NULL )) + goto DONE; + + // Delete HKEY_CLASSES_ROOT\ + strcpy( sz, s_pszReg ); + strcat( sz, szCLSID ); + recursiveDeleteKey( HKEY_CLASSES_ROOT, sz ); + } + + hr = S_OK; + +DONE: + + return hr; +} + +//////////////////////////////////////////////////////////////////////////////// diff --git a/Src/Plugins/DSP/dsp_sps/dxi/PlugInApp.h b/Src/Plugins/DSP/dsp_sps/dxi/PlugInApp.h new file mode 100644 index 00000000..f7215ed3 --- /dev/null +++ b/Src/Plugins/DSP/dsp_sps/dxi/PlugInApp.h @@ -0,0 +1,8 @@ +// PlugInApp.h : main header file for PlugIn.ax +// + +#ifndef _PLUGIN_APP_H_ +#define _PLUGIN_APP_H_ +extern HMODULE g_hInst; + +#endif // _PLUGIN_APP_H_ diff --git a/Src/Plugins/DSP/dsp_sps/dxi/PlugInGUIDs.h b/Src/Plugins/DSP/dsp_sps/dxi/PlugInGUIDs.h new file mode 100644 index 00000000..9476b4ba --- /dev/null +++ b/Src/Plugins/DSP/dsp_sps/dxi/PlugInGUIDs.h @@ -0,0 +1,5 @@ +///////////////////////////////////////////////////////////////////////////// +// Classes exported by this plug-in + +const GUID CLSID_Filter = { 0x2d63acbe, 0x64bd, 0x4fd9, { 0xa0, 0x23, 0xe3, 0xa7, 0x60, 0xc9, 0x6a, 0x14 } }; +const GUID CLSID_FilterPropPage = { 0xbf23bd81, 0x6e22, 0x42ab, { 0x9d, 0xa4, 0xc7, 0x93, 0xdb, 0xfc, 0x6, 0x85 } }; diff --git a/Src/Plugins/DSP/dsp_sps/dxi/ReadMe.txt b/Src/Plugins/DSP/dsp_sps/dxi/ReadMe.txt new file mode 100644 index 00000000..516d83e2 --- /dev/null +++ b/Src/Plugins/DSP/dsp_sps/dxi/ReadMe.txt @@ -0,0 +1,68 @@ +The Cakewalk DirectX Plug-In Wizard has created +the following files for you: + +AudioPlugIn.h, AudioPlugIn.cpp: + CAudioPlugIn, the DirectX plug-in object. + +AudioPlugInPropPage.h, AudioPlugInPropPage.cpp: + CAudioPlugInPropPage, an class that implements the plug-in's + property page (IPropertyPage). + +AudioPlugIn.rc + This is a listing of all of the Microsoft Windows resources that the + program uses. It includes the icons, bitmaps, and cursors that are stored + in the RES subdirectory. This file can be directly edited in Microsoft + Developer Studio. + +res\AudioPlugIn.rc2 + This file contains resources that are not edited by Microsoft + Developer Studio. You should place all resources not + editable by the resource editor in this file. + +AudioPlugIn.def + This file contains information about the DLL that must be + provided to run with Microsoft Windows. It defines parameters + such as the name and description of the DLL. It also exports + functions from the DLL. + +AudioPlugIn.clw + This file contains information used by ClassWizard to edit existing + classes or add new classes. ClassWizard also uses this file to store + information needed to create and edit message maps and dialog data + maps and to create prototype member functions. + +/////////////////////////////////////////////////////////// +Support files: + +AudioPlugInApp.h +AudioPlugInApp.cpp: + Entry points for component registration and deregistration. + +MediaParams.h +MediaParams.cpp: + CMediaParams, a helper class to implement all pertinent DirectX automation + intefaces, such as IMediaParams and IMediaParamsInfo. + +ParamEnvelope.h +ParamEnvelope.cpp: + CParamEnvelope, a container for a single parameter's envelope, i.e., its + evolving shape over time. CMediaParams keeps a collection of these. + +/////////////////////////////////////////////////////////// +Other standard files: + +StdAfx.h, StdAfx.cpp + These files are used to build a precompiled header (PCH) file + named AudioPlugIn.pch and a precompiled types file named StdAfx.obj. + +Resource.h + This is the standard header file, which defines new resource IDs. + Microsoft Developer Studio reads and updates this file. + +///////////////////////////////////////////////////////////////////////////// +Other notes: + +AppWizard uses "TODO:" to indicate parts of the source code you +should add to or customize. + +///////////////////////////////////////////////////////////////////////////// diff --git a/Src/Plugins/DSP/dsp_sps/dxi/StdAfx.cpp b/Src/Plugins/DSP/dsp_sps/dxi/StdAfx.cpp new file mode 100644 index 00000000..52fe71a8 --- /dev/null +++ b/Src/Plugins/DSP/dsp_sps/dxi/StdAfx.cpp @@ -0,0 +1,9 @@ +// stdafx.cpp : source file that includes just the standard includes +// DShowMFC.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + +// If the include file below cannot be opened, then you do not have DirectX 8 SDK installed, or do not +// have its headers in your configured include path. +#include diff --git a/Src/Plugins/DSP/dsp_sps/dxi/StdAfx.h b/Src/Plugins/DSP/dsp_sps/dxi/StdAfx.h new file mode 100644 index 00000000..7af62053 --- /dev/null +++ b/Src/Plugins/DSP/dsp_sps/dxi/StdAfx.h @@ -0,0 +1,43 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#if !defined(AFX_STDAFX_H__5BCD2B6C_0D14_4C1E_8269_E522431AE0DD__INCLUDED_) +#define AFX_STDAFX_H__5BCD2B6C_0D14_4C1E_8269_E522431AE0DD__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers + +#include +#include + +#ifdef _DEBUG +#define DEBUG // the DirectShow headers use this symbol +#endif + +#include +#include +#include +#include + +#ifndef WAVE_FORMAT_IEEE_FLOAT + #define WAVE_FORMAT_IEEE_FLOAT (3) +#endif + +#pragma warning( disable: 4786 ) // identifier was trucated to '255' characters in the debug information + +#include +#include +#include +using namespace std; + +#pragma warning( disable: 4355 ) // 'this' : used in base member initialization list + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_STDAFX_H__5BCD2B6C_0D14_4C1E_8269_E522431AE0DD__INCLUDED_) diff --git a/Src/Plugins/DSP/dsp_sps/dxi/dmoguids.lib b/Src/Plugins/DSP/dsp_sps/dxi/dmoguids.lib new file mode 100644 index 00000000..89039a8f Binary files /dev/null and b/Src/Plugins/DSP/dsp_sps/dxi/dmoguids.lib differ diff --git a/Src/Plugins/DSP/dsp_sps/dxi/include/CakeMedParam.h b/Src/Plugins/DSP/dsp_sps/dxi/include/CakeMedParam.h new file mode 100644 index 00000000..0ecf149a --- /dev/null +++ b/Src/Plugins/DSP/dsp_sps/dxi/include/CakeMedParam.h @@ -0,0 +1,461 @@ +/* this ALWAYS GENERATED file contains the definitions for the interfaces */ + + +/* File created by MIDL compiler version 5.01.0164 */ +/* at Fri May 03 10:13:47 2002 + */ +/* Compiler settings for CakeMedParam.idl: + Os (OptLev=s), W1, Zp8, env=Win32, ms_ext, c_ext + error checks: allocation ref bounds_check enum stub_data +*/ +//MIDL_FILE_HEADING( ) + + +/* verify that the version is high enough to compile this file*/ +#ifndef __REQUIRED_RPCNDR_H_VERSION__ +#define __REQUIRED_RPCNDR_H_VERSION__ 440 +#endif + +#include "rpc.h" +#include "rpcndr.h" + +#ifndef __RPCNDR_H_VERSION__ +#error this stub requires an updated version of +#endif // __RPCNDR_H_VERSION__ + +#ifndef COM_NO_WINDOWS_H +#include "windows.h" +#include "ole2.h" +#endif /*COM_NO_WINDOWS_H*/ + +#ifndef __CakeMedParam_h__ +#define __CakeMedParam_h__ + +#ifdef __cplusplus +extern "C"{ +#endif + +/* Forward Declarations */ + +#ifndef __IMediaParamsUICallback_FWD_DEFINED__ +#define __IMediaParamsUICallback_FWD_DEFINED__ +typedef interface IMediaParamsUICallback IMediaParamsUICallback; +#endif /* __IMediaParamsUICallback_FWD_DEFINED__ */ + + +#ifndef __IMediaParamsSetUICallback_FWD_DEFINED__ +#define __IMediaParamsSetUICallback_FWD_DEFINED__ +typedef interface IMediaParamsSetUICallback IMediaParamsSetUICallback; +#endif /* __IMediaParamsSetUICallback_FWD_DEFINED__ */ + + +#ifndef __IMediaParamsCapture_FWD_DEFINED__ +#define __IMediaParamsCapture_FWD_DEFINED__ +typedef interface IMediaParamsCapture IMediaParamsCapture; +#endif /* __IMediaParamsCapture_FWD_DEFINED__ */ + + +#ifndef __IMediaParamsUICallback_FWD_DEFINED__ +#define __IMediaParamsUICallback_FWD_DEFINED__ +typedef interface IMediaParamsUICallback IMediaParamsUICallback; +#endif /* __IMediaParamsUICallback_FWD_DEFINED__ */ + + +#ifndef __IMediaParamsSetUICallback_FWD_DEFINED__ +#define __IMediaParamsSetUICallback_FWD_DEFINED__ +typedef interface IMediaParamsSetUICallback IMediaParamsSetUICallback; +#endif /* __IMediaParamsSetUICallback_FWD_DEFINED__ */ + + +#ifndef __IMediaParamsCapture_FWD_DEFINED__ +#define __IMediaParamsCapture_FWD_DEFINED__ +typedef interface IMediaParamsCapture IMediaParamsCapture; +#endif /* __IMediaParamsCapture_FWD_DEFINED__ */ + + +void __RPC_FAR * __RPC_USER MIDL_user_allocate(size_t); +void __RPC_USER MIDL_user_free( void __RPC_FAR * ); + +#ifndef __IMediaParamsUICallback_INTERFACE_DEFINED__ +#define __IMediaParamsUICallback_INTERFACE_DEFINED__ + +/* interface IMediaParamsUICallback */ +/* [version][uuid][local][object] */ + + +EXTERN_C const IID IID_IMediaParamsUICallback; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("B8E0480A-E08D-4a5d-9228-248017032368") + IMediaParamsUICallback : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE ParamsBeginCapture( + /* [in] */ DWORD __RPC_FAR *aIndex, + /* [in] */ DWORD cPoints) = 0; + + virtual HRESULT STDMETHODCALLTYPE ParamsChanged( + /* [in] */ DWORD __RPC_FAR *aIndex, + /* [in] */ DWORD cPoints, + /* [in] */ MP_DATA __RPC_FAR *paData) = 0; + + virtual HRESULT STDMETHODCALLTYPE ParamsEndCapture( + /* [in] */ DWORD __RPC_FAR *aIndex, + /* [in] */ DWORD cPoints) = 0; + + }; + +#else /* C style interface */ + + typedef struct IMediaParamsUICallbackVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IMediaParamsUICallback __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IMediaParamsUICallback __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IMediaParamsUICallback __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *ParamsBeginCapture )( + IMediaParamsUICallback __RPC_FAR * This, + /* [in] */ DWORD __RPC_FAR *aIndex, + /* [in] */ DWORD cPoints); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *ParamsChanged )( + IMediaParamsUICallback __RPC_FAR * This, + /* [in] */ DWORD __RPC_FAR *aIndex, + /* [in] */ DWORD cPoints, + /* [in] */ MP_DATA __RPC_FAR *paData); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *ParamsEndCapture )( + IMediaParamsUICallback __RPC_FAR * This, + /* [in] */ DWORD __RPC_FAR *aIndex, + /* [in] */ DWORD cPoints); + + END_INTERFACE + } IMediaParamsUICallbackVtbl; + + interface IMediaParamsUICallback + { + CONST_VTBL struct IMediaParamsUICallbackVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IMediaParamsUICallback_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IMediaParamsUICallback_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IMediaParamsUICallback_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IMediaParamsUICallback_ParamsBeginCapture(This,aIndex,cPoints) \ + (This)->lpVtbl -> ParamsBeginCapture(This,aIndex,cPoints) + +#define IMediaParamsUICallback_ParamsChanged(This,aIndex,cPoints,paData) \ + (This)->lpVtbl -> ParamsChanged(This,aIndex,cPoints,paData) + +#define IMediaParamsUICallback_ParamsEndCapture(This,aIndex,cPoints) \ + (This)->lpVtbl -> ParamsEndCapture(This,aIndex,cPoints) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IMediaParamsUICallback_ParamsBeginCapture_Proxy( + IMediaParamsUICallback __RPC_FAR * This, + /* [in] */ DWORD __RPC_FAR *aIndex, + /* [in] */ DWORD cPoints); + + +void __RPC_STUB IMediaParamsUICallback_ParamsBeginCapture_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IMediaParamsUICallback_ParamsChanged_Proxy( + IMediaParamsUICallback __RPC_FAR * This, + /* [in] */ DWORD __RPC_FAR *aIndex, + /* [in] */ DWORD cPoints, + /* [in] */ MP_DATA __RPC_FAR *paData); + + +void __RPC_STUB IMediaParamsUICallback_ParamsChanged_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IMediaParamsUICallback_ParamsEndCapture_Proxy( + IMediaParamsUICallback __RPC_FAR * This, + /* [in] */ DWORD __RPC_FAR *aIndex, + /* [in] */ DWORD cPoints); + + +void __RPC_STUB IMediaParamsUICallback_ParamsEndCapture_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IMediaParamsUICallback_INTERFACE_DEFINED__ */ + + +#ifndef __IMediaParamsSetUICallback_INTERFACE_DEFINED__ +#define __IMediaParamsSetUICallback_INTERFACE_DEFINED__ + +/* interface IMediaParamsSetUICallback */ +/* [version][uuid][local][object] */ + + +EXTERN_C const IID IID_IMediaParamsSetUICallback; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("F5011136-C416-48b9-8C35-E7C5F9AA6FDF") + IMediaParamsSetUICallback : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE SetUICallback( + /* [in] */ IMediaParamsUICallback __RPC_FAR *pICallback) = 0; + + }; + +#else /* C style interface */ + + typedef struct IMediaParamsSetUICallbackVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IMediaParamsSetUICallback __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IMediaParamsSetUICallback __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IMediaParamsSetUICallback __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *SetUICallback )( + IMediaParamsSetUICallback __RPC_FAR * This, + /* [in] */ IMediaParamsUICallback __RPC_FAR *pICallback); + + END_INTERFACE + } IMediaParamsSetUICallbackVtbl; + + interface IMediaParamsSetUICallback + { + CONST_VTBL struct IMediaParamsSetUICallbackVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IMediaParamsSetUICallback_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IMediaParamsSetUICallback_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IMediaParamsSetUICallback_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IMediaParamsSetUICallback_SetUICallback(This,pICallback) \ + (This)->lpVtbl -> SetUICallback(This,pICallback) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IMediaParamsSetUICallback_SetUICallback_Proxy( + IMediaParamsSetUICallback __RPC_FAR * This, + /* [in] */ IMediaParamsUICallback __RPC_FAR *pICallback); + + +void __RPC_STUB IMediaParamsSetUICallback_SetUICallback_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IMediaParamsSetUICallback_INTERFACE_DEFINED__ */ + + +#ifndef __IMediaParamsCapture_INTERFACE_DEFINED__ +#define __IMediaParamsCapture_INTERFACE_DEFINED__ + +/* interface IMediaParamsCapture */ +/* [version][uuid][local][object] */ + + +EXTERN_C const IID IID_IMediaParamsCapture; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + MIDL_INTERFACE("970FED79-6DEB-4ec4-A6EE-F72C6BA545CC") + IMediaParamsCapture : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE ParamCapture( + /* [in] */ DWORD dwIndex, + /* [in] */ REFERENCE_TIME refTimeCapture, + /* [in] */ MP_FLAGS flags) = 0; + + virtual HRESULT STDMETHODCALLTYPE ParamRelease( + /* [in] */ DWORD dwIndex, + /* [in] */ REFERENCE_TIME refTimeRelease, + /* [in] */ MP_FLAGS flags) = 0; + + }; + +#else /* C style interface */ + + typedef struct IMediaParamsCaptureVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IMediaParamsCapture __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IMediaParamsCapture __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IMediaParamsCapture __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *ParamCapture )( + IMediaParamsCapture __RPC_FAR * This, + /* [in] */ DWORD dwIndex, + /* [in] */ REFERENCE_TIME refTimeCapture, + /* [in] */ MP_FLAGS flags); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *ParamRelease )( + IMediaParamsCapture __RPC_FAR * This, + /* [in] */ DWORD dwIndex, + /* [in] */ REFERENCE_TIME refTimeRelease, + /* [in] */ MP_FLAGS flags); + + END_INTERFACE + } IMediaParamsCaptureVtbl; + + interface IMediaParamsCapture + { + CONST_VTBL struct IMediaParamsCaptureVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IMediaParamsCapture_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IMediaParamsCapture_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IMediaParamsCapture_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IMediaParamsCapture_ParamCapture(This,dwIndex,refTimeCapture,flags) \ + (This)->lpVtbl -> ParamCapture(This,dwIndex,refTimeCapture,flags) + +#define IMediaParamsCapture_ParamRelease(This,dwIndex,refTimeRelease,flags) \ + (This)->lpVtbl -> ParamRelease(This,dwIndex,refTimeRelease,flags) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IMediaParamsCapture_ParamCapture_Proxy( + IMediaParamsCapture __RPC_FAR * This, + /* [in] */ DWORD dwIndex, + /* [in] */ REFERENCE_TIME refTimeCapture, + /* [in] */ MP_FLAGS flags); + + +void __RPC_STUB IMediaParamsCapture_ParamCapture_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IMediaParamsCapture_ParamRelease_Proxy( + IMediaParamsCapture __RPC_FAR * This, + /* [in] */ DWORD dwIndex, + /* [in] */ REFERENCE_TIME refTimeRelease, + /* [in] */ MP_FLAGS flags); + + +void __RPC_STUB IMediaParamsCapture_ParamRelease_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IMediaParamsCapture_INTERFACE_DEFINED__ */ + + + +#ifndef __CakeMedParam_LIBRARY_DEFINED__ +#define __CakeMedParam_LIBRARY_DEFINED__ + +/* library CakeMedParam */ +/* [helpstring][version][uuid] */ + + + + + +EXTERN_C const IID LIBID_CakeMedParam; +#endif /* __CakeMedParam_LIBRARY_DEFINED__ */ + +/* Additional Prototypes for ALL interfaces */ + +/* end of Additional Prototypes */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/Src/Plugins/DSP/dsp_sps/dxi/include/CakeMedParam_i.c b/Src/Plugins/DSP/dsp_sps/dxi/include/CakeMedParam_i.c new file mode 100644 index 00000000..a166776e --- /dev/null +++ b/Src/Plugins/DSP/dsp_sps/dxi/include/CakeMedParam_i.c @@ -0,0 +1,53 @@ +/* this file contains the actual definitions of */ +/* the IIDs and CLSIDs */ + +/* link this file in with the server and any clients */ + + +/* File created by MIDL compiler version 5.01.0164 */ +/* at Fri May 03 10:13:47 2002 + */ +/* Compiler settings for CakeMedParam.idl: + Os (OptLev=s), W1, Zp8, env=Win32, ms_ext, c_ext + error checks: allocation ref bounds_check enum stub_data +*/ +//MIDL_FILE_HEADING( ) +#ifdef __cplusplus +extern "C"{ +#endif + + +#ifndef __IID_DEFINED__ +#define __IID_DEFINED__ + +typedef struct _IID +{ + unsigned long x; + unsigned short s1; + unsigned short s2; + unsigned char c[8]; +} IID; + +#endif // __IID_DEFINED__ + +#ifndef CLSID_DEFINED +#define CLSID_DEFINED +typedef IID CLSID; +#endif // CLSID_DEFINED + +const IID IID_IMediaParamsUICallback = {0xB8E0480A,0xE08D,0x4a5d,{0x92,0x28,0x24,0x80,0x17,0x03,0x23,0x68}}; + + +const IID IID_IMediaParamsSetUICallback = {0xF5011136,0xC416,0x48b9,{0x8C,0x35,0xE7,0xC5,0xF9,0xAA,0x6F,0xDF}}; + + +const IID IID_IMediaParamsCapture = {0x970FED79,0x6DEB,0x4ec4,{0xA6,0xEE,0xF7,0x2C,0x6B,0xA5,0x45,0xCC}}; + + +const IID LIBID_CakeMedParam = {0xA8F8EF3E,0x4E39,0x49e2,{0x89,0xE7,0x3C,0x91,0x94,0x2C,0xC5,0x7B}}; + + +#ifdef __cplusplus +} +#endif + diff --git a/Src/Plugins/DSP/dsp_sps/dxi/include/DXi.h b/Src/Plugins/DSP/dsp_sps/dxi/include/DXi.h new file mode 100644 index 00000000..44ce0dd1 --- /dev/null +++ b/Src/Plugins/DSP/dsp_sps/dxi/include/DXi.h @@ -0,0 +1,121 @@ +#ifndef _DXI_H_ +#define _DXI_H_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#include + +// DirectX automation helper +#include "MediaParams.h" + +//////////////////////////////////////////////////////////////////////////////// + +struct AudioBuffer +{ + long cSamp; // number of samples in the buffer + long lOffset; // offset into the data to process + IMediaSample* pms; // the raw IMediaSample for this buffer + + AudioBuffer() : cSamp(0), lOffset(0), pms(NULL) {} + + //---------------------------------------------------------------------------- + // Get a pointer to the audio samples, zero-filling if necesssary + + float* GetPointer() + { + // Get the raw-pointer + BYTE* pb = NULL; + pms->GetPointer( &pb ); + + // We cannot defer the zero fill any longer! + if (bZero) + { + IDeferZeroFill* pdzf; + if (SUCCEEDED( pms->QueryInterface( IID_IDeferZeroFill, (void**)&pdzf ) )) + { + // IDeferZeroFill will have taken care of the zero-fill for us, by + // virtue of our calling IMediaSample::GetPointer. Nothing more to do. + pdzf->Release(); + } + else + { + // No IDeferZeroFill is available. We must zero-fill the hard way. + memset( pb, 0, cSamp * sizeof(float) ); + } + bZero = FALSE; + } + + return reinterpret_cast( pb + lOffset ); + } + + //---------------------------------------------------------------------------- + // Allow buffers to be tagged as being all zeroes, without actually filling + // any data until someone asks for the buffer pointer + + BOOL GetZerofill() const { return bZero; } + + void SetZerofill( BOOL bZerofill ) + { + bZero = bZerofill; + IDeferZeroFill* pdzf; + if (SUCCEEDED( pms->QueryInterface( IID_IDeferZeroFill, (void**)&pdzf ) )) + { + pdzf->put_NeedsZerofill( bZero ); + pdzf->Release(); + } + } + +private: + + BOOL bZero; +}; + + +//////////////////////////////////////////////////////////////////////////////// + +class CDXi : public CCritSec +{ +public: + + virtual HRESULT Initialize() = 0; + + virtual HRESULT IsValidInputFormat( const WAVEFORMATEX* pwfx ) const = 0; + virtual HRESULT IsValidOutputFormat( const WAVEFORMATEX* pwfx ) const = 0; + virtual HRESULT IsValidTransform( const WAVEFORMATEX* pwfxIn, const WAVEFORMATEX* pwfxOut ) const = 0; + virtual HRESULT SuggestOutputFormat( WAVEFORMATEX* pwfx ) const = 0; + + virtual const WAVEFORMATEX* GetInputFormat() const { return &m_wfxIn; } + virtual const WAVEFORMATEX* GetOutputFormat() const { return &m_wfxOut; } + + virtual HRESULT Process( LONGLONG llSampAudioTimestamp, + AudioBuffer* pbufIn, + AudioBuffer* pbufOut ) = 0; + + virtual HRESULT AllocateResources() = 0; + virtual HRESULT FreeResources() = 0; + + virtual int PersistGetSize() const = 0; + virtual HRESULT PersistLoad( IStream* pStream ) = 0; + virtual HRESULT PersistSave( IStream* pStream ) = 0; + +protected: + WAVEFORMATEX m_wfxIn; + WAVEFORMATEX m_wfxOut; + CMediaParams* m_pMediaParams; + + float GetParamValue( DWORD dwParam ) const + { + return m_pMediaParams->GetParamEnvelope( dwParam ).GetCurrentValue(); + } + + HRESULT GetParamDeltas( DWORD dwParam, double* pdDelta1, double* pdDelta2 ) const + { + return m_pMediaParams->GetParamEnvelope( dwParam ).GetCurrentDeltas( pdDelta1, pdDelta2 ); + } +}; + +//////////////////////////////////////////////////////////////////////////////// + +#endif //_DXI_H_ diff --git a/Src/Plugins/DSP/dsp_sps/dxi/include/DeferZeroFill.h b/Src/Plugins/DSP/dsp_sps/dxi/include/DeferZeroFill.h new file mode 100644 index 00000000..4aa13e8e --- /dev/null +++ b/Src/Plugins/DSP/dsp_sps/dxi/include/DeferZeroFill.h @@ -0,0 +1,33 @@ +// IDeferZeroFill interface exposed a custom memory allocator and media sample. +// +// Copyright (C) 1997- Cakewalk Music Software. All rights reserved. +// + +#ifndef _DEFERZEROFILL_H_ +#define _DEFERZEROFILL_H_ + +///////////////////////////////////////////////////////////////////////////// +// Clients that wish to create real copies of the GUID must first include INITGUID.H + +// {447DA113-4AC8-4833-849A-2BA285E1E52B} +DEFINE_GUID(IID_IDeferZeroFill, +0x447da113, 0x4ac8, 0x4833, 0x84, 0x9a, 0x2b, 0xa2, 0x85, 0xe1, 0xe5, 0x2b); + +#undef INTERFACE +#define INTERFACE IDeferZeroFill + +DECLARE_INTERFACE_( IDeferZeroFill, IUnknown ) +{ + // *** IUnknown methods *** + STDMETHOD_(HRESULT, QueryInterface)( THIS_ REFIID riid, LPVOID* ppvObj ) PURE; + STDMETHOD_(ULONG, AddRef)( THIS ) PURE; + STDMETHOD_(ULONG, Release)( THIS ) PURE; + + // *** IDeferZeroFill methods *** + STDMETHOD_(BOOL, get_NeedsZerofill)( THIS ) PURE; + STDMETHOD_(void, put_NeedsZerofill)( THIS_ BOOL bZero ) PURE; + STDMETHOD_(HRESULT, GetRawPointer)( THIS_ BYTE** ppBuffer ) PURE; +}; + +#endif // _DEFERZEROFILL_H_ + diff --git a/Src/Plugins/DSP/dsp_sps/dxi/res/AudioPlugIn.rc2 b/Src/Plugins/DSP/dsp_sps/dxi/res/AudioPlugIn.rc2 new file mode 100644 index 00000000..2f7a81fc --- /dev/null +++ b/Src/Plugins/DSP/dsp_sps/dxi/res/AudioPlugIn.rc2 @@ -0,0 +1,13 @@ +// +// MFCDELAY.RC2 - resources Microsoft Visual C++ does not edit directly +// + +#ifdef APSTUDIO_INVOKED + #error this file is not editable by Microsoft Visual C++ +#endif //APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// Add manually edited resources here... + +///////////////////////////////////////////////////////////////////////////// diff --git a/Src/Plugins/DSP/dsp_sps/dxi/resource.h b/Src/Plugins/DSP/dsp_sps/dxi/resource.h new file mode 100644 index 00000000..a51c6904 --- /dev/null +++ b/Src/Plugins/DSP/dsp_sps/dxi/resource.h @@ -0,0 +1,59 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by AudioPlugIn.rc +// +#define IDS_NAME_PLUGIN 1 +#define IDS_DESC_PLUGIN 2 +#define IDS_SETTINGS 3 +#define IDS_HELPFILE_PLUGIN 4 +#define IDD_PROPPAGE 200 +#define IDC_STATIC -1 +#define IDD_DIALOG1 101 +#define IDD_EVAL_HELP 166 +#define IDC_SLIDER1 1000 +#define IDC_SLIDER1_LABEL 1001 +#define IDC_SLIDER1_LABEL1 1001 +#define IDC_SLIDER1_LABEL2 1002 +#define IDC_SLIDER1_LABEL3 1003 +#define IDC_SLIDER2 1004 +#define IDC_SLIDER2_LABEL1 1005 +#define IDC_SLIDER2_LABEL2 1006 +#define IDC_SLIDER2_LABEL3 1007 +#define IDC_SLIDER3 1008 +#define IDC_SLIDER3_LABEL1 1009 +#define IDC_SLIDER3_LABEL2 1010 +#define IDC_SLIDER3_LABEL3 1011 +#define IDC_SLIDER4 1012 +#define IDC_LOAD 1013 +#define IDC_EDIT 1014 +#define IDC_SAVE 1015 +#define IDC_BYPASS 1016 +#define IDC_NEW 1017 +#define IDC_PRESET 1018 +#define IDC_SHOWHELP 1019 +#define IDC_SLIDER4_LABEL2 1020 +#define IDC_SLIDER4_LABEL3 1021 +#define IDC_SLIDER4_LABEL1 1022 +#define IDC_TRIGGER1 1023 +#define IDC_TRIGGER2 1024 +#define IDC_TRIGGER3 1025 +#define IDC_TRIGGER4 1026 +#define IDC_EDIT1 1089 +#define IDC_PERSAMPLE 1090 +#define IDC_INIT 1092 +#define IDC_ONSLIDERCHANGE 1093 +#define IDC_TAB1 1203 +#define IDC_PRESETNAME -1 + + + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 201 +#define _APS_NEXT_COMMAND_VALUE 32768 +#define _APS_NEXT_CONTROL_VALUE 1100 +#define _APS_NEXT_SYMED_VALUE 5 +#endif +#endif diff --git a/Src/Plugins/DSP/dsp_sps/function.bin b/Src/Plugins/DSP/dsp_sps/function.bin new file mode 100644 index 00000000..c9b369f0 Binary files /dev/null and b/Src/Plugins/DSP/dsp_sps/function.bin differ diff --git a/Src/Plugins/DSP/dsp_sps/general.bin b/Src/Plugins/DSP/dsp_sps/general.bin new file mode 100644 index 00000000..656d96cd Binary files /dev/null and b/Src/Plugins/DSP/dsp_sps/general.bin differ diff --git a/Src/Plugins/DSP/dsp_sps/operator.bin b/Src/Plugins/DSP/dsp_sps/operator.bin new file mode 100644 index 00000000..feb40e1e Binary files /dev/null and b/Src/Plugins/DSP/dsp_sps/operator.bin differ diff --git a/Src/Plugins/DSP/dsp_sps/res.rc b/Src/Plugins/DSP/dsp_sps/res.rc new file mode 100644 index 00000000..f815b4a2 --- /dev/null +++ b/Src/Plugins/DSP/dsp_sps/res.rc @@ -0,0 +1,210 @@ +//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_DIALOG1 DIALOG DISCARDABLE 0, 0, 343, 223 +STYLE DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +FONT 8, "MS Sans Serif" +BEGIN + PUSHBUTTON "Load",IDC_LOAD,3,4,41,14 + PUSHBUTTON "New",IDC_NEW,50,4,41,14 + PUSHBUTTON "",IDC_EDIT,133,4,46,14 + PUSHBUTTON "Save",IDC_SAVE,185,4,50,14 + PUSHBUTTON "Help",IDC_SHOWHELP,289,4,50,14 + LTEXT "Current preset:",IDC_STATIC,3,21,47,8 + LTEXT "",IDC_PRESET,3,32,175,10,SS_SUNKEN + CONTROL "Enable processing",IDC_BYPASS,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,3,47,74,10 + GROUPBOX "Signal Processing Controls",IDC_STATIC,3,61,176,159 + CONTROL "Slider1",IDC_SLIDER1,"msctls_trackbar32",TBS_AUTOTICKS | + TBS_VERT | WS_TABSTOP,11,70,11,116 + EDITTEXT IDC_SLIDER1_LABEL2,24,74,26,13,ES_AUTOHSCROLL + EDITTEXT IDC_SLIDER1_LABEL3,24,170,26,13,ES_AUTOHSCROLL + EDITTEXT IDC_SLIDER1_LABEL1,12,189,38,13,ES_AUTOHSCROLL + PUSHBUTTON "Trigger 1",IDC_TRIGGER1,12,204,38,11 + CONTROL "Slider1",IDC_SLIDER2,"msctls_trackbar32",TBS_AUTOTICKS | + TBS_VERT | WS_TABSTOP,51,70,11,116 + EDITTEXT IDC_SLIDER2_LABEL2,64,74,26,13,ES_AUTOHSCROLL + EDITTEXT IDC_SLIDER2_LABEL3,64,170,26,13,ES_AUTOHSCROLL + EDITTEXT IDC_SLIDER2_LABEL1,52,189,38,13,ES_AUTOHSCROLL + PUSHBUTTON "Trigger 2",IDC_TRIGGER2,52,204,38,11 + CONTROL "Slider1",IDC_SLIDER3,"msctls_trackbar32",TBS_AUTOTICKS | + TBS_VERT | WS_TABSTOP,91,70,11,116 + EDITTEXT IDC_SLIDER3_LABEL2,104,74,26,13,ES_AUTOHSCROLL + EDITTEXT IDC_SLIDER3_LABEL3,104,170,26,13,ES_AUTOHSCROLL + EDITTEXT IDC_SLIDER3_LABEL1,92,189,38,13,ES_AUTOHSCROLL + PUSHBUTTON "Trigger 3",IDC_TRIGGER3,92,204,38,11 + CONTROL "Slider1",IDC_SLIDER4,"msctls_trackbar32",TBS_AUTOTICKS | + TBS_VERT | WS_TABSTOP,130,70,11,116 + EDITTEXT IDC_SLIDER4_LABEL2,144,74,26,13,ES_AUTOHSCROLL + EDITTEXT IDC_SLIDER4_LABEL3,144,170,26,13,ES_AUTOHSCROLL + EDITTEXT IDC_SLIDER4_LABEL1,132,189,38,13,ES_AUTOHSCROLL + PUSHBUTTON "Trigger 4",IDC_TRIGGER4,132,204,38,11 + LTEXT "Initialization/format change:",IDC_STATIC,183,22,88,8 + EDITTEXT IDC_INIT,183,32,155,43,ES_MULTILINE | ES_AUTOVSCROLL | + ES_AUTOHSCROLL | ES_WANTRETURN | WS_VSCROLL + LTEXT "Slider change/initialization:",IDC_STATIC,183,77,85,8 + EDITTEXT IDC_ONSLIDERCHANGE,183,87,155,41,ES_MULTILINE | + ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN | + WS_VSCROLL + LTEXT "Per sample (or sample-pair):",IDC_STATIC,183,131,87,8 + EDITTEXT IDC_PERSAMPLE,183,141,155,80,ES_MULTILINE | + ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_WANTRETURN | + WS_VSCROLL +END + +IDD_EVAL_HELP DIALOG DISCARDABLE 0, 0, 308, 226 +STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "SPS Expression Help" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "Close",IDOK,4,209,50,13 + EDITTEXT IDC_EDIT1,12,22,286,177,ES_MULTILINE | ES_READONLY | + WS_VSCROLL + CONTROL "Tab1",IDC_TAB1,"SysTabControl32",0x0,4,4,300,200 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + IDD_DIALOG1, DIALOG + BEGIN + LEFTMARGIN, 3 + RIGHTMARGIN, 341 + TOPMARGIN, 4 + BOTTOMMARGIN, 221 + END + + IDD_EVAL_HELP, DIALOG + BEGIN + LEFTMARGIN, 4 + RIGHTMARGIN, 304 + TOPMARGIN, 4 + BOTTOMMARGIN, 222 + END +END +#endif // APSTUDIO_INVOKED + + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////// +// English (U.K.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENG) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// TEXT +// + +IDR_GENERAL TEXT DISCARDABLE "general.bin" +IDR_OPERATORS TEXT DISCARDABLE "operator.bin" +IDR_FUNCTIONS TEXT DISCARDABLE "function.bin" +IDR_CONSTANTS TEXT DISCARDABLE "constant.bin" + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE DISCARDABLE +BEGIN + 65535 "{4B567AEB-89CE-4881-9D7D-B31D7B65979A}" +END + +STRINGTABLE DISCARDABLE +BEGIN + IDS_SPS_SHOW_CONFIG "SPS: Show Config" + IDS_SHOW_EDITOR "Show Editor" + IDS_HIDE_EDITOR "Hide Editor" + IDS_SAVE_PRESET "Save Preset" + IDS_SPS_PRESETS "SPS Presets" + IDS_ALL_FILES "All files" + IDS_GENERAL "General" + IDS_OPERATORS "Operators" + IDS_FUNCTIONS "Functions" + IDS_CONSTANTS "Constants" + IDS_LOAD_PRESET "Load Preset" + IDS_CLEAR_CURRENT_SETTINGS "Clear current SPS settings?" + IDS_CONFIRMATION "Confirmation" + IDS_SPS_TITLE "Nullsoft Signal Processing Studio DSP" + IDS_SPS_MODULE_TITLE "Signal Processing Studio" +END + +#endif // English (U.K.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// +#include "version.rc2" + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/Src/Plugins/DSP/dsp_sps/resource.h b/Src/Plugins/DSP/dsp_sps/resource.h new file mode 100644 index 00000000..8288d34b --- /dev/null +++ b/Src/Plugins/DSP/dsp_sps/resource.h @@ -0,0 +1,70 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by res.rc +// +#define IDS_SPS_SHOW_CONFIG 1 +#define IDS_SHOW_EDITOR 2 +#define IDS_HIDE_EDITOR 3 +#define IDS_SAVE_PRESET 4 +#define IDS_SPS_PRESETS 5 +#define IDS_ALL_FILES 6 +#define IDS_GENERAL 7 +#define IDS_OPERATORS 8 +#define IDS_FUNCTIONS 9 +#define IDS_CONSTANTS 10 +#define IDS_LOAD_PRESET 11 +#define IDS_CLEAR_CURRENT_SETTINGS 12 +#define IDS_CONFIRMATION 13 +#define IDS_SPS_TITLE 14 +#define IDS_SPS_MODULE_TITLE 15 +#define IDD_DIALOG1 101 +#define IDR_GENERAL 102 +#define IDR_OPERATORS 103 +#define IDR_FUNCTIONS 104 +#define IDR_CONSTANTS 105 +#define IDD_EVAL_HELP 166 +#define IDC_SLIDER1 1000 +#define IDC_SLIDER1_LABEL 1001 +#define IDC_SLIDER1_LABEL1 1001 +#define IDC_SLIDER1_LABEL2 1002 +#define IDC_SLIDER1_LABEL3 1003 +#define IDC_SLIDER2 1004 +#define IDC_SLIDER2_LABEL1 1005 +#define IDC_SLIDER2_LABEL2 1006 +#define IDC_SLIDER2_LABEL3 1007 +#define IDC_SLIDER3 1008 +#define IDC_SLIDER3_LABEL1 1009 +#define IDC_SLIDER3_LABEL2 1010 +#define IDC_SLIDER3_LABEL3 1011 +#define IDC_SLIDER4 1012 +#define IDC_LOAD 1013 +#define IDC_EDIT 1014 +#define IDC_SAVE 1015 +#define IDC_BYPASS 1016 +#define IDC_NEW 1017 +#define IDC_PRESET 1018 +#define IDC_SHOWHELP 1019 +#define IDC_SLIDER4_LABEL2 1020 +#define IDC_SLIDER4_LABEL3 1021 +#define IDC_SLIDER4_LABEL1 1022 +#define IDC_TRIGGER1 1023 +#define IDC_TRIGGER2 1024 +#define IDC_TRIGGER3 1025 +#define IDC_TRIGGER4 1026 +#define IDC_EDIT1 1089 +#define IDC_PERSAMPLE 1090 +#define IDC_INIT 1092 +#define IDC_ONSLIDERCHANGE 1093 +#define IDC_TAB1 1203 +#define IDC_PRESETNAME -1 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 106 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1024 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/Src/Plugins/DSP/dsp_sps/sps_common.cpp b/Src/Plugins/DSP/dsp_sps/sps_common.cpp new file mode 100644 index 00000000..1e8df30b --- /dev/null +++ b/Src/Plugins/DSP/dsp_sps/sps_common.cpp @@ -0,0 +1,574 @@ +#include +#include "resource.h" +#include "..\..\..\winamp\dsp.h" +#include "sps_common.h" + +void NSEEL_HOSTSTUB_EnterMutex() {} +void NSEEL_HOSTSTUB_LeaveMutex() {} + +extern winampDSPModule mod; + +char *SPSHELP_gethelptext( int sel ) +{ + int help_id[] = { IDR_GENERAL,IDR_OPERATORS,IDR_FUNCTIONS,IDR_CONSTANTS }; + void *data = 0; + + HRSRC rsrc = FindResource( WASABI_API_LNG_HINST, MAKEINTRESOURCE( help_id[ sel ] ), "TEXT" ); + if ( rsrc ) + { + HGLOBAL resourceHandle = LoadResource( WASABI_API_LNG_HINST, rsrc ); + data = LockResource( resourceHandle ); + + return (char *) data; + } + + return 0; +} + +void SPS_initcontext( SPSEffectContext *ctx ) +{ + memset( ctx, 0, sizeof( SPSEffectContext ) ); + InitializeCriticalSection( &ctx->code_cs ); + ctx->vm_ctx = NSEEL_VM_alloc(); + ctx->code_needrecompile = 7; +} + +void SPS_quitcontext( SPSEffectContext *ctx ) +{ + if ( ctx->sample_buffer ) GlobalFree( ctx->sample_buffer ); + ctx->sample_buffer_len = 0; + ctx->sample_buffer = NULL; + + NSEEL_code_free( ctx->code[ 0 ] ); + NSEEL_code_free( ctx->code[ 1 ] ); + NSEEL_code_free( ctx->code[ 2 ] ); + memset( &ctx->code, 0, sizeof( ctx->code ) ); + + //megabuf_cleanup( ctx->vm_ctx ); + NSEEL_VM_free( ctx->vm_ctx ); + + ctx->vm_ctx = 0; + memset( &ctx->vars, 0, sizeof( ctx->vars ) ); + + DeleteCriticalSection( &ctx->code_cs ); +} + +static __inline int double2int( double v ) +{ +#if 0 + return (int) v; +#else + int a; + __asm + { + fld v; + fistp a; + } + return a; +#endif +} + +static __inline double int24todouble( unsigned char *int24 ) +{ + unsigned int a = ( int24[ 0 ] ) | ( int24[ 1 ] << 8 ) | ( int24[ 2 ] << 16 ); + + if ( a & 0x800000 ) + a |= 0xFF000000; + else + a &= 0xFFFFFF; + + return (double) ( ( (signed int) a ) + 0.5 ) / 8388607.5; +} + +static __inline void doubletoint24( double v, unsigned char *int24 ) +{ + v = ( v * 8388607.5 ) - 0.5; + if ( v <= -8388608.0 ) + { + int24[ 0 ] = int24[ 1 ] = 0; // 0x800000 is lowest possible value + int24[ 2 ] = 0x80; + } + else if ( v >= 8388607.0 ) + { + int24[ 0 ] = int24[ 1 ] = 0xff; // 0x7fffff is highest possible value + int24[ 2 ] = 0x7f; + } + else + { + int a = (int) v; + int24[ 0 ] = a & 0xff; + int24[ 1 ] = ( a >> 8 ) & 0xff; + int24[ 2 ] = ( a >> 16 ) & 0xff; + } +} + +int SPS_process_samples( SPSEffectContext *ctx, + void *samples, int numsamples, + int isfloat, int bps, int nch, + int srate, int maxout, int minout ) +{ + // can only do 1 or 2ch for now + if ( nch != 1 && nch != 2 ) + return numsamples; + + int samplepair_size = ( bps / 8 ) * nch; + if ( ctx->bypass ) + { + memset( ctx->triggers, 0, sizeof( ctx->triggers ) ); + + return numsamples; + } + + if ( !samples || numsamples < 1 ) + return numsamples; + + int rlen = numsamples * samplepair_size; + + if ( !ctx->sample_buffer || ctx->sample_buffer_len < rlen ) + { + ctx->sample_buffer_len = rlen * 2; + + if ( ctx->sample_buffer ) + GlobalFree( ctx->sample_buffer ); + + ctx->sample_buffer = (void *) GlobalAlloc( GMEM_FIXED, ctx->sample_buffer_len ); + } + + if ( !ctx->sample_buffer ) + return numsamples; + + int needinit = ctx->last_nch != nch || ctx->last_srate != srate; + ctx->last_nch = nch; + ctx->last_srate = srate; + + if ( ctx->code_needrecompile ) + { + EnterCriticalSection( &ctx->code_cs ); + if ( ctx->code_needrecompile & 1 ) + { + //NSEEL_VM_resetvars( ctx->vm_ctx ); + ctx->vars.spl0 = NSEEL_VM_regvar( ctx->vm_ctx, "spl0" ); + ctx->vars.spl1 = NSEEL_VM_regvar( ctx->vm_ctx, "spl1" ); + ctx->vars.skip = NSEEL_VM_regvar( ctx->vm_ctx, "skip" ); + ctx->vars.repeat = NSEEL_VM_regvar( ctx->vm_ctx, "repeat" ); + ctx->vars.nch = NSEEL_VM_regvar( ctx->vm_ctx, "nch" ); + ctx->vars.srate = NSEEL_VM_regvar( ctx->vm_ctx, "srate" ); + ctx->vars.sliders1 = NSEEL_VM_regvar( ctx->vm_ctx, "slider1" ); + ctx->vars.sliders2 = NSEEL_VM_regvar( ctx->vm_ctx, "slider2" ); + ctx->vars.sliders3 = NSEEL_VM_regvar( ctx->vm_ctx, "slider3" ); + ctx->vars.sliders4 = NSEEL_VM_regvar( ctx->vm_ctx, "slider4" ); + ctx->vars.trigger1 = NSEEL_VM_regvar( ctx->vm_ctx, "trig1" ); + ctx->vars.trigger2 = NSEEL_VM_regvar( ctx->vm_ctx, "trig2" ); + ctx->vars.trigger3 = NSEEL_VM_regvar( ctx->vm_ctx, "trig3" ); + ctx->vars.trigger4 = NSEEL_VM_regvar( ctx->vm_ctx, "trig4" ); + + needinit = 1; + NSEEL_code_free( ctx->code[ 0 ] ); + ctx->code[ 0 ] = NSEEL_code_compile( ctx->vm_ctx, ctx->curpreset.code_text[ 0 ], 0 ); + } + + if ( ctx->code_needrecompile & 2 ) + { + NSEEL_code_free( ctx->code[ 1 ] ); + ctx->code[ 1 ] = NSEEL_code_compile( ctx->vm_ctx, ctx->curpreset.code_text[ 1 ], 0 ); +#ifdef DEBUG + if ( !ctx->code[ 1 ] && NSEEL_code_getcodeerror( ctx->vm_ctx ) && *NSEEL_code_getcodeerror( ctx->vm_ctx ) ) + OutputDebugString( NSEEL_code_getcodeerror( ctx->vm_ctx ) ); +#endif + } + if ( ctx->code_needrecompile & 4 ) + { + NSEEL_code_free( ctx->code[ 2 ] ); + ctx->code[ 2 ] = NSEEL_code_compile( ctx->vm_ctx, ctx->curpreset.code_text[ 2 ], 0 ); + ctx->sliderchange = 1; + } + ctx->code_needrecompile = 0; + LeaveCriticalSection( &ctx->code_cs ); + } + + if ( !ctx->vars.spl0 ) + return numsamples; + + *( ctx->vars.nch ) = (double) nch; + *( ctx->vars.srate ) = (double) srate; + + int slidech = ctx->sliderchange; + ctx->sliderchange = 0; + + if ( ctx->triggers[ 0 ] ) + { + ctx->triggers[ 0 ]--; + *( ctx->vars.trigger1 ) = 1.0; + } + + if ( ctx->triggers[ 1 ] ) + { + ctx->triggers[ 1 ]--; + *( ctx->vars.trigger2 ) = 1.0; + } + + if ( ctx->triggers[ 2 ] ) + { + ctx->triggers[ 2 ]--; + *( ctx->vars.trigger3 ) = 1.0; + } + + if ( ctx->triggers[ 3 ] ) + { + ctx->triggers[ 3 ]--; + *( ctx->vars.trigger4 ) = 1.0; + } + + if ( needinit || slidech ) + { + *( ctx->vars.sliders1 ) = (double) ctx->curpreset.sliderpos[ 0 ] / 1000.0; + *( ctx->vars.sliders2 ) = (double) ctx->curpreset.sliderpos[ 1 ] / 1000.0; + *( ctx->vars.sliders3 ) = (double) ctx->curpreset.sliderpos[ 2 ] / 1000.0; + *( ctx->vars.sliders4 ) = (double) ctx->curpreset.sliderpos[ 3 ] / 1000.0; + } + + if ( needinit ) + { + //megabuf_cleanup( ctx->vm_ctx ); + NSEEL_code_execute( ctx->code[ 0 ] ); + } + + if ( needinit || slidech ) + { + NSEEL_code_execute( ctx->code[ 2 ] ); + } + + + if ( !maxout ) + maxout = numsamples; + + memcpy( ctx->sample_buffer, samples, rlen ); + int x = 0; + int outpos = 0; + + while ( x < numsamples && outpos < maxout ) + { + *( ctx->vars.skip ) = 0; + *( ctx->vars.repeat ) = 0; + + if ( isfloat ) + { + if ( bps == 32 ) + { + float *sbuf_float = (float *) ctx->sample_buffer; + if ( nch == 2 ) + { + *( ctx->vars.spl0 ) = (double) sbuf_float[ x + x ]; + *( ctx->vars.spl1 ) = (double) sbuf_float[ x + x + 1 ]; + } + else + { + *( ctx->vars.spl1 ) = + *( ctx->vars.spl0 ) = (double) sbuf_float[ x ]; + } + } + else if ( bps == 64 ) + { + double *sbuf_dbl = (double *) ctx->sample_buffer; + if ( nch == 2 ) + { + *( ctx->vars.spl0 ) = sbuf_dbl[ x + x ]; + *( ctx->vars.spl1 ) = sbuf_dbl[ x + x + 1 ]; + } + else + { + *( ctx->vars.spl1 ) = + *( ctx->vars.spl0 ) = sbuf_dbl[ x ]; + } + } + else + { + *( ctx->vars.spl1 ) = + *( ctx->vars.spl0 ) = 0.0; + } + } + else + { + if ( bps == 16 ) + { + short *sbuf_short = (short *) ctx->sample_buffer; + if ( nch == 2 ) + { + *( ctx->vars.spl0 ) = ( sbuf_short[ x + x ] + 0.5 ) / 32767.5; + *( ctx->vars.spl1 ) = ( sbuf_short[ x + x + 1 ] + 0.5 ) / 32767.5; + } + else + { + *( ctx->vars.spl1 ) = + *( ctx->vars.spl0 ) = ( sbuf_short[ x ] + 0.5 ) / 32767.5; + } + } + else if ( bps == 8 ) + { + unsigned char *sbuf_char = (unsigned char *) ctx->sample_buffer; + if ( nch == 2 ) + { + *( ctx->vars.spl0 ) = ( sbuf_char[ x + x ] - 127.5 ) / 127.5; + *( ctx->vars.spl1 ) = ( sbuf_char[ x + x + 1 ] - 127.5 ) / 127.5; + } + else + { + *( ctx->vars.spl1 ) = + *( ctx->vars.spl0 ) = ( sbuf_char[ x ] - 127.5 ) / 127.5; + } + } + else if ( bps == 24 ) + { + unsigned char *sbuf_char = (unsigned char *) ctx->sample_buffer; + if ( nch == 2 ) + { + int x6 = ( x << 2 ) + x + x; + *( ctx->vars.spl0 ) = int24todouble( sbuf_char + x6 ); + *( ctx->vars.spl1 ) = int24todouble( sbuf_char + x6 + 3 ); + } + else + { + *( ctx->vars.spl1 ) = + *( ctx->vars.spl0 ) = int24todouble( sbuf_char + x + x + x ); + } + } + else + { + // todo: 32 bit mode? + *( ctx->vars.spl1 ) = + *( ctx->vars.spl0 ) = 0.0; + } + } + + NSEEL_code_execute( ctx->code[ 1 ] ); + + if ( *( ctx->vars.skip ) < 0.0001 || (/*samples out*/ outpos + /* samples left */ ( numsamples - x ) ) < minout ) + { + if ( isfloat ) + { + if ( bps == 32 ) + { + float *samples_float = (float *) samples; + if ( nch == 2 ) + { + samples_float[ outpos + outpos ] = (float) *ctx->vars.spl0; + samples_float[ outpos + outpos + 1 ] = (float) *ctx->vars.spl1; + } + else + { + samples_float[ outpos ] = (float) *ctx->vars.spl0; + } + } + else if ( bps == 64 ) + { + double *samples_double = (double *) samples; + if ( nch == 2 ) + { + samples_double[ outpos + outpos ] = *ctx->vars.spl0; + samples_double[ outpos + outpos + 1 ] = *ctx->vars.spl1; + } + else + { + samples_double[ outpos ] = *ctx->vars.spl0; + } + } + } + else + { + if ( bps == 16 ) + { + short *samples_short = (short *) samples; + if ( nch == 2 ) + { + double d = *( ctx->vars.spl0 ) * 32767.5 - 0.5; + if ( d <= -32768.0 ) + samples_short[ outpos + outpos ] = -32768; + else if ( d >= 32767.0 ) + samples_short[ outpos + outpos ] = 32767; + else + samples_short[ outpos + outpos ] = double2int( d ); + + d = *( ctx->vars.spl1 ) * 32767.5 - 0.5; + if ( d <= -32768.0 ) + samples_short[ outpos + outpos + 1 ] = -32768; + else if ( d >= 32767.0 ) + samples_short[ outpos + outpos + 1 ] = 32767; + else + samples_short[ outpos + outpos + 1 ] = double2int( d ); + } + else + { + double d = *( ctx->vars.spl0 ) * 32767.5 - 0.5; + if ( d <= -32768.0 ) + samples_short[ outpos ] = -32768; + else if ( d >= 32767.0 ) + samples_short[ outpos ] = 32767; + else + samples_short[ outpos ] = double2int( d ); + } + } + else if ( bps == 8 ) + { + unsigned char *samples_char = (unsigned char *) samples; + if ( nch == 2 ) + { + double d = *( ctx->vars.spl0 ) * 127.5 + 127.5; + if ( d <= 0.0 ) + samples_char[ outpos + outpos ] = 0; + else if ( d >= 255.0 ) + samples_char[ outpos + outpos ] = 255; + else + samples_char[ outpos + outpos ] = double2int( d ); + + d = *( ctx->vars.spl1 ) * 127.5 + 127.5; + if ( d <= 0.0 ) + samples_char[ outpos + outpos + 1 ] = 0; + else if ( d >= 255.0 ) + samples_char[ outpos + outpos + 1 ] = 255; + else + samples_char[ outpos + outpos + 1 ] = double2int( d ); + } + else + { + double d = *( ctx->vars.spl0 ) * 127.5 + 127.5; + if ( d <= 0.0 ) + samples_char[ outpos ] = 0; + else if ( d >= 255.0 ) + samples_char[ outpos ] = 255; + else + samples_char[ outpos ] = double2int( d ); + } + } + else if ( bps == 24 ) + { + unsigned char *samples_char = (unsigned char *) samples; + if ( nch == 2 ) + { + int op6 = outpos + outpos + ( outpos << 2 ); + doubletoint24( *ctx->vars.spl0, samples_char + op6 ); + doubletoint24( *ctx->vars.spl1, samples_char + op6 + 3 ); + } + else + { + doubletoint24( *ctx->vars.spl0, samples_char + outpos + outpos + outpos ); + } + } + else + { + memcpy( (char *) samples + outpos * samplepair_size, (char *) ctx->sample_buffer + x * samplepair_size, + samplepair_size ); + // todo: 24/32 bit modes + } + } + outpos++; + + if ( *( ctx->vars.repeat ) < 0.0001 ) + x++; + } + else x++; + } + + return outpos; +} + +static void WriteInt( char *section, char *name, int value, char *fn ) +{ + char str[ 128 ]; + wsprintf( str, "%d", value ); + WritePrivateProfileString( section, name, str, fn ); +} + +void SPS_save_preset( SPSEffectContext *ctx, char *filename, char *section ) +{ + SPSPresetConfig *cfg = &ctx->curpreset; + + WriteInt( section, "slider1", cfg->sliderpos[ 0 ], filename ); + WriteInt( section, "slider2", cfg->sliderpos[ 1 ], filename ); + WriteInt( section, "slider3", cfg->sliderpos[ 2 ], filename ); + WriteInt( section, "slider4", cfg->sliderpos[ 3 ], filename ); + int x; + for ( x = 0; x < 4; x++ ) + { + int y; + for ( y = 0; y < 3; y++ ) + { + char buf[ 64 ]; + wsprintf( buf, "labels_%d_%d", x, y ); + WritePrivateProfileString( section, buf, cfg->slider_labels[ x ][ y ], filename ); + } + } + + int s = strlen( cfg->code_text[ 0 ] ); + WriteInt( section, "code0_size", s, filename ); + WritePrivateProfileStruct( section, "code0_data", cfg->code_text[ 0 ], s, filename ); + s = strlen( cfg->code_text[ 1 ] ); + WriteInt( section, "code1_size", s, filename ); + WritePrivateProfileStruct( section, "code1_data", cfg->code_text[ 1 ], s, filename ); + s = strlen( cfg->code_text[ 2 ] ); + WriteInt( section, "code2_size", s, filename ); + WritePrivateProfileStruct( section, "code2_data", cfg->code_text[ 2 ], s, filename ); +} + +void SPS_load_preset( SPSEffectContext *ctx, char *filename, char *section ) +{ + SPSPresetConfig *cfg = &ctx->curpreset; + EnterCriticalSection( &ctx->code_cs ); + cfg->sliderpos[ 0 ] = GetPrivateProfileInt( section, "slider1", 0, filename ); + cfg->sliderpos[ 1 ] = GetPrivateProfileInt( section, "slider2", 0, filename ); + cfg->sliderpos[ 2 ] = GetPrivateProfileInt( section, "slider3", 0, filename ); + cfg->sliderpos[ 3 ] = GetPrivateProfileInt( section, "slider4", 0, filename ); + int x; + for ( x = 0; x < 4; x++ ) + { + int y; + for ( y = 0; y < 3; y++ ) + { + char buf[ 64 ]; + wsprintf( buf, "labels_%d_%d", x, y ); + GetPrivateProfileString( section, buf, "", cfg->slider_labels[ x ][ y ], MAX_LABEL_LEN, filename ); + } + } + + int s = GetPrivateProfileInt( section, "code0_size", 0, filename ); + cfg->code_text[ 0 ][ 0 ] = 0; + if ( s > 0 && s < MAX_CODE_LEN - 1 ) + { + if ( GetPrivateProfileStruct( section, "code0_data", cfg->code_text[ 0 ], s, filename ) ) + cfg->code_text[ 0 ][ s ] = 0; + else + cfg->code_text[ 0 ][ 0 ] = 0; + } + + s = GetPrivateProfileInt( section, "code1_size", 0, filename ); + cfg->code_text[ 1 ][ 0 ] = 0; + if ( s > 0 && s < MAX_CODE_LEN - 1 ) + { + if ( GetPrivateProfileStruct( section, "code1_data", cfg->code_text[ 1 ], s, filename ) ) + cfg->code_text[ 1 ][ s ] = 0; + else + cfg->code_text[ 1 ][ 0 ] = 0; + } + s = GetPrivateProfileInt( section, "code2_size", 0, filename ); + cfg->code_text[ 2 ][ 0 ] = 0; + if ( s > 0 && s < MAX_CODE_LEN - 1 ) + { + if ( GetPrivateProfileStruct( section, "code2_data", cfg->code_text[ 2 ], s, filename ) ) + cfg->code_text[ 2 ][ s ] = 0; + else + cfg->code_text[ 2 ][ 0 ] = 0; + } + ctx->code_needrecompile = 7; + lstrcpyn( ctx->curpreset_name, filename, sizeof( ctx->curpreset_name ) ); + LeaveCriticalSection( &ctx->code_cs ); +} + +void SPS_initapp() +{ + NSEEL_init(); + //NSEEL_addfunctionex( "megabuf", 1, (int) _asm_megabuf, (int) _asm_megabuf_end - (int) _asm_megabuf, megabuf_ppproc ); +} + +void SPS_quitapp() +{ + NSEEL_quit(); +} \ No newline at end of file diff --git a/Src/Plugins/DSP/dsp_sps/sps_common.h b/Src/Plugins/DSP/dsp_sps/sps_common.h new file mode 100644 index 00000000..2157ca42 --- /dev/null +++ b/Src/Plugins/DSP/dsp_sps/sps_common.h @@ -0,0 +1,63 @@ +#ifndef _SPS_COMMON_H_ +#define _SPS_COMMON_H_ + +#if (_MSC_VER <= 1200) +typedef int intptr_t; +#endif + +#include "..\..\..\ns-eel2\ns-eel.h" +#include "api__dsp_sps.h" + +#define MAX_CODE_LEN 32768 +#define MAX_LABEL_LEN 32 + +char *SPSHELP_gethelptext(int sel); + +typedef struct +{ + char code_text[3][MAX_CODE_LEN]; + + char slider_labels[4][3][MAX_LABEL_LEN]; + int sliderpos[4]; +} SPSPresetConfig; + +// someday we'll have multiple of these, stackable like :) +typedef struct +{ + CRITICAL_SECTION code_cs; + int code_needrecompile; // &1 = init, &2 = per sample, &4=slider + NSEEL_VMCTX vm_ctx; + NSEEL_CODEHANDLE code[3]; + void *sample_buffer; + int sample_buffer_len; + int last_nch, last_srate; + struct + { + double *spl0, *spl1; + double *skip; + double *repeat; + double *nch; + double *srate; + double *sliders1,*sliders2,*sliders3,*sliders4; + double *trigger1,*trigger2,*trigger3,*trigger4; + } vars; + int triggers[4]; + int sliderchange; + + int bypass; // def1 + SPSPresetConfig curpreset; + char curpreset_name[2048]; +} +SPSEffectContext; + +void SPS_initapp(); +void SPS_quitapp(); +void SPS_initcontext(SPSEffectContext *ctx); +void SPS_quitcontext(SPSEffectContext *ctx); +int SPS_process_samples(SPSEffectContext *ctx, void *samples, + int numsamples, int isfloat, int bps, + int nch, int srate, int maxout, int minout); +void SPS_load_preset(SPSEffectContext *ctx, char *filename, char *section); +void SPS_save_preset(SPSEffectContext *ctx, char *filename, char *section); + +#endif \ No newline at end of file diff --git a/Src/Plugins/DSP/dsp_sps/sps_configdlg.h b/Src/Plugins/DSP/dsp_sps/sps_configdlg.h new file mode 100644 index 00000000..b6e462a1 --- /dev/null +++ b/Src/Plugins/DSP/dsp_sps/sps_configdlg.h @@ -0,0 +1,476 @@ +#ifndef SPS_CONFIGDLG_IMPL + +BOOL CALLBACK SPS_configWindowProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam); + +#else + +#ifdef SPS_CONFIGDLG_HIDEABLE_EDITOR +static void showHideSliders(HWND hwndDlg, SPSEffectContext *ctx) +{ + int x; + x=(SPS_CONFIGDLG_HIDEABLE_EDITOR || ctx->curpreset.slider_labels[0][0][0])?SW_SHOWNA:SW_HIDE; + ShowWindow(GetDlgItem(hwndDlg,IDC_SLIDER1),x); + ShowWindow(GetDlgItem(hwndDlg,IDC_SLIDER1_LABEL1),x); + ShowWindow(GetDlgItem(hwndDlg,IDC_SLIDER1_LABEL2),x); + ShowWindow(GetDlgItem(hwndDlg,IDC_SLIDER1_LABEL3),x); + x=(SPS_CONFIGDLG_HIDEABLE_EDITOR || ctx->curpreset.slider_labels[1][0][0])?SW_SHOWNA:SW_HIDE; + ShowWindow(GetDlgItem(hwndDlg,IDC_SLIDER2),x); + ShowWindow(GetDlgItem(hwndDlg,IDC_SLIDER2_LABEL1),x); + ShowWindow(GetDlgItem(hwndDlg,IDC_SLIDER2_LABEL2),x); + ShowWindow(GetDlgItem(hwndDlg,IDC_SLIDER2_LABEL3),x); + x=(SPS_CONFIGDLG_HIDEABLE_EDITOR || ctx->curpreset.slider_labels[2][0][0])?SW_SHOWNA:SW_HIDE; + ShowWindow(GetDlgItem(hwndDlg,IDC_SLIDER3),x); + ShowWindow(GetDlgItem(hwndDlg,IDC_SLIDER3_LABEL1),x); + ShowWindow(GetDlgItem(hwndDlg,IDC_SLIDER3_LABEL2),x); + ShowWindow(GetDlgItem(hwndDlg,IDC_SLIDER3_LABEL3),x); + x=(SPS_CONFIGDLG_HIDEABLE_EDITOR || ctx->curpreset.slider_labels[3][0][0])?SW_SHOWNA:SW_HIDE; + ShowWindow(GetDlgItem(hwndDlg,IDC_SLIDER4),x); + ShowWindow(GetDlgItem(hwndDlg,IDC_SLIDER4_LABEL1),x); + ShowWindow(GetDlgItem(hwndDlg,IDC_SLIDER4_LABEL2),x); + ShowWindow(GetDlgItem(hwndDlg,IDC_SLIDER4_LABEL3),x); +} + +static void showHideEditor(HWND hwndDlg, int isInit) +{ + int en=0; + static int lw; + RECT r; + GetWindowRect(hwndDlg,&r); + if (isInit) + { + lw=r.right-r.left; + } + if (!SPS_CONFIGDLG_HIDEABLE_EDITOR) + { + SetDlgItemText(hwndDlg,IDC_EDIT,WASABI_API_LNGSTRING(IDS_SHOW_EDITOR)); + RECT r2; + GetWindowRect(GetDlgItem(hwndDlg,IDC_SAVE),&r2); + SetWindowPos(hwndDlg,NULL,0,0,r2.left-r.left,r.bottom-r.top,SWP_NOZORDER|SWP_NOACTIVATE|SWP_NOMOVE); + } + else + { + if (!isInit) + { + SetWindowPos(hwndDlg,NULL,0,0,lw,r.bottom-r.top,SWP_NOZORDER|SWP_NOACTIVATE|SWP_NOMOVE); + } + SetDlgItemText(hwndDlg,IDC_EDIT,WASABI_API_LNGSTRING(IDS_HIDE_EDITOR)); + en=1; + } + + UINT tab[] = { + IDC_SLIDER1_LABEL1, + IDC_SLIDER1_LABEL2, + IDC_SLIDER1_LABEL3, + IDC_SLIDER2_LABEL1, + IDC_SLIDER2_LABEL2, + IDC_SLIDER2_LABEL3, + IDC_SLIDER3_LABEL1, + IDC_SLIDER3_LABEL2, + IDC_SLIDER3_LABEL3, + IDC_SLIDER4_LABEL1, + IDC_SLIDER4_LABEL2, + IDC_SLIDER4_LABEL3, + }; + + for (int x = 0; x < sizeof(tab)/sizeof(UINT); x ++) + { + EnableWindow(GetDlgItem(hwndDlg,tab[x]),en); + } + + UINT tab2[]={ + IDC_SAVE, + IDC_SHOWHELP, + IDC_INIT, + IDC_ONSLIDERCHANGE, + IDC_PERSAMPLE, + }; + + for (int x = 0; x < sizeof(tab2)/sizeof(UINT); x++) + { + ShowWindow(GetDlgItem(hwndDlg,tab2[x]),en?SW_SHOWNA:SW_HIDE); + } +} + +#endif + +char* BuildFilterString(void) +{ + static char filterStr[MAX_PATH] = {0}; + if(!filterStr[0]) + { + char* temp = filterStr; + //"SPS presets\0*.sps\0All files\0*.*\0" + WASABI_API_LNGSTRING_BUF(IDS_SPS_PRESETS,filterStr,128); + temp += lstrlen(filterStr)+1; + lstrcpyn(temp, "*.sps", MAX_PATH); + temp = temp + lstrlen(temp) + 1; + lstrcpyn(temp, WASABI_API_LNGSTRING(IDS_ALL_FILES), 128); + temp = temp + lstrlen(temp) + 1; + lstrcpyn(temp, "*.*", MAX_PATH); + *(temp = temp + lstrlen(temp) + 1) = 0; + } + return filterStr; +} + +static void updatePresetText(HWND hwndDlg, SPSEffectContext *ctx) +{ + char *p=strrchr(ctx->curpreset_name,'\\'); + if (!p) p=ctx->curpreset_name; + else p++; + char *p2=strrchr(p,'.'); + if (p2) *p2=0; + SetDlgItemText(hwndDlg,IDC_PRESET,p); + if (p2) *p2='.'; +} + +static void dosavePreset(HWND hwndDlg, SPSEffectContext *ctx) +{ + char temp[2048] = {0}; + OPENFILENAME l={sizeof(l),0}; + char buf1[2048],buf2[2048]; + GetCurrentDirectory(sizeof(buf2),buf2); + strcpy(buf1,g_path); + l.hwndOwner = hwndDlg; + l.lpstrFilter = BuildFilterString(); + l.lpstrFile = temp; + strcpy(temp,ctx->curpreset_name); + l.nMaxFile = 2048-1; + l.lpstrTitle = WASABI_API_LNGSTRING(IDS_SAVE_PRESET); + l.lpstrDefExt = "SPS"; + l.lpstrInitialDir = buf1; + l.Flags = OFN_HIDEREADONLY|OFN_EXPLORER|OFN_OVERWRITEPROMPT; + if (GetSaveFileName(&l)) + { + strcpy(ctx->curpreset_name,temp); + SPS_save_preset(ctx,ctx->curpreset_name,"SPS PRESET"); + updatePresetText(hwndDlg,ctx); + } + SetCurrentDirectory(buf2); +} + +static void presetToDialog(HWND hwndDlg, SPSEffectContext *ctx) +{ + SendDlgItemMessage(hwndDlg,IDC_SLIDER1,TBM_SETPOS,1,1000-ctx->curpreset.sliderpos[0]); + SendDlgItemMessage(hwndDlg,IDC_SLIDER2,TBM_SETPOS,1,1000-ctx->curpreset.sliderpos[1]); + SendDlgItemMessage(hwndDlg,IDC_SLIDER3,TBM_SETPOS,1,1000-ctx->curpreset.sliderpos[2]); + SendDlgItemMessage(hwndDlg,IDC_SLIDER4,TBM_SETPOS,1,1000-ctx->curpreset.sliderpos[3]); + + SetDlgItemText(hwndDlg,IDC_SLIDER1_LABEL1,ctx->curpreset.slider_labels[0][0]); + SetDlgItemText(hwndDlg,IDC_SLIDER1_LABEL2,ctx->curpreset.slider_labels[0][1]); + SetDlgItemText(hwndDlg,IDC_SLIDER1_LABEL3,ctx->curpreset.slider_labels[0][2]); + SetDlgItemText(hwndDlg,IDC_SLIDER2_LABEL1,ctx->curpreset.slider_labels[1][0]); + SetDlgItemText(hwndDlg,IDC_SLIDER2_LABEL2,ctx->curpreset.slider_labels[1][1]); + SetDlgItemText(hwndDlg,IDC_SLIDER2_LABEL3,ctx->curpreset.slider_labels[1][2]); + SetDlgItemText(hwndDlg,IDC_SLIDER3_LABEL1,ctx->curpreset.slider_labels[2][0]); + SetDlgItemText(hwndDlg,IDC_SLIDER3_LABEL2,ctx->curpreset.slider_labels[2][1]); + SetDlgItemText(hwndDlg,IDC_SLIDER3_LABEL3,ctx->curpreset.slider_labels[2][2]); + SetDlgItemText(hwndDlg,IDC_SLIDER4_LABEL1,ctx->curpreset.slider_labels[3][0]); + SetDlgItemText(hwndDlg,IDC_SLIDER4_LABEL2,ctx->curpreset.slider_labels[3][1]); + SetDlgItemText(hwndDlg,IDC_SLIDER4_LABEL3,ctx->curpreset.slider_labels[3][2]); + + SetDlgItemText(hwndDlg,IDC_INIT,ctx->curpreset.code_text[0]); + SetDlgItemText(hwndDlg,IDC_PERSAMPLE,ctx->curpreset.code_text[1]); + SetDlgItemText(hwndDlg,IDC_ONSLIDERCHANGE,ctx->curpreset.code_text[2]); + + updatePresetText(hwndDlg,ctx); +#ifdef SPS_CONFIGDLG_HIDEABLE_EDITOR + showHideSliders(hwndDlg,ctx); +#endif +} + +static int m_help_lastpage=4; +static char *m_localtext; +static void _dosetsel(HWND hwndDlg) +{ + HWND tabwnd=GetDlgItem(hwndDlg,IDC_TAB1); + int sel=TabCtrl_GetCurSel(tabwnd); + char *text=""; + + m_help_lastpage=sel; + + text=SPSHELP_gethelptext(sel); + + if (!text && sel == 4 && m_localtext) text=m_localtext; + + SetDlgItemText(hwndDlg,IDC_EDIT1,text); +} + +static BOOL CALLBACK evalHelpDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam) +{ + switch (uMsg) + { + case WM_INITDIALOG: + { + TCITEM item; + HWND tabwnd=GetDlgItem(hwndDlg,IDC_TAB1); + helpWnd=hwndDlg; + item.mask=TCIF_TEXT; + item.pszText=WASABI_API_LNGSTRING(IDS_GENERAL); + TabCtrl_InsertItem(tabwnd,0,&item); + item.pszText=WASABI_API_LNGSTRING(IDS_OPERATORS); + TabCtrl_InsertItem(tabwnd,1,&item); + item.pszText=WASABI_API_LNGSTRING(IDS_FUNCTIONS); + TabCtrl_InsertItem(tabwnd,2,&item); + item.pszText=WASABI_API_LNGSTRING(IDS_CONSTANTS); + TabCtrl_InsertItem(tabwnd,3,&item); + // fucko: context specific stuff + m_localtext=0; + if (lParam) + { + item.pszText=(char *)lParam; + m_localtext=item.pszText + strlen(item.pszText)+1; + TabCtrl_InsertItem(tabwnd,4,&item); + } + else if (m_help_lastpage > 3) m_help_lastpage=0; + + TabCtrl_SetCurSel(tabwnd,m_help_lastpage); + _dosetsel(hwndDlg); + } + return 0; + + case WM_COMMAND: + if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL) + { + EndDialog(hwndDlg,1); + helpWnd = 0; + } + return 0; + + case WM_NOTIFY: + { + LPNMHDR p=(LPNMHDR) lParam; + if (p->idFrom == IDC_TAB1 && p->code == TCN_SELCHANGE) _dosetsel(hwndDlg); + } + return 0; + } + return 0; +} + +BOOL CALLBACK SPS_configWindowProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam ) +{ + SPSEffectContext *ctx; + + if ( uMsg == WM_INITDIALOG ) + SetWindowLong( hwndDlg, DWL_USER, lParam ); + + ctx = (SPSEffectContext *) GetWindowLong( hwndDlg, DWL_USER ); + + if ( ctx ) + { + switch ( uMsg ) + { + case WM_INITDIALOG: + SetWindowText( hwndDlg, hdr.description ); + + SendDlgItemMessage( hwndDlg, IDC_SLIDER1, TBM_SETTICFREQ, 100, 0 ); + SendDlgItemMessage( hwndDlg, IDC_SLIDER2, TBM_SETTICFREQ, 100, 0 ); + SendDlgItemMessage( hwndDlg, IDC_SLIDER3, TBM_SETTICFREQ, 100, 0 ); + SendDlgItemMessage( hwndDlg, IDC_SLIDER4, TBM_SETTICFREQ, 100, 0 ); + SendDlgItemMessage( hwndDlg, IDC_SLIDER1, TBM_SETRANGEMIN, 0, 0 ); + SendDlgItemMessage( hwndDlg, IDC_SLIDER1, TBM_SETRANGEMAX, 0, 1000 ); + SendDlgItemMessage( hwndDlg, IDC_SLIDER2, TBM_SETRANGEMIN, 0, 0 ); + SendDlgItemMessage( hwndDlg, IDC_SLIDER2, TBM_SETRANGEMAX, 0, 1000 ); + SendDlgItemMessage( hwndDlg, IDC_SLIDER3, TBM_SETRANGEMIN, 0, 0 ); + SendDlgItemMessage( hwndDlg, IDC_SLIDER3, TBM_SETRANGEMAX, 0, 1000 ); + SendDlgItemMessage( hwndDlg, IDC_SLIDER4, TBM_SETRANGEMIN, 0, 0 ); + SendDlgItemMessage( hwndDlg, IDC_SLIDER4, TBM_SETRANGEMAX, 0, 1000 ); + +#ifdef SPS_CONFIGDLG_HIDEABLE_EDITOR + showHideEditor( hwndDlg, TRUE ); + showHideSliders( hwndDlg, ctx ); +#else + ShowWindow( GetDlgItem( hwndDlg, IDC_EDIT ), SW_HIDE ); +#endif + if ( !ctx->bypass ) + CheckDlgButton( hwndDlg, IDC_BYPASS, BST_CHECKED ); + + presetToDialog( hwndDlg, ctx ); + return 0; + + case WM_USER + 0x80: + ShowWindow( hwndDlg, SW_SHOW ); + SetForegroundWindow( hwndDlg ); + return 0; + + case WM_COMMAND: + { + int w = 0; + switch ( LOWORD( wParam ) ) + { + case IDC_SLIDER1_LABEL1: w++; + case IDC_SLIDER1_LABEL2: w++; + case IDC_SLIDER1_LABEL3: w++; + case IDC_SLIDER2_LABEL1: w++; + case IDC_SLIDER2_LABEL2: w++; + case IDC_SLIDER2_LABEL3: w++; + case IDC_SLIDER3_LABEL1: w++; + case IDC_SLIDER3_LABEL2: w++; + case IDC_SLIDER3_LABEL3: w++; + case IDC_SLIDER4_LABEL1: w++; + case IDC_SLIDER4_LABEL2: w++; + case IDC_SLIDER4_LABEL3: w++; + if ( HIWORD( wParam ) == EN_CHANGE ) + { + w = 12 - w; + GetDlgItemText( hwndDlg, LOWORD( wParam ), ctx->curpreset.slider_labels[ w / 3 ][ w % 3 ], MAX_LABEL_LEN ); + } + break; +#ifdef SPS_CONFIGDLG_HIDEABLE_EDITOR + case IDC_EDIT: + SPS_CONFIGDLG_HIDEABLE_EDITOR = !SPS_CONFIGDLG_HIDEABLE_EDITOR; + showHideEditor( hwndDlg, FALSE ); + showHideSliders( hwndDlg, ctx ); + break; +#endif + case IDC_BYPASS: + ctx->bypass = !IsDlgButtonChecked( hwndDlg, IDC_BYPASS ); + break; + + case IDC_LOAD: + { + SendMessage( mod.hwndParent, WM_WA_IPC, 0, IPC_PUSH_DISABLE_EXIT ); + char temp[ 2048 ] = { 0 }; + OPENFILENAME l = { sizeof( l ),0 }; + char buf1[ 2048 ], buf2[ 2048 ]; + GetCurrentDirectory( sizeof( buf2 ), buf2 ); + strcpy( buf1, g_path ); + l.lpstrInitialDir = buf1; + l.hwndOwner = hwndDlg; + l.lpstrFilter = BuildFilterString(); + l.lpstrFile = temp; + l.nMaxFile = 2048 - 1; + l.lpstrTitle = WASABI_API_LNGSTRING( IDS_LOAD_PRESET ); + l.lpstrDefExt = "SPS"; + l.Flags = OFN_HIDEREADONLY | OFN_EXPLORER; + if ( GetOpenFileName( &l ) ) + { + SPS_load_preset( ctx, temp, "SPS PRESET" ); + presetToDialog( hwndDlg, ctx ); + } + SetCurrentDirectory( buf2 ); + SendMessage( mod.hwndParent, WM_WA_IPC, 0, IPC_POP_DISABLE_EXIT ); + } + break; + + case IDC_NEW: + char title[ 32 ]; + if ( MessageBox( hwndDlg, WASABI_API_LNGSTRING( IDS_CLEAR_CURRENT_SETTINGS ), + WASABI_API_LNGSTRING_BUF( IDS_CONFIRMATION, title, 32 ), MB_YESNO ) == IDYES ) + { + EnterCriticalSection( &ctx->code_cs ); + memset( &ctx->curpreset, 0, sizeof( ctx->curpreset ) ); + ctx->code_needrecompile = 7; + memset( &ctx->triggers, 0, sizeof( ctx->triggers ) ); + ctx->curpreset_name[ 0 ] = 0; + LeaveCriticalSection( &ctx->code_cs ); + + presetToDialog( hwndDlg, ctx ); + } + break; + + case IDC_SAVE: + SendMessage( mod.hwndParent, WM_WA_IPC, 0, IPC_PUSH_DISABLE_EXIT ); + dosavePreset( hwndDlg, ctx ); + SendMessage( mod.hwndParent, WM_WA_IPC, 0, IPC_POP_DISABLE_EXIT ); + break; + + case IDC_INIT: + if ( HIWORD( wParam ) == EN_CHANGE ) + { + KillTimer( hwndDlg, 100 ); + SetTimer( hwndDlg, 100, 250, NULL ); + } + break; + + case IDC_PERSAMPLE: + if ( HIWORD( wParam ) == EN_CHANGE ) + { + KillTimer( hwndDlg, 101 ); + SetTimer( hwndDlg, 101, 250, NULL ); + } + break; + + case IDC_ONSLIDERCHANGE: + if ( HIWORD( wParam ) == EN_CHANGE ) + { + KillTimer( hwndDlg, 102 ); + SetTimer( hwndDlg, 102, 250, NULL ); + } + break; + + case IDC_SHOWHELP: + WASABI_API_DIALOGBOX( IDD_EVAL_HELP, hwndDlg, evalHelpDlgProc ); + break; + + case IDC_TRIGGER1: + ctx->triggers[ 0 ]++; + break; + + case IDC_TRIGGER2: + ctx->triggers[ 1 ]++; + break; + + case IDC_TRIGGER3: + ctx->triggers[ 2 ]++; + break; + + case IDC_TRIGGER4: + ctx->triggers[ 3 ]++; + break; + } + + return 0; + } + + case WM_TIMER: + if ( wParam == 100 || wParam == 101 || wParam == 102 ) + { + KillTimer( hwndDlg, wParam ); + EnterCriticalSection( &ctx->code_cs ); + GetDlgItemText( hwndDlg, wParam == 100 ? IDC_INIT : ( wParam == 101 ? IDC_PERSAMPLE : IDC_ONSLIDERCHANGE ), ctx->curpreset.code_text[ wParam - 100 ], MAX_CODE_LEN ); + ctx->code_needrecompile |= 1 << ( wParam - 100 ); + LeaveCriticalSection( &ctx->code_cs ); + } + return 0; + + case WM_CLOSE: +#ifdef SPS_CONFIGDLG_ON_WM_CLOSE + SPS_CONFIGDLG_ON_WM_CLOSE +#endif + return 0; + + case WM_VSCROLL: + { + HWND swnd = (HWND) lParam; + int t = (int) SendMessage( swnd, TBM_GETPOS, 0, 0 ); + if ( swnd == GetDlgItem( hwndDlg, IDC_SLIDER1 ) ) + { + ctx->curpreset.sliderpos[ 0 ] = 1000 - t; + ctx->sliderchange = 1; + } + + if ( swnd == GetDlgItem( hwndDlg, IDC_SLIDER2 ) ) + { + ctx->curpreset.sliderpos[ 1 ] = 1000 - t; + ctx->sliderchange = 1; + } + + if ( swnd == GetDlgItem( hwndDlg, IDC_SLIDER3 ) ) + { + ctx->curpreset.sliderpos[ 2 ] = 1000 - t; + ctx->sliderchange = 1; + } + + if ( swnd == GetDlgItem( hwndDlg, IDC_SLIDER4 ) ) + { + ctx->curpreset.sliderpos[ 3 ] = 1000 - t; + ctx->sliderchange = 1; + } + } + break; + } + return 0; + } +} + +#endif \ No newline at end of file diff --git a/Src/Plugins/DSP/dsp_sps/version.rc2 b/Src/Plugins/DSP/dsp_sps/version.rc2 new file mode 100644 index 00000000..48daf777 --- /dev/null +++ b/Src/Plugins/DSP/dsp_sps/version.rc2 @@ -0,0 +1,39 @@ + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// +#include "../../../Winamp/buildType.h" +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,2,0 + PRODUCTVERSION WINAMP_PRODUCTVER + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Winamp SA" + VALUE "FileDescription", "Winamp DSP Plug-in" + VALUE "FileVersion", "1,0,2,0" + VALUE "InternalName", "Nullsoft Signal Processing Studio DSP" + VALUE "LegalCopyright", "Copyright © 2003-2023 Winamp SA" + VALUE "LegalTrademarks", "Nullsoft and Winamp are trademarks of Winamp SA" + VALUE "OriginalFilename", "dsp_sps.dll" + VALUE "ProductName", "Winamp" + VALUE "ProductVersion", STR_WINAMP_PRODUCTVER + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/Src/Plugins/Encoder/enc_fhgaac/ADTSAACEncoder.cpp b/Src/Plugins/Encoder/enc_fhgaac/ADTSAACEncoder.cpp new file mode 100644 index 00000000..08ddb0a3 --- /dev/null +++ b/Src/Plugins/Encoder/enc_fhgaac/ADTSAACEncoder.cpp @@ -0,0 +1,119 @@ +#include "ADTSAACEncoder.h" +#include "mp4FastAAClib.h" +#include +#include "config.h" +#include "../nsutil/pcm.h" + +ADTSAACEncoder *ADTSAACEncoder::CreateDecoder(const AACConfiguration *cfg, int nch, int srate, int bps) +{ + MPEG4ENC_ERROR err; + + MPEG4ENC_SETUP setup; + setup.aot = AACConfig_GetAOT(cfg); + setup.nBitRate = AACConfig_GetBitrate(cfg, nch); + setup.bitrateMode = AACConfig_GetBitrateMode(cfg); + setup.quality = MP4_QUAL_HIGH; + setup.chMode = AACConfig_GetChannelMode(cfg, nch); + setup.sbrSignaling = MP4_SBRSIG_IMPLICIT; + setup.nSampleRateIn = srate; + setup.transportFormat = MP4_TT_ADTS; + setup.nGranuleLength = MP4_GRANULE_1024; + setup.metadataMode = MP4_METADATA_NONE; + + HANDLE_MPEG4ENC_ENCODER encoder=0; + err = MPEG4ENC_Configure(&encoder, &setup); + if (err != MPEG4ENC_NO_ERROR) + { + return 0; + } + + unsigned int first_samples; + err = MPEG4ENC_Open(&encoder, &first_samples); + if (err != MPEG4ENC_NO_ERROR) + { + + MPEG4ENC_Close(&encoder); + return 0; + } + float *sample_buffer = (float *)malloc(first_samples * sizeof(float)); + if (!sample_buffer) + { + MPEG4ENC_Close(&encoder); + return 0; + } + + ADTSAACEncoder *fhg_enc = new ADTSAACEncoder(encoder, &setup, nch, srate, bps, sample_buffer, first_samples); + if (!fhg_enc) + { + free(sample_buffer); + MPEG4ENC_Close(&encoder); + } + + AACConfig_GetToolString(&setup, fhg_enc->tool, sizeof(fhg_enc->tool)); + + return fhg_enc; +} + +ADTSAACEncoder::ADTSAACEncoder(HANDLE_MPEG4ENC_ENCODER encoder, const MPEG4ENC_SETUP *setup, int nch, int srate, int bps, float *sample_buffer, unsigned int next_samples) +: encoder(encoder), channels(nch), sample_rate(srate), bits_per_sample(bps), sample_buffer(sample_buffer), next_samples(next_samples) +{ + MPEG4ENC_INFO info; + MPEG4ENC_GetInfo(encoder, &info); + samples_per_frame = info.nSamplesFrame[0]; + + finishing=false; +} + +ADTSAACEncoder::~ADTSAACEncoder() +{ + free(sample_buffer); + MPEG4ENC_Close(&encoder); +} + +int ADTSAACEncoder::Encode(int framepos, void *in, int in_avail, int *in_used, void *out, int out_avail) +{ + if (!in_avail && !finishing) + return 0; + + size_t num_samples = in_avail / (bits_per_sample/8); + num_samples = min(num_samples, next_samples); + + nsutil_pcm_IntToFloat_Interleaved(sample_buffer, in, bits_per_sample, num_samples); + + int samples_consumed=0; + int out_used = 0; + MPEG4ENC_AUINFO *info; + MPEG4ENC_ERROR err = MPEG4ENC_Encode(encoder, sample_buffer, num_samples, &samples_consumed, &next_samples, (unsigned char * const)out, out_avail, &out_used, &info); + if (err != MPEG4ENC_NO_ERROR) + { + out_used = 0; /* in case it didn't get set */ + } + else if (err != MPEG4ENC_NO_ERROR) + { + return 0; + } + if (!finishing) + { + *in_used = samples_consumed * (bits_per_sample/8); + } + + return out_used; +} + +void ADTSAACEncoder::PrepareToFinish() +{ + finishing=true; +} + +void ADTSAACEncoder::Finish(const wchar_t *filename) +{ + /* TODO: + MPEG4ENC_INFO info; + MPEG4ENC_GetInfo(encoder, &info); + if (!resampling) + mp4_writer.WriteGaps(info.nDelay, decodable_samples-total_samples-info.nDelay, total_samples); + + mp4_writer.WriteTool(tool); + */ + +} diff --git a/Src/Plugins/Encoder/enc_fhgaac/ADTSAACEncoder.h b/Src/Plugins/Encoder/enc_fhgaac/ADTSAACEncoder.h new file mode 100644 index 00000000..b96bd863 --- /dev/null +++ b/Src/Plugins/Encoder/enc_fhgaac/ADTSAACEncoder.h @@ -0,0 +1,25 @@ +#pragma once +#include "mp4FastAAClib.h" +#include "config.h" +#include "encoder_common.h" + + +class ADTSAACEncoder : public EncoderCommon +{ +public: + static ADTSAACEncoder *CreateDecoder(const AACConfiguration *cfg, int nch, int srate, int bps); + ADTSAACEncoder(HANDLE_MPEG4ENC_ENCODER encoder, const MPEG4ENC_SETUP *setup, int nch, int srate, int bps, float *sample_buffer, unsigned int next_samples); + ~ADTSAACEncoder(); + int Encode(int framepos, void *in, int in_avail, int *in_used, void *out, int out_avail); + void PrepareToFinish(); + void Finish(const wchar_t *filename); +private: + HANDLE_MPEG4ENC_ENCODER encoder; + unsigned int channels; + unsigned int sample_rate; + unsigned int bits_per_sample; + unsigned int next_samples; + unsigned int samples_per_frame; + float *sample_buffer; + bool finishing; +}; \ No newline at end of file diff --git a/Src/Plugins/Encoder/enc_fhgaac/FhGAACEncoder.cpp b/Src/Plugins/Encoder/enc_fhgaac/FhGAACEncoder.cpp new file mode 100644 index 00000000..6d2b31b3 --- /dev/null +++ b/Src/Plugins/Encoder/enc_fhgaac/FhGAACEncoder.cpp @@ -0,0 +1,128 @@ +#include "FhGAACEncoder.h" +#include "mp4FastAAClib.h" +#include +#include "config.h" +#include "../nsutil/pcm.h" + +FhGAACEncoder *FhGAACEncoder::CreateDecoder(const AACConfiguration *cfg, int nch, int srate, int bps) +{ + MPEG4ENC_ERROR err; + + MPEG4ENC_SETUP setup; + setup.aot = AACConfig_GetAOT(cfg); + setup.nBitRate = AACConfig_GetBitrate(cfg, nch); + setup.bitrateMode = AACConfig_GetBitrateMode(cfg); + setup.quality = MP4_QUAL_HIGH; + setup.chMode = AACConfig_GetChannelMode(cfg, nch); + setup.sbrSignaling = MP4_SBRSIG_EXPL_BC; + setup.nSampleRateIn = srate; + setup.transportFormat = MP4_TT_RAW; + setup.nGranuleLength = MP4_GRANULE_1024; + setup.metadataMode = MP4_METADATA_NONE; + + HANDLE_MPEG4ENC_ENCODER encoder=0; + err = MPEG4ENC_Configure(&encoder, &setup); + if (err != MPEG4ENC_NO_ERROR) + { + return 0; + } + + unsigned int first_samples; + err = MPEG4ENC_Open(&encoder, &first_samples); + if (err != MPEG4ENC_NO_ERROR) + { + + MPEG4ENC_Close(&encoder); + return 0; + } + float *sample_buffer = (float *)malloc(first_samples * sizeof(float)); + if (!sample_buffer) + { + MPEG4ENC_Close(&encoder); + return 0; + } + + FhGAACEncoder *fhg_enc = new FhGAACEncoder(encoder, &setup, nch, srate, bps, sample_buffer, first_samples); + if (!fhg_enc) + { + free(sample_buffer); + MPEG4ENC_Close(&encoder); + } + + AACConfig_GetToolString(&setup, fhg_enc->tool, sizeof(fhg_enc->tool)); + return fhg_enc; +} + +FhGAACEncoder::FhGAACEncoder(HANDLE_MPEG4ENC_ENCODER encoder, const MPEG4ENC_SETUP *setup, int nch, int srate, int bps, float *sample_buffer, unsigned int next_samples) +: encoder(encoder), channels(nch), sample_rate(srate), bits_per_sample(bps), sample_buffer(sample_buffer), next_samples(next_samples) +{ + MPEG4ENC_INFO info; + MPEG4ENC_GetInfo(encoder, &info); + samples_per_frame = info.nSamplesFrame[0]; + + if (info.nSamplingRate[0] != srate) + resampling = true; + else + resampling = false; + finishing=false; + total_samples=0; + decodable_samples=0; + // TODO: move this somewhere where we can error-check + mp4_writer.AddAudioTrack(encoder, setup); +} + +FhGAACEncoder::~FhGAACEncoder() +{ + free(sample_buffer); + MPEG4ENC_Close(&encoder); +} + +int FhGAACEncoder::Encode(int framepos, void *in, int in_avail, int *in_used, void *out, int out_avail) +{ + if (!in_avail && !finishing) + return 0; + + size_t num_samples = in_avail / (bits_per_sample/8); + num_samples = min(num_samples, next_samples); + + nsutil_pcm_IntToFloat_Interleaved(sample_buffer, in, bits_per_sample, num_samples); + + int samples_consumed=0; + int out_used = 0; + MPEG4ENC_AUINFO *info; + MPEG4ENC_ERROR err = MPEG4ENC_Encode(encoder, sample_buffer, num_samples, &samples_consumed, &next_samples, (unsigned char * const)out, out_avail, &out_used, &info); + if (err == MPEG4ENC_NO_ERROR && out_used) + { + decodable_samples += samples_per_frame; + mp4_writer.Write(out, out_used, samples_per_frame); + } + else if (err != MPEG4ENC_NO_ERROR) + { + return 0; + } + + if (!finishing) + { + *in_used = samples_consumed * (bits_per_sample/8); + total_samples += samples_consumed / channels; + } + + return out_used; +} + +void FhGAACEncoder::PrepareToFinish() +{ + finishing=true; +} + +void FhGAACEncoder::Finish(const wchar_t *filename) +{ + MPEG4ENC_INFO info; + MPEG4ENC_GetInfo(encoder, &info); + if (!resampling) + mp4_writer.WriteGaps(info.nDelay, decodable_samples-total_samples-info.nDelay, total_samples); + + mp4_writer.WriteTool(tool); + + mp4_writer.CloseTo(filename); +} diff --git a/Src/Plugins/Encoder/enc_fhgaac/FhGAACEncoder.h b/Src/Plugins/Encoder/enc_fhgaac/FhGAACEncoder.h new file mode 100644 index 00000000..6bd8fec7 --- /dev/null +++ b/Src/Plugins/Encoder/enc_fhgaac/FhGAACEncoder.h @@ -0,0 +1,29 @@ +#pragma once +#include "MP4Writer.h" +#include "mp4FastAAClib.h" +#include "config.h" +#include "encoder_common.h" + +class FhGAACEncoder : public EncoderCommon +{ +public: + static FhGAACEncoder *CreateDecoder(const AACConfiguration *cfg, int nch, int srate, int bps); + FhGAACEncoder(HANDLE_MPEG4ENC_ENCODER encoder, const MPEG4ENC_SETUP *setup, int nch, int srate, int bps, float *sample_buffer, unsigned int next_samples); + ~FhGAACEncoder(); + int Encode(int framepos, void *in, int in_avail, int *in_used, void *out, int out_avail); + void PrepareToFinish(); + void Finish(const wchar_t *filename); +private: + HANDLE_MPEG4ENC_ENCODER encoder; + unsigned int channels; + unsigned int sample_rate; + unsigned int bits_per_sample; + unsigned int next_samples; + unsigned int samples_per_frame; + float *sample_buffer; + MP4Writer mp4_writer; + uint64_t total_samples; + uint64_t decodable_samples; + bool finishing; + bool resampling; +}; \ No newline at end of file diff --git a/Src/Plugins/Encoder/enc_fhgaac/MP4Writer.cpp b/Src/Plugins/Encoder/enc_fhgaac/MP4Writer.cpp new file mode 100644 index 00000000..00f6d811 --- /dev/null +++ b/Src/Plugins/Encoder/enc_fhgaac/MP4Writer.cpp @@ -0,0 +1,64 @@ +#include "MP4Writer.h" + +#include + +MP4Writer::MP4Writer() +{ + wchar_t tmppath[MAX_PATH-14] = {0}; + GetTempPathW(MAX_PATH-14,tmppath); + GetTempFileNameW(tmppath, L"mp4", 0, tempfile); + mp4File = MP4Create(tempfile); + if(!mp4File) + { + return; + } +} + +MP4Writer::~MP4Writer() +{ + /* in case it's lingering open */ + if (mp4File) + MP4Close(mp4File); +} + +void MP4Writer::CloseTo(const wchar_t *filename) +{ + MP4Close(mp4File); + mp4File=0; + MP4MakeIsmaCompliant(tempfile, 0, true); + DeleteFileW(filename); + if (MoveFileW(tempfile,filename) == 0) // if the function fails + { + CopyFileW(tempfile,filename, FALSE); + DeleteFileW(tempfile); + } +} + +void MP4Writer::WriteGaps(uint32_t pregap, uint32_t postgap, uint64_t totalSamples) +{ + char data[128] = {0}; + StringCchPrintfA(data, 128, " %08X %08X %08X %016X %08X %08X %08X %08X %08X %08X %08X %08X", 0, pregap, postgap, totalSamples, 0, 0,0, 0,0, 0,0, 0); + MP4SetMetadataFreeForm(mp4File, "iTunSMPB", (u_int8_t *)data, lstrlenA(data)); +} + +void MP4Writer::Write(const void *buf, size_t size, MP4Duration duration) +{ + MP4WriteSample(mp4File, mp4Track, (const uint8_t *)buf, size, duration); +} + +void MP4Writer::AddAudioTrack(const HANDLE_MPEG4ENC_ENCODER encoder, const MPEG4ENC_SETUP *setup) +{ + MPEG4ENC_INFO info; + MPEG4ENC_GetInfo(encoder, &info); + + MP4SetTimeScale(mp4File, info.nSamplingRate[0]); + + mp4Track = MP4AddAudioTrack(mp4File, info.nSamplingRate[0], info.nSamplesFrame[0], MP4_MPEG4_AUDIO_TYPE); + MP4SetAudioProfileLevel(mp4File, info.nProfLev); + MP4SetTrackESConfiguration(mp4File, mp4Track, info.ascBuf[0].ascBuffer, (info.ascBuf[0].nAscSizeBits+7)/8); +} + +void MP4Writer::WriteTool(const char *tool) +{ + MP4SetMetadataTool(mp4File, tool); +} \ No newline at end of file diff --git a/Src/Plugins/Encoder/enc_fhgaac/MP4Writer.h b/Src/Plugins/Encoder/enc_fhgaac/MP4Writer.h new file mode 100644 index 00000000..5ae6ea06 --- /dev/null +++ b/Src/Plugins/Encoder/enc_fhgaac/MP4Writer.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include "mp4FastAAClib.h" +#include "config.h" + +class MP4Writer +{ +public: + MP4Writer(); + ~MP4Writer(); + + void AddAudioTrack(const HANDLE_MPEG4ENC_ENCODER encoder, const MPEG4ENC_SETUP *setup); + void WriteGaps(uint32_t pregap, uint32_t postgap, uint64_t totalSamples); + void WriteTool(const char *tool); + void Write(const void *buf, size_t size, MP4Duration duration); + void CloseTo(const wchar_t *filename); + + bool OK() { return true; } + + MP4TrackId mp4Track; + MP4FileHandle mp4File; + wchar_t tempfile[MAX_PATH]; +}; + diff --git a/Src/Plugins/Encoder/enc_fhgaac/config.cpp b/Src/Plugins/Encoder/enc_fhgaac/config.cpp new file mode 100644 index 00000000..e899b052 --- /dev/null +++ b/Src/Plugins/Encoder/enc_fhgaac/config.cpp @@ -0,0 +1,258 @@ +#include "config.h" +#include +#include +#include +#include "mp4FastAAClib.h" +#include "preferences.h" + +AACConfigurationFile *AACConfig_Create(unsigned int type, const char *filename) +{ + AACConfigurationFile *cfg = (AACConfigurationFile*)calloc(1, sizeof(AACConfigurationFile)); + if (cfg) + { + cfg->type = type; + + if (filename) + lstrcpynA(cfg->config_file, filename, MAX_PATH); + else + cfg->config_file[0] = 0; + + AACConfig_Load(cfg); + } + return cfg; +} + +void AACConfig_Load(AACConfigurationFile *cfg) +{ + if (cfg->type == ENCODER_TYPE_MPEG4) + { + cfg->config.mode = GetPrivateProfileIntA("audio_fhgaac", "mode", AAC_DEFAULT_MODE, cfg->config_file); + cfg->config.profile = GetPrivateProfileIntA("audio_fhgaac", "profile", AAC_DEFAULT_PROFILE, cfg->config_file); + cfg->config.bitrate = GetPrivateProfileIntA("audio_fhgaac", "bitrate", AAC_DEFAULT_BITRATE, cfg->config_file); + cfg->config.preset = GetPrivateProfileIntA("audio_fhgaac", "preset", AAC_DEFAULT_PRESET, cfg->config_file); + cfg->config.surround = GetPrivateProfileIntA("audio_fhgaac", "surround", AAC_DEFAULT_SURROUND, cfg->config_file); + cfg->shoutcast = 0; + } + else + { + cfg->config.mode = AAC_MODE_CBR; + cfg->config.profile = GetPrivateProfileIntA("audio_adtsaac", "profile", AAC_PROFILE_HE, cfg->config_file); + cfg->config.bitrate = GetPrivateProfileIntA("audio_adtsaac", "bitrate", 64, cfg->config_file); + cfg->config.surround = GetPrivateProfileIntA("audio_adtsaac", "surround", AAC_DEFAULT_SURROUND, cfg->config_file); + cfg->shoutcast = GetPrivateProfileIntA("audio_adtsaac", "shoutcast", 0, cfg->config_file); + } +} + +void AACConfig_Save(const AACConfigurationFile *cfg) +{ + char temp[128] = {0}; + + if (cfg->type == ENCODER_TYPE_MPEG4) + { + StringCbPrintfA(temp, sizeof(temp), "%u", cfg->config.mode); + WritePrivateProfileStringA("audio_fhgaac", "mode", temp, cfg->config_file); + + StringCbPrintfA(temp, sizeof(temp), "%u", cfg->config.profile); + WritePrivateProfileStringA("audio_fhgaac", "profile", temp, cfg->config_file); + + StringCbPrintfA(temp, sizeof(temp), "%u", cfg->config.bitrate); + WritePrivateProfileStringA("audio_fhgaac", "bitrate", temp, cfg->config_file); + + StringCbPrintfA(temp, sizeof(temp), "%u", cfg->config.preset); + WritePrivateProfileStringA("audio_fhgaac", "preset", temp, cfg->config_file); + + StringCbPrintfA(temp, sizeof(temp), "%u", cfg->config.surround); + WritePrivateProfileStringA("audio_fhgaac", "surround", temp, cfg->config_file); + } + else + { + StringCbPrintfA(temp, sizeof(temp), "%u", cfg->config.profile); + WritePrivateProfileStringA("audio_adtsaac", "profile", temp, cfg->config_file); + + StringCbPrintfA(temp, sizeof(temp), "%u", cfg->config.bitrate); + WritePrivateProfileStringA("audio_adtsaac", "bitrate", temp, cfg->config_file); + + StringCbPrintfA(temp, sizeof(temp), "%u", cfg->config.surround); + WritePrivateProfileStringA("audio_adtsaac", "surround", temp, cfg->config_file); + } +} + +void AACConfig_GetBitrateRange(const AACConfiguration *cfg, int *low, int *high) +{ + switch(cfg->profile) + { + case AAC_PROFILE_AUTOMATIC: + *low = 12000; + *high = 448000; + break; + + case AAC_PROFILE_LC: + *low = 16000; + *high = 448000; + break; + + case AAC_PROFILE_HE: + *low = 16000; + *high = 128000; + break; + + case AAC_PROFILE_HE_V2: + *low = 12000; + *high = 56000; + break; + } +} + +AUD_OBJ_TYP AACConfig_GetAOT(const AACConfiguration *cfg) +{ + if (cfg->mode == AAC_MODE_VBR) + { + switch(cfg->preset) + { + case 1: + return AUD_OBJ_TYP_PS; + case 2: + return AUD_OBJ_TYP_HEAAC; + default: + return AUD_OBJ_TYP_LC; + } + } + else switch (cfg->profile) /* CBR */ + { + case AAC_PROFILE_AUTOMATIC: + if (cfg->bitrate <= 40) + return AUD_OBJ_TYP_PS; + else if (cfg->bitrate <= 80) + return AUD_OBJ_TYP_HEAAC; + else + return AUD_OBJ_TYP_LC; + case AAC_PROFILE_LC: + return AUD_OBJ_TYP_LC; + case AAC_PROFILE_HE: + return AUD_OBJ_TYP_HEAAC; + case AAC_PROFILE_HE_V2: + + return AUD_OBJ_TYP_PS; + + } + return AUD_OBJ_TYP_LC; +} + +int AACConfig_GetBitrate(const AACConfiguration *cfg, unsigned int channels) +{ + if (cfg->mode == AAC_MODE_VBR) + { + switch(cfg->preset) + { + case 1: + return 16000*channels; + case 2: + return 32000*channels; + case 3: + return 48000*channels; + case 4: + return 64000*channels; + case 5: + return 96000*channels; + case 6: + return 128000*channels; + default: + return 0; + } + } + else + return cfg->bitrate * 1000; +} + +MPEG4ENC_BITRATE_MODE AACConfig_GetBitrateMode(const AACConfiguration *cfg) +{ + if (cfg->mode == AAC_MODE_VBR) + { + /* by coincidence, these match + to help future maintainers, let's assert this fact */ + assert(MP4_BR_MODE_VBR_1 == (MPEG4ENC_BITRATE_MODE)1); + assert(MP4_BR_MODE_VBR_2 == (MPEG4ENC_BITRATE_MODE)2); + assert(MP4_BR_MODE_VBR_3 == (MPEG4ENC_BITRATE_MODE)3); + assert(MP4_BR_MODE_VBR_4 == (MPEG4ENC_BITRATE_MODE)4); + assert(MP4_BR_MODE_VBR_5 == (MPEG4ENC_BITRATE_MODE)5); + assert(MP4_BR_MODE_VBR_6 == (MPEG4ENC_BITRATE_MODE)6); + return (MPEG4ENC_BITRATE_MODE)cfg->preset; + } + else /* CBR */ + { + return MP4_BR_MODE_CBR; + } +} + +MPEG4ENC_CH_MODE AACConfig_GetChannelMode(const AACConfiguration *cfg, unsigned int channels) +{ + switch(channels) + { + case 1: + return MP4_CH_MODE_MONO; + case 2: + if (cfg->mode == AAC_MODE_VBR) + { + if (cfg->preset == 1) + return MP4_CH_MODE_PARAMETRIC_STEREO; + else + return MP4_CH_MODE_STEREO; + } + else /* CBR */ + { + if (AACConfig_GetAOT(cfg) == AUD_OBJ_TYP_PS) + return MP4_CH_MODE_PARAMETRIC_STEREO; + else + return MP4_CH_MODE_STEREO; + } + case 3: return MP4_CH_MODE_3; + case 4: return MP4_CH_MODE_4; + case 5: return MP4_CH_MODE_5; + case 6: return MP4_CH_MODE_5_1; + case 8: return MP4_CH_MODE_7_1; + default: + return MP4_CH_MODE_INVALID; + } +} + +void AACConfig_GetToolString(const MPEG4ENC_SETUP *setup, char tool[], size_t cch) +{ + char version[128] = {0}; + MPEG4ENC_GetVersionInfo(version, sizeof(version)/sizeof(*version)); + char *p = version; + while (p && *p) + { + if (*p != '.' && (*p < '0' || *p > '9')) + { + *p = 0; + break; + } + p++; + } + + + switch(setup->bitrateMode) + { + case MP4_BR_MODE_CBR: + StringCchPrintfA(tool, cch, "fhgaac v%s;CBR=%d", version, setup->nBitRate); + break; + case MP4_BR_MODE_VBR_1: + StringCchPrintfA(tool, cch, "fhgaac v%s;VBR=1", version); + break; + case MP4_BR_MODE_VBR_2: + StringCchPrintfA(tool, cch, "fhgaac v%s;VBR=2", version); + break; + case MP4_BR_MODE_VBR_3: + StringCchPrintfA(tool, cch, "fhgaac v%s;VBR=3", version); + break; + case MP4_BR_MODE_VBR_4: + StringCchPrintfA(tool, cch, "fhgaac v%s;VBR=4", version); + break; + case MP4_BR_MODE_VBR_5: + StringCchPrintfA(tool, cch, "fhgaac v%s;VBR=5", version); + break; + case MP4_BR_MODE_VBR_6: + StringCchPrintfA(tool, cch, "fhgaac v%s;VBR=6", version); + break; + } +} \ No newline at end of file diff --git a/Src/Plugins/Encoder/enc_fhgaac/config.h b/Src/Plugins/Encoder/enc_fhgaac/config.h new file mode 100644 index 00000000..5a7dc009 --- /dev/null +++ b/Src/Plugins/Encoder/enc_fhgaac/config.h @@ -0,0 +1,64 @@ +#pragma once + +#include "mp4FastAAClib.h" +#include // or MAX_PATH + + +#define ENCODER_TYPE_MPEG4 (mmioFOURCC('A','A','C','f')) +#define ENCODER_TYPE_ADTS (mmioFOURCC('A','D','T','S')) + +enum +{ + AAC_MODE_VBR=0, + AAC_MODE_CBR=1, + + /* these are the profile options when CBR is selected */ + AAC_PROFILE_AUTOMATIC=0, + AAC_PROFILE_LC=1, + AAC_PROFILE_HE=2, + AAC_PROFILE_HE_V2=3, + + /* Surround options */ + AAC_SURROUND_BCC = 0, /* Binaural Cue Coding, stereo + surround layer, aka MPEG-Surround */ + AAC_SURROUND_DISCRETE = 1, /* Discrete surround (traditional AAC surround sound with separate SCE per channel pair) */ + + /* defaults */ + AAC_DEFAULT_MODE = AAC_MODE_VBR, + AAC_DEFAULT_PROFILE = AAC_PROFILE_AUTOMATIC, + AAC_DEFAULT_BITRATE = 128, + AAC_DEFAULT_PRESET = 4, + AAC_DEFAULT_SURROUND = AAC_SURROUND_BCC, +}; + +struct AACConfiguration +{ + unsigned int mode; /* CBR or VBR */ + unsigned int profile; /* what flavor of AAC, e.g. LC, or automatic */ + unsigned int bitrate; /* bitrate for CBR */ + unsigned int preset; /* preset for VBR */ + unsigned int surround; /* 0 = discrete, 1 = MPEG Surround */ +}; + +struct AACConfigurationFile +{ + AACConfiguration config; + unsigned int channels; + unsigned int sample_rate; + unsigned int type; /* either ENCODER_TYPE_MPEG4 or ENCODER_TYPE_ADTS */ + unsigned int shoutcast; /* 0 by default, 1 if we're being invoked from dsp_sc */ + bool changing; /* used internally by preferences */ + char config_file[MAX_PATH]; +}; + +AACConfigurationFile *AACConfig_Create(unsigned int type, const char *filename); /* de-allocate with free() */ +void AACConfig_Load(AACConfigurationFile *cfg); +void AACConfig_Save(const AACConfigurationFile *cfg); +/* bitrates are in bits/sec (not kbps), divide by 1000 if you need to +TODO: ASSUMES 44.1kHz Stereo. We need to make the encoder API accept samplerate/channels input better! +*/ +void AACConfig_GetBitrateRange(const AACConfiguration *cfg, int *low, int *high); +AUD_OBJ_TYP AACConfig_GetAOT(const AACConfiguration *cfg); +int AACConfig_GetBitrate(const AACConfiguration *cfg, unsigned int channels); +MPEG4ENC_BITRATE_MODE AACConfig_GetBitrateMode(const AACConfiguration *cfg); +MPEG4ENC_CH_MODE AACConfig_GetChannelMode(const AACConfiguration *cfg, unsigned int channels); +void AACConfig_GetToolString(const MPEG4ENC_SETUP *setup, char tool[], size_t cch); \ No newline at end of file diff --git a/Src/Plugins/Encoder/enc_fhgaac/enc_fhgaac.rc b/Src/Plugins/Encoder/enc_fhgaac/enc_fhgaac.rc new file mode 100644 index 00000000..f1911026 --- /dev/null +++ b/Src/Plugins/Encoder/enc_fhgaac/enc_fhgaac.rc @@ -0,0 +1,150 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "#include ""version.rc2""\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_PREFERENCES_MP4 DIALOGEX 0, 0, 256, 167 +STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + GROUPBOX "MPEG-4 AAC Encoder Options",IDC_STATIC,0,0,255,130 + LTEXT "Mode:",IDC_STATIC,57,14,27,12,SS_CENTERIMAGE + COMBOBOX IDC_MODE,87,14,110,106,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + LTEXT "Profile:",IDC_STATIC_PROFILE,57,32,27,12,SS_CENTERIMAGE + COMBOBOX IDC_PROFILE,87,32,110,103,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + LTEXT "Preset:",IDC_STATIC_PRESET,9,43,24,15,SS_CENTERIMAGE + CONTROL "",IDC_SLIDER_PRESET,"msctls_trackbar32",TBS_AUTOTICKS | WS_TABSTOP,28,57,170,15 + EDITTEXT IDC_EDIT_PRESET,203,58,25,12,ES_AUTOHSCROLL | ES_READONLY + LTEXT "kbps",IDC_KBPS,231,60,16,8,SS_CENTERIMAGE + LTEXT "Smallest Size",IDC_STATIC,16,76,42,8 + LTEXT "Highest Quality",IDC_STATIC,168,76,50,8 + GROUPBOX "Output Information",IDC_STATIC,0,91,255,39 + LTEXT "",IDC_INFO_MODE,12,103,208,8 + LTEXT "",IDC_INFO_BITRATE,12,113,185,8 + CONTROL "Help",IDC_HELPLINK,"Button",BS_OWNERDRAW | WS_TABSTOP,223,108,19,10 + LTEXT "HE-AAC encoding licensed by ",IDC_STATIC,12,135,115,8,SS_CENTERIMAGE + CONTROL "Fraunhofer IIS",IDC_URL,"Button",BS_OWNERDRAW | WS_TABSTOP,12,146,50,8 + CONTROL 104,IDC_LOGO,"Static",SS_BITMAP | SS_NOTIFY,161,134,75,25 + LTEXT "",IDC_VERSION,12,157,115,8 +END + +IDD_PREFERENCES_ADTS DIALOGEX 0, 0, 256, 167 +STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + GROUPBOX "ADTS-AAC Encoder Options",IDC_STATIC,0,0,255,130 + LTEXT "Files created with this encoder will not play on most devices, including the iPod and Android! Use the MPEG-4 AAC encoder instead, unless your device specifically requires ADTS files.",IDC_WARNING,9,10,232,24 + LTEXT "Profile:",IDC_STATIC_PROFILE,57,37,27,12,SS_CENTERIMAGE + COMBOBOX IDC_PROFILE,87,37,110,103,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + LTEXT "Preset:",IDC_STATIC_PRESET,9,43,24,15,SS_CENTERIMAGE + CONTROL "",IDC_SLIDER_PRESET,"msctls_trackbar32",TBS_AUTOTICKS | WS_TABSTOP,28,57,170,15 + EDITTEXT IDC_EDIT_PRESET,203,58,25,12,ES_AUTOHSCROLL + LTEXT "kbps",IDC_KBPS,231,60,16,8,SS_CENTERIMAGE + LTEXT "Smallest Size",IDC_STATIC,16,76,42,8 + LTEXT "Highest Quality",IDC_STATIC,168,76,50,8 + GROUPBOX "Output Information",IDC_STATIC,0,91,255,39 + LTEXT "",IDC_INFO_MODE,12,103,208,8 + LTEXT "",IDC_INFO_BITRATE,12,113,185,8 + CONTROL "Help",IDC_HELPLINK,"Button",BS_OWNERDRAW | WS_TABSTOP,223,108,19,10 + LTEXT "HE-AAC encoding licensed by ",IDC_STATIC,12,135,115,8,SS_CENTERIMAGE + CONTROL "Fraunhofer IIS",IDC_URL,"Button",BS_OWNERDRAW | WS_TABSTOP,12,146,50,8 + CONTROL 104,IDC_LOGO,"Static",SS_BITMAP | SS_NOTIFY,161,134,75,25 + LTEXT "",IDC_VERSION,12,157,115,8 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Bitmap +// + +IDB_FHGLOGO BITMAP "fraunhofer_wa_prefs_noalpha.bmp" + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + 65535 "{E1763EF4-08AD-44a3-914A-8302748AB975}" +END + +STRINGTABLE +BEGIN + IDS_ENC_FHGAAC_DESC "MPEG-4 AAC Encoder v%s" + IDS_VBR_PRESET "Variable Bitrate, Preset %u, typical bitrate: %u kbps" + IDS_CBR_PRESET "Constant Bitrate, target bitrate: %u kbps" + IDS_VBR "Variable Bitrate (VBR)" + IDS_CBR "Constant Bitrate (CBR)" + IDS_AUTOMATIC "Automatic" + IDS_BITRATE "Bitrate:" + IDS_PRESET "Preset:" + IDS_VERSION "Version %s" + IDC_ENC_ADTS_DESC "SHOUTcast MPEG-2 ADTS-AAC Encoder v%s" +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// +#include "version.rc2" + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/Src/Plugins/Encoder/enc_fhgaac/enc_fhgaac.sln b/Src/Plugins/Encoder/enc_fhgaac/enc_fhgaac.sln new file mode 100644 index 00000000..7228e38b --- /dev/null +++ b/Src/Plugins/Encoder/enc_fhgaac/enc_fhgaac.sln @@ -0,0 +1,26 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "enc_fhgaac", "enc_fhgaac.vcproj", "{1EB5ECD6-A4E1-4AEE-99D3-3280F60433F2}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libmp4v2", "..\libmp4v2\libmp4v2.vcproj", "{EFB9B882-6A8B-463D-A8E3-A2807AFC5D9F}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {1EB5ECD6-A4E1-4AEE-99D3-3280F60433F2}.Debug|Win32.ActiveCfg = Debug|Win32 + {1EB5ECD6-A4E1-4AEE-99D3-3280F60433F2}.Debug|Win32.Build.0 = Debug|Win32 + {1EB5ECD6-A4E1-4AEE-99D3-3280F60433F2}.Release|Win32.ActiveCfg = Release|Win32 + {1EB5ECD6-A4E1-4AEE-99D3-3280F60433F2}.Release|Win32.Build.0 = Release|Win32 + {EFB9B882-6A8B-463D-A8E3-A2807AFC5D9F}.Debug|Win32.ActiveCfg = Debug|Win32 + {EFB9B882-6A8B-463D-A8E3-A2807AFC5D9F}.Debug|Win32.Build.0 = Debug|Win32 + {EFB9B882-6A8B-463D-A8E3-A2807AFC5D9F}.Release|Win32.ActiveCfg = Release|Win32 + {EFB9B882-6A8B-463D-A8E3-A2807AFC5D9F}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Src/Plugins/Encoder/enc_fhgaac/enc_fhgaac.vcproj b/Src/Plugins/Encoder/enc_fhgaac/enc_fhgaac.vcproj new file mode 100644 index 00000000..cfb24d94 --- /dev/null +++ b/Src/Plugins/Encoder/enc_fhgaac/enc_fhgaac.vcproj @@ -0,0 +1,322 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Src/Plugins/Encoder/enc_fhgaac/encoder_common.h b/Src/Plugins/Encoder/enc_fhgaac/encoder_common.h new file mode 100644 index 00000000..4b8882bd --- /dev/null +++ b/Src/Plugins/Encoder/enc_fhgaac/encoder_common.h @@ -0,0 +1,9 @@ +#pragma once +#include "../nsv/enc_if.h" +class EncoderCommon : public AudioCoder +{ +public: + virtual void PrepareToFinish()=0; + virtual void Finish(const wchar_t *filename){} + char tool[256]; +}; \ No newline at end of file diff --git a/Src/Plugins/Encoder/enc_fhgaac/fraunhofer_wa_prefs_noalpha.bmp b/Src/Plugins/Encoder/enc_fhgaac/fraunhofer_wa_prefs_noalpha.bmp new file mode 100644 index 00000000..f3edc518 Binary files /dev/null and b/Src/Plugins/Encoder/enc_fhgaac/fraunhofer_wa_prefs_noalpha.bmp differ diff --git a/Src/Plugins/Encoder/enc_fhgaac/libirc.lib b/Src/Plugins/Encoder/enc_fhgaac/libirc.lib new file mode 100644 index 00000000..c91176b7 Binary files /dev/null and b/Src/Plugins/Encoder/enc_fhgaac/libirc.lib differ diff --git a/Src/Plugins/Encoder/enc_fhgaac/link_control.cpp b/Src/Plugins/Encoder/enc_fhgaac/link_control.cpp new file mode 100644 index 00000000..5f34f40b --- /dev/null +++ b/Src/Plugins/Encoder/enc_fhgaac/link_control.cpp @@ -0,0 +1,63 @@ +#include +#include "link_control.h" + +static HCURSOR link_hand_cursor; +static LRESULT link_handlecursor(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + LRESULT ret = CallWindowProcW((WNDPROC)GetPropW(hwndDlg, L"link_proc"), hwndDlg, uMsg, wParam, lParam); + // override the normal cursor behaviour so we have a hand to show it is a link + if(uMsg == WM_SETCURSOR) + { + if((HWND)wParam == hwndDlg) + { + if(!link_hand_cursor) + { + link_hand_cursor = LoadCursor(NULL, IDC_HAND); + } + SetCursor(link_hand_cursor); + return TRUE; + } + } + return ret; +} + +void link_startsubclass(HWND hwndDlg, UINT id){ +HWND ctrl = GetDlgItem(hwndDlg, id); + SetPropW(ctrl, L"link_proc", + (HANDLE)(LONG_PTR)SetWindowLongPtrW(ctrl, GWLP_WNDPROC, (LONG_PTR)link_handlecursor)); +} + +void link_handledraw(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + if (uMsg == WM_DRAWITEM) + { + DRAWITEMSTRUCT *di = (DRAWITEMSTRUCT *)lParam; + if (di->CtlType == ODT_BUTTON) + { + wchar_t wt[123] = {0}; + int y; + RECT r; + HPEN hPen, hOldPen; + GetDlgItemTextW(hwndDlg, (INT)wParam, wt, 123); + + // draw text + SetTextColor(di->hDC, (di->itemState & ODS_SELECTED) ? RGB(220, 0, 0) : RGB(0, 0, 220)); + r = di->rcItem; + r.left += 2; + DrawTextW(di->hDC, wt, -1, &r, DT_VCENTER | DT_SINGLELINE); + + memset(&r, 0, sizeof(r)); + DrawTextW(di->hDC, wt, -1, &r, DT_SINGLELINE | DT_CALCRECT); + + // draw underline + y = di->rcItem.bottom - ((di->rcItem.bottom - di->rcItem.top) - (r.bottom - r.top)) / 2 - 1; + hPen = CreatePen(PS_SOLID, 0, (di->itemState & ODS_SELECTED) ? RGB(220, 0, 0) : RGB(0, 0, 220)); + hOldPen = (HPEN) SelectObject(di->hDC, hPen); + MoveToEx(di->hDC, di->rcItem.left + 2, y, NULL); + LineTo(di->hDC, di->rcItem.right + 2 - ((di->rcItem.right - di->rcItem.left) - (r.right - r.left)), y); + SelectObject(di->hDC, hOldPen); + DeleteObject(hPen); + + } + } +} diff --git a/Src/Plugins/Encoder/enc_fhgaac/link_control.h b/Src/Plugins/Encoder/enc_fhgaac/link_control.h new file mode 100644 index 00000000..a9008d48 --- /dev/null +++ b/Src/Plugins/Encoder/enc_fhgaac/link_control.h @@ -0,0 +1,7 @@ +#pragma once +#include + +extern "C" { +void link_startsubclass(HWND hwndDlg, UINT id); +void link_handledraw(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); +} \ No newline at end of file diff --git a/Src/Plugins/Encoder/enc_fhgaac/main.cpp b/Src/Plugins/Encoder/enc_fhgaac/main.cpp new file mode 100644 index 00000000..90fefb2a --- /dev/null +++ b/Src/Plugins/Encoder/enc_fhgaac/main.cpp @@ -0,0 +1,263 @@ +#pragma comment(linker, "-nodefaultlib:libmmd.lib") +#pragma message(__FILE__": telling linker to ignore libmmd.lib") + +#include "FhGAACEncoder.h" +#include +#include +#include +#include "../nsv/enc_if.h" +#include "config.h" +#include "preferences.h" +#include "resource.h" +#include +#include "../Agave/Language/api_language.h" +#include "../winamp/wa_ipc.h" +#include "../nu/AutoWideFn.h" +#include +#include "../nu/AutoWideFn.h" +#include "encoder_common.h" +#include "ADTSAACEncoder.h" + +#define ENC_VERSION "1.08" + +HWND winampwnd = 0; +api_service *WASABI_API_SVC = 0; +api_language *WASABI_API_LNG = 0; +api_application *WASABI_API_APP = 0; +HINSTANCE WASABI_API_LNG_HINST = 0, WASABI_API_ORIG_HINST = 0; +HINSTANCE enc_fhg_HINST = 0; + +static HINSTANCE GetMyInstance() +{ + MEMORY_BASIC_INFORMATION mbi = {0}; + if(VirtualQuery(GetMyInstance, &mbi, sizeof(mbi))) + return (HINSTANCE)mbi.AllocationBase; + return NULL; +} + +void GetLocalisationApiService(void) +{ + if (!enc_fhg_HINST) + enc_fhg_HINST = GetMyInstance(); + + if(winampwnd && !WASABI_API_LNG) + { + // loader so that we can get the localisation service api for use + if(!WASABI_API_SVC) + { + WASABI_API_SVC = (api_service*)SendMessage(winampwnd, WM_WA_IPC, 0, IPC_GET_API_SERVICE); + if (WASABI_API_SVC == (api_service*)1) + { + WASABI_API_SVC = NULL; + return; + } + } + + if (!WASABI_API_SVC) + return; + + if(!WASABI_API_APP) + { + waServiceFactory *sf = WASABI_API_SVC->service_getServiceByGuid(applicationApiServiceGuid); + if (sf) WASABI_API_APP = reinterpret_cast(sf->getInterface()); + } + + if(!WASABI_API_LNG) + { + waServiceFactory *sf = WASABI_API_SVC->service_getServiceByGuid(languageApiGUID); + if (sf) WASABI_API_LNG = reinterpret_cast(sf->getInterface()); + } + + // need to have this initialised before we try to do anything with localisation features + WASABI_API_START_LANG(GetMyInstance(),EncFhgAacLangGUID); + } +} + +extern "C" +{ + + unsigned int __declspec(dllexport) GetAudioTypes3(int idx, char *desc) + { + switch(idx) + { + case 0: + GetLocalisationApiService(); + StringCchPrintfA(desc, 1024, WASABI_API_LNGSTRING(IDS_ENC_FHGAAC_DESC), ENC_VERSION); + return ENCODER_TYPE_MPEG4; + case 1: + GetLocalisationApiService(); + StringCchPrintfA(desc, 1024, WASABI_API_LNGSTRING(IDC_ENC_ADTS_DESC), ENC_VERSION); + return ENCODER_TYPE_ADTS; + default: + return 0; + } + } + + AudioCoder __declspec(dllexport) *CreateAudio3(int nch, int srate, int bps, unsigned int srct, unsigned int *outt, char *configfile) + { + if (srct == mmioFOURCC('P','C','M',' ')) + { + switch(*outt) + { + case ENCODER_TYPE_MPEG4: + { + FhGAACEncoder *t=0; + AACConfigurationFile *wr = AACConfig_Create(ENCODER_TYPE_MPEG4, configfile); + if (!wr) + return 0; + + t = FhGAACEncoder::CreateDecoder(&wr->config, nch, srate, bps); + free(wr); + return t; + } + + case ENCODER_TYPE_ADTS: + { + ADTSAACEncoder *t=0; + AACConfigurationFile *wr = AACConfig_Create(ENCODER_TYPE_ADTS, configfile); + if (!wr) + return 0; + + t = ADTSAACEncoder::CreateDecoder(&wr->config, nch, srate, bps); + free(wr); + return t; + } + } + } + return NULL; + } + + HWND __declspec(dllexport) ConfigAudio3(HWND hwndParent, HINSTANCE hinst, unsigned int outt, const char *configfile) + { + switch(outt) + { + case ENCODER_TYPE_MPEG4: + { + AACConfigurationFile *wr = AACConfig_Create(ENCODER_TYPE_MPEG4, configfile); + if (!wr) + return 0; + + wr->channels = 2; /* dummy defaults */ + wr->sample_rate = 44100; /* dummy defaults */ + GetLocalisationApiService(); + return WASABI_API_CREATEDIALOGPARAMW(IDD_PREFERENCES_MP4, hwndParent, Preferences_MP4_DlgProc, (LPARAM)wr); + } + case ENCODER_TYPE_ADTS: + { + AACConfigurationFile *wr = AACConfig_Create(ENCODER_TYPE_ADTS, configfile); + if (!wr) + return 0; + + wr->channels = 2; /* dummy defaults */ + wr->sample_rate = 44100; /* dummy defaults */ + GetLocalisationApiService(); + return WASABI_API_CREATEDIALOGPARAMW(IDD_PREFERENCES_ADTS, hwndParent, Preferences_ADTS_DlgProc, (LPARAM)wr); + } + + } + return NULL; + } + +#if 0 // TODO + HWND __declspec(dllexport) ConfigAudio4(HWND hwndParent, HINSTANCE hinst, unsigned int outt, const char *configfile, unsigned int channels, unsigned int sample_rate) + { + switch(outt) + { + case ENCODER_TYPE_MPEG4: + { + AACConfigurationFile *wr = (AACConfigurationFile*)calloc(1, sizeof(AACConfigurationFile)); + if (!wr) + return 0; + + if (configfile) + lstrcpynA(wr->config_file, configfile, MAX_PATH); + else + wr->config_file[0] = 0; + + wr->channels = channels; + wr->sample_rate = sample_rate; + AACConfig_Load(wr); + GetLocalisationApiService(); + return WASABI_API_CREATEDIALOGPARAMW(IDD_PREFERENCES_MP4, hwndParent, Preferences_MP4_DlgProc, (LPARAM)wr); + } + case ENCODER_TYPE_ADTS: + { + AACConfigurationFile *wr = (AACConfigurationFile*)calloc(1, sizeof(AACConfigurationFile)); + if (!wr) + return 0; + + if (configfile) + lstrcpynA(wr->config_file, configfile, MAX_PATH); + else + wr->config_file[0] = 0; + + wr->channels = channels; + wr->sample_rate = sample_rate; + AACConfig_Load(wr); + GetLocalisationApiService(); + return WASABI_API_CREATEDIALOGPARAMW(IDD_PREFERENCES_ADTS, hwndParent, Preferences_ADTS_DlgProc, (LPARAM)wr); + } + + } + return NULL; + } +#endif + + void __declspec(dllexport) PrepareToFinish(const char *filename, AudioCoder *coder) + { + ((EncoderCommon*)coder)->PrepareToFinish(); + } + + void __declspec(dllexport) PrepareToFinishW(const wchar_t *filename, AudioCoder *coder) + { + ((EncoderCommon*)coder)->PrepareToFinish(); + } + + void __declspec(dllexport) FinishAudio3(const char *filename, AudioCoder *coder) + { + ((EncoderCommon*)coder)->Finish(AutoWideFn(filename)); + } + + void __declspec(dllexport) FinishAudio3W(const wchar_t *filename, AudioCoder *coder) + { + ((EncoderCommon*)coder)->Finish(filename); + } + + int __declspec(dllexport) GetConfigItem(unsigned int outt, const char *item, char *data, int len, char *configfile) + { + if (outt == mmioFOURCC('A','A','C','f')) + { + if (!lstrcmpiA(item,"extension")) + lstrcpynA(data,"m4a",len); + return 1; + } + else if (outt == mmioFOURCC('A','D','T','S')) + { + if (!lstrcmpiA(item,"extension")) + { + lstrcpynA(data,"aac",len); + } + else if (!lstrcmpiA(item,"aot")) + { + AACConfigurationFile *wr = AACConfig_Create(ENCODER_TYPE_ADTS, configfile); + if (wr) + { + StringCbPrintfA(data, len, "%u", AACConfig_GetAOT(&wr->config)); + } + else + { + StringCbPrintfA(data, len, "%u", AUD_OBJ_TYP_LC); + } + free(wr); + } + return 1; + } + return 0; + } + + void __declspec(dllexport) SetWinampHWND(HWND hwnd) + { + winampwnd = hwnd; + + } +} \ No newline at end of file diff --git a/Src/Plugins/Encoder/enc_fhgaac/mp4FastAAClib.h b/Src/Plugins/Encoder/enc_fhgaac/mp4FastAAClib.h new file mode 100644 index 00000000..e7350669 --- /dev/null +++ b/Src/Plugins/Encoder/enc_fhgaac/mp4FastAAClib.h @@ -0,0 +1,978 @@ +/************************* Fast MPEG AAC Audio Encoder ********************** + + (C) Copyright Fraunhofer IIS (2004-2010) + All Rights Reserved + + $Id: mp4FastAAClib.h,v 1.8 2013/10/29 00:56:15 dromagod Exp $ + Initial author: M. Schug / A. Groeschel + contents/description: Fast MPEG AAC Encoder Interface Library Functions + + This software and/or program is protected by copyright law and international + treaties. Any reproduction or distribution of this software and/or program, + or any portion of it, may result in severe civil and criminal penalties, and + will be prosecuted to the maximum extent possible under law. + +******************************************************************************/ + +#ifndef _mp4FastAAClib_h_ +#define _mp4FastAAClib_h_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* ------------------------ structure alignment ---------------------------*/ + +#if defined(WIN32) || defined(WIN64) +#pragma pack(push, 1) +#endif + +/*-------------------------- defines --------------------------------------*/ + +/* + * calling convention + */ + +#ifndef MPEG4ENCAPI +#if defined(WIN32) || defined(WIN64) +#define MPEG4ENCAPI __stdcall +#else +#define MPEG4ENCAPI +#endif +#endif + +/*-------------------- enum definitions -----------------------------------*/ + +typedef enum +{ + AUD_OBJ_TYP_LC = 2, /* AAC LC */ + AUD_OBJ_TYP_LTP = 4, /* AAC LTP */ + AUD_OBJ_TYP_HEAAC = 5, /* AAC LC + SBR */ + AUD_OBJ_TYP_ER_LC = 17, /* ER AAC LC */ + AUD_OBJ_TYP_ER_LTP = 19, /* ER AAC LTP */ + AUD_OBJ_TYP_ER_SCAL = 20, /* ER AAC LC scalable */ + AUD_OBJ_TYP_PS = 29, /* AAC LC + SBR + PS */ + AUD_OBJ_TYP_MP2_LC = 129, /* virtual AOT MP2 Low Complexity Profile */ + AUD_OBJ_TYP_MP2_SBR = 132, /* virtual AOT MP2 Low Complexity Profile with SBR */ + AUD_OBJ_TYP_SBR_DS = 133, /* virtual AOT for downsampled SBR */ + AUD_OBJ_TYP_ER_SCAL_SBR = 148, /* ER AAC LC scalable + SBR */ + AUD_OBJ_TYP_ER_SCAL_SBR_PS = 157, /* ER AAC LC scalable + SBR + PS */ + AUD_OBJ_TYP_MPS = 30 +} AUD_OBJ_TYP; + +typedef enum { + MP4_QUAL_FAST=0, + MP4_QUAL_MEDIUM, + MP4_QUAL_HIGH, + MP4_QUAL_HIGHEST /* Always resample to preferred sample rate */ +} MPEG4ENC_QUALITY; + +typedef enum { + MP4_TT_RAW = 0, + MP4_TT_ADIF = 1, + MP4_TT_ADTS = 2, + MP4_TT_ADTSCRC = 3, + MP4_TT_LOAS = 4, + MP4_TT_LOAS_NOSMC = 5, + MP4_TT_LATM = 6, + MP4_TT_LATM_NOSMC = 7, + /* MP4_TT_LOAS_CRC = 8, */ + /* MP4_TT_LATM_CRC = 9, */ + ___mp4_tt_dummy +} MPEG4ENC_TRANSPORT_TYPE; + +typedef enum { + /* These are the standard MPEG Channel mappings */ + MP4_CH_MODE_INVALID = 0, + MP4_CH_MODE_MONO, /* 1 channel mono */ + MP4_CH_MODE_STEREO, /* 2 channel stereo */ + MP4_CH_MODE_3, /* 3 channel audio ( center + left/right front speaker ) */ + MP4_CH_MODE_4, /* 4 channel audio ( center + left/right front + rear surround speaker ) */ + MP4_CH_MODE_5, /* 5 channel audio ( center + left/right front + left/right surround speaker ) */ + MP4_CH_MODE_5_1, /* 5.1 channel audio ( center + left/right front + left/right surround speaker + LFE ) */ + MP4_CH_MODE_7_1, /* 7.1 channel audio ( center + left/right front + + left/right outside front + left/right surround speaker + LFE ) */ + /* Channel mappings 8 to 15 are reserved */ + MP4_CH_MODE_6_1 = 11, /* 6.1 channel audio ( center + front left/right + surround left/right + rear surround center + LFE ) */ + MP4_CH_MODE_7_1_REAR_SURROUND = 12, /* 7.1 channel audio ( center + front left/right + surround left/right + rear surround left/right + LFE ) */ + MP4_CH_MODE_7_1_TOP_FRONT = 14, /* 7.1 channel audio ( center + front left/right + surround left/right + LFE + + TOP front left/right ) */ + + /* Some non standard channel mappings */ + MP4_CH_MODE_DUAL_MONO = 16, /* 2 independent channels */ + MP4_CH_MODE_4TIMES1, /* 4 independent channels */ + MP4_CH_MODE_6TIMES1, /* 6 independent channels */ + MP4_CH_MODE_8TIMES1, /* 8 independent channels */ + MP4_CH_MODE_12TIMES1, /* 12 independent channels */ + MP4_CH_MODE_16TIMES1, + MP4_CH_MODE_2TIMES2, /* 2 stereo channel pairs */ + MP4_CH_MODE_3TIMES2, /* 3 stereo channel pairs */ + MP4_CH_MODE_4TIMES2, /* 4 stereo channel pairs */ + MP4_CH_MODE_6TIMES2, /* 6 stereo channel pairs */ + + MP4_CH_MODE_7_1_SIDE_CHANNEL = 32, /* 7.1 channel audio ( center + left/right front + + left/right side channels + left/right surround speakers + LFE ) */ + MP4_CH_MODE_7_1_FRONT_CENTER, /* 7.1 channel audio ( center + left/right front + + left/right frontal center speakers + left/right surround speakers + LFE ) */ + + /* Channel mapping for parametric stereo + (only works with AUD_OBJ_TYP_HEAAC, AUD_OBJ_TYP_PS) */ + MP4_CH_MODE_PARAMETRIC_STEREO = 64, /* 2 channel stereo input, transmit 1 channel mono + SBR + PS */ + MP4_CH_MODE_MPEGS_5x5 = 128, /* 6 channel input, transmit 1/2 channel(s) (+ SBR) + MPEGS Payload */ +#ifdef SUPPORT_UPMIX + MP4_CH_MODE_MPEGS_SXPRO_UPMIX, /* 2 channel input, sxPro Upmix, transmit 2 channel(s) (+ SBR) + MPEGS Payload */ +#endif +#ifdef SUPPORT_MPS_7_X_7 + MP4_CH_MODE_MPEGS_7x7_REAR_SURROUND, /* 8 channel input (5.1 + left/right side speakers), transmit 2 channel(s) (+ SBR) + MPEGS Payload */ + /* 7.1 front center channel mapping is not yet supported! */ + MP4_CH_MODE_MPEGS_7x7_FRONT_CENTER, /* 8 channel input (5.1 + left/right frontal center speakers), transmit 2 channel(s) (+ SBR) + MPEGS Payload */ +#ifdef SUPPORT_MPS_7_5_7 + MP4_CH_MODE_MPEGS_757_FRONT_CENTER, /* 8 channel input (5.1 + left/right frontal center speakers), transmit 5 channel(s) (+ SBR) + MPEGS Payload */ + MP4_CH_MODE_MPEGS_757_REAR_SURROUND, /* 8 channel input (5.1 + left/right side speakers), transmit 5 channel(s) (+ SBR) + MPEGS Payload */ +#endif /* SUPPORT_MPS_7_5_7 */ +#endif /* SUPPORT_MPS_7_X_7 */ + + /* The following channel mappings are not yet supported! */ + MP4_CH_MODE_MPEGS_5x5_BLIND, + MP4_CH_MODE_MPEGS_ARBITRARY_DOWNMIX_MONO, /* 7 channel input, transmit 1 channel (+ SBR) + MPEGS Payload */ + MP4_CH_MODE_MPEGS_ARBITRARY_DOWNMIX_STEREO /* 8 channel input, transmit 2 channel(s) (+ SBR) + MPEGS Payload */ + +} MPEG4ENC_CH_MODE; + +typedef enum { + + MP4_MPEGS_DOWNMIX_DEFAULT = 0, + /* The following config (FORCE_STEREO) is not yet supported! */ + MP4_MPEGS_DOWNMIX_FORCE_STEREO, + MP4_MPEGS_DOWNMIX_MATRIX_COMPAT, + /* The following configs are not yet supported! */ + MP4_MPEGS_DOWNMIX_ARBITRARY_MONO, + MP4_MPEGS_DOWNMIX_ARBITRARY_STEREO +#ifdef SUPPORT_MPS_7_5_7 + , MP4_MPEGS_DOWNMIX_51 +#endif /* SUPPORT_MPS_7_5_7 */ + +} MPEG4ENC_MPEGS_DOWNMIX_CONFIG; + + +typedef enum { + MPEG4ENC_NO_ERROR = 0, + MPEG4ENC_UNKNOWN_ERROR, + MPEG4ENC_PARAM_ERROR, + MPEG4ENC_NOTIMPLEMENTED_ERROR, + MPEG4ENC_MEMORY_ERROR, + MPEG4ENC_INIT_ERROR, + MPEG4ENC_FATAL_ERROR, + MPEG4ENC_STACK_ALIGNMENT_ERROR, + MPEG4ENC_METADATA_ERROR, + MPEG4ENC_AOT_NOT_SUPPORTED = 64, + MPEG4ENC_CHMODE_NOT_SUPPORTED, + MPEG4ENC_BRMODE_NOT_SUPPORTED, + MPEG4ENC_WARNING_MIN = 128, + MPEG4ENC_WARNING_STACK_ALIGNMENT = MPEG4ENC_WARNING_MIN, + MPEG4ENC_WARNING_METADATA, + MPEG4ENC_WARNING_NOSYNC_TRIGGERED +} MPEG4ENC_ERROR; + +typedef enum { + MP4_SBRSIG_IMPLICIT = 0, /* implicit signaling (signaling 1) */ + MP4_SBRSIG_EXPL_BC = 1, /* explicit backward compatible signaling (signaling 2.B.) */ + MP4_SBRSIG_EXPL_HIER = 2 /* explicit hierarchical signaling (signaling 2.A.) */ +} MPEG4ENC_SIGNALING_MODE; + +typedef enum { + MP4_MPEGS_PAYLOAD_EMBED = 0, /* in case of MPEG-4 transportation, embed payload into AAC payload */ + MP4_MPEGS_NO_PAYLOAD_EMBED = 1, /* in case of MPEG-4 transportation, do *not* embed payload into AAC payload, but transport payload in extra stream */ + MP4_MPEGS_PAYLOAD_EMBED_ASCEXT = 2 /* M16117 */ +} MPEG4ENC_MPEGS_PAYLOAD_MODE; + +typedef enum { + MP4_BR_MODE_CBR = 0, + MP4_BR_MODE_VBR_1 = 1, + MP4_BR_MODE_VBR_2 = 2, + MP4_BR_MODE_VBR_3 = 3, + MP4_BR_MODE_VBR_4 = 4, + MP4_BR_MODE_VBR_5 = 5, + MP4_BR_MODE_VBR_6 = 6, + MP4_BR_MODE_SFR = 7, /* Superframing */ + MP4_BR_MODE_DABPLUS = 8, /* Superframing + DAB+ constraints */ + MP4_BR_MODE_DRMPLUS = 9, /* Superframing + DRM+ constraints */ + MP4_BR_MODE_DMB = 10 +} MPEG4ENC_BITRATE_MODE; + +typedef enum{ + MP4_GRANULE_960 = 960, + MP4_GRANULE_1024 = 1024 +} MPEG4ENC_GRANULE_LEN; + +typedef enum { + MP4_METADATA_NONE = 0, /* do not embed any metadata */ + MP4_METADATA_MPEG, /* embed MPEG defined metadata only */ + MP4_METADATA_MPEG_ETSI /* embed all metadata */ +} MPEG4ENC_METADATA_MODE; + +typedef enum { + MP4_METADATA_DRC_NONE = 0, + MP4_METADATA_DRC_FILMSTANDARD, + MP4_METADATA_DRC_FILMLIGHT, + MP4_METADATA_DRC_MUSICSTANDARD, + MP4_METADATA_DRC_MUSICLIGHT, + MP4_METADATA_DRC_SPEECH, +#ifdef SUPPORT_METADATA_DRC_MOBILE + MP4_METADATA_DRC_MOBILE, +#endif + MP4_METADATA_DRC_EMBED_EXTERN = -1, + MP4_METADATA_DRC_NOT_PRESENT = -2 +} MPEG4ENC_METADATA_DRC_PROFILE; + +typedef enum { + MPEG4ENC_METADATA_DMX_GAIN_0_dB = 0, + MPEG4ENC_METADATA_DMX_GAIN_1_5_dB = 1, + MPEG4ENC_METADATA_DMX_GAIN_3_dB = 2, + MPEG4ENC_METADATA_DMX_GAIN_4_5_dB = 3, + MPEG4ENC_METADATA_DMX_GAIN_6_dB = 4, + MPEG4ENC_METADATA_DMX_GAIN_7_5_dB = 5, + MPEG4ENC_METADATA_DMX_GAIN_9_dB = 6, + MPEG4ENC_METADATA_DMX_GAIN_INF = 7, +} MPEG4ENC_METADATA_DMX_GAIN; + +typedef enum { + MP4_METADATA_DSUR_NOT_INDICATED = 0, /* Dolby Surround mode not indicated */ + MP4_METADATA_DSUR_NOT_USED = 1, /* 2-ch audio part is not Dolby surround encoded */ + MP4_METADATA_DSUR_IS_USED = 2 /* 2-ch audio part is Dolby surround encoded */ +} MPEG4ENC_METADATA_DSUR_IND; + +typedef enum { /* see ETSI TS 101 154 V1.11.1, section C.5.2.2.3 and C.5.3 */ + MP4_METADATA_DRCPRESENTATION_NOT_INDICATED = 0, + MP4_METADATA_DRCPRESENTATION_MODE_1 = 1, + MP4_METADATA_DRCPRESENTATION_MODE_2 = 2 +} MPEG4ENC_METADATA_DRCPRESENTATION; + +typedef enum { + MP4_MAX_ASC_SIZE = 64, + MP4_MAX_SMC_SIZE = 256, + MAX_DRC_BANDS = (1<<4), + MP4_MAX_NUM_STREAMS = 2 +} MPEG4ENC_DEFINES; + + +typedef enum { + MPEG4ENC_SYNCFRAME_STARTUP = 0, + MPEG4ENC_SYNCFRAME_SWITCH, + MPEG4ENC_SYNCFRAME_DASH +} MPEG4ENC_SYNCFRAME_TYPES; + +typedef enum { + MP4_MPSDMXGAIN_INVALID = -1, + MP4_MPSDMXGAIN_0_dB = 0, + MP4_MPSDMXGAIN_1_5_dB = 1, + MP4_MPSDMXGAIN_3_dB = 2, + MP4_MPSDMXGAIN_4_5_dB = 3, + MP4_MPSDMXGAIN_6_dB = 4, + MP4_MPSDMXGAIN_7_5_dB = 5, + MP4_MPSDMXGAIN_9_dB = 6, + MP4_MPSDMXGAIN_12_dB = 7 +} MPEG4ENC_MPS_DMX_GAIN; + +#ifdef SUPPORT_UPMIX +typedef enum { + MP4_SXPRO_DEFAULT = 0, + MP4_SXPRO_DRY, + MP4_SXPRO_VIBRANT +} MP4_SXPRO_UPMIX_WORKMODE; + +typedef enum { + MP4_SXPRO_LFE_OFF = 0, + MP4_SXPRO_LFE_ON +} MP4_SXPRO_UPMIX_LFE; +#endif + +/*-------------------- structure definitions ------------------------------*/ + +typedef struct { + AUD_OBJ_TYP aot; + int nBitRate; + MPEG4ENC_BITRATE_MODE bitrateMode; + MPEG4ENC_QUALITY quality; + MPEG4ENC_CH_MODE chMode; + int nSampleRateIn; + MPEG4ENC_TRANSPORT_TYPE transportFormat; + MPEG4ENC_SIGNALING_MODE sbrSignaling; + MPEG4ENC_GRANULE_LEN nGranuleLength; + MPEG4ENC_METADATA_MODE metadataMode; +} MPEG4ENC_SETUP; + +typedef enum{ + MP4_THREADING_MODE_SINGLE = 1, + MP4_THREADING_MODE_MULTIPLE_BLOCKING, + MP4_THREADING_MODE_MULTIPLE_NOBLOCKING +} MPEG4ENC_THREADING_MODE; + + +typedef MPEG4ENC_SETUP *HANDLE_MPEG4ENC_SETUP; + +struct MPEG4ENC_ENCODER; +typedef struct MPEG4ENC_ENCODER * HANDLE_MPEG4ENC_ENCODER; + +typedef struct +{ + int nOutputStreams; /* number of output streams */ + int nAccessUnitsPerStream[MP4_MAX_NUM_STREAMS]; /* number of AUs in bitstream buffer */ + int *pnAccessUnitOffset[MP4_MAX_NUM_STREAMS]; /* offset of AUs per stream, i.e. pnAccessUnitOffset[stream][numberAuPerStream] */ + int *pByteCnt[MP4_MAX_NUM_STREAMS]; /* lenght of each single AU in bitstream buffer */ + int *pIsSync[MP4_MAX_NUM_STREAMS]; /* flag, signaling if AU is self contained i.e. does not contain backward dependencies */ +} MPEG4ENC_AUINFO; + +typedef struct { + int nAscSizeBits; + unsigned char ascBuffer[MP4_MAX_ASC_SIZE]; +} MPEG4ENC_ASCBUF; + +typedef struct { + int nSmcSizeBits; + unsigned char smcBuffer[MP4_MAX_ASC_SIZE]; +} MPEG4ENC_SMCBUF; + +typedef struct +{ + float fBandWidth; /* audio bandwidth in Hz */ + int nDelay; /* encoder delay in units of sample frames */ + int nDelayCore; /* encoder delay in units of sample frames */ + int nCbBufSizeMin; /* minimum size of output buffer (bytes) */ + int nSyncFrameDelay; + + int nBitRate[MP4_MAX_NUM_STREAMS]; + int nMaxBitRate[MP4_MAX_NUM_STREAMS]; + int nBitResMax[MP4_MAX_NUM_STREAMS]; + int nSamplingRate[MP4_MAX_NUM_STREAMS]; + int nSamplesFrame[MP4_MAX_NUM_STREAMS]; + + unsigned int nAncBytesPerFrame; + int aot; + int nValidAsc; + MPEG4ENC_ASCBUF ascBuf[MP4_MAX_NUM_STREAMS]; + MPEG4ENC_SMCBUF smcBuf; + int nProfLev; + + char pVersion[50]; + char pBuildDate[50]; + +} MPEG4ENC_INFO; + +typedef struct MPEG4ENC_METADATA +{ + MPEG4ENC_METADATA_DRC_PROFILE drc_profile; /* MPEG DRC compression profile */ + MPEG4ENC_METADATA_DRC_PROFILE comp_profile; /* ETSI heavy compression profile */ + + float drc_TargetRefLevel; /* used to define expected level to */ + float comp_TargetRefLevel; /* adjust limiter to avoid overload */ + + float drc_ext; /* external feed DRC compression value */ + float comp_ext; /* external feed heavy compression value */ + + int prog_ref_level_present; /* flag, if prog_ref_level is present */ + float prog_ref_level; /* Programme Reference Level = Dialogue Level: */ + /* -31.75dB .. 0 dB ; stepsize: 0.25dB */ + + int PCE_mixdown_idx_present; /* flag, if dmx-idx should be written in programme config element */ + int ETSI_DmxLvl_present; /* flag, if dmx-lvl should be written in ETSI-ancData */ + MPEG4ENC_METADATA_DMX_GAIN centerMixLevel; /* center downmix level */ + MPEG4ENC_METADATA_DMX_GAIN surroundMixLevel; /* surround downmix level */ + + MPEG4ENC_METADATA_DSUR_IND dolbySurroundMode; /* Indication for Dolby Surround Encoding Mode */ + + MPEG4ENC_METADATA_DRCPRESENTATION drcPresentationMode; /* DRC presentation mode (ETSI) */ + + /* preprocessing */ + int dcFilter; /* flag specifying if DC filtering is applied to input */ + int lfeLowpassFilter; /* flag specifying if 120 Hz low-pass filter is applied to LFE channel */ + int surPhase90; /* flag specifying if 90 degree phase shift is applied to surround channels */ + int surAtt3dB; /* flag specifying if 3 dB attenuation is applied to surround channels */ + +} MPEG4ENC_METADATA; + +typedef struct MPEG4ENC_EXTMETADATA +{ +#if 1 /* ENABLE_ISO14496_3_2009_AMD4 */ + /* not fully supported yet */ + /* extended ancillary data */ + int pseudoSurroundEnable; /* flag */ + int extAncDataEnable; /* flag */ + int extDownmixLevelEnable; /* flag */ + int extDownmixLevel_A; /* downmix level index A (0...7, according to table) */ + int extDownmixLevel_B; /* downmix level index B (0...7, according to table) */ + int dmxGainEnable; /* flag */ + float dmxGain5; /* gain factor for downmix to 5 channels */ + float dmxGain2; /* gain factor for downmix to 2 channels */ + int lfeDmxEnable; /* flag */ + int lfeDmxLevel; /* downmix level index for LFE (0..15, according to table) */ +#endif +} MPEG4ENC_EXTMETADATA; + +typedef struct MPEG4ENC_METADATA *HANDLE_MPEG4ENC_METADATA; +typedef struct MPEG4ENC_EXTMETADATA *HANDLE_MPEG4ENC_EXTMETADATA; + + +/*-------------------- function prototypes --------------------------------*/ + + +/*--------------------------------------------------------------------------- + + functionname: MPEG4ENC_Configure + description : fills encoder handle structure with default values + to be called before MPEG4ENC_Open + returns: MPEG4ENC_NO_ERROR on success, an appropriate error code else + + ---------------------------------------------------------------------------*/ +MPEG4ENC_ERROR MPEG4ENCAPI +MPEG4ENC_Configure ( + HANDLE_MPEG4ENC_ENCODER *phMp4Enc, /* adress of encoder handle */ + const HANDLE_MPEG4ENC_SETUP hSetup /* handle to filled setup stucture */ + ); + +/*--------------------------------------------------------------------------- + + functionname:MPEG4ENC_GetVersionInfo + description: get Version Number information about the encoding process + returns: MPEG4ENC_ERROR (error code) + + ---------------------------------------------------------------------------*/ +MPEG4ENC_ERROR MPEG4ENCAPI +MPEG4ENC_GetVersionInfo(char *const pVersionInfo, + const int bufSize); + +/*--------------------------------------------------------------------------- + + functionname: MPEG4ENC_Open + description: allocate and initialize a new encoder instance + samplesFirst holds the desired number of input + samples (of all channels) for the first frame + returns: MPEG4ENC_NO_ERROR on success, an appropriate error code else + + ---------------------------------------------------------------------------*/ + +MPEG4ENC_ERROR MPEG4ENCAPI +MPEG4ENC_Open( + HANDLE_MPEG4ENC_ENCODER *phMp4Enc, /* pointer to encoder handle, initialized on return */ + unsigned int* const pSamplesFirst /* number of samples needed to encode the first frame */ + ); + +/*--------------------------------------------------------------------------- + + functionname: MPEG4ENC_Close + description: deallocate an encoder instance + returns: MPEG4ENC_NO_ERROR on success, an appropriate error code else + + ---------------------------------------------------------------------------*/ + +MPEG4ENC_ERROR MPEG4ENCAPI +MPEG4ENC_Close ( + HANDLE_MPEG4ENC_ENCODER* phMp4Enc /* pointer to an encoder handle */ + ); + +/*--------------------------------------------------------------------------- + + functionname: MPEG4ENC_Encode + description: encode the passed samples + modifies: pSamplesConsumed: number of used samples + pSamplesNext: number of samples needed to encode + the next frame + pOutputBytes: number of valid bytes in pOutput + returns: MPEG4ENC_NO_ERROR on success, an appropriate error code else + + ---------------------------------------------------------------------------*/ + +MPEG4ENC_ERROR MPEG4ENCAPI +MPEG4ENC_Encode( + HANDLE_MPEG4ENC_ENCODER const hMp4Enc, /* an encoder handle */ + const float* const pSamples, /* pointer to audio samples, interleaved*/ + const int nSamples, /* number of samples + must be a multiple of number of input channels */ + int* pSamplesConsumed, /* number of used input samples, + will be a multiple of number of input channels */ + unsigned int* const pSamplesNext, /* number of desired samples for a complete frame */ + unsigned char* const pOutput, /* pointer to bitstream buffer */ + const int nOutputBufSize, /* the size of the output buffer; + must be large enough to receive all data */ + int* const pOutputBytes, /* number of bytes in bitstream buffer */ + MPEG4ENC_AUINFO **ppAuInfo /* */ + ); + +/*--------------------------------------------------------------------------- + + functionname: MPEG4ENC_SRInfo + description : returns the sample rate range and + to be called before MPEG4ENC_Open + returns: MPEG4ENC_NO_ERROR on success, + MPEG4ENC_INIT_ERROR if the bitrate, channel, aot configuration + is not supported + + ---------------------------------------------------------------------------*/ +MPEG4ENC_ERROR MPEG4ENCAPI +MPEG4ENC_SRInfo ( + + const int bitRate, /* the targeted bit rate */ + const MPEG4ENC_BITRATE_MODE bitrateMode, /* the bitrateMode */ + const MPEG4ENC_CH_MODE chMode, /* the number of channels to encode */ + const AUD_OBJ_TYP aot, /* the audio object type */ + const MPEG4ENC_QUALITY quality, /* encoder quality */ + int *const sampleRateMin, /* lowest supported */ + int *const sampleRateMax, /* highest supported */ + int *const sampleRatePref /* preferred + sampling frequency for the given + bitrate, channel, aot configuration */ + ); + +/*--------------------------------------------------------------------------- + + functionname: MPEG4ENC_GetInfo + description: get information about the encoding process + returns: MPEG4ENC_NO_ERROR on success, an appropriate error code else + + ---------------------------------------------------------------------------*/ +MPEG4ENC_ERROR MPEG4ENCAPI +MPEG4ENC_GetInfo(const HANDLE_MPEG4ENC_ENCODER hMp4Enc, + MPEG4ENC_INFO * const pInfo); + +/*----------------------------------------------------------------------------- + + functionname: MPEG4ENC_SetAncDataRate + description: Sets bitrate for ancillary data + returns: MPEG4ENC_NO_ERROR on success, an appropriate error code else + + ------------------------------------------------------------------------------*/ +MPEG4ENC_ERROR MPEG4ENCAPI +MPEG4ENC_SetAncDataRate( + HANDLE_MPEG4ENC_ENCODER hMp4Enc, + int nAncDataRate + ); + +/*----------------------------------------------------------------------------- + + functionname: MPEG4ENC_SetAncData + description: Passes ancillary data to encoder + returns: MPEG4ENC_NO_ERROR on success, an appropriate error code else + + ------------------------------------------------------------------------------*/ +MPEG4ENC_ERROR MPEG4ENCAPI +MPEG4ENC_SetAncData( + HANDLE_MPEG4ENC_ENCODER const hMp4Enc, /* an encoder handle */ + unsigned char* pAncBytes, /* ancillary data buffer */ + unsigned int* pNumAncBytes /* ancillary data bytes left */ + ); + +/*----------------------------------------------------------------------------- + + functionname: MPEG4ENC_SetOffsets + description: changes mapping of input audio channels to AAC channels + returns: MPEG4ENC_NO_ERROR on success, an appropriate error code else + + ------------------------------------------------------------------------------*/ +MPEG4ENC_ERROR MPEG4ENCAPI +MPEG4ENC_SetOffsets( + const HANDLE_MPEG4ENC_ENCODER hMp4Enc, + const unsigned int nChannels, + const unsigned int *const channelOffset + ); + +/*----------------------------------------------------------------------------- + + functionname: MPEG4ENC_SetSbrTransmissionConfig + description: changes signaling interval of SBR header, additional CRC bits + for SBR data + returns: MPEG4ENC_NO_ERROR on success, an appropriate error code else + + ------------------------------------------------------------------------------*/ +MPEG4ENC_ERROR MPEG4ENCAPI +MPEG4ENC_SetSbrTransmissionConfig( + const HANDLE_MPEG4ENC_ENCODER hMp4Enc, + const int bUseCRC, + const float sendHeaderTimeInterval + ); + + +/*----------------------------------------------------------------------------- + + functionname: MPEG4ENC_SetInbandPceTimeInterval + description: set update interval for explicit in band PCE transmission + sendPceTimeInterval > 0 -> regular time interval in seconds + sendPceTimeInterval == 0 -> send no PCE (MPEG-2 only) + sendPceTimeInterval < 0 -> send PCE only the 1st frame + returns: MPEG4ENC_NO_ERROR on success, an appropriate error code else + + ------------------------------------------------------------------------------*/ +MPEG4ENC_ERROR MPEG4ENCAPI +MPEG4ENC_SetInbandPceTimeInterval(const HANDLE_MPEG4ENC_ENCODER hMp4Enc, + const float sendPceTimeInterval); + +/*----------------------------------------------------------------------------- + + functionname: MPEG4ENC_SetAdtsPceTimeInterval + description: set update interval for explicit channel signaling via PCE in + case of ADTS transport stream, MPEG-2/4 AAC and + channel_configuration == 0 + sendPceTimeInterval > 0 -> regular time interval in seconds + sendPceTimeInterval == 0 -> send no PCE (MPEG-2 only) + sendPceTimeInterval < 0 -> send PCE only the 1st frame + returns: MPEG4ENC_NO_ERROR on success, an appropriate error code else + + ------------------------------------------------------------------------------*/ +MPEG4ENC_ERROR MPEG4ENCAPI +MPEG4ENC_SetAdtsPceTimeInterval( + const HANDLE_MPEG4ENC_ENCODER hMp4Enc, + const float sendPceTimeInterval + ); + +/*----------------------------------------------------------------------------- + + functionname: MPEG4ENC_MpsSetSscTimeInterval + description: set update interval for transmission of SpatialSpecificConfig + (SSC) in case of encoding using MPEG Surround + sendSscTimeInterval > 0 -> regular time interval in seconds + sendSscTimeInterval == 0 -> send SSC every (MPEGS) frame + sendSscTimeInterval < 0 -> send SSC only the 1st frame + - in combination with MPEGS only + - MPEGS payload mode has to be MP4_MPEGS_PAYLOAD_EMBED, because + otherwise the SSC is transmitted in a seperate ESD, which has + to be handled by the user + returns: MPEG4ENC_NO_ERROR on success, an appropriate error code else + + ------------------------------------------------------------------------------*/ +MPEG4ENC_ERROR MPEG4ENCAPI +MPEG4ENC_MpsSetSscTimeInterval( + const HANDLE_MPEG4ENC_ENCODER hMp4Enc, + const float sendSscTimeInterval + ); + +/*----------------------------------------------------------------------------- + + functionname: MPEG4ENC_MpsSetDownmixConfig + description: set MPEG Surround Downmix Configuration + returns: MPEG4ENC_NO_ERROR on success, an appropriate error code else + + ------------------------------------------------------------------------------*/ +MPEG4ENC_ERROR MPEG4ENCAPI +MPEG4ENC_MpsSetDownmixConfig( + const HANDLE_MPEG4ENC_ENCODER hMp4Enc, + const MPEG4ENC_MPEGS_DOWNMIX_CONFIG mpegsDownmixCfg + ); + +/*----------------------------------------------------------------------------- + + functionname: MPEG4ENC_MpsSetPayloadMode + description: set MPEG Surround Payload Transmission Mode + returns: MPEG4ENC_NO_ERROR on success, an appropriate error code else + + ------------------------------------------------------------------------------*/ +MPEG4ENC_ERROR MPEG4ENCAPI +MPEG4ENC_MpsSetPayloadMode( + const HANDLE_MPEG4ENC_ENCODER hMp4Enc, + const MPEG4ENC_MPEGS_PAYLOAD_MODE mpegsPayloadMode + ); + +/*----------------------------------------------------------------------------- + + functionname: MPEG4ENC_SetThreadingMode (deprecated) + description: sets threading mode to single threaded, multiple threaded + returns: MPEG4ENC_NO_ERROR on success, an appropriate error code else + + Please note that this function is deprecated and should not be used any more. + + ------------------------------------------------------------------------------*/ +MPEG4ENC_ERROR MPEG4ENCAPI +MPEG4ENC_SetThreadingMode(const HANDLE_MPEG4ENC_ENCODER hMp4Enc, + const MPEG4ENC_THREADING_MODE threadingMode); + + +/*----------------------------------------------------------------------------- + + functionname: MPEG4ENC_GetError + description: get error text + returns: pointer to an error text + + ------------------------------------------------------------------------------*/ +char* MPEG4ENCAPI +MPEG4ENC_GetError(MPEG4ENC_ERROR error); + +/*----------------------------------------------------------------------------- + + functionname: MPEG4ENC_SetBandwidth + description: set bandwidth by user, returns with actual used bandwidth + returns: MPEG4ENC_NO_ERROR on success, an appropriate error code else + + ------------------------------------------------------------------------------*/ +MPEG4ENC_ERROR MPEG4ENCAPI +MPEG4ENC_SetBandwidth(const HANDLE_MPEG4ENC_ENCODER hMp4Enc, + const float proposedBandwidth, + float* usedBandwidth); + +/*----------------------------------------------------------------------------- + + functionname: MPEG4ENC_SetStereoPrePro + description: set bandwidth by user, returns with actual used bandwidth + returns: MPEG4ENC_NO_ERROR on success, an appropriate error code else + + ------------------------------------------------------------------------------*/ +MPEG4ENC_ERROR MPEG4ENCAPI +MPEG4ENC_SetStereoPrePro(const HANDLE_MPEG4ENC_ENCODER hMp4Enc, + const int enableStereoPrePro); + + +/*----------------------------------------------------------------------------- + + functionname: MPEG4ENC_SetLatmSmcTimeInterval + description: set update interval for appearance of stream mux config in + case of LOAS/LATM transport stream + sendSmcTimeInterval > 0 -> regular time interval (every n-th frame, default: 8) + sendSmcTimeInterval == 0 -> send no inband StreamMuxConfig + sendSmcTimeInterval < 0 -> send StreamMuxConfig only the 1st frame + returns: MPEG4ENC_NO_ERROR on success, an appropriate error code else + + ------------------------------------------------------------------------------*/ +MPEG4ENC_ERROR MPEG4ENCAPI +MPEG4ENC_SetLatmSmcTimeInterval( + const HANDLE_MPEG4ENC_ENCODER hMp4Enc, + const int sendSmcTimeInterval + ); + +/*----------------------------------------------------------------------------- + + functionname: MPEG4ENC_SetLatmNrOfSubframes + description: set the nr of subframes per latm frame in + case of LOAS/LATM transport stream + nrOfSubframes < 1 -> reserved + nrOfSubframes >= 1 -> use 'nrOfSubframes' + + optional, default is 1 + + returns: MPEG4ENC_NO_ERROR on success, an appropriate error code else + + ------------------------------------------------------------------------------*/ +MPEG4ENC_ERROR MPEG4ENCAPI +MPEG4ENC_SetLatmNrOfSubframes( + const HANDLE_MPEG4ENC_ENCODER hMp4Enc, + const int nrOfSubframes + ); + + +/*----------------------------------------------------------------------------- + + functionname: MPEG4ENC_GetLatmSmc + description: returns pointer to and size of LATM stream mux config + + returns: MPEG4ENC_NO_ERROR on success, an appropriate error code else + + ------------------------------------------------------------------------------*/ +MPEG4ENC_ERROR MPEG4ENCAPI +MPEG4ENC_GetLatmSmc( + const HANDLE_MPEG4ENC_ENCODER hMp4Enc, + unsigned char** buffer, + int* nBits + ); + + +/*--------------------------------------------------------------------------- + + functionname: MPEG4ENC_Submit_Metadata + description: submit metadata + returns: MPEG4ENC_ERROR (error code) + + ---------------------------------------------------------------------------*/ +MPEG4ENC_ERROR MPEG4ENCAPI +MPEG4ENC_Submit_Metadata( + const HANDLE_MPEG4ENC_ENCODER hMpeg4Enc, /* an encoder handle */ + const HANDLE_MPEG4ENC_METADATA pMetadata /* pointer to metadata */ + ); + + +/*--------------------------------------------------------------------------- + + functionname: MPEG4ENC_Submit_ExtMetadata + description: submit metadata + returns: MPEG4ENC_ERROR (error code) + + ---------------------------------------------------------------------------*/ +MPEG4ENC_ERROR MPEG4ENCAPI +MPEG4ENC_Submit_ExtMetadata( + const HANDLE_MPEG4ENC_ENCODER hMpeg4Enc, /* an encoder handle */ + const HANDLE_MPEG4ENC_EXTMETADATA pExtMetadata /* pointer to extended metadata */ + ); + + + +/*--------------------------------------------------------------------------- + + functionname: MPEG4ENC_BRInfo + description : Provides the compatible bitrate range + returns: MPEG4ENC_ERROR (error code) + + ---------------------------------------------------------------------------*/ +MPEG4ENC_ERROR MPEG4ENCAPI +MPEG4ENC_BRInfo ( + const AUD_OBJ_TYP aot, + const MPEG4ENC_CH_MODE chMode, + const int samplingRate, + int* brMin, + int* brMax); + + +/*--------------------------------------------------------------------------- + + functionname: MPEG4ENC_SetSbrSpeechConfig + description : Sets SBR Speech config flag + returns: MPEG4ENC_ERROR (error code) + + ---------------------------------------------------------------------------*/ +MPEG4ENC_ERROR MPEG4ENCAPI +MPEG4ENC_SetSbrSpeechConfig( + const HANDLE_MPEG4ENC_ENCODER hMp4Enc, + unsigned int flag + ); + + +/*--------------------------------------------------------------------------- + + functionname: MPEG4ENC_SetSbrTimeDiffCoding + description : Sets SBR time differential coding (TDC); + flag==0: Do not use TDC + flag==1: Use TDC + returns: MPEG4ENC_ERROR (error code) + + ---------------------------------------------------------------------------*/ +MPEG4ENC_ERROR MPEG4ENCAPI +MPEG4ENC_SetSbrTimeDiffCoding( + const HANDLE_MPEG4ENC_ENCODER hMp4Enc, + unsigned int flag + ); + + +/*--------------------------------------------------------------------------- + + functionname: MPEG4ENC_SetUseIntensityStereo + description : Sets intensity stereo coding (IS); + flag==1: Use IS (default) + flag==0: Do not use IS + returns: MPEG4ENC_ERROR (error code) + + ---------------------------------------------------------------------------*/ +MPEG4ENC_ERROR MPEG4ENCAPI +MPEG4ENC_SetUseIntensityStereo( + const HANDLE_MPEG4ENC_ENCODER hMp4Enc, + unsigned int flag + ); + + +/*----------------------------------------------------------------------------- + + functionname: MPEG4ENC_SendChCfgZero + description: will always use channel config zero + pce although a standard + channel config could be signalled + returns: MPEG4ENC_NO_ERROR on success, an appropriate error code else + + ------------------------------------------------------------------------------*/ +MPEG4ENC_ERROR MPEG4ENCAPI +MPEG4ENC_SendChCfgZero( + const HANDLE_MPEG4ENC_ENCODER hMp4Enc + ); + + + +/*----------------------------------------------------------------------------- + + functionname: MPEG4ENC_SetSyncFrame + description: will generate a synchaable frame + returns: MPEG4ENC_NO_ERROR on success, an appropriate error code else + + ------------------------------------------------------------------------------*/ +MPEG4ENC_ERROR MPEG4ENCAPI +MPEG4ENC_SetSyncFrame( + const HANDLE_MPEG4ENC_ENCODER hMp4Enc + ); + + +/*----------------------------------------------------------------------------- + + functionname: MPEG4ENC_SetSyncFrameWithType + description: will generate a synchaable frame + returns: MPEG4ENC_NO_ERROR on success, an appropriate error code else + + ------------------------------------------------------------------------------*/ +MPEG4ENC_ERROR MPEG4ENCAPI +MPEG4ENC_SetSyncFrameWithType( + const HANDLE_MPEG4ENC_ENCODER hMp4Enc, + const MPEG4ENC_SYNCFRAME_TYPES syncType + ); + + +/*--------------------------------------------------------------------------- + + functionname: MPEG4ENC_InitDASH + description : Configure encoder for DASH mode + returns: MPEG4ENC_ERROR (error code) + + ---------------------------------------------------------------------------*/ +MPEG4ENC_ERROR MPEG4ENCAPI +MPEG4ENC_InitDASH( + const HANDLE_MPEG4ENC_ENCODER hMp4Enc + ); + + +/*--------------------------------------------------------------------------- + + functionname: MPEG4ENC_SetTransportType + description : Reconfigure Transport Format + returns: MPEG4ENC_ERROR (error code) + + ---------------------------------------------------------------------------*/ +MPEG4ENC_ERROR MPEG4ENCAPI +MPEG4ENC_SetTransportType( + const HANDLE_MPEG4ENC_ENCODER hMp4Enc, + const MPEG4ENC_TRANSPORT_TYPE transportType + ); + + +/*--------------------------------------------------------------------------- + + functionname: MPEG4ENC_SetMPEG4Flag + description : Reconfigure MPEG-2/4 compliance + returns: MPEG4ENC_ERROR (error code) + + ---------------------------------------------------------------------------*/ +MPEG4ENC_ERROR MPEG4ENCAPI +MPEG4ENC_SetMPEG4Flag( + const HANDLE_MPEG4ENC_ENCODER hMp4Enc, + const int mpeg4Flag + ); + + +#ifdef SUPPORT_UPMIX +/*--------------------------------------------------------------------------- + + functionname: MPEG4ENC_SetSXProUpmixParameter + description : Sets SXPro parameters; + umxMode: Upmix workmode + umxLFE: Upmix LFE on/off + returns: MPEG4ENC_ERROR (error code) + + ---------------------------------------------------------------------------*/ +MPEG4ENC_ERROR MPEG4ENCAPI +MPEG4ENC_SetSXProUpmixParameter( + const HANDLE_MPEG4ENC_ENCODER hMp4Enc, + const MP4_SXPRO_UPMIX_WORKMODE umxMode, + const MP4_SXPRO_UPMIX_LFE umxLFE + ); +#endif + + +/*---------------------------------------------------------------------------*/ +#if defined(WIN32) || defined(WIN64) +#pragma pack(pop) +#endif + +/*-------------------------------------------------------------------------*/ + +#ifdef __cplusplus +} +#endif + +#endif /* _mp4FastAAClib_h_ */ diff --git a/Src/Plugins/Encoder/enc_fhgaac/mp4FastAAClib.lib b/Src/Plugins/Encoder/enc_fhgaac/mp4FastAAClib.lib new file mode 100644 index 00000000..842651eb Binary files /dev/null and b/Src/Plugins/Encoder/enc_fhgaac/mp4FastAAClib.lib differ diff --git a/Src/Plugins/Encoder/enc_fhgaac/preferences.cpp b/Src/Plugins/Encoder/enc_fhgaac/preferences.cpp new file mode 100644 index 00000000..2c853b1e --- /dev/null +++ b/Src/Plugins/Encoder/enc_fhgaac/preferences.cpp @@ -0,0 +1,122 @@ +#include "preferences.h" +#include "resource.h" +#include "../nu/ComboBox.h" +#include "../nu/Slider.h" +#include "../Agave/Language/api_language.h" +#include +/* note to maintainers: +the combobox code assumes that the options are in numerical order starting from 0 +if this assumption ever changes, you'll need to map AAC_MODE_* and AAC_PROFILE_* to/from indices +there are some asserts to test this (which will only test in debug mode of course) +*/ +const int bitrate_slider_precision = 4; + +static void SetSliderBitrate(HWND hwnd, unsigned int bitrate) +{ + Slider preset_slider(hwnd, IDC_SLIDER_PRESET); + int low = (unsigned int)SendMessage(GetDlgItem(hwnd,IDC_SLIDER_PRESET), TBM_GETRANGEMIN, 0, 0); + int position = (bitrate - low)/bitrate_slider_precision + low; + preset_slider.SetPosition(position, Slider::REDRAW); +} + +unsigned int GetSliderBitrate(HWND hwnd) +{ + int low = (unsigned int)SendMessage(GetDlgItem(hwnd,IDC_SLIDER_PRESET), TBM_GETRANGEMIN, 0, 0); + int position = SendMessage(GetDlgItem(hwnd,IDC_SLIDER_PRESET),TBM_GETPOS,0,0); + unsigned int bitrate = (position - low)*bitrate_slider_precision + low; + return bitrate; +} + +void UpdateInfo(HWND hwnd, const AACConfiguration *config) +{ + wchar_t temp[128] = {0}; + + switch(AACConfig_GetAOT(config)) + { + case AUD_OBJ_TYP_LC: + SetDlgItemText(hwnd, IDC_INFO_MODE, L"MPEG-4 AAC LC"); + break; + case AUD_OBJ_TYP_HEAAC: + SetDlgItemText(hwnd, IDC_INFO_MODE, L"MPEG-4 HE-AAC"); + break; + case AUD_OBJ_TYP_PS: + SetDlgItemText(hwnd, IDC_INFO_MODE, L"MPEG-4 HE-AACv2"); + break; + } + if (config->mode == AAC_MODE_VBR) + { + StringCbPrintfW(temp, sizeof(temp), WASABI_API_LNGSTRINGW(IDS_VBR_PRESET), config->preset, AACConfig_GetBitrate(config, 2 /* TODO! */)/1000); + SetDlgItemText(hwnd, IDC_INFO_BITRATE, temp); + } + else + { + StringCbPrintfW(temp, sizeof(temp), WASABI_API_LNGSTRINGW(IDS_CBR_PRESET), AACConfig_GetBitrate(config, 2 /* TODO! */)/1000); + SetDlgItemText(hwnd, IDC_INFO_BITRATE, temp); + } +} + +/* call this to update the UI according to the configuration values */ +void UpdateUI(HWND hwnd, AACConfigurationFile *config) +{ + wchar_t temp[128] = {0}; + ComboBox mode_list(hwnd, IDC_MODE); + ComboBox profile_list(hwnd, IDC_PROFILE); + + config->changing = true; + Slider preset_slider(hwnd, IDC_SLIDER_PRESET); + if (config->config.mode == AAC_MODE_VBR) + { + preset_slider.SetRange(1, 6, Slider::REDRAW); + preset_slider.SetTickFrequency(1); + preset_slider.SetPosition(config->config.preset, Slider::REDRAW); + SetDlgItemText(hwnd, IDC_STATIC_PRESET, WASABI_API_LNGSTRINGW(IDS_PRESET)); + StringCbPrintf(temp, sizeof(temp), L"%u", config->config.preset); + SetDlgItemText(hwnd, IDC_EDIT_PRESET, temp); + profile_list.SelectItem(AAC_PROFILE_AUTOMATIC); + EnableWindow(profile_list, FALSE); + ShowWindow(GetDlgItem(hwnd, IDC_KBPS), SW_HIDE); + } + else /* implied: if (config->config.mode == AAC_MODE_CBR) */ + { + int low, high; + AACConfig_GetBitrateRange(&config->config, &low, &high); + low = ((low/1000+3)&~3); /* convert to kbps round up to nearest multiple of 4 */ + high = ((high/1000)&~3); /* convert to kbps round down to nearest multiple of 4 */ + + int slider_high = low + (high - low)/bitrate_slider_precision; + preset_slider.SetRange(low, slider_high, Slider::REDRAW); + if (config->config.profile >= AAC_PROFILE_HE) + preset_slider.SetTickFrequency(1); + else + preset_slider.SetTickFrequency(4); + SetSliderBitrate(hwnd, config->config.bitrate); + config->config.bitrate = GetSliderBitrate(hwnd); + SetDlgItemText(hwnd, IDC_STATIC_PRESET, WASABI_API_LNGSTRINGW(IDS_BITRATE)); + StringCbPrintf(temp, sizeof(temp), L"%u", config->config.bitrate); + SetDlgItemText(hwnd, IDC_EDIT_PRESET, temp); + profile_list.SelectItem(config->config.profile); + EnableWindow(profile_list, TRUE); + ShowWindow(GetDlgItem(hwnd, IDC_KBPS), SW_SHOWNA); + } + config->changing = false; +} + +int Preferences_GetEncoderVersion(char *version_string, size_t cch) +{ + char version[128] = {0}; + MPEG4ENC_GetVersionInfo(version, sizeof(version)/sizeof(*version)); + char *p = version; + while (p && *p) + { + if (*p != '.' && (*p < '0' || *p > '9')) + { + *p = 0; + break; + } + p++; + } + + StringCchPrintfA(version_string, cch, WASABI_API_LNGSTRING(IDS_VERSION), version); + + return 0; +} \ No newline at end of file diff --git a/Src/Plugins/Encoder/enc_fhgaac/preferences.h b/Src/Plugins/Encoder/enc_fhgaac/preferences.h new file mode 100644 index 00000000..0ed9cabc --- /dev/null +++ b/Src/Plugins/Encoder/enc_fhgaac/preferences.h @@ -0,0 +1,18 @@ +#pragma once +#include +#include "config.h" + +extern HINSTANCE enc_fhg_HINST; +INT_PTR CALLBACK Preferences_MP4_DlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); +INT_PTR CALLBACK Preferences_ADTS_DlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); + +/* common to both */ +extern const int bitrate_slider_precision; +void UpdateInfo(HWND hwnd, const AACConfiguration *config); +void UpdateUI(HWND hwnd, AACConfigurationFile *config); +int Preferences_GetEncoderVersion(char *version_string, size_t cch); +unsigned int GetSliderBitrate(HWND hwnd); + +#include +extern api_application *applicationApi; +#define WASABI_API_APP applicationApi \ No newline at end of file diff --git a/Src/Plugins/Encoder/enc_fhgaac/preferences_adts.cpp b/Src/Plugins/Encoder/enc_fhgaac/preferences_adts.cpp new file mode 100644 index 00000000..88231e5a --- /dev/null +++ b/Src/Plugins/Encoder/enc_fhgaac/preferences_adts.cpp @@ -0,0 +1,124 @@ +#include "../Agave/Language/api_language.h" +#include "config.h" +#include "preferences.h" +#include "../Winamp/wa_ipc.h" +#include +#include +#include "resource.h" +#include "link_control.h" +#include "../nu/ComboBox.h" +#include "../nu/Slider.h" +#include + +extern HWND winampwnd; + +INT_PTR CALLBACK Preferences_ADTS_DlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch(msg) + { + case WM_INITDIALOG: + { + AACConfigurationFile *cfg = (AACConfigurationFile *)lParam; + cfg->changing = false; + SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)cfg); + + + if (cfg->shoutcast == 1) + { + ShowWindow(GetDlgItem(hwnd, IDC_WARNING), SW_HIDE); + } + + ComboBox profile_list(hwnd, IDC_PROFILE); + profile_list.AddString(WASABI_API_LNGSTRINGW(IDS_AUTOMATIC), AAC_PROFILE_AUTOMATIC); assert(AAC_PROFILE_AUTOMATIC == 0); + profile_list.AddString(L"AAC LC", AAC_PROFILE_LC); assert(AAC_PROFILE_LC == 1); + profile_list.AddString(L"HE-AAC", AAC_PROFILE_HE); assert(AAC_PROFILE_HE == 2); + profile_list.AddString(L"HE-AAC v2", AAC_PROFILE_HE_V2); assert(AAC_PROFILE_HE_V2 == 3); + + UpdateUI(hwnd, cfg); + UpdateInfo(hwnd, &cfg->config); + link_startsubclass(hwnd, IDC_URL); + link_startsubclass(hwnd, IDC_LOGO); + link_startsubclass(hwnd, IDC_HELPLINK); + + char temp[128] = {0}; + if (Preferences_GetEncoderVersion(temp, sizeof(temp)/sizeof(*temp)) == 0) + { + SetDlgItemTextA(hwnd, IDC_VERSION, temp); + } + + // this will make sure that we've got the aacplus logo shown even when using a localised version + SendDlgItemMessage(hwnd,IDC_LOGO,STM_SETIMAGE,IMAGE_BITMAP, (LPARAM)LoadImage(WASABI_API_ORIG_HINST?WASABI_API_ORIG_HINST:enc_fhg_HINST,MAKEINTRESOURCE(IDB_FHGLOGO),IMAGE_BITMAP,0,0,LR_SHARED)); + } + break; + + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDC_HELPLINK: + SendMessage(winampwnd, WM_WA_IPC, (WPARAM)"http://help.winamp.com/", IPC_OPEN_URL); + break; + case IDC_LOGO: + case IDC_URL: + SendMessage(winampwnd, WM_WA_IPC, (WPARAM)"http://www.iis.fraunhofer.de/en/bf/amm", IPC_OPEN_URL); + break; + + case IDC_EDIT_PRESET: + if (HIWORD(wParam) == EN_CHANGE) + { + AACConfigurationFile *cfg = (AACConfigurationFile *)GetWindowLongPtr(hwnd, GWLP_USERDATA); + if (!cfg->changing) + { + BOOL s=0; + unsigned int t=GetDlgItemInt(hwnd,IDC_EDIT_PRESET,&s,0); + if (s) + { + cfg->config.bitrate = t; + UpdateUI(hwnd, cfg); + UpdateInfo(hwnd, &cfg->config); + AACConfig_Save(cfg); + } + } + } + break; + + case IDC_PROFILE: + if (HIWORD(wParam) == CBN_SELCHANGE) + { + AACConfigurationFile *cfg = (AACConfigurationFile *)GetWindowLongPtr(hwnd, GWLP_USERDATA); + cfg->config.profile = (unsigned int)ComboBox(hwnd, IDC_PROFILE).GetSelection(); + UpdateUI(hwnd, cfg); + UpdateInfo(hwnd, &cfg->config); + AACConfig_Save(cfg); + } + break; + } + break; + + case WM_HSCROLL: + if (GetDlgItem(hwnd, IDC_SLIDER_PRESET) == (HWND)lParam) + { + wchar_t temp[128] = {0}; + AACConfigurationFile *cfg = (AACConfigurationFile *)GetWindowLongPtr(hwnd, GWLP_USERDATA); + + cfg->config.bitrate = GetSliderBitrate(hwnd); + StringCbPrintf(temp, sizeof(temp), L"%u", cfg->config.bitrate); + SetDlgItemText(hwnd, IDC_EDIT_PRESET, temp); + UpdateInfo(hwnd, &cfg->config); + AACConfig_Save(cfg); + } + break; + } + + link_handledraw(hwnd,msg,wParam,lParam); + + const int controls[] = + { + IDC_SLIDER_PRESET, + }; + if (FALSE != WASABI_API_APP->DirectMouseWheel_ProcessDialogMessage(hwnd, msg, wParam, lParam, controls, ARRAYSIZE(controls))) + { + return 1; + } + + return 0; +} \ No newline at end of file diff --git a/Src/Plugins/Encoder/enc_fhgaac/preferences_mp4.cpp b/Src/Plugins/Encoder/enc_fhgaac/preferences_mp4.cpp new file mode 100644 index 00000000..7f06dfb0 --- /dev/null +++ b/Src/Plugins/Encoder/enc_fhgaac/preferences_mp4.cpp @@ -0,0 +1,152 @@ +#include "../Agave/Language/api_language.h" +#include "config.h" +#include "preferences.h" +#include "../Winamp/wa_ipc.h" +#include +#include +#include "resource.h" +#include "link_control.h" +#include "../nu/ComboBox.h" +#include "../nu/Slider.h" +#include + +extern HWND winampwnd; + +INT_PTR CALLBACK Preferences_MP4_DlgProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch(msg) + { + case WM_INITDIALOG: + { + AACConfigurationFile *cfg = (AACConfigurationFile *)lParam; + cfg->changing = false; + SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)cfg); + + ComboBox mode_list(hwnd, IDC_MODE); + mode_list.AddString(WASABI_API_LNGSTRINGW(IDS_VBR), AAC_MODE_VBR); assert(AAC_MODE_VBR == 0); + mode_list.AddString(WASABI_API_LNGSTRINGW(IDS_CBR), AAC_MODE_CBR); assert(AAC_MODE_CBR == 1); + mode_list.SelectItem(cfg->config.mode); + + ComboBox profile_list(hwnd, IDC_PROFILE); + profile_list.AddString(WASABI_API_LNGSTRINGW(IDS_AUTOMATIC), AAC_PROFILE_AUTOMATIC); assert(AAC_PROFILE_AUTOMATIC == 0); + profile_list.AddString(L"AAC LC", AAC_PROFILE_LC); assert(AAC_PROFILE_LC == 1); + profile_list.AddString(L"HE-AAC", AAC_PROFILE_HE); assert(AAC_PROFILE_HE == 2); + profile_list.AddString(L"HE-AAC v2", AAC_PROFILE_HE_V2); assert(AAC_PROFILE_HE_V2 == 3); + + UpdateUI(hwnd, cfg); + UpdateInfo(hwnd, &cfg->config); + link_startsubclass(hwnd, IDC_URL); + link_startsubclass(hwnd, IDC_LOGO); + link_startsubclass(hwnd, IDC_HELPLINK); + + char temp[128] = {0}; + if (Preferences_GetEncoderVersion(temp, sizeof(temp)/sizeof(*temp)) == 0) + { + SetDlgItemTextA(hwnd, IDC_VERSION, temp); + } + + // this will make sure that we've got the aacplus logo shown even when using a localised version + SendDlgItemMessage(hwnd,IDC_LOGO,STM_SETIMAGE,IMAGE_BITMAP, (LPARAM)LoadImage(WASABI_API_ORIG_HINST?WASABI_API_ORIG_HINST:enc_fhg_HINST,MAKEINTRESOURCE(IDB_FHGLOGO),IMAGE_BITMAP,0,0,LR_SHARED)); + } + break; + + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDC_HELPLINK: + SendMessage(winampwnd, WM_WA_IPC, (WPARAM)"http://help.winamp.com/", IPC_OPEN_URL); + break; + case IDC_LOGO: + case IDC_URL: + SendMessage(winampwnd, WM_WA_IPC, (WPARAM)"http://www.iis.fraunhofer.de/en/bf/amm", IPC_OPEN_URL); + break; + + + case IDC_MODE: + if (HIWORD(wParam) == CBN_SELCHANGE) + { + AACConfigurationFile *cfg = (AACConfigurationFile *)GetWindowLongPtr(hwnd, GWLP_USERDATA); + cfg->config.mode = (unsigned int)ComboBox(hwnd, IDC_MODE).GetSelection(); + UpdateUI(hwnd, cfg); + UpdateInfo(hwnd, &cfg->config); + AACConfig_Save(cfg); + } + break; + + case IDC_EDIT_PRESET: + if (HIWORD(wParam) == EN_CHANGE) + { + AACConfigurationFile *cfg = (AACConfigurationFile *)GetWindowLongPtr(hwnd, GWLP_USERDATA); + if (!cfg->changing) + { + BOOL s=0; + unsigned int t=GetDlgItemInt(hwnd,IDC_EDIT_PRESET,&s,0); + if (s) + { + if (cfg->config.mode == AAC_MODE_VBR) + { + cfg->config.preset= t; + } + else + { + cfg->config.bitrate = t; + } + UpdateUI(hwnd, cfg); + UpdateInfo(hwnd, &cfg->config); + AACConfig_Save(cfg); + } + } + } + break; + + case IDC_PROFILE: + if (HIWORD(wParam) == CBN_SELCHANGE) + { + AACConfigurationFile *cfg = (AACConfigurationFile *)GetWindowLongPtr(hwnd, GWLP_USERDATA); + cfg->config.profile = (unsigned int)ComboBox(hwnd, IDC_PROFILE).GetSelection(); + UpdateUI(hwnd, cfg); + UpdateInfo(hwnd, &cfg->config); + AACConfig_Save(cfg); + } + break; + } + break; + + case WM_HSCROLL: + if (GetDlgItem(hwnd, IDC_SLIDER_PRESET) == (HWND)lParam) + { + wchar_t temp[128] = {0}; + AACConfigurationFile *cfg = (AACConfigurationFile *)GetWindowLongPtr(hwnd, GWLP_USERDATA); + if (cfg->config.mode == AAC_MODE_VBR) + { + cfg->config.preset = (unsigned int)SendMessage(GetDlgItem(hwnd,IDC_SLIDER_PRESET),TBM_GETPOS,0,0); + StringCbPrintf(temp, sizeof(temp), L"%u", cfg->config.preset); + SetDlgItemText(hwnd, IDC_EDIT_PRESET, temp); + UpdateInfo(hwnd, &cfg->config); + AACConfig_Save(cfg); + } + else + { + cfg->config.bitrate = GetSliderBitrate(hwnd); + StringCbPrintf(temp, sizeof(temp), L"%u", cfg->config.bitrate); + SetDlgItemText(hwnd, IDC_EDIT_PRESET, temp); + UpdateInfo(hwnd, &cfg->config); + AACConfig_Save(cfg); + } + } + break; + } + + link_handledraw(hwnd,msg,wParam,lParam); + + const int controls[] = + { + IDC_SLIDER_PRESET, + }; + if (FALSE != WASABI_API_APP->DirectMouseWheel_ProcessDialogMessage(hwnd, msg, wParam, lParam, controls, ARRAYSIZE(controls))) + { + return 1; + } + + return 0; +} \ No newline at end of file diff --git a/Src/Plugins/Encoder/enc_fhgaac/resource.h b/Src/Plugins/Encoder/enc_fhgaac/resource.h new file mode 100644 index 00000000..9904233d --- /dev/null +++ b/Src/Plugins/Encoder/enc_fhgaac/resource.h @@ -0,0 +1,46 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by enc_fhgaac.rc +// +#define IDS_ENC_FHGAAC_DESC 1 +#define VS_VERSION_INFO 1 +#define IDS_ENC_FHGAAC_VER 2 +#define IDS_VBR_PRESET 2 +#define IDS_CBR_PRESET 3 +#define IDS_VBR 4 +#define IDS_CBR 5 +#define IDS_AUTOMATIC 6 +#define IDS_BITRATE 7 +#define IDS_PRESET 8 +#define IDD_PREFERENCES_MP4 9 +#define IDS_VERSION 9 +#define IDD_PREFERENCES_ADTS 10 +#define IDC_ENC_ADTS_DESC 10 +#define IDB_FHGLOGO 104 +#define IDC_MODE 1002 +#define IDC_PROFILE 1003 +#define IDC_STATIC_PROFILE 1004 +#define IDC_STATIC_PRESET 1006 +#define IDC_SLIDER_PRESET 1007 +#define IDC_EDIT_PRESET 1008 +#define IDC_INFO_MODE 1009 +#define IDC_INFO_BITRATE 1010 +#define IDC_INFO_WARNING 1011 +#define IDC_KBPS 1014 +#define IDB_LOGO 1015 +#define IDC_LOGO 1015 +#define IDC_URL 1017 +#define IDC_HELPLINK 1018 +#define IDC_VERSION 1019 +#define IDC_WARNING 1020 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 105 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1021 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/Src/Plugins/Encoder/enc_fhgaac/version.rc2 b/Src/Plugins/Encoder/enc_fhgaac/version.rc2 new file mode 100644 index 00000000..f94ece26 --- /dev/null +++ b/Src/Plugins/Encoder/enc_fhgaac/version.rc2 @@ -0,0 +1,39 @@ + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// +#include "../../../Winamp/buildType.h" +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,8,0 + PRODUCTVERSION WINAMP_PRODUCTVER + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Winamp SA" + VALUE "FileDescription", "Winamp Encoder Plug-in" + VALUE "FileVersion", "1,0,8,0" + VALUE "InternalName", "Nullsoft AAC Encoder" + VALUE "LegalCopyright", "Copyright © 2011-2019 Winamp SA" + VALUE "LegalTrademarks", "Nullsoft and Winamp are trademarks of Winamp SA" + VALUE "OriginalFilename", "enc_fhgaac.dll" + VALUE "ProductName", "Winamp" + VALUE "ProductVersion", STR_WINAMP_PRODUCTVER + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/Src/Plugins/Encoder/enc_flac/AudioCoderFlac.cpp b/Src/Plugins/Encoder/enc_flac/AudioCoderFlac.cpp new file mode 100644 index 00000000..38bb124e --- /dev/null +++ b/Src/Plugins/Encoder/enc_flac/AudioCoderFlac.cpp @@ -0,0 +1,203 @@ +#include "AudioCoderFlac.h" +#include +#include + +AudioCoderFlac::AudioCoderFlac(unsigned int nch, unsigned int bps, unsigned int samplerate, unsigned int compression) +{ + /* initialize stuff first so we can clean up safely if things go wrong */ + finished = false; + finishedBytes = 0; + padding = 0; + encoder = 0; + win32State.bytesWritten = 0; + win32State.handle = INVALID_HANDLE_VALUE; + tempFile[0]=0; + + wchar_t tempPath[MAX_PATH-14] = {0}; + GetTempPath(MAX_PATH-14, tempPath); + GetTempFileName(tempPath, L"wfl", 0, tempFile); + win32State.handle = CreateFile(tempFile, GENERIC_READ|GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0); + + if (win32State.handle != INVALID_HANDLE_VALUE) + { + this->nch = nch; + this->bps = bps; + encoder = FLAC__stream_encoder_new(); + + if (!encoder) + return; + + // set stream info + if (!FLAC__stream_encoder_set_channels(encoder, nch) + || !FLAC__stream_encoder_set_bits_per_sample(encoder, bps) + || !FLAC__stream_encoder_set_sample_rate(encoder, samplerate) + || !FLAC__stream_encoder_set_total_samples_estimate(encoder, 0) + || !FLAC__stream_encoder_set_compression_level(encoder, compression) + || !FLAC__stream_encoder_set_blocksize(encoder, 0)) + { + FLAC__stream_encoder_delete(encoder); + encoder=0; + return; + } + // TODO: set any more config stuff + + // TODO: seektable? + //FLAC__StreamMetadata *seektable = FLAC__metadata_object_new(FLAC__METADATA_TYPE_SEEKTABLE); + + padding = FLAC__metadata_object_new(FLAC__METADATA_TYPE_PADDING); + if (padding) + { + padding->length = 16384; // TODO: configurable padding size + if (!FLAC__stream_encoder_set_metadata(encoder, &padding, 1)) + { + FLAC__stream_encoder_delete(encoder); + encoder=0; + return; + } + } + + if (FLAC__stream_encoder_init_stream(encoder, Win32_Write, Win32_Seek, Win32_Tell, NULL, &win32State) != FLAC__STREAM_ENCODER_INIT_STATUS_OK) + { + FLAC__stream_encoder_delete(encoder); + encoder=0; + return; + } + } +} + +bool AudioCoderFlac::OK() +{ + if (!encoder) + return false; + + return FLAC__stream_encoder_get_state(encoder) == FLAC__STREAM_ENCODER_OK; +} + +AudioCoderFlac::~AudioCoderFlac() +{ + if (encoder) + FLAC__stream_encoder_delete(encoder); + + if (padding) + FLAC__metadata_object_delete(padding); + + if (win32State.handle != INVALID_HANDLE_VALUE) + CloseHandle(win32State.handle); + +} + +static void Copy8(FLAC__int32 *buffer, void *inputData, int numSamples) +{ + uint8_t *in = (uint8_t *)inputData; + + for (int i=0;i65536) + numSamples = 65536; + + switch (bps) + { + case 8: + Copy8(buffer, in, numSamples); + break; + case 16: + Copy16(buffer, in, numSamples); + break; + case 24: + Copy24(buffer, in, numSamples); + break; + case 32: + Copy32(buffer, in, numSamples); + break; + } + + FLAC__bool result = FLAC__stream_encoder_process_interleaved(encoder, buffer, numSamples/nch); + + if (result) + { + *in_used = numSamples*(bps/8); + return (int)(win32State.bytesWritten - startBytes); + } + + return 0; +} + +void AudioCoderFlac::PrepareToFinish() +{ + FLAC__uint64 startBytes = win32State.bytesWritten; + FLAC__stream_encoder_finish(encoder); + finishedBytes = win32State.bytesWritten - startBytes; +} + +void AudioCoderFlac::Finish(const wchar_t *destination) +{ + if (win32State.handle != INVALID_HANDLE_VALUE) + CloseHandle(win32State.handle); + + win32State.handle = INVALID_HANDLE_VALUE; + if (!MoveFile(tempFile, destination)) + { + if (CopyFile(tempFile, destination, FALSE)) + DeleteFile(tempFile); + } +} \ No newline at end of file diff --git a/Src/Plugins/Encoder/enc_flac/AudioCoderFlac.h b/Src/Plugins/Encoder/enc_flac/AudioCoderFlac.h new file mode 100644 index 00000000..e28eba96 --- /dev/null +++ b/Src/Plugins/Encoder/enc_flac/AudioCoderFlac.h @@ -0,0 +1,29 @@ +#pragma once +#include "../nsv/enc_if.h" +#include +#include "StreamFileWin32.h" + +typedef struct { + unsigned int compression; // 0-8 +} configtype; + +class AudioCoderFlac : public AudioCoder +{ +public: + AudioCoderFlac(unsigned int nch, unsigned int bps, unsigned int samplerate, unsigned int compression); + ~AudioCoderFlac(); + int Encode(int framepos, void *in, int in_avail, int *in_used, void *out, int out_avail); //returns bytes in out + void PrepareToFinish(); + void Finish(const wchar_t *destination); + bool OK(); + +private: + FLAC__StreamEncoder *encoder; + FLAC__StreamMetadata *padding; + Win32_State win32State; + unsigned int nch; + unsigned int bps; + wchar_t tempFile[MAX_PATH]; + bool finished; + FLAC__uint64 finishedBytes; +}; \ No newline at end of file diff --git a/Src/Plugins/Encoder/enc_flac/StreamFileWin32.cpp b/Src/Plugins/Encoder/enc_flac/StreamFileWin32.cpp new file mode 100644 index 00000000..04c152dd --- /dev/null +++ b/Src/Plugins/Encoder/enc_flac/StreamFileWin32.cpp @@ -0,0 +1,69 @@ +#include "StreamFileWin32.h" +#include + +static __int64 Seek64(HANDLE hf, __int64 distance, DWORD MoveMethod) +{ + LARGE_INTEGER li; + + li.QuadPart = distance; + + li.LowPart = SetFilePointer (hf, li.LowPart, &li.HighPart, MoveMethod); + + if (li.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) + { + li.QuadPart = -1; + } + + return li.QuadPart; +} + + +FLAC__StreamEncoderWriteStatus Win32_Write(const FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], size_t bytes, unsigned samples, unsigned current_frame, void *client_data) +{ + assert(bytes <= 4294967295U); + + HANDLE file = ((Win32_State *)client_data)->handle; + if(bytes > 0) + { + assert(sizeof(FLAC__byte) == 1); + DWORD bytesWritten = 0, bytesToWrite = (DWORD)bytes; + BOOL result = WriteFile(file, buffer, bytesToWrite, &bytesWritten, NULL); + ((Win32_State *)client_data)->bytesWritten += bytesWritten; + + if (!result) + return FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR; + return FLAC__STREAM_ENCODER_WRITE_STATUS_OK; + } + else + return FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR; +} + +FLAC__StreamEncoderSeekStatus Win32_Seek(const FLAC__StreamEncoder *encoder, FLAC__uint64 absolute_byte_offset, void *client_data) +{ + HANDLE file = ((Win32_State *)client_data)->handle; + + __int64 result = Seek64(file, absolute_byte_offset, FILE_BEGIN); + + if (result == INVALID_SET_FILE_POINTER) + return FLAC__STREAM_ENCODER_SEEK_STATUS_ERROR; + else + { + return FLAC__STREAM_ENCODER_SEEK_STATUS_OK; + } +} + +FLAC__StreamEncoderTellStatus Win32_Tell(const FLAC__StreamEncoder *encoder, FLAC__uint64 *absolute_byte_offset, void *client_data) +{ + HANDLE file = ((Win32_State *)client_data)->handle; + + __int64 position = Seek64(file, 0, FILE_CURRENT); + + if (position == INVALID_SET_FILE_POINTER) + return FLAC__STREAM_ENCODER_TELL_STATUS_ERROR; + else + { + *absolute_byte_offset=position; + return FLAC__STREAM_ENCODER_TELL_STATUS_OK; + } +} + diff --git a/Src/Plugins/Encoder/enc_flac/StreamFileWin32.h b/Src/Plugins/Encoder/enc_flac/StreamFileWin32.h new file mode 100644 index 00000000..6c09bc46 --- /dev/null +++ b/Src/Plugins/Encoder/enc_flac/StreamFileWin32.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include + +struct Win32_State +{ +public: + HANDLE handle; + FLAC__uint64 bytesWritten; +}; + +FLAC__StreamEncoderWriteStatus Win32_Write(const FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], size_t bytes, unsigned samples, unsigned current_frame, void *client_data); +FLAC__StreamEncoderSeekStatus Win32_Seek(const FLAC__StreamEncoder *encoder, FLAC__uint64 absolute_byte_offset, void *client_data); +FLAC__StreamEncoderTellStatus Win32_Tell(const FLAC__StreamEncoder *encoder, FLAC__uint64 *absolute_byte_offset, void *client_data); +void Progress(const FLAC__StreamEncoder *encoder, FLAC__uint64 bytes_written, FLAC__uint64 samples_written, unsigned frames_written, unsigned total_frames_estimate, void *client_data); + + diff --git a/Src/Plugins/Encoder/enc_flac/api__enc_flac.h b/Src/Plugins/Encoder/enc_flac/api__enc_flac.h new file mode 100644 index 00000000..2b62bfb4 --- /dev/null +++ b/Src/Plugins/Encoder/enc_flac/api__enc_flac.h @@ -0,0 +1,10 @@ +#ifndef NULLSOFT_ENC_FLAC_API_H +#define NULLSOFT_ENC_FLAC_API_H + +#include "api/service/api_service.h" +#include "../Agave/Language/api_language.h" + +#include "api/application/api_application.h" +#define WASABI_API_APP application + +#endif // !NULLSOFT_ENC_FLAC_API_H diff --git a/Src/Plugins/Encoder/enc_flac/enc_flac2.rc b/Src/Plugins/Encoder/enc_flac/enc_flac2.rc new file mode 100644 index 00000000..adb678f3 --- /dev/null +++ b/Src/Plugins/Encoder/enc_flac/enc_flac2.rc @@ -0,0 +1,119 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "#include ""version.rc2""\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_CONFIG DIALOGEX 0, 0, 256, 167 +STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_SYSMENU +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + GROUPBOX "FLAC Encoder Options",IDC_STATIC,0,0,256,166 + LTEXT "Compression factor:",IDC_STATIC,7,13,66,8 + CONTROL "",IDC_COMPRESSIONSLIDER,"msctls_trackbar32",TBS_AUTOTICKS | TBS_TOOLTIPS | WS_TABSTOP,5,23,243,17 + LTEXT "Fast Encoding",IDC_STATIC,9,43,46,8 + LTEXT "Best Compression",IDC_STATIC,191,43,58,8 + LTEXT "FLAC is the Free Lossless Audio Codec, ",IDC_STATIC,5,143,128,8 + CONTROL "http://flac.sf.net/",IDC_URL,"Button",BS_OWNERDRAW | WS_TABSTOP,131,142,62,9 + LTEXT "",IDC_STATIC_FLAC_VER,5,151,242,8 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_CONFIG, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 179 + TOPMARGIN, 7 + BOTTOMMARGIN, 83 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + IDS_LIBFLAC_DLL_MISSING "[libflac.dll missing]" + IDS_ENC_FLAC_DESC "FLAC Encoder v%s (libFLAC v%s)" +END + +STRINGTABLE +BEGIN + 65535 "{73760073-560C-433b-BC59-3FCC94CDEA4A}" +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// +#include "version.rc2" + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/Src/Plugins/Encoder/enc_flac/enc_flac2.sln b/Src/Plugins/Encoder/enc_flac/enc_flac2.sln new file mode 100644 index 00000000..fd840cc5 --- /dev/null +++ b/Src/Plugins/Encoder/enc_flac/enc_flac2.sln @@ -0,0 +1,56 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29613.14 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "enc_flac", "enc_flac2.vcxproj", "{922008CC-4A0E-4EA1-92F2-AA822965BDF6}" + ProjectSection(ProjectDependencies) = postProject + {4FC28B55-2A14-43D5-86F7-201054F338A9} = {4FC28B55-2A14-43D5-86F7-201054F338A9} + {4CEFBC83-C215-11DB-8314-0800200C9A66} = {4CEFBC83-C215-11DB-8314-0800200C9A66} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libFLAC_dynamic", "..\libFLAC\libFLAC_dynamic.vcxproj", "{4CEFBC83-C215-11DB-8314-0800200C9A66}" + ProjectSection(ProjectDependencies) = postProject + {4FC28B55-2A14-43D5-86F7-201054F338A9} = {4FC28B55-2A14-43D5-86F7-201054F338A9} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libogg", "..\libogg\libogg.vcxproj", "{4FC28B55-2A14-43D5-86F7-201054F338A9}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {922008CC-4A0E-4EA1-92F2-AA822965BDF6}.Debug|Win32.ActiveCfg = Debug|Win32 + {922008CC-4A0E-4EA1-92F2-AA822965BDF6}.Debug|Win32.Build.0 = Debug|Win32 + {922008CC-4A0E-4EA1-92F2-AA822965BDF6}.Debug|x64.ActiveCfg = Debug|x64 + {922008CC-4A0E-4EA1-92F2-AA822965BDF6}.Release|Win32.ActiveCfg = Release|Win32 + {922008CC-4A0E-4EA1-92F2-AA822965BDF6}.Release|Win32.Build.0 = Release|Win32 + {922008CC-4A0E-4EA1-92F2-AA822965BDF6}.Release|x64.ActiveCfg = Release|x64 + {4CEFBC83-C215-11DB-8314-0800200C9A66}.Debug|Win32.ActiveCfg = Debug|Win32 + {4CEFBC83-C215-11DB-8314-0800200C9A66}.Debug|Win32.Build.0 = Debug|Win32 + {4CEFBC83-C215-11DB-8314-0800200C9A66}.Debug|x64.ActiveCfg = Debug|x64 + {4CEFBC83-C215-11DB-8314-0800200C9A66}.Debug|x64.Build.0 = Debug|x64 + {4CEFBC83-C215-11DB-8314-0800200C9A66}.Release|Win32.ActiveCfg = Release|Win32 + {4CEFBC83-C215-11DB-8314-0800200C9A66}.Release|Win32.Build.0 = Release|Win32 + {4CEFBC83-C215-11DB-8314-0800200C9A66}.Release|x64.ActiveCfg = Release|x64 + {4CEFBC83-C215-11DB-8314-0800200C9A66}.Release|x64.Build.0 = Release|x64 + {4FC28B55-2A14-43D5-86F7-201054F338A9}.Debug|Win32.ActiveCfg = Debug|Win32 + {4FC28B55-2A14-43D5-86F7-201054F338A9}.Debug|Win32.Build.0 = Debug|Win32 + {4FC28B55-2A14-43D5-86F7-201054F338A9}.Debug|x64.ActiveCfg = Debug|x64 + {4FC28B55-2A14-43D5-86F7-201054F338A9}.Debug|x64.Build.0 = Debug|x64 + {4FC28B55-2A14-43D5-86F7-201054F338A9}.Release|Win32.ActiveCfg = Release|Win32 + {4FC28B55-2A14-43D5-86F7-201054F338A9}.Release|Win32.Build.0 = Release|Win32 + {4FC28B55-2A14-43D5-86F7-201054F338A9}.Release|x64.ActiveCfg = Release|x64 + {4FC28B55-2A14-43D5-86F7-201054F338A9}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {A0F24E75-21BB-4DDF-A287-A481B554B4EB} + EndGlobalSection +EndGlobal diff --git a/Src/Plugins/Encoder/enc_flac/enc_flac2.vcxproj b/Src/Plugins/Encoder/enc_flac/enc_flac2.vcxproj new file mode 100644 index 00000000..b937baea --- /dev/null +++ b/Src/Plugins/Encoder/enc_flac/enc_flac2.vcxproj @@ -0,0 +1,277 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + enc_flac + {922008CC-4A0E-4EA1-92F2-AA822965BDF6} + enc_flac + 10.0.19041.0 + + + + DynamicLibrary + v142 + Unicode + + + DynamicLibrary + v142 + Unicode + + + DynamicLibrary + v142 + Unicode + + + DynamicLibrary + v142 + Unicode + + + + + + + + + + + + + + + + + + + false + $(PlatformShortName)_$(Configuration)\ + $(PlatformShortName)_$(Configuration)\ + $(IncludePath) + $(LibraryPath) + + + false + $(PlatformShortName)_$(Configuration)\ + $(PlatformShortName)_$(Configuration)\ + + + false + $(PlatformShortName)_$(Configuration)\ + $(PlatformShortName)_$(Configuration)\ + $(IncludePath) + $(LibraryPath) + + + false + $(PlatformShortName)_$(Configuration)\ + $(PlatformShortName)_$(Configuration)\ + + + false + + + + + false + Debug + x86-windows-static-md + + + + + false + x86-windows-static-md + + + + + false + x86-windows-static-md + Debug + + + + + false + x86-windows-static-md + + + + Disabled + ..\..\..\Wasabi;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;_USRDLL;ENC_FLAC2_EXPORTS;%(PreprocessorDefinitions) + false + true + EnableFastChecks + MultiThreadedDebugDLL + true + true + Level3 + ProgramDatabase + $(IntDir)$(TargetName).pdb + + + shlwapi.lib;uxtheme.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)$(TargetExt) + uxtheme.dll;%(DelayLoadDLLs) + true + $(IntDir)$(TargetName).pdb + Windows + false + $(IntDir)$(TargetName).lib + MachineX86 + false + %(AdditionalLibraryDirectories) + + + xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ +xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ + Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\' + + + + + Disabled + ..\..\..\Wasabi;%(AdditionalIncludeDirectories) + WIN64;_DEBUG;_WINDOWS;_USRDLL;ENC_FLAC2_EXPORTS;%(PreprocessorDefinitions) + false + true + EnableFastChecks + MultiThreadedDebugDLL + true + true + Level3 + ProgramDatabase + $(IntDir)$(TargetName).pdb + + + shlwapi.lib;uxtheme.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)$(TargetExt) + uxtheme.dll;%(DelayLoadDLLs) + true + $(IntDir)$(TargetName).pdb + Windows + false + $(IntDir)$(TargetName).lib + false + %(AdditionalLibraryDirectories) + + + xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ +xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ + Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\' + + + + + MinSpace + Size + ..\..\..\Wasabi;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_WINDOWS;_USRDLL;ENC_FLAC2_EXPORTS;%(PreprocessorDefinitions) + true + true + MultiThreadedDLL + true + true + true + Level3 + None + $(IntDir)$(TargetName).pdb + + + shlwapi.lib;uxtheme.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)$(TargetExt) + uxtheme.dll;%(DelayLoadDLLs) + false + $(IntDir)$(TargetName).pdb + Windows + true + true + false + $(IntDir)$(TargetName).lib + MachineX86 + %(AdditionalLibraryDirectories) + false + + + xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ + Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\' + + + + + MinSpace + Size + ..\..\..\Wasabi;%(AdditionalIncludeDirectories) + WIN64;NDEBUG;_WINDOWS;_USRDLL;ENC_FLAC2_EXPORTS;%(PreprocessorDefinitions) + true + true + MultiThreadedDLL + true + true + true + Level3 + None + $(IntDir)$(TargetName).pdb + + + shlwapi.lib;uxtheme.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)$(TargetExt) + uxtheme.dll;%(DelayLoadDLLs) + false + $(IntDir)$(TargetName).pdb + Windows + true + true + false + $(IntDir)$(TargetName).lib + %(AdditionalLibraryDirectories) + false + + + xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ + Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\' + + + + + + + + + + + + + + + + + + + {3e0bfa8a-b86a-42e9-a33f-ec294f823f7f} + + + + + + \ No newline at end of file diff --git a/Src/Plugins/Encoder/enc_flac/enc_flac2.vcxproj.filters b/Src/Plugins/Encoder/enc_flac/enc_flac2.vcxproj.filters new file mode 100644 index 00000000..41b6f1a1 --- /dev/null +++ b/Src/Plugins/Encoder/enc_flac/enc_flac2.vcxproj.filters @@ -0,0 +1,44 @@ + + + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + {c641c9a9-b7d5-4744-bd44-2edc534f2425} + + + {63979e7c-4d4b-4fc2-89c6-3f476c40adfe} + + + {cc58b311-a8ed-4e0e-852b-cc82f0d2a83e} + + + + + Ressource Files + + + \ No newline at end of file diff --git a/Src/Plugins/Encoder/enc_flac/main.cpp b/Src/Plugins/Encoder/enc_flac/main.cpp new file mode 100644 index 00000000..d0507c59 --- /dev/null +++ b/Src/Plugins/Encoder/enc_flac/main.cpp @@ -0,0 +1,320 @@ +#include "api__enc_flac.h" +#include "../Winamp/wa_ipc.h" +#include "../nsv/enc_if.h" +#include "../nu/AutoWideFn.h" +#include "AudioCoderFlac.h" +#include "resource.h" +#include +#include +#include +#include +#include +#include +#include + +#define ENC_VERSION "2.46" + +HWND winampwnd = 0; +int isthemethere = 0; +api_service *WASABI_API_SVC = 0; +api_language *WASABI_API_LNG = 0; +api_application *WASABI_API_APP = 0; +HINSTANCE WASABI_API_LNG_HINST = 0, WASABI_API_ORIG_HINST = 0; + +typedef struct +{ + configtype cfg; + char configfile[MAX_PATH]; +} +configwndrec; + +void readconfig(const char *configfile, configtype *cfg) +{ + if (configfile) + { + cfg->compression = GetPrivateProfileIntA("audio_flac", "compression", 5, configfile); + } + else + { + cfg->compression = 5; + } +} + +void writeconfig(const char *configfile, configtype *cfg) +{ + if (configfile) + { + char str[64] = {0}; + StringCchPrintfA(str, 64, "%u", cfg->compression); + WritePrivateProfileStringA("audio_flac", "compression", str, configfile); + } +} + +static HINSTANCE GetMyInstance() +{ + MEMORY_BASIC_INFORMATION mbi = {0}; + if(VirtualQuery(GetMyInstance, &mbi, sizeof(mbi))) + return (HINSTANCE)mbi.AllocationBase; + return NULL; +} + +void GetLocalisationApiService(void) +{ + if(!WASABI_API_LNG) + { + // loader so that we can get the localisation service api for use + if(!WASABI_API_SVC) + { + WASABI_API_SVC = (api_service*)SendMessage(winampwnd, WM_WA_IPC, 0, IPC_GET_API_SERVICE); + if (WASABI_API_SVC == (api_service*)1) + { + WASABI_API_SVC = NULL; + return; + } + } + + if(!WASABI_API_APP) + { + waServiceFactory *sf = WASABI_API_SVC->service_getServiceByGuid(applicationApiServiceGuid); + if (sf) WASABI_API_APP = reinterpret_cast(sf->getInterface()); + } + + if(!WASABI_API_LNG) + { + waServiceFactory *sf = WASABI_API_SVC->service_getServiceByGuid(languageApiGUID); + if (sf) WASABI_API_LNG = reinterpret_cast(sf->getInterface()); + } + + // need to have this initialised before we try to do anything with localisation features + WASABI_API_START_LANG(GetMyInstance(),EncFlacLangGUID); + } +} + +static const char *GetFLACVersion() +{ + return "1.4.2"; +} + +static HCURSOR link_hand_cursor; +LRESULT link_handlecursor(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + LRESULT ret = CallWindowProcW((WNDPROC)GetPropW(hwndDlg, L"link_proc"), hwndDlg, uMsg, wParam, lParam); + // override the normal cursor behaviour so we have a hand to show it is a link + if(uMsg == WM_SETCURSOR) + { + if((HWND)wParam == hwndDlg) + { + if(!link_hand_cursor) + { + link_hand_cursor = LoadCursor(NULL, IDC_HAND); + } + SetCursor(link_hand_cursor); + return TRUE; + } + } + return ret; +} + +void link_handledraw(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + if (uMsg == WM_DRAWITEM) + { + DRAWITEMSTRUCT *di = (DRAWITEMSTRUCT *)lParam; + if (di->CtlType == ODT_BUTTON) + { + wchar_t wt[123] = {0}; + int y; + RECT r; + HPEN hPen, hOldPen; + GetDlgItemTextW(hwndDlg, (int)wParam, wt, sizeof(wt)/sizeof(wt[0])); + + // due to the fun of theming and owner drawing we have to get the background colour + if(isthemethere){ + HTHEME hTheme = OpenThemeData(hwndDlg, L"Tab"); + if (hTheme) { + DrawThemeParentBackground(di->hwndItem, di->hDC, &di->rcItem); + CloseThemeData(hTheme); + } + } + + // draw text + SetTextColor(di->hDC, (di->itemState & ODS_SELECTED) ? RGB(220, 0, 0) : RGB(0, 0, 220)); + r = di->rcItem; + r.left += 2; + DrawTextW(di->hDC, wt, -1, &r, DT_VCENTER | DT_SINGLELINE); + + memset(&r, 0, sizeof(r)); + DrawTextW(di->hDC, wt, -1, &r, DT_SINGLELINE | DT_CALCRECT); + + // draw underline + y = di->rcItem.bottom - ((di->rcItem.bottom - di->rcItem.top) - (r.bottom - r.top)) / 2 - 1; + hPen = CreatePen(PS_SOLID, 0, (di->itemState & ODS_SELECTED) ? RGB(220, 0, 0) : RGB(0, 0, 220)); + hOldPen = (HPEN) SelectObject(di->hDC, hPen); + MoveToEx(di->hDC, di->rcItem.left + 2, y, NULL); + LineTo(di->hDC, di->rcItem.right + 2 - ((di->rcItem.right - di->rcItem.left) - (r.right - r.left)), y); + SelectObject(di->hDC, hOldPen); + DeleteObject(hPen); + } + } +} + +void link_startsubclass(HWND hwndDlg, UINT id) +{ +HWND ctrl = GetDlgItem(hwndDlg, id); + if(!GetPropW(ctrl, L"link_proc")) + { + SetPropW(ctrl, L"link_proc", + (HANDLE)SetWindowLongPtrW(ctrl, GWLP_WNDPROC, (LONG_PTR)link_handlecursor)); + } +} + +BOOL CALLBACK DlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + static configwndrec *wr; + switch(uMsg) + { + case WM_INITDIALOG: + { + wr = (configwndrec *)lParam; + SendMessage(GetDlgItem(hwndDlg,IDC_COMPRESSIONSLIDER),TBM_SETRANGE,TRUE,MAKELONG(0,8)); + SendMessage(GetDlgItem(hwndDlg,IDC_COMPRESSIONSLIDER),TBM_SETPOS,TRUE,wr->cfg.compression); + const char *libFlacVer = GetFLACVersion(); + if (libFlacVer && *libFlacVer) + { + char flac_string[1024] = {0}; + StringCchPrintfA(flac_string, 1024, "libFLAC v%s",libFlacVer); + SetDlgItemTextA(hwndDlg, IDC_STATIC_FLAC_VER, flac_string); + } + link_startsubclass(hwndDlg, IDC_URL); + } + break; + + case WM_COMMAND: + if(LOWORD(wParam) == IDC_URL) + { + SendMessage(winampwnd, WM_WA_IPC, (WPARAM)"http://flac.sf.net/", IPC_OPEN_URL); + } + break; + + case WM_NOTIFY: + if(wParam == IDC_COMPRESSIONSLIDER) + { + LPNMHDR l = (LPNMHDR)lParam; + if(l->idFrom == IDC_COMPRESSIONSLIDER) + wr->cfg.compression = (unsigned int)SendMessage(GetDlgItem(hwndDlg,IDC_COMPRESSIONSLIDER),TBM_GETPOS,0,0); + } + break; + + case WM_DESTROY: + writeconfig(wr->configfile,&wr->cfg); + free(wr); wr=NULL; + break; + } + + const int controls[] = + { + IDC_COMPRESSIONSLIDER, + }; + if (FALSE != WASABI_API_APP->DirectMouseWheel_ProcessDialogMessage(hwndDlg, uMsg, wParam, lParam, controls, ARRAYSIZE(controls))) + { + return TRUE; + } + + link_handledraw(hwndDlg,uMsg,wParam,lParam); + return 0; +} + +extern "C" +{ + unsigned int __declspec(dllexport) GetAudioTypes3(int idx, char *desc) + { + if (idx == 0) + { + GetLocalisationApiService(); + const char *libFlacVer = GetFLACVersion(); + StringCchPrintfA(desc, 1024, WASABI_API_LNGSTRING(IDS_ENC_FLAC_DESC), ENC_VERSION, libFlacVer); + return mmioFOURCC('F', 'L', 'A', 'C'); + } + return 0; + } + + AudioCoder __declspec(dllexport) *CreateAudio3(int nch, int srate, int bps, unsigned int srct, unsigned int *outt, char *configfile) + { + if (srct == mmioFOURCC('P', 'C', 'M', ' ') && *outt == mmioFOURCC('F', 'L', 'A', 'C')) + { + configtype cfg; + readconfig(configfile, &cfg); + *outt = mmioFOURCC('F', 'L', 'A', 'C'); + AudioCoderFlac *t=new AudioCoderFlac(nch, bps, srate, cfg.compression); + if (!t->OK()) + { + delete t; + return NULL; + } + return t; + } + return NULL; + } + + void __declspec(dllexport) FinishAudio3(const char *filename, AudioCoder *coder) + { + ((AudioCoderFlac*)coder)->Finish(AutoWideFn(filename)); + } + + void __declspec(dllexport) FinishAudio3W(const wchar_t *filename, AudioCoder *coder) + { + ((AudioCoderFlac*)coder)->Finish(filename); + } + + void __declspec(dllexport) PrepareToFinish(const char *filename, AudioCoder *coder) + { + ((AudioCoderFlac*)coder)->PrepareToFinish(); + } + + void __declspec(dllexport) PrepareToFinishW(const wchar_t *filename, AudioCoder *coder) + { + ((AudioCoderFlac*)coder)->PrepareToFinish(); + } + + + HWND __declspec(dllexport) ConfigAudio3(HWND hwndParent, HINSTANCE hinst, unsigned int outt, char *configfile) + { + if (outt == mmioFOURCC('F', 'L', 'A', 'C')) + { + configwndrec *wr = (configwndrec*)malloc(sizeof(configwndrec)); + if (configfile) StringCchCopyA(wr->configfile, MAX_PATH, configfile); + else wr->configfile[0] = 0; + readconfig(configfile, &wr->cfg); + GetLocalisationApiService(); + return WASABI_API_CREATEDIALOGPARAMW(IDD_CONFIG, hwndParent, DlgProc, (LPARAM)wr); + } + return NULL; + } + + int __declspec(dllexport) SetConfigItem(unsigned int outt, char *item, char *data, char *configfile) + { + // nothing yet + return 0; + } + + int __declspec(dllexport) GetConfigItem(unsigned int outt, char *item, char *data, int len, char *configfile) + { + if (outt == mmioFOURCC('F', 'L', 'A', 'C')) + { + /* + configtype cfg; + readconfig(configfile, &cfg); + */ + if (!_stricmp(item, "bitrate")) StringCchCopyA(data, len, "755"); // FUCKO: this is ment to be an estimate for approximations of output filesize (used by ml_pmp). Improve this. + else if (!_stricmp(item,"extension")) StringCchCopyA(data, len, "flac"); + return 1; + } + return 0; + } + + void __declspec(dllexport) SetWinampHWND(HWND hwnd) + { + winampwnd = hwnd; + isthemethere = !SendMessage(hwnd,WM_WA_IPC,IPC_ISWINTHEMEPRESENT,IPC_USE_UXTHEME_FUNC); + } +}; \ No newline at end of file diff --git a/Src/Plugins/Encoder/enc_flac/resource.h b/Src/Plugins/Encoder/enc_flac/resource.h new file mode 100644 index 00000000..6d233f22 --- /dev/null +++ b/Src/Plugins/Encoder/enc_flac/resource.h @@ -0,0 +1,24 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by enc_flac2.rc +// +#define IDD_CONFIG 9 +#define IDS_LIBFLAC_DLL_MISSING 102 +#define IDS_STRING103 103 +#define IDS_ENC_FLAC_DESC 103 +#define IDC_SLIDER1 1001 +#define IDC_COMPRESSIONSLIDER 1001 +#define IDC_STATIC_FLAC_VER 1002 +#define IDC_BUTTON1 1003 +#define IDC_URL 1003 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 103 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1004 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/Src/Plugins/Encoder/enc_flac/version.rc2 b/Src/Plugins/Encoder/enc_flac/version.rc2 new file mode 100644 index 00000000..6bc10f30 --- /dev/null +++ b/Src/Plugins/Encoder/enc_flac/version.rc2 @@ -0,0 +1,39 @@ + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// +#include "../../../Winamp/buildType.h" +VS_VERSION_INFO VERSIONINFO + FILEVERSION 2,46,0,0 + PRODUCTVERSION WINAMP_PRODUCTVER + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Winamp SA" + VALUE "FileDescription", "Winamp Encoder Plug-in" + VALUE "FileVersion", "2,46,0,0" + VALUE "InternalName", "Nullsoft FLAC Encoder" + VALUE "LegalCopyright", "Copyright © 2006-2023 Winamp SA" + VALUE "LegalTrademarks", "Nullsoft and Winamp are trademarks of Winamp SA" + VALUE "OriginalFilename", "enc_flac.dll" + VALUE "ProductName", "Winamp" + VALUE "ProductVersion", STR_WINAMP_PRODUCTVER + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/Src/Plugins/Encoder/enc_lame/24bit.cpp b/Src/Plugins/Encoder/enc_lame/24bit.cpp new file mode 100644 index 00000000..d1b83b1f --- /dev/null +++ b/Src/Plugins/Encoder/enc_lame/24bit.cpp @@ -0,0 +1,147 @@ +#include "MP3Coder.h" +#include + +static const float const_1_div_128_ = 32768.0 / 128.0; /* 8 bit multiplier */ +static const double const_1_div_2147483648_ = 32768.0 / 2147483648.0; /* 32 bit multiplier */ + +static void Int8_To_Float32(float *dest, void *sourceBuffer, signed int sourceStride, unsigned int count) +{ + signed char *src = (signed char*)sourceBuffer; + + while( count-- ) + { + float samp = *src * const_1_div_128_; + *dest = samp; + + src += sourceStride; + dest ++; + } +} + +static void Int24_To_Float32(float *dest, void *sourceBuffer, signed int sourceStride, unsigned int count) +{ + unsigned char *src = (unsigned char*)sourceBuffer; + + while ( count-- ) + { + signed long temp = (((long)src[0]) << 8); + temp = temp | (((long)src[1]) << 16); + temp = temp | (((long)src[2]) << 24); + *dest = (float) ((double)temp * const_1_div_2147483648_); + src += sourceStride * 3; + dest ++; + } +} + +int AudioCoderMP3_24::Encode(int framepos, void *in, int in_avail, int *in_used, void *out, int out_avail) +{ + if (m_err) return -1; + if (!hbeStream) + { + if (beInitStream(&beConfig, &ibuf_size_spls, &obuf_size, (PHBE_STREAM) &hbeStream) != BE_ERR_SUCCESSFUL) + { + m_err++; + return -1; + } + ibuf_size = ibuf_size_spls * bytesPerSample; + + if (is_downmix) ibuf_size *= 2; + + bs = (char*)malloc(ibuf_size); + bs_size = 0; + } + + *in_used = 0; + + int needbytes = ibuf_size - bs_size; + if (needbytes > 0 && in_avail > 0) + { + if (needbytes > in_avail) + needbytes = in_avail; + memcpy(bs + bs_size, in, needbytes); + bs_size += needbytes; + *in_used = needbytes; + } + + if (!done) + { + if (bs_size < (int)ibuf_size) return 0; + } + + if (out_avail < (int)obuf_size) return 0; + + int feedBytes = min(bs_size, (int)ibuf_size); + int feedSamples = feedBytes / bytesPerSample; + bs_size -= feedBytes; + /* + if (is_downmix) + { + int x; + int l = feedBytes / 4; + short *b = (short*)bs; + for (x = 0; x < l; x ++) + { + b[x] = b[x * 2] / 2 + b[x * 2 + 1] / 2; + } + } + */ + DWORD dwo = 0; + + if (feedSamples) + { + if (mono_input) + { + float *float_l = (float *)alloca(sizeof(float) * feedSamples); + switch(bytesPerSample) + { + case 8: + Int8_To_Float32(float_l, bs, 1, feedSamples); + break; + case 24: + Int24_To_Float32(float_l, bs, 1, feedSamples); + break; + } + + + if (beEncodeChunkFloatS16NI(hbeStream, feedSamples, float_l, float_l, (unsigned char *)out, &dwo) != BE_ERR_SUCCESSFUL) + { + m_err++; + return -1; + } + } + else + { + float *float_l = (float *)alloca(sizeof(float) * feedSamples / 2); + float *float_r = (float *)alloca(sizeof(float) * feedSamples / 2); + switch(bytesPerSample) + { + case 1: // 8 bit + Int8_To_Float32(float_l, bs, 2, feedSamples / 2); + Int8_To_Float32(float_r, bs + 1, 2, feedSamples / 2); + break; + case 3: // 24 bit + Int24_To_Float32(float_l, bs, 2, feedSamples / 2); + Int24_To_Float32(float_r, bs + 3, 2, feedSamples / 2); + break; + } + + if (beEncodeChunkFloatS16NI(hbeStream, feedSamples / 2, float_l, float_r, (unsigned char *)out, &dwo) != BE_ERR_SUCCESSFUL) + { + m_err++; + return -1; + } + } + } + + if (!dwo && done == 1) + { + if (beDeinitStream(hbeStream, (unsigned char *)out, &dwo) != BE_ERR_SUCCESSFUL) + { + m_err++; + return -1; + } + done++; + } + + return dwo; +} \ No newline at end of file diff --git a/Src/Plugins/Encoder/enc_lame/BladeMP3EncDLL.c b/Src/Plugins/Encoder/enc_lame/BladeMP3EncDLL.c new file mode 100644 index 00000000..e982cad2 --- /dev/null +++ b/Src/Plugins/Encoder/enc_lame/BladeMP3EncDLL.c @@ -0,0 +1,1028 @@ +/* +* Blade DLL Interface for LAME. +* +* Copyright (c) 1999 - 2002 A.L. Faber +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Library General Public +* License as published by the Free Software Foundation; either +* version 2 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Library General Public License for more details. +* +* You should have received a copy of the GNU Library General Public +* License along with this library; if not, write to the +* Free Software Foundation, Inc., 59 Temple Place - Suite 330, +* Boston, MA 02111-1307, USA. +*/ + +#include +#include +#include "BladeMP3EncDLL.h" +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define Min(A, B) ((A) < (B) ? (A) : (B)) +#define Max(A, B) ((A) > (B) ? (A) : (B)) + +#define _RELEASEDEBUG 0 + + // lame_enc DLL version number + const BYTE MAJORVERSION = 1; + const BYTE MINORVERSION = 32; + + + // Local variables + static DWORD dwSampleBufferSize = 0; + static HMODULE gs_hModule = NULL; + static BOOL gs_bLogFile = FALSE; + static lame_global_flags* gfp_save = NULL; + + // Local function prototypes + static void dump_config(lame_global_flags* gfp); + static void DebugPrintf(const char* pzFormat, ...); + static void DispErr(wchar_t const* strErr); + static void PresetOptions(lame_global_flags* gfp, LONG myPreset); + + + static void DebugPrintf(const char* pzFormat, ...) + { + char szBuffer[1024] = { '\0', }; + char szFileName[MAX_PATH + 1] = { '\0', }; + va_list ap; + + // Get the full module (DLL) file name + GetModuleFileNameA(gs_hModule, + szFileName, + sizeof(szFileName)); + + // change file name extention + szFileName[strlen(szFileName) - 3] = 't'; + szFileName[strlen(szFileName) - 2] = 'x'; + szFileName[strlen(szFileName) - 1] = 't'; + + // start at beginning of the list + va_start(ap, pzFormat); + + // copy it to the string buffer + _vsnprintf(szBuffer, sizeof(szBuffer), pzFormat, ap); + + // log it to the file? + if (gs_bLogFile) + { + FILE* fp = NULL; + + // try to open the log file + fp = fopen(szFileName, "a+"); + + // check file open result + if (fp) + { + // write string to the file + fputs(szBuffer, fp); + + // close the file + fclose(fp); + } + } + +#if defined _DEBUG || defined _RELEASEDEBUG + OutputDebugStringA(szBuffer); +#endif + + va_end(ap); + } + + + static void PresetOptions(lame_global_flags* gfp, LONG myPreset) + { + switch (myPreset) + { + /*-1*/case LQP_NOPRESET: + break; + + /*0*/case LQP_NORMAL_QUALITY: + /* lame_set_quality( gfp, 5 );*/ + break; + + /*1*/case LQP_LOW_QUALITY: + lame_set_quality(gfp, 9); + break; + + /*2*/case LQP_HIGH_QUALITY: + lame_set_quality(gfp, 2); + break; + + /*3*/case LQP_VOICE_QUALITY: // --voice flag for experimental voice mode + lame_set_mode(gfp, MONO); + lame_set_preset(gfp, 56); + break; + + /*4*/case LQP_R3MIX: // --R3MIX + lame_set_preset(gfp, R3MIX); + break; + + /*5*/case LQP_VERYHIGH_QUALITY: + lame_set_quality(gfp, 0); + break; + + /*6*/case LQP_STANDARD: // --PRESET STANDARD + lame_set_preset(gfp, STANDARD); + break; + + /*7*/case LQP_FAST_STANDARD: // --PRESET FAST STANDARD + lame_set_preset(gfp, STANDARD_FAST); + break; + + /*8*/case LQP_EXTREME: // --PRESET EXTREME + lame_set_preset(gfp, EXTREME); + break; + + /*9*/case LQP_FAST_EXTREME: // --PRESET FAST EXTREME: + lame_set_preset(gfp, EXTREME_FAST); + break; + + /*10*/case LQP_INSANE: // --PRESET INSANE + lame_set_preset(gfp, INSANE); + break; + + /*11*/case LQP_ABR: // --PRESET ABR + // handled in beInitStream + break; + + /*12*/case LQP_CBR: // --PRESET CBR + // handled in beInitStream + break; + + /*13*/case LQP_MEDIUM: // --PRESET MEDIUM + lame_set_preset(gfp, MEDIUM); + break; + + /*14*/case LQP_FAST_MEDIUM: // --PRESET FAST MEDIUM + lame_set_preset(gfp, MEDIUM_FAST); + break; + + /*1000*/case LQP_PHONE: + lame_set_mode(gfp, MONO); + lame_set_preset(gfp, 16); + break; + + /*2000*/case LQP_SW: + lame_set_mode(gfp, MONO); + lame_set_preset(gfp, 24); + break; + + /*3000*/case LQP_AM: + lame_set_mode(gfp, MONO); + lame_set_preset(gfp, 40); + break; + + /*4000*/case LQP_FM: + lame_set_preset(gfp, 112); + break; + + /*5000*/case LQP_VOICE: + lame_set_mode(gfp, MONO); + lame_set_preset(gfp, 56); + break; + + /*6000*/case LQP_RADIO: + lame_set_preset(gfp, 112); + break; + + /*7000*/case LQP_TAPE: + lame_set_preset(gfp, 112); + break; + + /*8000*/case LQP_HIFI: + lame_set_preset(gfp, 160); + break; + + /*9000*/case LQP_CD: + lame_set_preset(gfp, 192); + break; + + /*10000*/case LQP_STUDIO: + lame_set_preset(gfp, 256); + break; + + } + } + + + __declspec(dllexport) BE_ERR beInitStream(PBE_CONFIG pbeConfig, PDWORD dwSamples, PDWORD dwBufferSize, PHBE_STREAM phbeStream) + { + int actual_bitrate; + //2001-12-18 + BE_CONFIG lameConfig = { 0, }; + int nInitReturn = 0; + lame_global_flags* gfp = NULL; + + // Init the global flags structure + gfp = lame_init(); + *phbeStream = (HBE_STREAM)gfp; + + // clear out structure + memset(&lameConfig, 0x00, CURRENT_STRUCT_SIZE); + + // Check if this is a regular BLADE_ENCODER header + if (pbeConfig->dwConfig != BE_CONFIG_LAME) + { + int nCRC = pbeConfig->format.mp3.bCRC; + int nVBR = (nCRC >> 12) & 0x0F; + + // Copy parameter from old Blade structure + lameConfig.format.LHV1.dwSampleRate = pbeConfig->format.mp3.dwSampleRate; + //for low bitrates, LAME will automatically downsample for better + //sound quality. Forcing output samplerate = input samplerate is not a good idea + //unless the user specifically requests it: + //lameConfig.format.LHV1.dwReSampleRate=pbeConfig->format.mp3.dwSampleRate; + lameConfig.format.LHV1.nMode = (pbeConfig->format.mp3.byMode & 0x0F); + lameConfig.format.LHV1.dwBitrate = pbeConfig->format.mp3.wBitrate; + lameConfig.format.LHV1.bPrivate = pbeConfig->format.mp3.bPrivate; + lameConfig.format.LHV1.bOriginal = pbeConfig->format.mp3.bOriginal; + lameConfig.format.LHV1.bCRC = nCRC & 0x01; + lameConfig.format.LHV1.bCopyright = pbeConfig->format.mp3.bCopyright; + + // Fill out the unknowns + lameConfig.format.LHV1.dwStructSize = CURRENT_STRUCT_SIZE; + lameConfig.format.LHV1.dwStructVersion = CURRENT_STRUCT_VERSION; + + // Get VBR setting from fourth nibble + if (nVBR > 0) + { + lameConfig.format.LHV1.bWriteVBRHeader = TRUE; + lameConfig.format.LHV1.bEnableVBR = TRUE; + lameConfig.format.LHV1.nVBRQuality = nVBR - 1; + } + + // Get Quality from third nibble + lameConfig.format.LHV1.nPreset = ((nCRC >> 8) & 0x0F); + + } + else + { + // Copy the parameters + memcpy(&lameConfig, pbeConfig, pbeConfig->format.LHV1.dwStructSize); + } + + // --------------- Set arguments to LAME encoder ------------------------- + + // Set input sample frequency + lame_set_in_samplerate(gfp, lameConfig.format.LHV1.dwSampleRate); + + // disable INFO/VBR tag by default. + // if this tag is used, the calling program must call beWriteVBRTag() + // after encoding. But the original DLL documentation does not + // require the + // app to call beWriteVBRTag() unless they have specifically + // set LHV1.bWriteVBRHeader=TRUE. Thus the default setting should + // be disabled. + lame_set_bWriteVbrTag(gfp, 0); + + //2001-12-18 Dibrom's ABR preset stuff + + if (lameConfig.format.LHV1.nPreset == LQP_ABR) // --ALT-PRESET ABR + { + actual_bitrate = lameConfig.format.LHV1.dwVbrAbr_bps / 1000; + + // limit range + if (actual_bitrate > 320) + { + actual_bitrate = 320; + } + + if (actual_bitrate < 8) + { + actual_bitrate = 8; + } + + lame_set_preset(gfp, actual_bitrate); + } + + // end Dibrom's ABR preset 2001-12-18 ****** START OF CBR + + if (lameConfig.format.LHV1.nPreset == LQP_CBR) // --ALT-PRESET CBR + { + actual_bitrate = lameConfig.format.LHV1.dwBitrate; + lame_set_preset(gfp, actual_bitrate); + lame_set_VBR(gfp, vbr_off); + } + + // end Dibrom's CBR preset 2001-12-18 + + // The following settings only used when preset is not one of the LAME QUALITY Presets + if ((int)lameConfig.format.LHV1.nPreset < (int)LQP_STANDARD) + { + switch (lameConfig.format.LHV1.nMode) + { + case BE_MP3_MODE_STEREO: + lame_set_mode(gfp, STEREO); + lame_set_num_channels(gfp, 2); + break; + case BE_MP3_MODE_JSTEREO: + lame_set_mode(gfp, JOINT_STEREO); + //lame_set_force_ms( gfp, bForceMS ); // no check box to force this? + lame_set_num_channels(gfp, 2); + break; + case BE_MP3_MODE_MONO: + lame_set_mode(gfp, MONO); + lame_set_num_channels(gfp, 1); + break; + case BE_MP3_MODE_DUALCHANNEL: + lame_set_mode(gfp, DUAL_CHANNEL); + lame_set_num_channels(gfp, 2); + break; + default: + { + DebugPrintf("Invalid lameConfig.format.LHV1.nMode, value is %d\n", lameConfig.format.LHV1.nMode); + return BE_ERR_INVALID_FORMAT_PARAMETERS; + } + } + + if (lameConfig.format.LHV1.bEnableVBR) + { + /* set VBR quality */ + lame_set_VBR_q(gfp, lameConfig.format.LHV1.nVBRQuality); + + /* select proper VBR method */ + switch (lameConfig.format.LHV1.nVbrMethod) + { + case VBR_METHOD_NONE: + lame_set_VBR(gfp, vbr_off); + break; + + case VBR_METHOD_DEFAULT: + lame_set_VBR(gfp, vbr_default); + break; + + case VBR_METHOD_OLD: + lame_set_VBR(gfp, vbr_rh); + break; + + case VBR_METHOD_MTRH: + case VBR_METHOD_NEW: + /* + * the --vbr-mtrh commandline switch is obsolete. + * now --vbr-mtrh is known as --vbr-new + */ + lame_set_VBR(gfp, vbr_mtrh); + break; + + case VBR_METHOD_ABR: + lame_set_VBR(gfp, vbr_abr); + break; + + default: + /* unsupported VBR method */ + assert(FALSE); + } + } + else + { + /* use CBR encoding method, so turn off VBR */ + lame_set_VBR(gfp, vbr_off); + } + + /* Set bitrate. (CDex users always specify bitrate=Min bitrate when using VBR) */ + lame_set_brate(gfp, lameConfig.format.LHV1.dwBitrate); + + /* check if we have to use ABR, in order to backwards compatible, this + * condition should still be checked indepedent of the nVbrMethod method + */ + if (lameConfig.format.LHV1.dwVbrAbr_bps > 0) + { + /* set VBR method to ABR */ + lame_set_VBR(gfp, vbr_abr); + + /* calculate to kbps, round to nearest kbps */ + lame_set_VBR_mean_bitrate_kbps(gfp, (lameConfig.format.LHV1.dwVbrAbr_bps + 500) / 1000); + + /* limit range */ + if (lame_get_VBR_mean_bitrate_kbps(gfp) > 320) + { + lame_set_VBR_mean_bitrate_kbps(gfp, 320); + } + + if (lame_get_VBR_mean_bitrate_kbps(gfp) < 8) + { + lame_set_VBR_mean_bitrate_kbps(gfp, 8); + } + } + + } + + // First set all the preset options + if (LQP_NOPRESET != lameConfig.format.LHV1.nPreset) + { + PresetOptions(gfp, lameConfig.format.LHV1.nPreset); + } + + + // Set frequency resampling rate, if specified + if (lameConfig.format.LHV1.dwReSampleRate > 0) + { + lame_set_out_samplerate(gfp, lameConfig.format.LHV1.dwReSampleRate); + } + + + switch (lameConfig.format.LHV1.nMode) + { + case BE_MP3_MODE_MONO: + lame_set_mode(gfp, MONO); + lame_set_num_channels(gfp, 1); + break; + + default: + break; + } + + + // Use strict ISO encoding? + lame_set_strict_ISO(gfp, (lameConfig.format.LHV1.bStrictIso) ? 1 : 0); + + // Set copyright flag? + if (lameConfig.format.LHV1.bCopyright) + { + lame_set_copyright(gfp, 1); + } + + // Do we have to tag it as non original + if (!lameConfig.format.LHV1.bOriginal) + { + lame_set_original(gfp, 0); + } + else + { + lame_set_original(gfp, 1); + } + + // Add CRC? + if (lameConfig.format.LHV1.bCRC) + { + lame_set_error_protection(gfp, 1); + } + else + { + lame_set_error_protection(gfp, 0); + } + + // Set private bit? + if (lameConfig.format.LHV1.bPrivate) + { + lame_set_extension(gfp, 1); + } + else + { + lame_set_extension(gfp, 0); + } + + + // Set VBR min bitrate, if specified + if (lameConfig.format.LHV1.dwBitrate > 0) + { + lame_set_VBR_min_bitrate_kbps(gfp, lameConfig.format.LHV1.dwBitrate); + } + + // Set Maxbitrate, if specified + if (lameConfig.format.LHV1.dwMaxBitrate > 0) + { + lame_set_VBR_max_bitrate_kbps(gfp, lameConfig.format.LHV1.dwMaxBitrate); + } + // Set bit resovoir option + if (lameConfig.format.LHV1.bNoRes) + { + lame_set_disable_reservoir(gfp, 1); + } + + // check if the VBR tag is required + if (lameConfig.format.LHV1.bWriteVBRHeader) + { + lame_set_bWriteVbrTag(gfp, 1); + } + else + { + lame_set_bWriteVbrTag(gfp, 0); + } + + // Override Quality setting, use HIGHBYTE = NOT LOWBYTE to be backwards compatible + if ((lameConfig.format.LHV1.nQuality & 0xFF) == + ((~(lameConfig.format.LHV1.nQuality >> 8)) & 0xFF)) + { + lame_set_quality(gfp, lameConfig.format.LHV1.nQuality & 0xFF); + } + + if (0 != (nInitReturn = lame_init_params(gfp))) + { + return nInitReturn; + } + + //LAME encoding call will accept any number of samples. + if (0 == lame_get_version(gfp)) + { + // For MPEG-II, only 576 samples per frame per channel + *dwSamples = 576 * lame_get_num_channels(gfp); + } + else + { + // For MPEG-I, 1152 samples per frame per channel + *dwSamples = 1152 * lame_get_num_channels(gfp); + } + + // Set the input sample buffer size, so we know what we can expect + dwSampleBufferSize = *dwSamples; + + // Set MP3 buffer size, conservative estimate + *dwBufferSize = (DWORD)(1.25 * (*dwSamples / lame_get_num_channels(gfp)) + 7200); + + // For debugging purposes + dump_config(gfp); + + // Everything went OK, thus return SUCCESSFUL + return BE_ERR_SUCCESSFUL; + } + + + + __declspec(dllexport) BE_ERR beFlushNoGap(HBE_STREAM hbeStream, PBYTE pOutput, PDWORD pdwOutput) + { + int nOutputSamples = 0; + + lame_global_flags* gfp = (lame_global_flags*)hbeStream; + + // Init the global flags structure + nOutputSamples = lame_encode_flush_nogap(gfp, pOutput, LAME_MAXMP3BUFFER); + + if (nOutputSamples < 0) + { + *pdwOutput = 0; + return BE_ERR_BUFFER_TOO_SMALL; + } + else + { + *pdwOutput = nOutputSamples; + } + + return BE_ERR_SUCCESSFUL; + } + + __declspec(dllexport) BE_ERR beDeinitStream(HBE_STREAM hbeStream, PBYTE pOutput, PDWORD pdwOutput) + { + int nOutputSamples = 0; + + lame_global_flags* gfp = (lame_global_flags*)hbeStream; + + nOutputSamples = lame_encode_flush(gfp, pOutput, 0); + + if (nOutputSamples < 0) + { + *pdwOutput = 0; + return BE_ERR_BUFFER_TOO_SMALL; + } + else + { + *pdwOutput = nOutputSamples; + } + + return BE_ERR_SUCCESSFUL; + } + + + __declspec(dllexport) BE_ERR beCloseStream(HBE_STREAM hbeStream) + { + lame_global_flags* gfp = (lame_global_flags*)hbeStream; + + // lame will be close in VbrWriteTag function + if (!lame_get_bWriteVbrTag(gfp)) + { + // clean up of allocated memory + lame_close(gfp); + + gfp_save = NULL; + } + else + { + gfp_save = (lame_global_flags*)hbeStream; + } + + // DeInit encoder + return BE_ERR_SUCCESSFUL; + } + + + + __declspec(dllexport) VOID beVersion(PBE_VERSION pbeVersion) + { + // DLL Release date + char lpszDate[20] = { '\0', }; + char lpszTemp[5] = { '\0', }; + lame_version_t lv = { 0, }; + + + // Set DLL interface version + pbeVersion->byDLLMajorVersion = MAJORVERSION; + pbeVersion->byDLLMinorVersion = MINORVERSION; + + get_lame_version_numerical(&lv); + + // Set Engine version number (Same as Lame version) + pbeVersion->byMajorVersion = (BYTE)lv.major; + pbeVersion->byMinorVersion = (BYTE)lv.minor; + pbeVersion->byAlphaLevel = (BYTE)lv.alpha; + pbeVersion->byBetaLevel = (BYTE)lv.beta; + +#ifdef MMX_choose_table + pbeVersion->byMMXEnabled = 1; +#else + pbeVersion->byMMXEnabled = 0; +#endif + + memset(pbeVersion->btReserved, 0, sizeof(pbeVersion->btReserved)); + + // Get compilation date + strcpy(lpszDate, __DATE__); + + // Get the first three character, which is the month + strncpy(lpszTemp, lpszDate, 3); + lpszTemp[3] = '\0'; + pbeVersion->byMonth = 1; + + // Set month + if (strcmp(lpszTemp, "Jan") == 0) pbeVersion->byMonth = 1; + if (strcmp(lpszTemp, "Feb") == 0) pbeVersion->byMonth = 2; + if (strcmp(lpszTemp, "Mar") == 0) pbeVersion->byMonth = 3; + if (strcmp(lpszTemp, "Apr") == 0) pbeVersion->byMonth = 4; + if (strcmp(lpszTemp, "May") == 0) pbeVersion->byMonth = 5; + if (strcmp(lpszTemp, "Jun") == 0) pbeVersion->byMonth = 6; + if (strcmp(lpszTemp, "Jul") == 0) pbeVersion->byMonth = 7; + if (strcmp(lpszTemp, "Aug") == 0) pbeVersion->byMonth = 8; + if (strcmp(lpszTemp, "Sep") == 0) pbeVersion->byMonth = 9; + if (strcmp(lpszTemp, "Oct") == 0) pbeVersion->byMonth = 10; + if (strcmp(lpszTemp, "Nov") == 0) pbeVersion->byMonth = 11; + if (strcmp(lpszTemp, "Dec") == 0) pbeVersion->byMonth = 12; + + // Get day of month string (char [4..5]) + pbeVersion->byDay = (BYTE)atoi(lpszDate + 4); + + // Get year of compilation date (char [7..10]) + pbeVersion->wYear = (WORD)atoi(lpszDate + 7); + + memset(pbeVersion->zHomepage, 0x00, BE_MAX_HOMEPAGE); + + strcpy(pbeVersion->zHomepage, "http://www.mp3dev.org/"); + } + + __declspec(dllexport) BE_ERR beEncodeChunk(HBE_STREAM hbeStream, DWORD nSamples, + PSHORT pSamples, PBYTE pOutput, PDWORD pdwOutput) + { + // Encode it + int dwSamples; + int nOutputSamples = 0; + lame_global_flags* gfp = (lame_global_flags*)hbeStream; + + dwSamples = nSamples / lame_get_num_channels(gfp); + + // old versions of lame_enc.dll required exactly 1152 samples + // and worked even if nSamples accidently set to 2304 + // simulate this behavoir: + if (1 == lame_get_num_channels(gfp) && nSamples == 2304) + { + dwSamples /= 2; + } + + + if (1 == lame_get_num_channels(gfp)) + { + nOutputSamples = lame_encode_buffer(gfp, pSamples, pSamples, dwSamples, pOutput, 0); + } + else + { + nOutputSamples = lame_encode_buffer_interleaved(gfp, pSamples, dwSamples, pOutput, 0); + } + + + if (nOutputSamples < 0) + { + *pdwOutput = 0; + return BE_ERR_BUFFER_TOO_SMALL; + } + else + { + *pdwOutput = (DWORD)nOutputSamples; + } + + return BE_ERR_SUCCESSFUL; + } + + + // accept floating point audio samples, scaled to the range of a signed 16-bit + // integer (within +/- 32768), in non-interleaved channels -- DSPguru, jd + __declspec(dllexport) BE_ERR beEncodeChunkFloatS16NI(HBE_STREAM hbeStream, DWORD nSamples, + PFLOAT buffer_l, PFLOAT buffer_r, PBYTE pOutput, PDWORD pdwOutput) + { + int nOutputSamples; + lame_global_flags* gfp = (lame_global_flags*)hbeStream; + + nOutputSamples = lame_encode_buffer_float(gfp, buffer_l, buffer_r, nSamples, pOutput, 0); + + if (nOutputSamples >= 0) + { + *pdwOutput = (DWORD)nOutputSamples; + } + else + { + *pdwOutput = 0; + return BE_ERR_BUFFER_TOO_SMALL; + } + + return BE_ERR_SUCCESSFUL; + } + + static int + maybeSyncWord(FILE* fpStream) + { + unsigned char mp3_frame_header[4]; + size_t nbytes = fread(mp3_frame_header, 1, sizeof(mp3_frame_header), fpStream); + if (nbytes != sizeof(mp3_frame_header)) { + return -1; + } + if (mp3_frame_header[0] != 0xffu) { + return -1; /* doesn't look like a sync word */ + } + if ((mp3_frame_header[1] & 0xE0u) != 0xE0u) { + return -1; /* doesn't look like a sync word */ + } + return 0; + } + + static int + skipId3v2(FILE* fpStream, size_t lametag_frame_size) + { + size_t nbytes; + size_t id3v2TagSize = 0; + unsigned char id3v2Header[10]; + + /* seek to the beginning of the stream */ + if (fseek(fpStream, 0, SEEK_SET) != 0) { + return -2; /* not seekable, abort */ + } + /* read 10 bytes in case there's an ID3 version 2 header here */ + nbytes = fread(id3v2Header, 1, sizeof(id3v2Header), fpStream); + if (nbytes != sizeof(id3v2Header)) { + return -3; /* not readable, maybe opened Write-Only */ + } + /* does the stream begin with the ID3 version 2 file identifier? */ + if (!strncmp((char*)id3v2Header, "ID3", 3)) { + /* the tag size (minus the 10-byte header) is encoded into four + * bytes where the most significant bit is clear in each byte + */ + id3v2TagSize = (((id3v2Header[6] & 0x7f) << 21) + | ((id3v2Header[7] & 0x7f) << 14) + | ((id3v2Header[8] & 0x7f) << 7) + | (id3v2Header[9] & 0x7f)) + + sizeof id3v2Header; + } + /* Seek to the beginning of the audio stream */ + if (fseek(fpStream, id3v2TagSize, SEEK_SET) != 0) { + return -2; + } + if (maybeSyncWord(fpStream) != 0) { + return -1; + } + if (fseek(fpStream, id3v2TagSize + lametag_frame_size, SEEK_SET) != 0) { + return -2; + } + if (maybeSyncWord(fpStream) != 0) { + return -1; + } + /* OK, it seems we found our LAME-Tag/Xing frame again */ + /* Seek to the beginning of the audio stream */ + if (fseek(fpStream, id3v2TagSize, SEEK_SET) != 0) { + return -2; + } + return 0; + } + + static BE_ERR + updateLameTagFrame(lame_global_flags* gfp, FILE* fpStream) + { + size_t n = lame_get_lametag_frame(gfp, 0, 0); /* ask for bufer size */ + + if (n > 0) + { + unsigned char* buffer = 0; + size_t m = 1; + + if (0 != skipId3v2(fpStream, n)) + { + DispErr("Error updating LAME-tag frame:\n\n" + "can't locate old frame\n"); + return BE_ERR_INVALID_FORMAT_PARAMETERS; + } + + buffer = (unsigned char*)malloc(n); + + if (buffer == 0) + { + DispErr("Error updating LAME-tag frame:\n\n" + "can't allocate frame buffer\n"); + return BE_ERR_INVALID_FORMAT_PARAMETERS; + } + + /* Put it all to disk again */ + n = lame_get_lametag_frame(gfp, buffer, n); + if (n > 0) + { + m = fwrite(buffer, n, 1, fpStream); + } + free(buffer); + + if (m != 1) + { + DispErr("Error updating LAME-tag frame:\n\n" + "couldn't write frame into file\n"); + return BE_ERR_INVALID_FORMAT_PARAMETERS; + } + } + return BE_ERR_SUCCESSFUL; + } + + __declspec(dllexport) BE_ERR beWriteInfoTag(HBE_STREAM hbeStream, + LPCSTR lpszFileName) + { + FILE* fpStream = NULL; + BE_ERR beResult = BE_ERR_SUCCESSFUL; + + lame_global_flags* gfp = (lame_global_flags*)hbeStream; + + if (NULL != gfp) + { + // Do we have to write the VBR tag? + if (lame_get_bWriteVbrTag(gfp)) + { + // Try to open the file + fpStream = fopen(lpszFileName, "rb+"); + + // Check file open result + if (NULL == fpStream) + { + beResult = BE_ERR_INVALID_FORMAT_PARAMETERS; + DispErr("Error updating LAME-tag frame:\n\n" + "can't open file for reading and writing\n"); + } + else + { + beResult = updateLameTagFrame(gfp, fpStream); + + // Close the file stream + fclose(fpStream); + } + } + + // clean up of allocated memory + lame_close(gfp); + } + else + { + beResult = BE_ERR_INVALID_FORMAT_PARAMETERS; + } + + // return result + return beResult; + } + + // for backwards compatiblity + __declspec(dllexport) BE_ERR beWriteVBRHeader(LPCSTR lpszFileName) + { + return beWriteInfoTag((HBE_STREAM)gfp_save, lpszFileName); + } + + + BOOL APIENTRY DllMain(HANDLE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved) + { + (void)lpReserved; + gs_hModule = (HMODULE)hModule; + + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + // Enable debug/logging? + gs_bLogFile = GetPrivateProfileIntA("Debug", "WriteLogFile", gs_bLogFile, "lame_enc.ini"); + break; + case DLL_THREAD_ATTACH: + break; + case DLL_THREAD_DETACH: + break; + case DLL_PROCESS_DETACH: + break; + } + return TRUE; + } + + + static void dump_config(lame_global_flags* gfp) + { + DebugPrintf("\n\nLame_enc configuration options:\n"); + DebugPrintf("==========================================================\n"); + + DebugPrintf("version =%d\n", lame_get_version(gfp)); + DebugPrintf("Layer =3\n"); + DebugPrintf("mode ="); + switch (lame_get_mode(gfp)) + { + case STEREO: DebugPrintf("Stereo\n"); break; + case JOINT_STEREO: DebugPrintf("Joint-Stereo\n"); break; + case DUAL_CHANNEL: DebugPrintf("Forced Stereo\n"); break; + case MONO: DebugPrintf("Mono\n"); break; + case NOT_SET: /* FALLTROUGH */ + default: DebugPrintf("Error (unknown)\n"); break; + } + + DebugPrintf("Input sample rate =%.1f kHz\n", lame_get_in_samplerate(gfp) / 1000.0); + DebugPrintf("Output sample rate =%.1f kHz\n", lame_get_out_samplerate(gfp) / 1000.0); + + DebugPrintf("bitrate =%d kbps\n", lame_get_brate(gfp)); + DebugPrintf("Quality Setting =%d\n", lame_get_quality(gfp)); + + DebugPrintf("Low pass frequency =%d\n", lame_get_lowpassfreq(gfp)); + DebugPrintf("Low pass width =%d\n", lame_get_lowpasswidth(gfp)); + + DebugPrintf("High pass frequency =%d\n", lame_get_highpassfreq(gfp)); + DebugPrintf("High pass width =%d\n", lame_get_highpasswidth(gfp)); + + DebugPrintf("No short blocks =%d\n", lame_get_no_short_blocks(gfp)); + DebugPrintf("Force short blocks =%d\n", lame_get_force_short_blocks(gfp)); + + DebugPrintf("de-emphasis =%d\n", lame_get_emphasis(gfp)); + DebugPrintf("private flag =%d\n", lame_get_extension(gfp)); + + DebugPrintf("copyright flag =%d\n", lame_get_copyright(gfp)); + DebugPrintf("original flag =%d\n", lame_get_original(gfp)); + DebugPrintf("CRC =%s\n", lame_get_error_protection(gfp) ? "on" : "off"); + DebugPrintf("Fast mode =%s\n", (lame_get_quality(gfp)) ? "enabled" : "disabled"); + DebugPrintf("Force mid/side stereo =%s\n", (lame_get_force_ms(gfp)) ? "enabled" : "disabled"); + DebugPrintf("Disable Reservoir =%d\n", lame_get_disable_reservoir(gfp)); + DebugPrintf("Allow diff-short =%d\n", lame_get_allow_diff_short(gfp)); + DebugPrintf("Interchannel masking =%f\n", lame_get_interChRatio(gfp)); + DebugPrintf("Strict ISO Encoding =%s\n", (lame_get_strict_ISO(gfp)) ? "Yes" : "No"); + DebugPrintf("Scale =%5.2f\n", lame_get_scale(gfp)); + + DebugPrintf("VBR =%s, VBR_q =%d, VBR method =", + (lame_get_VBR(gfp) != vbr_off) ? "enabled" : "disabled", + lame_get_VBR_q(gfp)); + + switch (lame_get_VBR(gfp)) + { + case vbr_off: DebugPrintf("vbr_off\n"); break; + case vbr_mt: DebugPrintf("vbr_mt \n"); break; + case vbr_rh: DebugPrintf("vbr_rh \n"); break; + case vbr_mtrh: DebugPrintf("vbr_mtrh \n"); break; + case vbr_abr: + DebugPrintf("vbr_abr (average bitrate %d kbps)\n", lame_get_VBR_mean_bitrate_kbps(gfp)); + break; + default: + DebugPrintf("error, unknown VBR setting\n"); + break; + } + + DebugPrintf("Vbr Min bitrate =%d kbps\n", lame_get_VBR_min_bitrate_kbps(gfp)); + DebugPrintf("Vbr Max bitrate =%d kbps\n", lame_get_VBR_max_bitrate_kbps(gfp)); + + DebugPrintf("Write VBR Header =%s\n", (lame_get_bWriteVbrTag(gfp)) ? "Yes" : "No"); + DebugPrintf("VBR Hard min =%d\n", lame_get_VBR_hard_min(gfp)); + + DebugPrintf("ATH Only =%d\n", lame_get_ATHonly(gfp)); + DebugPrintf("ATH short =%d\n", lame_get_ATHshort(gfp)); + DebugPrintf("ATH no =%d\n", lame_get_noATH(gfp)); + DebugPrintf("ATH type =%d\n", lame_get_ATHtype(gfp)); + DebugPrintf("ATH lower =%f\n", lame_get_ATHlower(gfp)); + DebugPrintf("ATH aa =%d\n", lame_get_athaa_type(gfp)); + //DebugPrintf("ATH aa loudapprox =%d\n", lame_get_athaa_loudapprox( gfp ) ); + DebugPrintf("ATH aa sensitivity =%f\n", lame_get_athaa_sensitivity(gfp)); + + DebugPrintf("Experimental nspsytune =%d\n", lame_get_exp_nspsytune(gfp)); + DebugPrintf("Experimental X =%d\n", lame_get_experimentalX(gfp)); + DebugPrintf("Experimental Y =%d\n", lame_get_experimentalY(gfp)); + DebugPrintf("Experimental Z =%d\n", lame_get_experimentalZ(gfp)); + } + + + static void DispErr(wchar_t const* strErr) + { + MessageBox(NULL, strErr, L"LAME_ENC.DLL", MB_OK | MB_ICONHAND); + } + +#ifdef __cplusplus +} +#endif diff --git a/Src/Plugins/Encoder/enc_lame/BladeMP3EncDLL.h b/Src/Plugins/Encoder/enc_lame/BladeMP3EncDLL.h new file mode 100644 index 00000000..20aaff4d --- /dev/null +++ b/Src/Plugins/Encoder/enc_lame/BladeMP3EncDLL.h @@ -0,0 +1,278 @@ +/* + * Blade DLL Interface for LAME. + * + * Copyright (c) 1999 A.L. Faber + * Based on bladedll.h version 1.0 written by Jukka Poikolainen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef ___BLADEDLL_H_INCLUDED___ +#define ___BLADEDLL_H_INCLUDED___ + +#pragma pack(push) +#pragma pack(1) + +#ifdef __cplusplus +extern "C" { +#endif + +/* encoding formats */ + +#define BE_CONFIG_MP3 0 +#define BE_CONFIG_LAME 256 + +/* type definitions */ + +typedef unsigned long HBE_STREAM; +typedef HBE_STREAM *PHBE_STREAM; +typedef unsigned long BE_ERR; + +/* error codes */ + +#define BE_ERR_SUCCESSFUL 0x00000000 +#define BE_ERR_INVALID_FORMAT 0x00000001 +#define BE_ERR_INVALID_FORMAT_PARAMETERS 0x00000002 +#define BE_ERR_NO_MORE_HANDLES 0x00000003 +#define BE_ERR_INVALID_HANDLE 0x00000004 +#define BE_ERR_BUFFER_TOO_SMALL 0x00000005 + +/* other constants */ + +#define BE_MAX_HOMEPAGE 128 + +/* format specific variables */ + +#define BE_MP3_MODE_STEREO 0 +#define BE_MP3_MODE_JSTEREO 1 +#define BE_MP3_MODE_DUALCHANNEL 2 +#define BE_MP3_MODE_MONO 3 + + + +#define MPEG1 1 +#define MPEG2 0 + +#ifdef _BLADEDLL +#undef FLOAT + #include +#endif + +#define CURRENT_STRUCT_VERSION 1 +#define CURRENT_STRUCT_SIZE sizeof(BE_CONFIG) // is currently 331 bytes + +/* OBSOLETE, VALUES STILL WORK +typedef enum +{ + NORMAL_QUALITY=0, + LOW_QUALITY, + HIGH_QUALITY, + VOICE_QUALITY +} LAME_QUALTIY_PRESET; + +*/ + + +typedef enum +{ + VBR_METHOD_NONE = -1, + VBR_METHOD_DEFAULT = 0, + VBR_METHOD_OLD = 1, + VBR_METHOD_NEW = 2, + VBR_METHOD_MTRH = 3, + VBR_METHOD_ABR = 4 +} VBRMETHOD; + +typedef enum +{ + LQP_NOPRESET=-1, + + // QUALITY PRESETS + LQP_NORMAL_QUALITY = 0, + LQP_LOW_QUALITY = 1, + LQP_HIGH_QUALITY = 2, + LQP_VOICE_QUALITY = 3, + LQP_R3MIX = 4, + LQP_VERYHIGH_QUALITY = 5, + LQP_STANDARD = 6, + LQP_FAST_STANDARD = 7, + LQP_EXTREME = 8, + LQP_FAST_EXTREME = 9, + LQP_INSANE = 10, + LQP_ABR = 11, + LQP_CBR = 12, + LQP_MEDIUM = 13, + LQP_FAST_MEDIUM = 14, + + // NEW PRESET VALUES + LQP_PHONE =1000, + LQP_SW =2000, + LQP_AM =3000, + LQP_FM =4000, + LQP_VOICE =5000, + LQP_RADIO =6000, + LQP_TAPE =7000, + LQP_HIFI =8000, + LQP_CD =9000, + LQP_STUDIO =10000 + +} LAME_QUALTIY_PRESET; + + + +typedef struct { + DWORD dwConfig; // BE_CONFIG_XXXXX + // Currently only BE_CONFIG_MP3 is supported + union { + + struct { + + DWORD dwSampleRate; // 48000, 44100 and 32000 allowed. RG note: also seems to support 16000, 22050, 24000. + BYTE byMode; // BE_MP3_MODE_STEREO, BE_MP3_MODE_DUALCHANNEL, BE_MP3_MODE_MONO + WORD wBitrate; // 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256 and 320 allowed. RG note: also seems to support 8,16,24. + BOOL bPrivate; + BOOL bCRC; + BOOL bCopyright; + BOOL bOriginal; + + } mp3; // BE_CONFIG_MP3 + + struct + { + // STRUCTURE INFORMATION + DWORD dwStructVersion; + DWORD dwStructSize; + + // BASIC ENCODER SETTINGS + DWORD dwSampleRate; // SAMPLERATE OF INPUT FILE + DWORD dwReSampleRate; // DOWNSAMPLERATE, 0=ENCODER DECIDES + LONG nMode; // BE_MP3_MODE_STEREO, BE_MP3_MODE_DUALCHANNEL, BE_MP3_MODE_MONO + DWORD dwBitrate; // CBR bitrate, VBR min bitrate + DWORD dwMaxBitrate; // CBR ignored, VBR Max bitrate + LONG nPreset; // Quality preset, use one of the settings of the LAME_QUALITY_PRESET enum + DWORD dwMpegVersion; // FUTURE USE, MPEG-1 OR MPEG-2 + DWORD dwPsyModel; // FUTURE USE, SET TO 0 + DWORD dwEmphasis; // FUTURE USE, SET TO 0 + + // BIT STREAM SETTINGS + BOOL bPrivate; // Set Private Bit (TRUE/FALSE) + BOOL bCRC; // Insert CRC (TRUE/FALSE) + BOOL bCopyright; // Set Copyright Bit (TRUE/FALSE) + BOOL bOriginal; // Set Original Bit (TRUE/FALSE) + + // VBR STUFF + BOOL bWriteVBRHeader; // WRITE XING VBR HEADER (TRUE/FALSE) + BOOL bEnableVBR; // USE VBR ENCODING (TRUE/FALSE) + INT nVBRQuality; // VBR QUALITY 0..9 + DWORD dwVbrAbr_bps; // Use ABR in stead of nVBRQuality + VBRMETHOD nVbrMethod; + BOOL bNoRes; // Disable Bit resorvoir + + // MISC SETTINGS + BOOL bStrictIso; // Use strict ISO encoding rules (TRUE/FALSE) + WORD nQuality; // Quality Setting, HIGH BYTE should be NOT LOW byte, otherwhise quality=5 + + // FUTURE USE, SET TO 0, align strucutre to 331 bytes + BYTE btReserved[255-4*sizeof(DWORD) - sizeof( WORD )]; + + } LHV1; // LAME header version 1 + + struct { + + DWORD dwSampleRate; + BYTE byMode; + WORD wBitrate; + BYTE byEncodingMethod; + + } aac; + + } format; + +} BE_CONFIG, *PBE_CONFIG; + + +typedef struct { + + // BladeEnc DLL Version number + + BYTE byDLLMajorVersion; + BYTE byDLLMinorVersion; + + // BladeEnc Engine Version Number + + BYTE byMajorVersion; + BYTE byMinorVersion; + + // DLL Release date + + BYTE byDay; + BYTE byMonth; + WORD wYear; + + // BladeEnc Homepage URL + + CHAR zHomepage[BE_MAX_HOMEPAGE + 1]; + + BYTE byAlphaLevel; + BYTE byBetaLevel; + BYTE byMMXEnabled; + + BYTE btReserved[125]; + + +} BE_VERSION, *PBE_VERSION; + +#ifndef _BLADEDLL + +typedef BE_ERR (*BEINITSTREAM) (PBE_CONFIG, PDWORD, PDWORD, PHBE_STREAM); +typedef BE_ERR (*BEENCODECHUNK) (HBE_STREAM, DWORD, PSHORT, PBYTE, PDWORD); +// added for floating point audio -- DSPguru, jd +typedef BE_ERR (*BEENCODECHUNKFLOATS16NI) (HBE_STREAM, DWORD, PFLOAT, PFLOAT, PBYTE, PDWORD); +typedef BE_ERR (*BEDEINITSTREAM) (HBE_STREAM, PBYTE, PDWORD); +typedef BE_ERR (*BECLOSESTREAM) (HBE_STREAM); +typedef VOID (*BEVERSION) (PBE_VERSION); +typedef VOID (*BEWRITEVBRHEADER) (LPCSTR); + +#define TEXT_BEINITSTREAM "beInitStream" +#define TEXT_BEENCODECHUNK "beEncodeChunk" +#define TEXT_BEENCODECHUNKFLOATS16NI "beEncodeChunkFloatS16NI" +#define TEXT_BEDEINITSTREAM "beDeinitStream" +#define TEXT_BECLOSESTREAM "beCloseStream" +#define TEXT_BEVERSION "beVersion" +#define TEXT_BEWRITEVBRHEADER "beWriteVBRHeader" + +#else + +__declspec(dllexport) BE_ERR beInitStream(PBE_CONFIG pbeConfig, PDWORD dwSamples, PDWORD dwBufferSize, PHBE_STREAM phbeStream); +__declspec(dllexport) BE_ERR beEncodeChunk(HBE_STREAM hbeStream, DWORD nSamples, PSHORT pSamples, PBYTE pOutput, PDWORD pdwOutput); +// added for floating point audio -- DSPguru, jd +__declspec(dllexport) BE_ERR beEncodeChunkFloatS16NI(HBE_STREAM hbeStream, DWORD nSamples, PFLOAT buffer_l, PFLOAT buffer_r, PBYTE pOutput, PDWORD pdwOutput); +__declspec(dllexport) BE_ERR beDeinitStream(HBE_STREAM hbeStream, PBYTE pOutput, PDWORD pdwOutput); +__declspec(dllexport) BE_ERR beCloseStream(HBE_STREAM hbeStream); +__declspec(dllexport) VOID beVersion(PBE_VERSION pbeVersion); +__declspec(dllexport) BE_ERR beWriteVBRHeader(LPCSTR lpszFileName); +__declspec(dllexport) BE_ERR beFlushNoGap(HBE_STREAM hbeStream, PBYTE pOutput, PDWORD pdwOutput); +__declspec(dllexport) BE_ERR beWriteInfoTag( HBE_STREAM hbeStream, LPCSTR lpszFileName); + +#endif + +#pragma pack(pop) + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/Src/Plugins/Encoder/enc_lame/MP3Coder.cpp b/Src/Plugins/Encoder/enc_lame/MP3Coder.cpp new file mode 100644 index 00000000..7bf7d605 --- /dev/null +++ b/Src/Plugins/Encoder/enc_lame/MP3Coder.cpp @@ -0,0 +1,196 @@ +#include "MP3Coder.h" + +AudioCoderMP3::~AudioCoderMP3() +{ + if (hbeStream) beCloseStream(hbeStream); hbeStream = 0; + if (bs) free(bs); bs = 0; +} + +int AudioCoderMP3::GetLastError() { return m_err; }; + +void AudioCoderMP3::setVbrFilename(char *filename) +{ + if (hbeStream) beCloseStream(hbeStream); + hbeStream = 0; + if (filename) + { + beWriteVBRHeader(filename); + } +} + +int AudioCoderMP3::Encode(int framepos, void *in, int in_avail, int *in_used, void *out, int out_avail) +{ + if (m_err) return -1; + if (!hbeStream) + { + if (beInitStream(&beConfig, &ibuf_size_spls, &obuf_size, (PHBE_STREAM) &hbeStream) != BE_ERR_SUCCESSFUL) + { + m_err++; + return -1; + } + ibuf_size = ibuf_size_spls * bytesPerSample; + + if (is_downmix) ibuf_size *= 2; + + bs = (char*)malloc(ibuf_size); + bs_size = 0; + } + + + *in_used = 0; + + int needbytes = ibuf_size - bs_size; + if (needbytes > 0 && in_avail > 0) + { + if (needbytes > in_avail) + needbytes = in_avail; + memcpy(bs + bs_size, in, needbytes); + bs_size += needbytes; + *in_used = needbytes; + } + if (!done) + { + if (bs_size < (int)ibuf_size) return 0; + + } + + if (out_avail < (int)obuf_size) return 0; + + int feedBytes = min(bs_size, (int)ibuf_size); + int feedSamples = feedBytes / bytesPerSample; + bs_size -= feedBytes; + + if (is_downmix) + { + int x; + int l = feedBytes / 4; + short *b = (short*)bs; + for (x = 0; x < l; x ++) + { + b[x] = b[x * 2] / 2 + b[x * 2 + 1] / 2; + } + feedSamples/=2; + } + DWORD dwo = 0; + + if (feedSamples) + { + if (beEncodeChunk(hbeStream, feedSamples, (short*)bs, (unsigned char*)out, &dwo) != BE_ERR_SUCCESSFUL) + { + m_err++; + return -1; + } + } + if (!dwo && done==1) + { + if (beDeinitStream(hbeStream, (unsigned char *)out, &dwo) != BE_ERR_SUCCESSFUL) + { + m_err++; + return -1; + } + done++; + } + + return dwo; +} + +void AudioCoderMP3::PrepareToFinish() +{ + done = 1; +} + +AudioCoderMP3::AudioCoderMP3(int nch, int srate, int bps, configtype *cfg) + : obuf_size(0), ibuf_size(0), ibuf_size_spls(0), done(false), bs_size(0) +{ + m_err = 0; + hbeStream = 0; + bs = 0; + is_downmix = 0; + mono_input=0; + + memset(&beConfig, 0, sizeof(beConfig)); // clear all fields + + if (srate != 32000 + && srate != 44100 + && srate != 48000 + && srate != 16000 /* MPEG 2 sample rates */ + && srate != 22050 + && srate != 24000 + && srate != 11025 /* MPEG 2.5 sample rates */ + && srate != 12000 + && srate != 8000) + { + //MessageBox(NULL, "The only valid audio sampling rates for the LAME mp3 encoder are:\r\t16000\r\t22050\r\t24000\r\t32000\r\t44100\r\t48000\r\rPlease modify the encoding profile to use one of these sampling rates, and then try again.", "Invalid sampling rate", MB_OK); + m_err++; + return ; + } + + // use the LAME config structure + beConfig.dwConfig = BE_CONFIG_LAME; + + // this are the default settings for testcase.wav + beConfig.format.LHV1.dwStructVersion = 1; + beConfig.format.LHV1.dwStructSize = sizeof(beConfig); + beConfig.format.LHV1.dwSampleRate = srate; // INPUT FREQUENCY + + beConfig.format.LHV1.nMode = cfg->stereo_mode; + if (nch < 2) + { + beConfig.format.LHV1.nMode = BE_MP3_MODE_MONO; + mono_input=1; + } + else + { + if (beConfig.format.LHV1.nMode == BE_MP3_MODE_MONO) + is_downmix = 1; + // beConfig.format.LHV1.nMode = BE_MP3_MODE_STEREO; + } + + beConfig.format.LHV1.dwBitrate = cfg->bitrate; + beConfig.format.LHV1.dwMaxBitrate = (cfg->vbr_method != -1 ? cfg->vbr_max_bitrate : cfg->bitrate); + + beConfig.format.LHV1.dwReSampleRate = 0; // DOWNSAMPLERATE, 0=ENCODER DECIDES + + if (beConfig.format.LHV1.dwMaxBitrate < 32) // less than 32, let's force mono + { + if (nch > 1) + { + beConfig.format.LHV1.nMode = BE_MP3_MODE_MONO; + is_downmix = 1; + } + } + + /*int effective_nch = (beConfig.format.LHV1.nMode == BE_MP3_MODE_MONO) ? 1 : 2; + + if (beConfig.format.LHV1.dwReSampleRate >= 32000 && + beConfig.format.LHV1.dwMaxBitrate / effective_nch <= 32) + { + beConfig.format.LHV1.dwReSampleRate /= 2; + }*/ +/* + if (beConfig.format.LHV1.dwReSampleRate < 32000) + beConfig.format.LHV1.dwMpegVersion = MPEG2; // MPEG VERSION (I or II) + else + beConfig.format.LHV1.dwMpegVersion = MPEG1; // MPEG VERSION (I or II) +*/ + + beConfig.format.LHV1.nPreset = cfg->quality; + + beConfig.format.LHV1.dwPsyModel = 0; // USE DEFAULT PSYCHOACOUSTIC MODEL + beConfig.format.LHV1.dwEmphasis = 0; // NO EMPHASIS TURNED ON + + beConfig.format.LHV1.bOriginal = 0; // SET ORIGINAL FLAG + beConfig.format.LHV1.bCRC = 0; // INSERT CRC + beConfig.format.LHV1.bCopyright = 0; // SET COPYRIGHT FLAG + beConfig.format.LHV1.bPrivate = 0; // SET PRIVATE FLAG + beConfig.format.LHV1.bNoRes = 0; + beConfig.format.LHV1.bWriteVBRHeader = 1; + + beConfig.format.LHV1.bEnableVBR = cfg->vbr_method != VBR_METHOD_NONE; + beConfig.format.LHV1.nVBRQuality = cfg->vbr; + //beConfig.format.LHV1.dwVbrAbr_bps = (cfg->vbr_method != VBR_METHOD_NONE ? 1000 * cfg->abr_bitrate : 0); + beConfig.format.LHV1.dwVbrAbr_bps = (cfg->vbr_method == VBR_METHOD_ABR ? 1000 * cfg->abr_bitrate : 0); + beConfig.format.LHV1.nVbrMethod = (VBRMETHOD)cfg->vbr_method; + + bytesPerSample = bps / 8; +} \ No newline at end of file diff --git a/Src/Plugins/Encoder/enc_lame/MP3Coder.h b/Src/Plugins/Encoder/enc_lame/MP3Coder.h new file mode 100644 index 00000000..9a16b603 --- /dev/null +++ b/Src/Plugins/Encoder/enc_lame/MP3Coder.h @@ -0,0 +1,62 @@ +#ifndef NULLSOFT_MP3_CODER_H +#define NULLSOFT_MP3_CODER_H + +#include +#include "../nsv/enc_if.h" +#include "BladeMP3EncDLL.h" +#ifndef _BLADEDLL +extern BEINITSTREAM beInitStream; +extern BECLOSESTREAM beCloseStream; +extern BEENCODECHUNK beEncodeChunk; +extern BEDEINITSTREAM beDeinitStream; +extern BEWRITEVBRHEADER beWriteVBRHeader; +extern BEVERSION beVersion; +extern BEENCODECHUNKFLOATS16NI beEncodeChunkFloatS16NI; +#endif // !_BLADEDLL + + + +typedef struct +{ + int bitrate; + int vbr_max_bitrate; + int abr_bitrate; + int stereo_mode; //0=stereo,1=jstereo,2=mchannel,3=mono + int quality; //0=normal,1=low,2=high,3=voice,4=r3mix,5=vh + + int vbr; // 0=high-9=low + int vbr_method; // -1=none, 0=default, 1=old, 2=new, 3=mtrh, 4=abr + +} +configtype; +class AudioCoderMP3 : public AudioCoder +{ +public: + AudioCoderMP3(int nch, int srate, int bps, configtype *cfg); + int Encode(int framepos, void *in, int in_avail, int *in_used, void *out, int out_avail); + virtual ~AudioCoderMP3(); + int GetLastError(); + void setVbrFilename(char *filename); + void PrepareToFinish(); +protected: + int m_err; + DWORD obuf_size; + DWORD ibuf_size, ibuf_size_spls; + HBE_STREAM hbeStream; + BE_CONFIG beConfig; + int bytesPerSample; + int done; + char *bs; + int bs_size; + int is_downmix; + int mono_input; +}; + +class AudioCoderMP3_24 : public AudioCoderMP3 +{ +public: + AudioCoderMP3_24(int nch, int srate, int bps, configtype *cfg) : AudioCoderMP3(nch, srate, bps, cfg) {} +int Encode(int framepos, void *in, int in_avail, int *in_used, void *out, int out_avail); +}; + +#endif \ No newline at end of file diff --git a/Src/Plugins/Encoder/enc_lame/enc_lame.rc b/Src/Plugins/Encoder/enc_lame/enc_lame.rc new file mode 100644 index 00000000..b6d63988 --- /dev/null +++ b/Src/Plugins/Encoder/enc_lame/enc_lame.rc @@ -0,0 +1,145 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "#include ""version.rc2""\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_MP3 DIALOGEX 0, 0, 256, 167 +STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD +FONT 8, "MS Shell Dlg", 0, 0, 0x0 +BEGIN + GROUPBOX "MP3 Encoder Options",IDC_STATIC,0,0,256,166 + LTEXT "Mode:",IDC_STATIC,4,13,21,8 + COMBOBOX IDC_VBRMETHOD,28,11,52,155,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_STEREOMODE,87,11,66,184,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + LTEXT "Bitrate:",IDC_BITRATE_TEXT1,6,28,80,8 + COMBOBOX IDC_BITRATE,87,25,38,98,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + LTEXT "kbps",IDC_BITRATE_TEXT2,129,28,16,8 + LTEXT "VBR Maximum Bitrate:",IDC_MAXBITRATE_TEXT1,6,42,77,8 + COMBOBOX IDC_MAXBITRATE,87,39,38,98,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + LTEXT "kbps",IDC_MAXBITRATE_TEXT2,129,42,16,8 + LTEXT "Average Bitrate:",IDC_AVGBITRATE_TEXT1,6,56,77,8 + COMBOBOX IDC_AVGBITRATE,87,53,38,98,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + LTEXT "kbps",IDC_AVGBITRATE_TEXT2,129,56,16,8 + LTEXT "Quality:",IDC_STATIC,7,74,24,8 + COMBOBOX IDC_QUALITY,33,72,98,110,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + LTEXT "VBR Q:",IDC_VBRQ_TEXT,136,74,25,8 + COMBOBOX IDC_VBRQ,161,72,40,98,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + LTEXT "MPEG Layer-3 audio coding technology licensed from Fraunhofer IIS and Thomson.",IDC_STATIC,5,92,148,18,NOT WS_VISIBLE + LTEXT "MPEG Layer-3 encoding technology by Mp3dev.",IDC_STATIC,5,115,154,8 + LTEXT "",IDC_LAMEVERSION,136,153,114,8,0,WS_EX_RIGHT +END + +IDD_MP3_MISSING DIALOGEX 0, 0, 256, 167 +STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD +FONT 8, "MS Shell Dlg", 0, 0, 0x0 +BEGIN + GROUPBOX "MP3 Encoder Options",-1,0,0,256,166 + LTEXT "MP3 encoding support is not available as ""lame_enc.dll"" is not present.\n\nYou will need to manually download ""lame_enc.dll"" to the Winamp\Shared folder to enable MP3 encoding support.",-1,12,14,231,34 + LTEXT "Click here for information on where you can obtain ""lame_enc.dll"".",-1,12,54,231,10 + CONTROL "here",IDC_URL1,"Button",BS_OWNERDRAW | WS_TABSTOP,27,54,15,8 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + 65535 "{F1534ECA-6E64-42c2-9781-812E61154515}" +END + +STRINGTABLE +BEGIN + IDS_BITRATE "Bitrate:" + IDS_ABR_MIN_BITRATE "ABR Minimum Bitrate" + IDS_VBR_MIN_BITRATE "VBR Minimum Bitrate" + IDS_N_A "N/A" + IDS_ABR_MAX_BITRATE "ABR Maximum Bitrate" + IDS_VBR_MAX_BITRATE "VBR Maximum Bitrate" + IDS_AVERAGE_BITRATE "Average Bitrate" + IDS_STEREO "Stereo" + IDS_JOINT_STEREO "Joint Stereo" + IDS_MULTI_CHANNEL "Multi-Channel" + IDS_MONO "Mono" + IDS_NORMAL "Normal" + IDS_LOW "Low" + IDS_HIGH "High" + IDS_VOICE "Voice" + IDS_R3MIX "R3mix" +END + +STRINGTABLE +BEGIN + IDS_VERY_HIGH "Very High" + IDS_HIGH_BRACKET "(High)" + IDS_LOW_BRACKET "(Low)" + IDS_ENC_LAME_DESC "MP3 Encoder %s" + IDS_ENC_LAME_DESC_MISSING "MP3 Encoder %s [NOT LOADED]" +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// +#include "version.rc2" + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/Src/Plugins/Encoder/enc_lame/enc_lame.sln b/Src/Plugins/Encoder/enc_lame/enc_lame.sln new file mode 100644 index 00000000..4af92741 --- /dev/null +++ b/Src/Plugins/Encoder/enc_lame/enc_lame.sln @@ -0,0 +1,30 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29509.3 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "enc_lame", "enc_lame.vcxproj", "{861F24D8-9B22-42FA-B056-7A86D6B4500C}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {861F24D8-9B22-42FA-B056-7A86D6B4500C}.Debug|Win32.ActiveCfg = Debug|Win32 + {861F24D8-9B22-42FA-B056-7A86D6B4500C}.Debug|Win32.Build.0 = Debug|Win32 + {861F24D8-9B22-42FA-B056-7A86D6B4500C}.Debug|x64.ActiveCfg = Debug|x64 + {861F24D8-9B22-42FA-B056-7A86D6B4500C}.Debug|x64.Build.0 = Debug|x64 + {861F24D8-9B22-42FA-B056-7A86D6B4500C}.Release|Win32.ActiveCfg = Release|Win32 + {861F24D8-9B22-42FA-B056-7A86D6B4500C}.Release|Win32.Build.0 = Release|Win32 + {861F24D8-9B22-42FA-B056-7A86D6B4500C}.Release|x64.ActiveCfg = Release|x64 + {861F24D8-9B22-42FA-B056-7A86D6B4500C}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {CC41ED85-6766-472A-9C3A-5E89A14D82B6} + EndGlobalSection +EndGlobal diff --git a/Src/Plugins/Encoder/enc_lame/enc_lame.vcxproj b/Src/Plugins/Encoder/enc_lame/enc_lame.vcxproj new file mode 100644 index 00000000..6dcaa1eb --- /dev/null +++ b/Src/Plugins/Encoder/enc_lame/enc_lame.vcxproj @@ -0,0 +1,293 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {861F24D8-9B22-42FA-B056-7A86D6B4500C} + enc_lame + 10.0.19041.0 + + + + DynamicLibrary + v142 + Unicode + + + DynamicLibrary + v142 + Unicode + + + DynamicLibrary + v142 + Unicode + + + DynamicLibrary + v142 + Unicode + + + + + + + + + + + + + + + + + + + false + $(PlatformShortName)_$(Configuration)\ + $(PlatformShortName)_$(Configuration)\ + $(IncludePath) + $(LibraryPath) + + + false + $(PlatformShortName)_$(Configuration)\ + $(PlatformShortName)_$(Configuration)\ + $(IncludePath) + $(LibraryPath) + + + false + $(PlatformShortName)_$(Configuration)\ + $(PlatformShortName)_$(Configuration)\ + $(IncludePath) + $(LibraryPath) + + + false + $(PlatformShortName)_$(Configuration)\ + $(PlatformShortName)_$(Configuration)\ + $(IncludePath) + $(LibraryPath) + + + false + + + false + + + Debug + x86-windows-static-md + + + false + + + x86-windows-static-md + + + false + + + x86-windows-static-md + Debug + + + false + + + x86-windows-static-md + + + + Disabled + ..\..\..\Wasabi;..\..\..\external_dependencies\vcpkg\$(PlatformShortName)-windows-static\include\lame + _WINDOWS;_USRDLL;_BLADEDLL;NSV_CODER_MP3_EXPORTS;WINDOWS_IGNORE_PACKING_MISMATCH;WIN32;_DEBUG;%(PreprocessorDefinitions) + false + true + EnableFastChecks + MultiThreadedDebugDLL + Level3 + ProgramDatabase + Default + $(IntDir)$(TargetName).pdb + + + _DEBUG;%(PreprocessorDefinitions) + 0x0409 + + + shlwapi.lib;libmp3lame-static.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)$(TargetExt) + true + $(IntDir)$(TargetName).pdb + false + $(IntDir)$(TargetName).lib + MachineX86 + false + ..\..\..\external_dependencies\vcpkg\$(PlatformShortName)-windows-static\debug\lib\;%(AdditionalLibraryDirectories) + + + xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ +xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ +if exist ..\..\..\resources\libraries\lame_enc.dll xcopy /Y /D ..\..\..\resources\libraries\lame_enc.dll ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ + Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ and if exist ..\..\..\resources\libraries\lame_enc.dll xcopy /Y /D ..\..\..\resources\libraries\lame_enc.dll ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Shared\' + + + + + Disabled + ..\..\..\Wasabi;..\..\..\external_dependencies\vcpkg\$(PlatformShortName)-windows-static\include\lame + _DEBUG;_WINDOWS;_USRDLL;_BLADEDLL;NSV_CODER_MP3_EXPORTS;WIN64;%(PreprocessorDefinitions) + false + true + EnableFastChecks + MultiThreadedDebugDLL + Level3 + ProgramDatabase + $(IntDir)$(TargetName).pdb + + + _DEBUG;%(PreprocessorDefinitions) + 0x0409 + + + shlwapi.lib;..\..\..\external_dependencies\vcpkg\$(PlatformShortName)-windows-static\debug\lib\libmp3lame-static.lib + $(OutDir)$(TargetName)$(TargetExt) + true + $(IntDir)$(TargetName).pdb + false + $(IntDir)$(TargetName).lib + false + ..\..\..\external_dependencies\vcpkg\$(PlatformShortName)-windows-static\debug\lib\;%(AdditionalLibraryDirectories) + + + xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ +xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ +if exist ..\..\..\resources\libraries\lame_enc.dll xcopy /Y /D ..\..\..\resources\libraries\lame_enc.dll ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ + Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ and if exist ..\..\..\resources\libraries\lame_enc.dll xcopy /Y /D ..\..\..\resources\libraries\lame_enc.dll ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Shared\' + + + + + MinSpace + OnlyExplicitInline + Size + ..\..\..\Wasabi;..\..\..\external_dependencies\vcpkg\$(PlatformShortName)-windows-static\include\lame + _WINDOWS;_USRDLL;_BLADEDLL;NSV_CODER_MP3_EXPORTS;WINDOWS_IGNORE_PACKING_MISMATCH;NDEBUG;WIN32;%(PreprocessorDefinitions) + true + true + MultiThreadedDLL + true + Level3 + None + Default + $(IntDir)$(TargetName).pdb + + + NDEBUG;%(PreprocessorDefinitions) + 0x0409 + + + shlwapi.lib;libmp3lame-static.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)$(TargetExt) + false + $(IntDir)$(TargetName).pdb + true + true + false + $(IntDir)$(TargetName).lib + MachineX86 + false + ..\..\..\external_dependencies\vcpkg\$(PlatformShortName)-windows-static\lib\;%(AdditionalLibraryDirectories) + + + xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ +if exist ..\..\..\resources\libraries\lame_enc.dll xcopy /Y /D ..\..\..\resources\libraries\lame_enc.dll ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ + Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ and if exist ..\..\..\resources\libraries\lame_enc.dll xcopy /Y /D ..\..\..\resources\libraries\lame_enc.dll ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Shared\' + + + + + MinSpace + OnlyExplicitInline + Size + ..\..\..\Wasabi;..\..\..\external_dependencies\vcpkg\$(PlatformShortName)-windows-static\include\lame + NDEBUG;_WINDOWS;_USRDLL;NSV_CODER_MP3_EXPORTS;WIN64;%(PreprocessorDefinitions) + true + true + MultiThreadedDLL + true + Level3 + None + $(IntDir)$(TargetName).pdb + + + NDEBUG;%(PreprocessorDefinitions) + 0x0409 + + + shlwapi.lib;..\..\..\external_dependencies\vcpkg\$(PlatformShortName)-windows-static\lib\libmp3lame-static.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)$(TargetExt) + false + $(IntDir)$(TargetName).pdb + true + true + false + $(IntDir)$(TargetName).lib + false + ..\..\..\external_dependencies\vcpkg\$(PlatformShortName)-windows-static\debug\lib\;%(AdditionalLibraryDirectories) + + + xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ +if exist ..\resources\libraries\lame_enc.dll xcopy /Y /D ..\..\..\resources\libraries\lame_enc.dll ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ + Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ and if exist ..\..\..\resources\libraries\lame_enc.dll xcopy /Y /D ..\..\..\resources\libraries\lame_enc.dll ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Shared\' + + + + + + + + + + + + + + + + + + + + + {3e0bfa8a-b86a-42e9-a33f-ec294f823f7f} + + + + + + \ No newline at end of file diff --git a/Src/Plugins/Encoder/enc_lame/enc_lame.vcxproj.filters b/Src/Plugins/Encoder/enc_lame/enc_lame.vcxproj.filters new file mode 100644 index 00000000..67ebcd12 --- /dev/null +++ b/Src/Plugins/Encoder/enc_lame/enc_lame.vcxproj.filters @@ -0,0 +1,44 @@ + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + + + {dc9d6b9d-69d1-40a7-97c9-870cfa399631} + + + {3e0eac86-0886-4e85-a300-8b6cb497e2a7} + + + {bf0f4053-1c32-4453-a394-6542b1301613} + + + + + Ressource Files + + + \ No newline at end of file diff --git a/Src/Plugins/Encoder/enc_lame/main.cpp b/Src/Plugins/Encoder/enc_lame/main.cpp new file mode 100644 index 00000000..197c3bb1 --- /dev/null +++ b/Src/Plugins/Encoder/enc_lame/main.cpp @@ -0,0 +1,503 @@ +/* +** nsv_coder_lame: main.cpp - LAME mp3 encoder plug-in +** (requires lame_enc.dll) +** +** Copyright (C) 2001-2006 Nullsoft, Inc. +** +** This software is provided 'as-is', without any express or implied warranty. +** In no event will the authors be held liable for any damages arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, including commercial +** applications, and to alter it and redistribute it freely, subject to the following restrictions: +** 1. The origin of this software must not be misrepresented; you must not claim that you wrote the +** original software. If you use this software in a product, an acknowledgment in the product +** documentation would be appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be misrepresented as +** being the original software. +** 3. This notice may not be removed or altered from any source distribution. +*/ + +#define ENC_VERSION "v1.38" + +#include +#include "resource.h" +#include "BladeMP3EncDLL.h" +#include "MP3Coder.h" +#include +#include +#include + +// wasabi based services for localisation support +#include +#include "../Agave/Language/api_language.h" +#include "../winamp/wa_ipc.h" + +HWND winampwnd = 0; +api_service *WASABI_API_SVC = 0; +api_language *WASABI_API_LNG = 0; +HINSTANCE WASABI_API_LNG_HINST = 0, WASABI_API_ORIG_HINST = 0; + +int g_valid_bitrates[] = { 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, -1 }; + +static wchar_t lamedll[MAX_PATH]=L""; +HINSTANCE g_lamedll = 0; + +//BEINITSTREAM beInitStream=0; +//BECLOSESTREAM beCloseStream=0; +//BEENCODECHUNK beEncodeChunk=0; +//BEDEINITSTREAM beDeinitStream=0; +//BEWRITEVBRHEADER beWriteVBRHeader=0; +//BEVERSION beVersion=0; +//BEENCODECHUNKFLOATS16NI beEncodeChunkFloatS16NI=0; + +static HINSTANCE GetMyInstance() +{ + MEMORY_BASIC_INFORMATION mbi = {0}; + if(VirtualQuery(GetMyInstance, &mbi, sizeof(mbi))) + return (HINSTANCE)mbi.AllocationBase; + return NULL; +} + +void GetLocalisationApiService(void) +{ + if(!WASABI_API_LNG) + { + // loader so that we can get the localisation service api for use + if(!WASABI_API_SVC) + { + WASABI_API_SVC = (api_service*)SendMessage(winampwnd, WM_WA_IPC, 0, IPC_GET_API_SERVICE); + if (WASABI_API_SVC == (api_service*)1) + { + WASABI_API_SVC = NULL; + return; + } + } + + if(!WASABI_API_LNG) + { + waServiceFactory *sf; + sf = WASABI_API_SVC->service_getServiceByGuid(languageApiGUID); + if (sf) WASABI_API_LNG = reinterpret_cast(sf->getInterface()); + } + + // need to have this initialised before we try to do anything with localisation features + WASABI_API_START_LANG(GetMyInstance(),EncLameLangGUID); + } +} +/* +BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) +{ + return TRUE; +}*/ + +typedef struct +{ + configtype cfg; + char configfile[MAX_PATH]; +} +configwndrec; + +void BuildVersionString(wchar_t version[128]) +{ + BE_VERSION ver; + beVersion(&ver); + + if (ver.byBetaLevel) + StringCchPrintf(version, 128, L"%u.%ub%u", (unsigned int)ver.byMajorVersion, (unsigned int)ver.byMinorVersion, (unsigned int)ver.byBetaLevel); + else if (ver.byAlphaLevel) + StringCchPrintf(version, 128, L"%u.%ua%u", (unsigned int)ver.byMajorVersion, (unsigned int)ver.byMinorVersion, (unsigned int)ver.byAlphaLevel); + else + StringCchPrintf(version, 128, L"%u.%u", (unsigned int)ver.byMajorVersion, (unsigned int)ver.byMinorVersion); +} + +void readconfig(char *configfile, configtype *cfg) +{ + cfg->bitrate = 128; + cfg->vbr_max_bitrate = 320; + cfg->abr_bitrate = 128; + + cfg->stereo_mode = 1; + cfg->quality = LQP_FAST_STANDARD; + + cfg->vbr = 2; + cfg->vbr_method = VBR_METHOD_NEW; + + if (configfile) + GetPrivateProfileStructA("audio_mp3l", "conf", cfg, sizeof(configtype), configfile); +} + +void writeconfig(char* configfile, configtype *cfg) +{ + if (configfile) + WritePrivateProfileStructA("audio_mp3l", "conf", cfg, sizeof(configtype), configfile); +} + +extern "C" +{ + unsigned int __declspec(dllexport) GetAudioTypes3(int idx, char *desc) + { + //if ((g_lamedll != NULL) && (beInitStream != NULL) && (beCloseStream != NULL) && (beEncodeChunk != NULL) && (beDeinitStream != NULL) && (beWriteVBRHeader != NULL) && (beVersion != NULL)) + if (idx == 0) + { + GetLocalisationApiService(); + StringCchPrintfA(desc, 1024, WASABI_API_LNGSTRING(IDS_ENC_LAME_DESC), ENC_VERSION); + return mmioFOURCC('M', 'P', '3', 'l'); + } + return 0; + } + + AudioCoder __declspec(dllexport) *CreateAudio3(int nch, int srate, int bps, unsigned int srct, unsigned int *outt, char *configfile) + { + if (nch > 2 || srate > 48000) + return NULL; + + if (srct == mmioFOURCC('P', 'C', 'M', ' ') && *outt == mmioFOURCC('M', 'P', '3', 'l')) + { + configtype cfg; + readconfig(configfile, &cfg); + *outt = mmioFOURCC('M', 'P', '3', ' '); + AudioCoderMP3 *t=0; + if (bps != 16) + t = new AudioCoderMP3_24(nch, srate, bps, &cfg); + else + t = new AudioCoderMP3(nch, srate, bps, &cfg); + if (t->GetLastError()) + { + delete t; + return NULL; + } + return t; + } + return NULL; + } + + void __declspec(dllexport) FinishAudio3(const char *filename, AudioCoder *coder) + { + //beWriteVBRHeader(filename); + ((AudioCoderMP3*)coder)->setVbrFilename((char *)filename); //apparently this needs to be called after beCloseStream + } + + void __declspec(dllexport) PrepareToFinish(const char *filename, AudioCoder *coder) + { + ((AudioCoderMP3*)coder)->PrepareToFinish(); + } + + static void setBitrates(HWND hwndDlg, int mi, int ma) + { + int i = 0; + while (g_valid_bitrates[i] > 0) + { + if (g_valid_bitrates[i] == mi) SendDlgItemMessage(hwndDlg, IDC_BITRATE, CB_SETCURSEL, i, 0); + if (g_valid_bitrates[i] == ma) SendDlgItemMessage(hwndDlg, IDC_MAXBITRATE, CB_SETCURSEL, i, 0); + i++; + } + } + + BOOL CALLBACK DlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) + { + if (uMsg == WM_USER + 666) + { + int vbr_meth = (int)SendDlgItemMessage(hwndDlg, IDC_VBRMETHOD, CB_GETCURSEL, 0, 0); + if (vbr_meth == CB_ERR) vbr_meth = 0; + vbr_meth--; + EnableWindow(GetDlgItem(hwndDlg, IDC_VBRQ), vbr_meth != -1 && vbr_meth != 4); + EnableWindow(GetDlgItem(hwndDlg, IDC_VBRQ_TEXT), vbr_meth != -1 && vbr_meth != 4); + SetDlgItemTextW(hwndDlg, IDC_BITRATE_TEXT1, WASABI_API_LNGSTRINGW((vbr_meth == -1 ? IDS_BITRATE : vbr_meth == 4 ? IDS_ABR_MIN_BITRATE : IDS_VBR_MIN_BITRATE))); + SetDlgItemTextW(hwndDlg, IDC_MAXBITRATE_TEXT1, WASABI_API_LNGSTRINGW((vbr_meth == -1 ? IDS_N_A : vbr_meth == 4 ? IDS_ABR_MAX_BITRATE : IDS_VBR_MAX_BITRATE))); + SetDlgItemTextW(hwndDlg, IDC_AVGBITRATE_TEXT1, WASABI_API_LNGSTRINGW((vbr_meth != 4 ? IDS_N_A : IDS_AVERAGE_BITRATE))); + + EnableWindow(GetDlgItem(hwndDlg, IDC_MAXBITRATE_TEXT1), vbr_meth != -1); + EnableWindow(GetDlgItem(hwndDlg, IDC_MAXBITRATE_TEXT2), vbr_meth != -1); + EnableWindow(GetDlgItem(hwndDlg, IDC_MAXBITRATE), vbr_meth != -1); + EnableWindow(GetDlgItem(hwndDlg, IDC_AVGBITRATE_TEXT1), vbr_meth == 4); + EnableWindow(GetDlgItem(hwndDlg, IDC_AVGBITRATE_TEXT2), vbr_meth == 4); + EnableWindow(GetDlgItem(hwndDlg, IDC_AVGBITRATE), vbr_meth == 4); + + int qual = (int)SendDlgItemMessage(hwndDlg, IDC_QUALITY, CB_GETCURSEL, 0, 0); + if (qual == CB_ERR) qual = 0; + switch (qual) + { + case LQP_R3MIX: + case LQP_STANDARD: + case LQP_FAST_STANDARD: + case LQP_EXTREME: + case LQP_FAST_EXTREME: + case LQP_INSANE: + case LQP_MEDIUM: + case LQP_FAST_MEDIUM: + case LQP_ABR: + case LQP_CBR: + EnableWindow(GetDlgItem(hwndDlg, IDC_STEREOMODE), 0); + EnableWindow(GetDlgItem(hwndDlg, IDC_VBRMETHOD), 0); + break; + default: + EnableWindow(GetDlgItem(hwndDlg, IDC_STEREOMODE), 1); + EnableWindow(GetDlgItem(hwndDlg, IDC_VBRMETHOD), 1); + break; + } + if (qual == 4) EnableWindow(GetDlgItem(hwndDlg, IDC_VBRQ), 0); + } + + if (uMsg == WM_USER + 667) + { + int qual = (int)SendDlgItemMessage(hwndDlg, IDC_QUALITY, CB_GETCURSEL, 0, 0); + if (qual == CB_ERR) qual = 0; + switch (qual) + { + case LQP_R3MIX: + SendDlgItemMessage(hwndDlg, IDC_VBRMETHOD, CB_SETCURSEL, 3, 0); + setBitrates(hwndDlg, 96, 224); + SendDlgItemMessage(hwndDlg, IDC_STEREOMODE, CB_SETCURSEL, 1, 0); + SendDlgItemMessage(hwndDlg, IDC_VBRQ, CB_SETCURSEL, 1, 0); + break; + case LQP_STANDARD: /* check for alt-preset standard */ + SendDlgItemMessage(hwndDlg, IDC_VBRMETHOD, CB_SETCURSEL, 2, 0); + setBitrates(hwndDlg, 32, 320); + SendDlgItemMessage(hwndDlg, IDC_STEREOMODE, CB_SETCURSEL, 1, 0); + SendDlgItemMessage(hwndDlg, IDC_VBRQ, CB_SETCURSEL, 2, 0); + break; + case LQP_FAST_STANDARD: /* check for alt-preset fast standard */ + SendDlgItemMessage(hwndDlg, IDC_VBRMETHOD, CB_SETCURSEL, 3, 0); + setBitrates(hwndDlg, 32, 320); + SendDlgItemMessage(hwndDlg, IDC_STEREOMODE, CB_SETCURSEL, 1, 0); + SendDlgItemMessage(hwndDlg, IDC_VBRQ, CB_SETCURSEL, 2, 0); + break; + case LQP_MEDIUM: /* check for alt-preset medium */ + SendDlgItemMessage(hwndDlg, IDC_VBRMETHOD, CB_SETCURSEL, 2, 0); + setBitrates(hwndDlg, 32, 320); + SendDlgItemMessage(hwndDlg, IDC_STEREOMODE, CB_SETCURSEL, 1, 0); + SendDlgItemMessage(hwndDlg, IDC_VBRQ, CB_SETCURSEL, 2, 0); + break; + case LQP_FAST_MEDIUM: /* check for alt-preset fast medium */ + SendDlgItemMessage(hwndDlg, IDC_VBRMETHOD, CB_SETCURSEL, 3, 0); + setBitrates(hwndDlg, 32, 320); + SendDlgItemMessage(hwndDlg, IDC_STEREOMODE, CB_SETCURSEL, 1, 0); + SendDlgItemMessage(hwndDlg, IDC_VBRQ, CB_SETCURSEL, 2, 0); + break; + case LQP_EXTREME: /* check for alt-preset extreme */ + SendDlgItemMessage(hwndDlg, IDC_VBRMETHOD, CB_SETCURSEL, 2, 0); + setBitrates(hwndDlg, 32, 320); + SendDlgItemMessage(hwndDlg, IDC_STEREOMODE, CB_SETCURSEL, 1, 0); + SendDlgItemMessage(hwndDlg, IDC_VBRQ, CB_SETCURSEL, 2, 0); + break; + case LQP_FAST_EXTREME: /* check for alt-preset fast extreme */ + SendDlgItemMessage(hwndDlg, IDC_VBRMETHOD, CB_SETCURSEL, 3, 0); + setBitrates(hwndDlg, 32, 320); + SendDlgItemMessage(hwndDlg, IDC_STEREOMODE, CB_SETCURSEL, 1, 0); + SendDlgItemMessage(hwndDlg, IDC_VBRQ, CB_SETCURSEL, 2, 0); + break; + case LQP_INSANE: /* check for alt-preset fast insane */ + SendDlgItemMessage(hwndDlg, IDC_VBRMETHOD, CB_SETCURSEL, 0, 0); + setBitrates(hwndDlg, 320, 320); + SendDlgItemMessage(hwndDlg, IDC_STEREOMODE, CB_SETCURSEL, 1, 0); + break; + case LQP_ABR: /* check for alt-preset fast insane */ + SendDlgItemMessage(hwndDlg, IDC_VBRMETHOD, CB_SETCURSEL, 5, 0); + setBitrates(hwndDlg, 64, 320); + SendDlgItemMessage(hwndDlg, IDC_STEREOMODE, CB_SETCURSEL, 1, 0); + SendDlgItemMessage(hwndDlg, IDC_VBRQ, CB_SETCURSEL, 2, 0); + break; + case LQP_CBR: /* check for alt-preset fast insane */ + SendDlgItemMessage(hwndDlg, IDC_VBRMETHOD, CB_SETCURSEL, 0, 0); + SendDlgItemMessage(hwndDlg, IDC_STEREOMODE, CB_SETCURSEL, 1, 0); + break; + } + SendMessage(hwndDlg, WM_USER + 666, 0, 0); + } + + if (uMsg == WM_INITDIALOG) + { +#if defined (_WIN64) + SetWindowLongPtr(hwndDlg, GWLP_USERDATA, lParam); +#else + SetWindowLong(hwndDlg, GWL_USERDATA, lParam); +#endif + wchar_t versionText[128] = {0}; + BuildVersionString(versionText); + SetDlgItemText(hwndDlg, IDC_LAMEVERSION, versionText); + if (lParam) + { + configwndrec *wc = (configwndrec*)lParam; + + // -1=none, 0=default, 1=old, 2=new, 3=mtrh, 4=abr + SendDlgItemMessageW(hwndDlg, IDC_VBRMETHOD, CB_ADDSTRING, 0, (LPARAM)L"CBR"); + SendDlgItemMessageW(hwndDlg, IDC_VBRMETHOD, CB_ADDSTRING, 0, (LPARAM)L"VBR default"); + SendDlgItemMessageW(hwndDlg, IDC_VBRMETHOD, CB_ADDSTRING, 0, (LPARAM)L"VBR old"); + SendDlgItemMessageW(hwndDlg, IDC_VBRMETHOD, CB_ADDSTRING, 0, (LPARAM)L"VBR new"); + SendDlgItemMessageW(hwndDlg, IDC_VBRMETHOD, CB_ADDSTRING, 0, (LPARAM)L"VBR mtrh"); + SendDlgItemMessageW(hwndDlg, IDC_VBRMETHOD, CB_ADDSTRING, 0, (LPARAM)L"ABR"); + SendDlgItemMessageW(hwndDlg, IDC_VBRMETHOD, CB_SETCURSEL, wc->cfg.vbr_method + 1, 0); + + //0=stereo,1=jstereo,2=mchannel,3=mono + SendDlgItemMessageW(hwndDlg, IDC_STEREOMODE, CB_ADDSTRING, 0, (LPARAM)WASABI_API_LNGSTRINGW(IDS_STEREO)); + SendDlgItemMessageW(hwndDlg, IDC_STEREOMODE, CB_ADDSTRING, 0, (LPARAM)WASABI_API_LNGSTRINGW(IDS_JOINT_STEREO)); + SendDlgItemMessageW(hwndDlg, IDC_STEREOMODE, CB_ADDSTRING, 0, (LPARAM)WASABI_API_LNGSTRINGW(IDS_MULTI_CHANNEL)); + SendDlgItemMessageW(hwndDlg, IDC_STEREOMODE, CB_ADDSTRING, 0, (LPARAM)WASABI_API_LNGSTRINGW(IDS_MONO)); + SendDlgItemMessageW(hwndDlg, IDC_STEREOMODE, CB_SETCURSEL, wc->cfg.stereo_mode, 0); + + { + int i = 0; + while (g_valid_bitrates[i] > 0) + { + wchar_t buf[64] = {0}; + StringCchPrintf(buf, 64, L"%d", g_valid_bitrates[i]); + SendDlgItemMessage(hwndDlg, IDC_BITRATE , CB_ADDSTRING, 0, (LPARAM)buf); + SendDlgItemMessage(hwndDlg, IDC_MAXBITRATE, CB_ADDSTRING, 0, (LPARAM)buf); + SendDlgItemMessage(hwndDlg, IDC_AVGBITRATE, CB_ADDSTRING, 0, (LPARAM)buf); + SendDlgItemMessage(hwndDlg, IDC_BITRATE , CB_SETITEMDATA, i, g_valid_bitrates[i]); + SendDlgItemMessage(hwndDlg, IDC_MAXBITRATE, CB_SETITEMDATA, i, g_valid_bitrates[i]); + SendDlgItemMessage(hwndDlg, IDC_AVGBITRATE, CB_SETITEMDATA, i, g_valid_bitrates[i]); + i++; + }; + + i = 0; + while (g_valid_bitrates[i] > 0) + { + if (g_valid_bitrates[i] == wc->cfg.bitrate ) SendDlgItemMessage(hwndDlg, IDC_BITRATE , CB_SETCURSEL, i, 0); + if (g_valid_bitrates[i] == wc->cfg.abr_bitrate ) SendDlgItemMessage(hwndDlg, IDC_AVGBITRATE, CB_SETCURSEL, i, 0); + if (g_valid_bitrates[i] == wc->cfg.vbr_max_bitrate) SendDlgItemMessage(hwndDlg, IDC_MAXBITRATE, CB_SETCURSEL, i, 0); + i++; + } + } + + SendDlgItemMessageW(hwndDlg, IDC_QUALITY, CB_ADDSTRING, 0, (LPARAM)WASABI_API_LNGSTRINGW(IDS_NORMAL)); + SendDlgItemMessageW(hwndDlg, IDC_QUALITY, CB_ADDSTRING, 0, (LPARAM)WASABI_API_LNGSTRINGW(IDS_LOW)); + SendDlgItemMessageW(hwndDlg, IDC_QUALITY, CB_ADDSTRING, 0, (LPARAM)WASABI_API_LNGSTRINGW(IDS_HIGH)); + SendDlgItemMessageW(hwndDlg, IDC_QUALITY, CB_ADDSTRING, 0, (LPARAM)WASABI_API_LNGSTRINGW(IDS_VOICE)); + SendDlgItemMessageW(hwndDlg, IDC_QUALITY, CB_ADDSTRING, 0, (LPARAM)WASABI_API_LNGSTRINGW(IDS_R3MIX)); + SendDlgItemMessageW(hwndDlg, IDC_QUALITY, CB_ADDSTRING, 0, (LPARAM)WASABI_API_LNGSTRINGW(IDS_VERY_HIGH)); + SendDlgItemMessageW(hwndDlg, IDC_QUALITY, CB_ADDSTRING, 0, (LPARAM)L"--alt-preset standard"); + SendDlgItemMessageW(hwndDlg, IDC_QUALITY, CB_ADDSTRING, 0, (LPARAM)L"--alt-preset fast standard"); + SendDlgItemMessageW(hwndDlg, IDC_QUALITY, CB_ADDSTRING, 0, (LPARAM)L"--alt-preset extreme"); + SendDlgItemMessageW(hwndDlg, IDC_QUALITY, CB_ADDSTRING, 0, (LPARAM)L"--alt-preset fast extreme"); + SendDlgItemMessageW(hwndDlg, IDC_QUALITY, CB_ADDSTRING, 0, (LPARAM)L"--alt-preset insane"); + SendDlgItemMessageW(hwndDlg, IDC_QUALITY, CB_ADDSTRING, 0, (LPARAM)L"--alt-preset abr"); + SendDlgItemMessageW(hwndDlg, IDC_QUALITY, CB_ADDSTRING, 0, (LPARAM)L"--alt-preset cbr"); + SendDlgItemMessageW(hwndDlg, IDC_QUALITY, CB_ADDSTRING, 0, (LPARAM)L"--alt-preset medium"); + SendDlgItemMessageW(hwndDlg, IDC_QUALITY, CB_ADDSTRING, 0, (LPARAM)L"--alt-preset fast medium"); + SendDlgItemMessageW(hwndDlg, IDC_QUALITY, CB_SETCURSEL, wc->cfg.quality, 0); + + int x; + for (x = 0; x < 10; x ++) + { + wchar_t buf[123] = {0}; + StringCchPrintfW(buf, 123, L"%d%s", x, x == 0 ? WASABI_API_LNGSTRINGW(IDS_HIGH_BRACKET) : x == 9 ? WASABI_API_LNGSTRINGW(IDS_LOW_BRACKET) : L""); + SendDlgItemMessageW(hwndDlg, IDC_VBRQ, CB_ADDSTRING, 0, (LPARAM)buf); + } + SendDlgItemMessage(hwndDlg, IDC_VBRQ, CB_SETCURSEL, wc->cfg.vbr, 0); + + SendMessage(hwndDlg, WM_USER + 666, 0, 0); + } + } + + if (uMsg == WM_COMMAND) + { + if (LOWORD(wParam) == IDC_VBRMETHOD && HIWORD(wParam) == CBN_SELCHANGE) + SendMessage(hwndDlg, WM_USER + 666, 0, 0); + if (LOWORD(wParam) == IDC_QUALITY && HIWORD(wParam) == CBN_SELCHANGE) + SendMessage(hwndDlg, WM_USER + 667, 0, 0); + } + + if (uMsg == WM_DESTROY) + { +#if defined (_WIN64) + configwndrec *wc = (configwndrec*)SetWindowLongPtr(hwndDlg, GWLP_USERDATA, 0); +#else + configwndrec* wc = (configwndrec*)SetWindowLong(hwndDlg, GWL_USERDATA, 0); +#endif + if (wc) + { + wc->cfg.bitrate = (int)SendDlgItemMessage(hwndDlg, IDC_BITRATE , CB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_BITRATE , CB_GETCURSEL, 0, 0), 0); + wc->cfg.vbr_max_bitrate = (int)SendDlgItemMessage(hwndDlg, IDC_MAXBITRATE, CB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_MAXBITRATE, CB_GETCURSEL, 0, 0), 0); + wc->cfg.abr_bitrate = (int)SendDlgItemMessage(hwndDlg, IDC_AVGBITRATE, CB_GETITEMDATA, SendDlgItemMessage(hwndDlg, IDC_AVGBITRATE, CB_GETCURSEL, 0, 0), 0); + int vbr_meth = (int)SendDlgItemMessage(hwndDlg, IDC_VBRMETHOD, CB_GETCURSEL, 0, 0); + if (vbr_meth == CB_ERR) vbr_meth = 0; + wc->cfg.vbr_method = vbr_meth - 1; + wc->cfg.stereo_mode = (int)SendDlgItemMessage(hwndDlg, IDC_STEREOMODE, CB_GETCURSEL, 0, 0); + wc->cfg.quality = (int)SendDlgItemMessage(hwndDlg, IDC_QUALITY, CB_GETCURSEL, 0, 0); + wc->cfg.vbr = (int)SendDlgItemMessage(hwndDlg, IDC_VBRQ, CB_GETCURSEL, 0, 0); + writeconfig(wc->configfile, &wc->cfg); + free(wc); + } + } + return 0; + } + + HWND __declspec(dllexport) ConfigAudio3(HWND hwndParent, HINSTANCE hinst, unsigned int outt, char*configfile) + { + if (outt == mmioFOURCC('M', 'P', '3', 'l')) + { + configwndrec *wr = (configwndrec*)malloc(sizeof(configwndrec)); + if (configfile) lstrcpynA(wr->configfile, configfile, MAX_PATH); + else wr->configfile[0] = 0; + + readconfig(configfile, &wr->cfg); + GetLocalisationApiService(); + return WASABI_API_CREATEDIALOGPARAMW(IDD_MP3, hwndParent, DlgProc, (LPARAM)wr); + } + return NULL; + } + + int __declspec(dllexport) SetConfigItem(unsigned int outt, wchar_t*item, char *data, char* configfile) + { + if (outt == mmioFOURCC('M', 'P', '3', 'l')) + { + configtype cfg; + readconfig(configfile, &cfg); + if (!lstrcmpi(item, L"bitrate")) // assume CBR bitrate + { + cfg.abr_bitrate = cfg.bitrate = atoi(data); + cfg.vbr_method = -1; + } + writeconfig(configfile, &cfg); + return 1; + } + return 0; + } + + int __declspec(dllexport) GetConfigItem(unsigned int outt, wchar_t *item, wchar_t *data, int len, char* configfile) + { + if (outt == mmioFOURCC('M', 'P', '3', 'l')) + { + configtype cfg; + readconfig(configfile, &cfg); + if (!lstrcmpi(item, L"bitrate")) + { + int bitrate; + if(cfg.vbr_method == -1) bitrate = cfg.bitrate; + else if(cfg.vbr_method == 4) bitrate = cfg.abr_bitrate; + else bitrate = (cfg.bitrate + cfg.vbr_max_bitrate) / 2; + StringCchPrintf(data,len,L"%d",bitrate); + } + return 1; + } + return 0; + } + + void __declspec(dllexport) SetWinampHWND(HWND hwnd) + { + winampwnd = hwnd; + + // this is called when the encoder is needed (and is slightly better than the dllmain loading from before) + + /*if (!g_lamedll) + { + if (!lamedll[0]) + { + PathCombineW(lamedll, (wchar_t*)SendMessage(hwnd, WM_WA_IPC, 0, IPC_GETSHAREDDLLDIRECTORYW),L"lame_enc.dll"); + } + g_lamedll = LoadLibraryW(lamedll); + }*/ + + /*if (g_lamedll) + { + beInitStream = (BEINITSTREAM) &beInitStream; + beCloseStream = (BECLOSESTREAM) GetProcAddress(g_lamedll, TEXT_BECLOSESTREAM); + beEncodeChunk = (BEENCODECHUNK) GetProcAddress(g_lamedll, TEXT_BEENCODECHUNK); + beDeinitStream = (BEDEINITSTREAM) GetProcAddress(g_lamedll, TEXT_BEDEINITSTREAM); + beWriteVBRHeader = (BEWRITEVBRHEADER) GetProcAddress(g_lamedll, TEXT_BEWRITEVBRHEADER); + beVersion = (BEVERSION) GetProcAddress(g_lamedll, TEXT_BEVERSION); + beEncodeChunkFloatS16NI = (BEENCODECHUNKFLOATS16NI)GetProcAddress(g_lamedll, TEXT_BEENCODECHUNKFLOATS16NI); + }*/ + } +}; \ No newline at end of file diff --git a/Src/Plugins/Encoder/enc_lame/resource.h b/Src/Plugins/Encoder/enc_lame/resource.h new file mode 100644 index 00000000..12db614a --- /dev/null +++ b/Src/Plugins/Encoder/enc_lame/resource.h @@ -0,0 +1,57 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by enc_lame.rc +// +#define IDS_BITRATE 0 +#define IDS_ABR_MIN_BITRATE 1 +#define IDS_VBR_MIN_BITRATE 2 +#define IDS_N_A 3 +#define IDS_ABR_MAX_BITRATE 4 +#define IDS_VBR_MAX_BITRATE 5 +#define IDS_AVERAGE_BITRATE 6 +#define IDS_STEREO 7 +#define IDS_JOINT_STEREO 8 +#define IDS_MULTI_CHANNEL 9 +#define IDS_MONO 10 +#define IDS_NORMAL 11 +#define IDS_LOW 12 +#define IDS_HIGH 13 +#define IDS_VOICE 14 +#define IDS_R3MIX 15 +#define IDS_VERY_HIGH 16 +#define IDS_HIGH_BRACKET 17 +#define IDS_LOW_BRACKET 18 +#define IDS_ENC_LAME_DESC 19 +#define IDS_ENC_LAME_DESC_MISSING 20 +#define IDD_MP3 102 +#define IDD_MP3_MISSING 105 +#define IDC_BITRATE 1000 +#define IDC_VBR 1001 +#define IDC_QUALITY 1002 +#define IDC_JSTEREO1 1003 +#define IDC_JSTEREO2 1004 +#define IDC_BITRATE_TEXT1 1004 +#define IDC_MAXBITRATE 1005 +#define IDC_BITRATE_TEXT2 1006 +#define IDC_MAXBITRATE_TEXT1 1007 +#define IDC_MAXBITRATE_TEXT2 1008 +#define IDC_VBRMETHOD 1009 +#define IDC_AVGBITRATE_TEXT1 1010 +#define IDC_AVGBITRATE 1011 +#define IDC_AVGBITRATE_TEXT2 1012 +#define IDC_STEREOMODE 1013 +#define IDC_VBRQ_TEXT 1014 +#define IDC_VBRQ 1015 +#define IDC_LAMEVERSION 1016 +#define IDC_URL1 1026 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 106 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1017 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/Src/Plugins/Encoder/enc_lame/version.rc2 b/Src/Plugins/Encoder/enc_lame/version.rc2 new file mode 100644 index 00000000..a1a762ea --- /dev/null +++ b/Src/Plugins/Encoder/enc_lame/version.rc2 @@ -0,0 +1,39 @@ + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// +#include "../../../Winamp/buildType.h" +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,38,0,0 + PRODUCTVERSION WINAMP_PRODUCTVER + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Winamp SA" + VALUE "FileDescription", "Winamp Encoder Plug-in" + VALUE "FileVersion", "1,38,0,0" + VALUE "InternalName", "Nullsoft MP3 Encoder" + VALUE "LegalCopyright", "Copyright © 2006-2023 Winamp SA" + VALUE "LegalTrademarks", "Nullsoft and Winamp are trademarks of Winamp SA" + VALUE "OriginalFilename", "enc_lame.dll" + VALUE "ProductName", "Winamp" + VALUE "ProductVersion", STR_WINAMP_PRODUCTVER + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/Src/Plugins/Encoder/enc_vorbis/Libs/libogg_static.lib b/Src/Plugins/Encoder/enc_vorbis/Libs/libogg_static.lib new file mode 100644 index 00000000..a4010fe6 Binary files /dev/null and b/Src/Plugins/Encoder/enc_vorbis/Libs/libogg_static.lib differ diff --git a/Src/Plugins/Encoder/enc_vorbis/Libs/vorbis_static.lib b/Src/Plugins/Encoder/enc_vorbis/Libs/vorbis_static.lib new file mode 100644 index 00000000..8b5409f9 Binary files /dev/null and b/Src/Plugins/Encoder/enc_vorbis/Libs/vorbis_static.lib differ diff --git a/Src/Plugins/Encoder/enc_vorbis/Libs/vorbisenc_static.lib b/Src/Plugins/Encoder/enc_vorbis/Libs/vorbisenc_static.lib new file mode 100644 index 00000000..4f7d7cc0 Binary files /dev/null and b/Src/Plugins/Encoder/enc_vorbis/Libs/vorbisenc_static.lib differ diff --git a/Src/Plugins/Encoder/enc_vorbis/enc_vorbis.rc b/Src/Plugins/Encoder/enc_vorbis/enc_vorbis.rc new file mode 100644 index 00000000..9eec33de --- /dev/null +++ b/Src/Plugins/Encoder/enc_vorbis/enc_vorbis.rc @@ -0,0 +1,100 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "#include ""version.rc2""\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_CONFIG DIALOGEX 0, 0, 256, 167 +STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_SYSMENU +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + GROUPBOX "Ogg Vorbis Encoder Options",IDC_STATIC,0,0,256,166 + LTEXT "Quality Factor: 0.0",IDC_VBRVAL,7,13,149,8 + CONTROL "",IDC_VBRQUALITY,"msctls_trackbar32",TBS_AUTOTICKS | WS_TABSTOP,5,23,243,17 + LTEXT "Low Quality\nSmall Files",IDC_VBR1,9,43,44,22 + LTEXT "High Quality\nLarge Files",IDC_VBR2,208,43,40,22 + LTEXT "Ogg Vorbis is an open, free audio format,\nThis encoder is based on aoTuV,",IDC_STATIC,5,143,242,19 + CONTROL "http://xiph.org/vorbis/",IDC_URL1,"Button",BS_OWNERDRAW | WS_TABSTOP,138,143,78,8 + CONTROL "https://ao-yumi.github.io/aotuv_web/index.html",IDC_URL2,"Button",BS_OWNERDRAW | WS_TABSTOP,109,151,138,8 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + 65535 "{A23C2B70-C66B-475e-8A67-E0F33FD5BD12}" +END + +STRINGTABLE +BEGIN + IDS_ENC_VORBIS_DESC "Ogg Vorbis Encoder" + IDS_QUALITY_FACTOR_F_KBPS "Quality Factor: %.1lf (%d kbps)" +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// +#include "version.rc2" + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/Src/Plugins/Encoder/enc_vorbis/enc_vorbis.sln b/Src/Plugins/Encoder/enc_vorbis/enc_vorbis.sln new file mode 100644 index 00000000..bc12bed6 --- /dev/null +++ b/Src/Plugins/Encoder/enc_vorbis/enc_vorbis.sln @@ -0,0 +1,60 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29519.181 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "enc_vorbis", "enc_vorbis.vcxproj", "{3AC18E6A-7C92-4156-8117-CAFC6A781DAD}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libvorbis_static", "..\libvorbis\win32\vorbis_static.vcxproj", "{49238ED1-3146-49AB-9523-E9826EE4A0C8}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libvorbisfile", "..\libvorbis\win32\vorbisfile_static.vcxproj", "{EC9475D2-FEE2-4F8C-9BB9-A11D5EB597C4}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libogg", "..\libogg\libogg.vcxproj", "{4FC28B55-2A14-43D5-86F7-201054F338A9}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {3AC18E6A-7C92-4156-8117-CAFC6A781DAD}.Debug|Win32.ActiveCfg = Debug|Win32 + {3AC18E6A-7C92-4156-8117-CAFC6A781DAD}.Debug|Win32.Build.0 = Debug|Win32 + {3AC18E6A-7C92-4156-8117-CAFC6A781DAD}.Debug|x64.ActiveCfg = Debug|x64 + {3AC18E6A-7C92-4156-8117-CAFC6A781DAD}.Debug|x64.Build.0 = Debug|x64 + {3AC18E6A-7C92-4156-8117-CAFC6A781DAD}.Release|Win32.ActiveCfg = Release|Win32 + {3AC18E6A-7C92-4156-8117-CAFC6A781DAD}.Release|Win32.Build.0 = Release|Win32 + {3AC18E6A-7C92-4156-8117-CAFC6A781DAD}.Release|x64.ActiveCfg = Release|x64 + {3AC18E6A-7C92-4156-8117-CAFC6A781DAD}.Release|x64.Build.0 = Release|x64 + {49238ED1-3146-49AB-9523-E9826EE4A0C8}.Debug|Win32.ActiveCfg = Debug|Win32 + {49238ED1-3146-49AB-9523-E9826EE4A0C8}.Debug|Win32.Build.0 = Debug|Win32 + {49238ED1-3146-49AB-9523-E9826EE4A0C8}.Debug|x64.ActiveCfg = Debug|x64 + {49238ED1-3146-49AB-9523-E9826EE4A0C8}.Debug|x64.Build.0 = Debug|x64 + {49238ED1-3146-49AB-9523-E9826EE4A0C8}.Release|Win32.ActiveCfg = Release|Win32 + {49238ED1-3146-49AB-9523-E9826EE4A0C8}.Release|Win32.Build.0 = Release|Win32 + {49238ED1-3146-49AB-9523-E9826EE4A0C8}.Release|x64.ActiveCfg = Release|x64 + {49238ED1-3146-49AB-9523-E9826EE4A0C8}.Release|x64.Build.0 = Release|x64 + {EC9475D2-FEE2-4F8C-9BB9-A11D5EB597C4}.Debug|Win32.ActiveCfg = Debug|Win32 + {EC9475D2-FEE2-4F8C-9BB9-A11D5EB597C4}.Debug|Win32.Build.0 = Debug|Win32 + {EC9475D2-FEE2-4F8C-9BB9-A11D5EB597C4}.Debug|x64.ActiveCfg = Debug|x64 + {EC9475D2-FEE2-4F8C-9BB9-A11D5EB597C4}.Debug|x64.Build.0 = Debug|x64 + {EC9475D2-FEE2-4F8C-9BB9-A11D5EB597C4}.Release|Win32.ActiveCfg = Release|Win32 + {EC9475D2-FEE2-4F8C-9BB9-A11D5EB597C4}.Release|Win32.Build.0 = Release|Win32 + {EC9475D2-FEE2-4F8C-9BB9-A11D5EB597C4}.Release|x64.ActiveCfg = Release|x64 + {EC9475D2-FEE2-4F8C-9BB9-A11D5EB597C4}.Release|x64.Build.0 = Release|x64 + {4FC28B55-2A14-43D5-86F7-201054F338A9}.Debug|Win32.ActiveCfg = Debug|Win32 + {4FC28B55-2A14-43D5-86F7-201054F338A9}.Debug|Win32.Build.0 = Debug|Win32 + {4FC28B55-2A14-43D5-86F7-201054F338A9}.Debug|x64.ActiveCfg = Debug|x64 + {4FC28B55-2A14-43D5-86F7-201054F338A9}.Debug|x64.Build.0 = Debug|x64 + {4FC28B55-2A14-43D5-86F7-201054F338A9}.Release|Win32.ActiveCfg = Release|Win32 + {4FC28B55-2A14-43D5-86F7-201054F338A9}.Release|Win32.Build.0 = Release|Win32 + {4FC28B55-2A14-43D5-86F7-201054F338A9}.Release|x64.ActiveCfg = Release|x64 + {4FC28B55-2A14-43D5-86F7-201054F338A9}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {3845173A-0CB9-4C0E-86B0-284CCE7B043A} + EndGlobalSection +EndGlobal diff --git a/Src/Plugins/Encoder/enc_vorbis/enc_vorbis.vcxproj b/Src/Plugins/Encoder/enc_vorbis/enc_vorbis.vcxproj new file mode 100644 index 00000000..fff72316 --- /dev/null +++ b/Src/Plugins/Encoder/enc_vorbis/enc_vorbis.vcxproj @@ -0,0 +1,297 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {3AC18E6A-7C92-4156-8117-CAFC6A781DAD} + enc_vorbis + 10.0.19041.0 + + + + DynamicLibrary + v142 + Unicode + + + DynamicLibrary + v142 + Unicode + + + DynamicLibrary + v142 + Unicode + + + DynamicLibrary + v142 + Unicode + + + + + + + + + + + + + + + + + + + false + $(PlatformShortName)_$(Configuration)\ + $(PlatformShortName)_$(Configuration)\ + $(IncludePath) + $(LibraryPath) + + + false + $(PlatformShortName)_$(Configuration)\ + $(PlatformShortName)_$(Configuration)\ + + + false + $(PlatformShortName)_$(Configuration)\ + $(PlatformShortName)_$(Configuration)\ + $(IncludePath) + $(LibraryPath) + + + false + $(PlatformShortName)_$(Configuration)\ + $(PlatformShortName)_$(Configuration)\ + + + false + + + + + false + x86-windows-static-md + + + + + false + x86-windows-static-md + + + + + false + Debug + x86-windows-static-md + + + + + false + x86-windows-static-md + Debug + + + + Disabled + .;..\..\..\Wasabi;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;_USRDLL;NSV_CODER_MP3_EXPORTS;%(PreprocessorDefinitions) + false + true + EnableFastChecks + MultiThreadedDebugDLL + $(IntDir) + $(IntDir) + $(IntDir)$(TargetName).pdb + Level3 + ProgramDatabase + Default + + + _DEBUG;%(PreprocessorDefinitions) + 0x0409 + + + /ignore:4204 %(AdditionalOptions) + uxtheme.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)$(TargetExt) + ..\..\..\external_dependencies\openmpt-trunk\build\lib\$(PlatformShortName)_$(Configuration);%(AdditionalLibraryDirectories) + uxtheme.dll;%(DelayLoadDLLs) + true + $(IntDir)$(TargetName).pdb + false + $(IntDir)$(TargetName).lib + MachineX86 + false + + + xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ +xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ + Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\' + + + + + Disabled + .;..\..\..\Wasabi;%(AdditionalIncludeDirectories) + WIN64;_DEBUG;_WINDOWS;_USRDLL;NSV_CODER_MP3_EXPORTS;%(PreprocessorDefinitions) + false + true + EnableFastChecks + MultiThreadedDebugDLL + $(IntDir) + $(IntDir) + $(IntDir)$(TargetName).pdb + Level3 + ProgramDatabase + Default + 4312;4701;%(DisableSpecificWarnings) + + + _DEBUG;%(PreprocessorDefinitions) + 0x0409 + + + /ignore:4204 %(AdditionalOptions) + uxtheme.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)$(TargetExt) + ..\..\..\external_dependencies\openmpt-trunk\build\lib\$(PlatformShortName)_$(Configuration);%(AdditionalLibraryDirectories) + uxtheme.dll;%(DelayLoadDLLs) + true + $(IntDir)$(TargetName).pdb + false + $(IntDir)$(TargetName).lib + false + + + xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ +xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ + Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\' + + + + + MinSpace + true + Size + .;..\..\..\Wasabi;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_WINDOWS;_USRDLL;NSV_CODER_MP3_EXPORTS;%(PreprocessorDefinitions) + true + true + MultiThreadedDLL + true + $(IntDir) + $(IntDir) + $(IntDir)$(TargetName).pdb + Level3 + None + 4701;%(DisableSpecificWarnings) + + + NDEBUG;%(PreprocessorDefinitions) + 0x0409 + + + /ignore:4210 %(AdditionalOptions) + uxtheme.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)$(TargetExt) + ..\..\..\external_dependencies\openmpt-trunk\build\lib\$(PlatformShortName)_$(Configuration);%(AdditionalLibraryDirectories) + uxtheme.dll;%(DelayLoadDLLs) + false + $(IntDir)$(TargetName).pdb + true + true + false + $(IntDir)$(TargetName).lib + MachineX86 + false + + + xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ + Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\' + + + + + MinSpace + true + Size + .;..\..\..\Wasabi;%(AdditionalIncludeDirectories) + WIN64;NDEBUG;_WINDOWS;_USRDLL;NSV_CODER_MP3_EXPORTS;%(PreprocessorDefinitions) + true + true + MultiThreadedDLL + true + $(IntDir) + $(IntDir) + $(IntDir)$(TargetName).pdb + Level3 + None + 4312;4701;%(DisableSpecificWarnings) + + + NDEBUG;%(PreprocessorDefinitions) + 0x0409 + + + /ignore:4210 %(AdditionalOptions) + uxtheme.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)$(TargetExt) + ..\..\..\external_dependencies\openmpt-trunk\build\lib\$(PlatformShortName)_$(Configuration);%(AdditionalLibraryDirectories) + uxtheme.dll;%(DelayLoadDLLs) + false + $(IntDir)$(TargetName).pdb + true + true + false + $(IntDir)$(TargetName).lib + false + + + xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ + Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\' + + + + + {3e0bfa8a-b86a-42e9-a33f-ec294f823f7f} + + + + + WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;NSV_CODER_MP3_EXPORTS + WIN64;_DEBUG;_WINDOWS;_MBCS;_USRDLL;NSV_CODER_MP3_EXPORTS + + + + + + + + + + + + \ No newline at end of file diff --git a/Src/Plugins/Encoder/enc_vorbis/enc_vorbis.vcxproj.filters b/Src/Plugins/Encoder/enc_vorbis/enc_vorbis.vcxproj.filters new file mode 100644 index 00000000..78b0008e --- /dev/null +++ b/Src/Plugins/Encoder/enc_vorbis/enc_vorbis.vcxproj.filters @@ -0,0 +1,29 @@ + + + + + {60891243-3702-45a9-8316-6dd4c4fe1657} + + + {0dcd9aec-855c-4009-8908-41d2d4c57e21} + + + {327bdd16-a664-4de5-9595-33490dffe0f7} + + + + + Header Files + + + + + Ressource Files + + + + + Source Files + + + \ No newline at end of file diff --git a/Src/Plugins/Encoder/enc_vorbis/main.cpp b/Src/Plugins/Encoder/enc_vorbis/main.cpp new file mode 100644 index 00000000..d42a4ede --- /dev/null +++ b/Src/Plugins/Encoder/enc_vorbis/main.cpp @@ -0,0 +1,595 @@ +/* +** enc_vorbis: main.cpp - Ogg Vorbis encoder plug-in +** +** Copyright (C) 2001-2012 Nullsoft, Inc. +** +** This software is provided 'as-is', without any express or implied warranty. +** In no event will the authors be held liable for any damages arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, including commercial +** applications, and to alter it and redistribute it freely, subject to the following restrictions: +** 1. The origin of this software must not be misrepresented; you must not claim that you wrote the +** original software. If you use this software in a product, an acknowledgment in the product +** documentation would be appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be misrepresented as +** being the original software. +** 3. This notice may not be removed or altered from any source distribution. +*/ + +#define ENC_VERSION "v1.58" + +#include +#include +#include +#include +#include "resource.h" +#include "../nsv/enc_if.h" +#include + +// wasabi based services for localisation support +#include +#include "../Agave/Language/api_language.h" +#include +#include "../winamp/wa_ipc.h" + +HWND winampwnd = 0; +int isthemethere = 0; +api_service *WASABI_API_SVC = 0; +api_application *WASABI_API_APP = 0; +api_language *WASABI_API_LNG = 0; +HINSTANCE WASABI_API_LNG_HINST = 0, WASABI_API_ORIG_HINST = 0; + +BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) +{ + return TRUE; +} + +typedef struct +{ + bool cfg_abr_use_max,cfg_abr_use_min; + UINT cfg_mode; + + float cfg_vbrquality; + UINT cfg_abr_nominal; + UINT cfg_abr_max; + UINT cfg_abr_min; +} configtype; + +typedef struct +{ + configtype cfg; + char *configfile; +} +configwndrec; + +void readconfig(char *configfile, configtype *cfg) +{ + cfg->cfg_abr_use_max=0; + cfg->cfg_abr_use_min=0; + cfg->cfg_mode=0; //VBR + + cfg->cfg_vbrquality=0.4f; + + cfg->cfg_abr_nominal=160; + cfg->cfg_abr_max=352; + cfg->cfg_abr_min=32; + + if (configfile) GetPrivateProfileStructA("audio_ogg","conf",cfg,sizeof(configtype),configfile); + cfg->cfg_mode=0; // VBR, fuckers. +} + +void writeconfig(char *configfile, configtype *cfg) +{ + if (configfile) WritePrivateProfileStructA("audio_ogg","conf",cfg,sizeof(configtype),configfile); +} + +static HINSTANCE GetMyInstance() +{ + MEMORY_BASIC_INFORMATION mbi = {0}; + if(VirtualQuery(GetMyInstance, &mbi, sizeof(mbi))) + return (HINSTANCE)mbi.AllocationBase; + return NULL; +} + +void GetLocalisationApiService(void) +{ + if(!WASABI_API_LNG) + { + // loader so that we can get the localisation service api for use + if(!WASABI_API_SVC) + { + WASABI_API_SVC = (api_service*)SendMessage(winampwnd, WM_WA_IPC, 0, IPC_GET_API_SERVICE); + if (WASABI_API_SVC == (api_service*)1) + { + WASABI_API_SVC = NULL; + return; + } + } + + if(!WASABI_API_APP) + { + waServiceFactory *sf = WASABI_API_SVC->service_getServiceByGuid(applicationApiServiceGuid); + if (sf) WASABI_API_APP = reinterpret_cast(sf->getInterface()); + } + + if(!WASABI_API_LNG) + { + waServiceFactory *sf; + sf = WASABI_API_SVC->service_getServiceByGuid(languageApiGUID); + if (sf) WASABI_API_LNG = reinterpret_cast(sf->getInterface()); + } + + // need to have this initialised before we try to do anything with localisation features + WASABI_API_START_LANG(GetMyInstance(),EncVorbisLangGUID); + } +} + +class AudioCoderOgg : public AudioCoder +{ + public: + AudioCoderOgg(int nch, int srate, int bps, configtype *cfg); + int Encode(int framepos, void *in, int in_avail, int *in_used, void *out, int out_avail); + ~AudioCoderOgg() + { + ogg_stream_clear(&os); + vorbis_block_clear(&vb); + vorbis_dsp_clear(&vd); + vorbis_comment_clear(&vc); + vorbis_info_clear(&vi); + } + int GetLastError() { return m_err; }; + private: + int m_err; + int m_bps, m_nch; + + ogg_stream_state os; /* take physical pages, weld into a logical stream of packets */ + ogg_page og; /* one Ogg bitstream page. Vorbis packets are inside */ + ogg_packet op; /* one raw packet of data for decode */ + + vorbis_info vi; /* struct that stores all the static vorbis bitstream settings */ + vorbis_comment vc; /* struct that stores all the user comments */ + + vorbis_dsp_state vd; /* central working state for the packet->PCM decoder */ + vorbis_block vb; /* local working space for packet->PCM decode */ + + int m_inbuf; +}; + +int AudioCoderOgg::Encode(int framepos, void *in, int in_avail, int *in_used, void *out, int out_avail) +{ + if (m_err) return -1; + *in_used = 0; //only necessary for flawed impl that dont reset this to zero each time (BAD rOn) + + int wrote=0; + char *dest = (char*)out; + + if (!in_avail && !framepos) + { + vorbis_analysis_wrote(&vd,0); + } + + for (;;) + { + /* vorbis does some data preanalysis, then divvies up blocks for + more involved (potentially parallel) processing. Get a single + block for encoding now */ + + while (m_inbuf || vorbis_analysis_blockout(&vd,&vb)==1) + { + /* analysis */ + if (!m_inbuf) + { + vorbis_analysis(&vb,&op); + vorbis_bitrate_addblock(&vb); + } + + while (m_inbuf || vorbis_bitrate_flushpacket(&vd, &op)) + { + /* weld the packet into the bitstream */ + if (!m_inbuf) ogg_stream_packetin(&os,&op); + + /* write out pages (if any) */ + while (m_inbuf || ogg_stream_pageout(&os,&og)) + { + int l=og.header_len+og.body_len; + if(out_avail= in_avail || wrote) return wrote; + + // bring in more pcm samples + if (in_avail > *in_used) + { + UINT i; + int c; + int bytes=in_avail-*in_used; + void *buf=(char*)in + *in_used; + + if (bytes > 1024) bytes=1024; + + *in_used+=bytes; + + UINT nsam=bytes/((m_bps>>3)*m_nch); + float **buffer=vorbis_analysis_buffer(&vd,nsam); + switch(m_bps) + { + case 8: + { + BYTE* rbuf=(BYTE*)buf; + for(i=0;icfg_mode==0) poo=vorbis_encode_init_vbr(&vi,nch,srate,cfg->cfg_vbrquality); + else + { + UINT nominal,min,max; + if (cfg->cfg_mode==1) + {//abr + nominal=cfg->cfg_abr_nominal * 1000; + min=cfg->cfg_abr_use_min ? cfg->cfg_abr_min * 1000: -1; + max=cfg->cfg_abr_use_max ? cfg->cfg_abr_max * 1000: -1; + } + else//cbr + { + nominal=min=max=cfg->cfg_abr_nominal*1000; + } + poo=vorbis_encode_init(&vi,nch,srate,max,nominal,min); + } + + if (poo) + { + vorbis_info_clear(&vi); + m_err++; + return; + } + + vorbis_comment_init(&vc); + vorbis_comment_add(&vc, "ENCODEDBY=Winamp"); + + /* set up the analysis state and auxiliary encoding storage */ + vorbis_analysis_init(&vd,&vi); + vorbis_block_init(&vd,&vb); + + /* set up our packet->stream encoder */ + /* pick a random serial number; that way we can more likely build + chained streams just by concatenation */ + + ogg_stream_init(&os,GetTickCount()^(GetTickCount()<<16));//fixme : rand + + /* Vorbis streams begin with three headers; the initial header (with + most of the codec setup parameters) which is mandated by the Ogg + bitstream spec. The second header holds any comment fields. The + third header holds the bitstream codebook. We merely need to + make the headers, then pass them to libvorbis one at a time; + libvorbis handles the additional Ogg bitstream constraints */ + + { + ogg_packet header; + ogg_packet header_comm; + ogg_packet header_code; + + vorbis_analysis_headerout(&vd,&vc,&header,&header_comm,&header_code); + ogg_stream_packetin(&os,&header); /* automatically placed in its own page */ + ogg_stream_packetin(&os,&header_comm); + ogg_stream_packetin(&os,&header_code); + } +} + +extern "C" +{ + unsigned int __declspec(dllexport) GetAudioTypes3(int idx, char *desc) + { + if (idx==0) + { + GetLocalisationApiService(); + StringCchPrintfA(desc, 1024, "%s %s (aoTuV b6.03)", WASABI_API_LNGSTRING(IDS_ENC_VORBIS_DESC), ENC_VERSION); + return mmioFOURCC('O','G','G',' '); + } + return 0; + } + + AudioCoder __declspec(dllexport) *CreateAudio3(int nch, int srate, int bps, unsigned int srct, unsigned int *outt, char *configfile) + { + if (srct == mmioFOURCC('P','C','M',' ') && *outt == mmioFOURCC('O','G','G',' ')) + { + configtype cfg; + readconfig(configfile,&cfg); + AudioCoderOgg *t = new AudioCoderOgg(nch,srate,bps,&cfg); + if (t->GetLastError()) + { + delete t; + return NULL; + } + return t; + } + return NULL; + } + + void __declspec(dllexport) FinishAudio3(const char *filename, AudioCoder *coder) + { + } + + #define doshow(x,y) ShowWindow(x,(y)?SW_SHOWNA:SW_HIDE) + + static HCURSOR link_hand_cursor; + LRESULT link_handlecursor(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) + { + LRESULT ret = CallWindowProcW((WNDPROC)GetPropW(hwndDlg, L"link_proc"), hwndDlg, uMsg, wParam, lParam); + // override the normal cursor behaviour so we have a hand to show it is a link + if(uMsg == WM_SETCURSOR) + { + if((HWND)wParam == hwndDlg) + { + if(!link_hand_cursor) + { + link_hand_cursor = LoadCursor(NULL, IDC_HAND); + } + SetCursor(link_hand_cursor); + return TRUE; + } + } + return ret; + } + + void link_handledraw(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) + { + if (uMsg == WM_DRAWITEM) + { + DRAWITEMSTRUCT *di = (DRAWITEMSTRUCT *)lParam; + if (di->CtlType == ODT_BUTTON) + { + wchar_t wt[123] = {0}; + int y; + RECT r; + HPEN hPen, hOldPen; + GetDlgItemTextW(hwndDlg, (int)wParam, wt, sizeof(wt)/sizeof(wt[0])); + + // due to the fun of theming and owner drawing we have to get the background colour + if(isthemethere){ + HTHEME hTheme = OpenThemeData(hwndDlg, L"Tab"); + if (hTheme) { + DrawThemeParentBackground(di->hwndItem, di->hDC, &di->rcItem); + CloseThemeData(hTheme); + } + } + + // draw text + SetTextColor(di->hDC, (di->itemState & ODS_SELECTED) ? RGB(220, 0, 0) : RGB(0, 0, 220)); + r = di->rcItem; + r.left += 2; + DrawTextW(di->hDC, wt, -1, &r, DT_VCENTER | DT_SINGLELINE); + + memset(&r, 0, sizeof(r)); + DrawTextW(di->hDC, wt, -1, &r, DT_SINGLELINE | DT_CALCRECT); + + // draw underline + y = di->rcItem.bottom - ((di->rcItem.bottom - di->rcItem.top) - (r.bottom - r.top)) / 2 - 1; + hPen = CreatePen(PS_SOLID, 0, (di->itemState & ODS_SELECTED) ? RGB(220, 0, 0) : RGB(0, 0, 220)); + hOldPen = (HPEN) SelectObject(di->hDC, hPen); + MoveToEx(di->hDC, di->rcItem.left + 2, y, NULL); + LineTo(di->hDC, di->rcItem.right + 2 - ((di->rcItem.right - di->rcItem.left) - (r.right - r.left)), y); + SelectObject(di->hDC, hOldPen); + DeleteObject(hPen); + } + } + } + + void link_startsubclass(HWND hwndDlg, UINT id) + { + HWND ctrl = GetDlgItem(hwndDlg, id); + if(!GetPropW(ctrl, L"link_proc")) + { + SetPropW(ctrl, L"link_proc", + (HANDLE)SetWindowLongPtrW(ctrl, GWLP_WNDPROC, (LONG_PTR)link_handlecursor)); + } + } + + BOOL CALLBACK DlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam) + { + if (uMsg == WM_USER+667) + { + int p=(int)SendDlgItemMessage(hwndDlg,IDC_VBRQUALITY,TBM_GETPOS,0,0)-10; + char tmp[512] = {0}; + double dispVal = ((double)p)/10; + vorbis_info vi = {0}; + int br = 128; + + vorbis_info_init(&vi); + if(vorbis_encode_init_vbr(&vi, 2, 44100, (float) (p / 100.0))) + br=128; // Mode setup failed: go with a default. + else + { + br = vi.bitrate_nominal / 1000; + vorbis_info_clear(&vi); + } + + StringCchPrintfA(tmp, 512, WASABI_API_LNGSTRING(IDS_QUALITY_FACTOR_F_KBPS), dispVal, br); + + SetDlgItemTextA(hwndDlg,IDC_VBRVAL,tmp); + + link_startsubclass(hwndDlg, IDC_URL1); + link_startsubclass(hwndDlg, IDC_URL2); + } + + else if (uMsg == WM_INITDIALOG) + { +#if defined (_WIN64) + SetWindowLong(hwndDlg,GWLP_USERDATA,(LONG)lParam); +#else + SetWindowLong(hwndDlg, GWL_USERDATA, lParam); +#endif + if (lParam) + { + configwndrec *wc=(configwndrec*)lParam; + SendDlgItemMessage(hwndDlg,IDC_VBRQUALITY,TBM_SETRANGE,0,MAKELONG(0,110)); + SendDlgItemMessage(hwndDlg,IDC_VBRQUALITY,TBM_SETTICFREQ,5,0); + SendDlgItemMessage(hwndDlg,IDC_VBRQUALITY,TBM_SETPAGESIZE,0,10); + SendDlgItemMessage(hwndDlg,IDC_VBRQUALITY,TBM_SETPOS,TRUE,(int)(wc->cfg.cfg_vbrquality*100.0f + (wc->cfg.cfg_vbrquality < 0.0 ? - 0.5f : 0.5f)) + 10); + SendMessage(hwndDlg,WM_USER+667,0,0); + } + } + + else if (uMsg == WM_HSCROLL) + { + HWND swnd = (HWND) lParam; + if (swnd == GetDlgItem(hwndDlg, IDC_VBRQUALITY)) + { + int p=(int)SendDlgItemMessage(hwndDlg,IDC_VBRQUALITY,TBM_GETPOS,0,0)-10; + +#if defined (_WIN64) + configwndrec* wc = (configwndrec*)GetWindowLong(hwndDlg, GWLP_USERDATA); +#else + configwndrec* wc = (configwndrec*)GetWindowLong(hwndDlg, GWL_USERDATA); +#endif + if (wc) + { + wc->cfg.cfg_vbrquality=(float)p/100.0f; + } + SendMessage(hwndDlg,WM_USER+667,0,0); + } + } + + else if (uMsg == WM_COMMAND) + { + if(LOWORD(wParam) == IDC_URL1) + { + SendMessage(winampwnd, WM_WA_IPC, (WPARAM)"http://xiph.org/vorbis/", IPC_OPEN_URL); + } + else if(LOWORD(wParam) == IDC_URL2) + { + SendMessage(winampwnd, WM_WA_IPC, (WPARAM)"https://ao-yumi.github.io/aotuv_web/index.html", IPC_OPEN_URL); + } + } + + else if (uMsg == WM_DESTROY) + { +#if defined (_WIN64) + configwndrec* wc = (configwndrec*)SetWindowLong(hwndDlg, GWLP_USERDATA, 0); +#else + configwndrec* wc = (configwndrec*)SetWindowLong(hwndDlg, GWL_USERDATA, 0); +#endif + if (wc) + { + wc->cfg.cfg_mode=0; + writeconfig(wc->configfile,&wc->cfg); + free(wc->configfile); + free(wc); + } + } + + const int controls[] = + { + IDC_VBRQUALITY, + }; + if (FALSE != WASABI_API_APP->DirectMouseWheel_ProcessDialogMessage(hwndDlg, uMsg, wParam, lParam, controls, ARRAYSIZE(controls))) + { + return TRUE; + } + + link_handledraw(hwndDlg, uMsg, wParam, lParam); + return 0; + } + + HWND __declspec(dllexport) ConfigAudio3(HWND hwndParent, HINSTANCE hinst, unsigned int outt, char *configfile) + { + if (outt == mmioFOURCC('O','G','G',' ')) + { + configwndrec *wr=(configwndrec*)malloc(sizeof(configwndrec)); + if (configfile) wr->configfile=_strdup(configfile); + else wr->configfile=0; + + readconfig(configfile,&wr->cfg); + GetLocalisationApiService(); + return WASABI_API_CREATEDIALOGPARAMW(IDD_CONFIG,hwndParent,DlgProc,(LPARAM)wr); + } + return NULL; + } + + void __declspec(dllexport) SetWinampHWND(HWND hwnd) + { + winampwnd = hwnd; + isthemethere = !SendMessage(hwnd,WM_WA_IPC,IPC_ISWINTHEMEPRESENT,IPC_USE_UXTHEME_FUNC); + } +}; \ No newline at end of file diff --git a/Src/Plugins/Encoder/enc_vorbis/ogg/include/ogg/config_types.h b/Src/Plugins/Encoder/enc_vorbis/ogg/include/ogg/config_types.h new file mode 100644 index 00000000..750e29dd --- /dev/null +++ b/Src/Plugins/Encoder/enc_vorbis/ogg/include/ogg/config_types.h @@ -0,0 +1,25 @@ +#ifndef __CONFIG_TYPES_H__ +#define __CONFIG_TYPES_H__ + +/* these are filled in by configure */ +#define INCLUDE_INTTYPES_H @INCLUDE_INTTYPES_H@ +#define INCLUDE_STDINT_H @INCLUDE_STDINT_H@ +#define INCLUDE_SYS_TYPES_H @INCLUDE_SYS_TYPES_H@ + +#if INCLUDE_INTTYPES_H +# include +#endif +#if INCLUDE_STDINT_H +# include +#endif +#if INCLUDE_SYS_TYPES_H +# include +#endif + +typedef @SIZE16@ ogg_int16_t; +typedef @USIZE16@ ogg_uint16_t; +typedef @SIZE32@ ogg_int32_t; +typedef @USIZE32@ ogg_uint32_t; +typedef @SIZE64@ ogg_int64_t; + +#endif diff --git a/Src/Plugins/Encoder/enc_vorbis/ogg/include/ogg/ogg.h b/Src/Plugins/Encoder/enc_vorbis/ogg/include/ogg/ogg.h new file mode 100644 index 00000000..7609fc24 --- /dev/null +++ b/Src/Plugins/Encoder/enc_vorbis/ogg/include/ogg/ogg.h @@ -0,0 +1,210 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: toplevel libogg include + last mod: $Id$ + + ********************************************************************/ +#ifndef _OGG_H +#define _OGG_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +typedef struct { + void *iov_base; + size_t iov_len; +} ogg_iovec_t; + +typedef struct { + long endbyte; + int endbit; + + unsigned char *buffer; + unsigned char *ptr; + long storage; +} oggpack_buffer; + +/* ogg_page is used to encapsulate the data in one Ogg bitstream page *****/ + +typedef struct { + unsigned char *header; + long header_len; + unsigned char *body; + long body_len; +} ogg_page; + +/* ogg_stream_state contains the current encode/decode state of a logical + Ogg bitstream **********************************************************/ + +typedef struct { + unsigned char *body_data; /* bytes from packet bodies */ + long body_storage; /* storage elements allocated */ + long body_fill; /* elements stored; fill mark */ + long body_returned; /* elements of fill returned */ + + + int *lacing_vals; /* The values that will go to the segment table */ + ogg_int64_t *granule_vals; /* granulepos values for headers. Not compact + this way, but it is simple coupled to the + lacing fifo */ + long lacing_storage; + long lacing_fill; + long lacing_packet; + long lacing_returned; + + unsigned char header[282]; /* working space for header encode */ + int header_fill; + + int e_o_s; /* set when we have buffered the last packet in the + logical bitstream */ + int b_o_s; /* set after we've written the initial page + of a logical bitstream */ + long serialno; + long pageno; + ogg_int64_t packetno; /* sequence number for decode; the framing + knows where there's a hole in the data, + but we need coupling so that the codec + (which is in a separate abstraction + layer) also knows about the gap */ + ogg_int64_t granulepos; + +} ogg_stream_state; + +/* ogg_packet is used to encapsulate the data and metadata belonging + to a single raw Ogg/Vorbis packet *************************************/ + +typedef struct { + unsigned char *packet; + long bytes; + long b_o_s; + long e_o_s; + + ogg_int64_t granulepos; + + ogg_int64_t packetno; /* sequence number for decode; the framing + knows where there's a hole in the data, + but we need coupling so that the codec + (which is in a separate abstraction + layer) also knows about the gap */ +} ogg_packet; + +typedef struct { + unsigned char *data; + int storage; + int fill; + int returned; + + int unsynced; + int headerbytes; + int bodybytes; +} ogg_sync_state; + +/* Ogg BITSTREAM PRIMITIVES: bitstream ************************/ + +extern void oggpack_writeinit(oggpack_buffer *b); +extern int oggpack_writecheck(oggpack_buffer *b); +extern void oggpack_writetrunc(oggpack_buffer *b,long bits); +extern void oggpack_writealign(oggpack_buffer *b); +extern void oggpack_writecopy(oggpack_buffer *b,void *source,long bits); +extern void oggpack_reset(oggpack_buffer *b); +extern void oggpack_writeclear(oggpack_buffer *b); +extern void oggpack_readinit(oggpack_buffer *b,unsigned char *buf,int bytes); +extern void oggpack_write(oggpack_buffer *b,unsigned long value,int bits); +extern long oggpack_look(oggpack_buffer *b,int bits); +extern long oggpack_look1(oggpack_buffer *b); +extern void oggpack_adv(oggpack_buffer *b,int bits); +extern void oggpack_adv1(oggpack_buffer *b); +extern long oggpack_read(oggpack_buffer *b,int bits); +extern long oggpack_read1(oggpack_buffer *b); +extern long oggpack_bytes(oggpack_buffer *b); +extern long oggpack_bits(oggpack_buffer *b); +extern unsigned char *oggpack_get_buffer(oggpack_buffer *b); + +extern void oggpackB_writeinit(oggpack_buffer *b); +extern int oggpackB_writecheck(oggpack_buffer *b); +extern void oggpackB_writetrunc(oggpack_buffer *b,long bits); +extern void oggpackB_writealign(oggpack_buffer *b); +extern void oggpackB_writecopy(oggpack_buffer *b,void *source,long bits); +extern void oggpackB_reset(oggpack_buffer *b); +extern void oggpackB_writeclear(oggpack_buffer *b); +extern void oggpackB_readinit(oggpack_buffer *b,unsigned char *buf,int bytes); +extern void oggpackB_write(oggpack_buffer *b,unsigned long value,int bits); +extern long oggpackB_look(oggpack_buffer *b,int bits); +extern long oggpackB_look1(oggpack_buffer *b); +extern void oggpackB_adv(oggpack_buffer *b,int bits); +extern void oggpackB_adv1(oggpack_buffer *b); +extern long oggpackB_read(oggpack_buffer *b,int bits); +extern long oggpackB_read1(oggpack_buffer *b); +extern long oggpackB_bytes(oggpack_buffer *b); +extern long oggpackB_bits(oggpack_buffer *b); +extern unsigned char *oggpackB_get_buffer(oggpack_buffer *b); + +/* Ogg BITSTREAM PRIMITIVES: encoding **************************/ + +extern int ogg_stream_packetin(ogg_stream_state *os, ogg_packet *op); +extern int ogg_stream_iovecin(ogg_stream_state *os, ogg_iovec_t *iov, + int count, long e_o_s, ogg_int64_t granulepos); +extern int ogg_stream_pageout(ogg_stream_state *os, ogg_page *og); +extern int ogg_stream_pageout_fill(ogg_stream_state *os, ogg_page *og, int nfill); +extern int ogg_stream_flush(ogg_stream_state *os, ogg_page *og); +extern int ogg_stream_flush_fill(ogg_stream_state *os, ogg_page *og, int nfill); + +/* Ogg BITSTREAM PRIMITIVES: decoding **************************/ + +extern int ogg_sync_init(ogg_sync_state *oy); +extern int ogg_sync_clear(ogg_sync_state *oy); +extern int ogg_sync_reset(ogg_sync_state *oy); +extern int ogg_sync_destroy(ogg_sync_state *oy); +extern int ogg_sync_check(ogg_sync_state *oy); + +extern char *ogg_sync_buffer(ogg_sync_state *oy, long size); +extern int ogg_sync_wrote(ogg_sync_state *oy, long bytes); +extern long ogg_sync_pageseek(ogg_sync_state *oy,ogg_page *og); +extern int ogg_sync_pageout(ogg_sync_state *oy, ogg_page *og); +extern int ogg_stream_pagein(ogg_stream_state *os, ogg_page *og); +extern int ogg_stream_packetout(ogg_stream_state *os,ogg_packet *op); +extern int ogg_stream_packetpeek(ogg_stream_state *os,ogg_packet *op); + +/* Ogg BITSTREAM PRIMITIVES: general ***************************/ + +extern int ogg_stream_init(ogg_stream_state *os,int serialno); +extern int ogg_stream_clear(ogg_stream_state *os); +extern int ogg_stream_reset(ogg_stream_state *os); +extern int ogg_stream_reset_serialno(ogg_stream_state *os,int serialno); +extern int ogg_stream_destroy(ogg_stream_state *os); +extern int ogg_stream_check(ogg_stream_state *os); +extern int ogg_stream_eos(ogg_stream_state *os); + +extern void ogg_page_checksum_set(ogg_page *og); + +extern int ogg_page_version(const ogg_page *og); +extern int ogg_page_continued(const ogg_page *og); +extern int ogg_page_bos(const ogg_page *og); +extern int ogg_page_eos(const ogg_page *og); +extern ogg_int64_t ogg_page_granulepos(const ogg_page *og); +extern int ogg_page_serialno(const ogg_page *og); +extern long ogg_page_pageno(const ogg_page *og); +extern int ogg_page_packets(const ogg_page *og); + +extern void ogg_packet_clear(ogg_packet *op); + + +#ifdef __cplusplus +} +#endif + +#endif /* _OGG_H */ diff --git a/Src/Plugins/Encoder/enc_vorbis/ogg/include/ogg/os_types.h b/Src/Plugins/Encoder/enc_vorbis/ogg/include/ogg/os_types.h new file mode 100644 index 00000000..b8f56308 --- /dev/null +++ b/Src/Plugins/Encoder/enc_vorbis/ogg/include/ogg/os_types.h @@ -0,0 +1,148 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2002 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: #ifdef jail to whip a few platforms into the UNIX ideal. + last mod: $Id$ + + ********************************************************************/ +#ifndef _OS_TYPES_H +#define _OS_TYPES_H + +/* make it easy on the folks that want to compile the libs with a + different malloc than stdlib */ +#define _ogg_malloc malloc +#define _ogg_calloc calloc +#define _ogg_realloc realloc +#define _ogg_free free + +#if defined(_WIN32) + +# if defined(__CYGWIN__) +# include + typedef int16_t ogg_int16_t; + typedef uint16_t ogg_uint16_t; + typedef int32_t ogg_int32_t; + typedef uint32_t ogg_uint32_t; + typedef int64_t ogg_int64_t; + typedef uint64_t ogg_uint64_t; +# elif defined(__MINGW32__) +# include + typedef short ogg_int16_t; + typedef unsigned short ogg_uint16_t; + typedef int ogg_int32_t; + typedef unsigned int ogg_uint32_t; + typedef long long ogg_int64_t; + typedef unsigned long long ogg_uint64_t; +# elif defined(__MWERKS__) + typedef long long ogg_int64_t; + typedef int ogg_int32_t; + typedef unsigned int ogg_uint32_t; + typedef short ogg_int16_t; + typedef unsigned short ogg_uint16_t; +# else +# if defined(_MSC_VER) && (_MSC_VER >= 1800) /* MSVC 2013 and newer */ +# include + typedef int16_t ogg_int16_t; + typedef uint16_t ogg_uint16_t; + typedef int32_t ogg_int32_t; + typedef uint32_t ogg_uint32_t; + typedef int64_t ogg_int64_t; + typedef uint64_t ogg_uint64_t; +# else + /* MSVC/Borland */ + typedef __int64 ogg_int64_t; + typedef __int32 ogg_int32_t; + typedef unsigned __int32 ogg_uint32_t; + typedef __int16 ogg_int16_t; + typedef unsigned __int16 ogg_uint16_t; +# endif +# endif + +#elif (defined(__APPLE__) && defined(__MACH__)) /* MacOS X Framework build */ + +# include + typedef int16_t ogg_int16_t; + typedef uint16_t ogg_uint16_t; + typedef int32_t ogg_int32_t; + typedef uint32_t ogg_uint32_t; + typedef int64_t ogg_int64_t; + +#elif defined(__HAIKU__) + + /* Haiku */ +# include + typedef short ogg_int16_t; + typedef unsigned short ogg_uint16_t; + typedef int ogg_int32_t; + typedef unsigned int ogg_uint32_t; + typedef long long ogg_int64_t; + +#elif defined(__BEOS__) + + /* Be */ +# include + typedef int16_t ogg_int16_t; + typedef uint16_t ogg_uint16_t; + typedef int32_t ogg_int32_t; + typedef uint32_t ogg_uint32_t; + typedef int64_t ogg_int64_t; + +#elif defined (__EMX__) + + /* OS/2 GCC */ + typedef short ogg_int16_t; + typedef unsigned short ogg_uint16_t; + typedef int ogg_int32_t; + typedef unsigned int ogg_uint32_t; + typedef long long ogg_int64_t; + +#elif defined (DJGPP) + + /* DJGPP */ + typedef short ogg_int16_t; + typedef int ogg_int32_t; + typedef unsigned int ogg_uint32_t; + typedef long long ogg_int64_t; + +#elif defined(R5900) + + /* PS2 EE */ + typedef long ogg_int64_t; + typedef int ogg_int32_t; + typedef unsigned ogg_uint32_t; + typedef short ogg_int16_t; + +#elif defined(__SYMBIAN32__) + + /* Symbian GCC */ + typedef signed short ogg_int16_t; + typedef unsigned short ogg_uint16_t; + typedef signed int ogg_int32_t; + typedef unsigned int ogg_uint32_t; + typedef long long int ogg_int64_t; + +#elif defined(__TMS320C6X__) + + /* TI C64x compiler */ + typedef signed short ogg_int16_t; + typedef unsigned short ogg_uint16_t; + typedef signed int ogg_int32_t; + typedef unsigned int ogg_uint32_t; + typedef long long int ogg_int64_t; + +#else + +# include + +#endif + +#endif /* _OS_TYPES_H */ diff --git a/Src/Plugins/Encoder/enc_vorbis/resource.h b/Src/Plugins/Encoder/enc_vorbis/resource.h new file mode 100644 index 00000000..21379029 --- /dev/null +++ b/Src/Plugins/Encoder/enc_vorbis/resource.h @@ -0,0 +1,51 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by enc_vorbis.rc +// +#define IDS_QUALITY_FACTOR_F 0 +#define IDS_ENC_VORBIS_DESC 1 +#define IDS_QUALITY_FACTOR_F_KBPS 2 +#define IDD_CONFIG 103 +#define IDC_BITRATE 1000 +#define IDC_VBR 1001 +#define IDC_QUALITY 1002 +#define IDC_JSTEREO1 1003 +#define IDC_JSTEREO2 1004 +#define IDC_BITRATE_TEXT1 1004 +#define IDC_MAXBITRATE 1005 +#define IDC_MINBITRATE 1005 +#define IDC_BITRATE_TEXT2 1006 +#define IDC_MAXBITRATE_TEXT1 1007 +#define IDC_MINBITRATE_TEXT1 1007 +#define IDC_MAXBITRATE_TEXT2 1008 +#define IDC_MINBITRATE_TEXT2 1008 +#define IDC_VBRMETHOD 1009 +#define IDC_AVGBITRATE_TEXT1 1010 +#define IDC_MAXBITRATE2_TEXT1 1010 +#define IDC_AVGBITRATE 1011 +#define IDC_MAXBITRATE2 1011 +#define IDC_AVGBITRATE_TEXT2 1012 +#define IDC_MAXBITRATE2_TEXT2 1012 +#define IDC_STEREOMODE 1013 +#define IDC_VBRQ_TEXT 1014 +#define IDC_VBRQ 1015 +#define IDC_VBRVAL 1016 +#define IDC_VBRTEXT 1017 +#define IDC_VBRQUALITY 1018 +#define IDC_MINBITRATE_CHECK 1019 +#define IDC_MAXBITRATE_CHECK 1020 +#define IDC_VBR1 1023 +#define IDC_VBR2 1024 +#define IDC_URL1 1026 +#define IDC_URL2 1027 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 106 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1028 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/Src/Plugins/Encoder/enc_vorbis/version.rc2 b/Src/Plugins/Encoder/enc_vorbis/version.rc2 new file mode 100644 index 00000000..0361dd38 --- /dev/null +++ b/Src/Plugins/Encoder/enc_vorbis/version.rc2 @@ -0,0 +1,39 @@ + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// +#include "../../../Winamp/buildType.h" +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,58,0,0 + PRODUCTVERSION WINAMP_PRODUCTVER + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Winamp SA" + VALUE "FileDescription", "Winamp Encoder Plug-in" + VALUE "FileVersion", "1,58,0,0" + VALUE "InternalName", "Nullsoft Ogg Vorbis Encoder" + VALUE "LegalCopyright", "Copyright © 2006-2023 Winamp SA" + VALUE "LegalTrademarks", "Nullsoft and Winamp are trademarks of Winamp SA" + VALUE "OriginalFilename", "enc_vorbis.dll" + VALUE "ProductName", "Winamp" + VALUE "ProductVersion", STR_WINAMP_PRODUCTVER + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/Src/Plugins/Encoder/enc_vorbis/vorbis/include/vorbis/codec.h b/Src/Plugins/Encoder/enc_vorbis/vorbis/include/vorbis/codec.h new file mode 100644 index 00000000..a29a5df4 --- /dev/null +++ b/Src/Plugins/Encoder/enc_vorbis/vorbis/include/vorbis/codec.h @@ -0,0 +1,243 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2001 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + + ******************************************************************** + + function: libvorbis codec headers + last mod: $Id: codec.h,v 1.1 2010/11/09 22:53:50 dromagod Exp $ + + ********************************************************************/ + +#ifndef _vorbis_codec_h_ +#define _vorbis_codec_h_ + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +#include + +typedef struct vorbis_info{ + int version; + int channels; + long rate; + + /* The below bitrate declarations are *hints*. + Combinations of the three values carry the following implications: + + all three set to the same value: + implies a fixed rate bitstream + only nominal set: + implies a VBR stream that averages the nominal bitrate. No hard + upper/lower limit + upper and or lower set: + implies a VBR bitstream that obeys the bitrate limits. nominal + may also be set to give a nominal rate. + none set: + the coder does not care to speculate. + */ + + long bitrate_upper; + long bitrate_nominal; + long bitrate_lower; + long bitrate_window; + + void *codec_setup; +} vorbis_info; + +/* vorbis_dsp_state buffers the current vorbis audio + analysis/synthesis state. The DSP state belongs to a specific + logical bitstream ****************************************************/ +typedef struct vorbis_dsp_state{ + int analysisp; + vorbis_info *vi; + + float **pcm; + float **pcmret; + int pcm_storage; + int pcm_current; + int pcm_returned; + + int preextrapolate; + int eofflag; + + long lW; + long W; + long nW; + long centerW; + + ogg_int64_t granulepos; + ogg_int64_t sequence; + + ogg_int64_t glue_bits; + ogg_int64_t time_bits; + ogg_int64_t floor_bits; + ogg_int64_t res_bits; + + void *backend_state; +} vorbis_dsp_state; + +typedef struct vorbis_block{ + /* necessary stream state for linking to the framing abstraction */ + float **pcm; /* this is a pointer into local storage */ + oggpack_buffer opb; + + long lW; + long W; + long nW; + int pcmend; + int mode; + + int eofflag; + ogg_int64_t granulepos; + ogg_int64_t sequence; + vorbis_dsp_state *vd; /* For read-only access of configuration */ + + /* local storage to avoid remallocing; it's up to the mapping to + structure it */ + void *localstore; + long localtop; + long localalloc; + long totaluse; + struct alloc_chain *reap; + + /* bitmetrics for the frame */ + long glue_bits; + long time_bits; + long floor_bits; + long res_bits; + + void *internal; + +} vorbis_block; + +/* vorbis_block is a single block of data to be processed as part of +the analysis/synthesis stream; it belongs to a specific logical +bitstream, but is independant from other vorbis_blocks belonging to +that logical bitstream. *************************************************/ + +struct alloc_chain{ + void *ptr; + struct alloc_chain *next; +}; + +/* vorbis_info contains all the setup information specific to the + specific compression/decompression mode in progress (eg, + psychoacoustic settings, channel setup, options, codebook + etc). vorbis_info and substructures are in backends.h. +*********************************************************************/ + +/* the comments are not part of vorbis_info so that vorbis_info can be + static storage */ +typedef struct vorbis_comment{ + /* unlimited user comment fields. libvorbis writes 'libvorbis' + whatever vendor is set to in encode */ + char **user_comments; + int *comment_lengths; + int comments; + char *vendor; + +} vorbis_comment; + + +/* libvorbis encodes in two abstraction layers; first we perform DSP + and produce a packet (see docs/analysis.txt). The packet is then + coded into a framed OggSquish bitstream by the second layer (see + docs/framing.txt). Decode is the reverse process; we sync/frame + the bitstream and extract individual packets, then decode the + packet back into PCM audio. + + The extra framing/packetizing is used in streaming formats, such as + files. Over the net (such as with UDP), the framing and + packetization aren't necessary as they're provided by the transport + and the streaming layer is not used */ + +/* Vorbis PRIMITIVES: general ***************************************/ + +extern void vorbis_info_init(vorbis_info *vi); +extern void vorbis_info_clear(vorbis_info *vi); +extern int vorbis_info_blocksize(vorbis_info *vi,int zo); +extern void vorbis_comment_init(vorbis_comment *vc); +extern void vorbis_comment_add(vorbis_comment *vc, const char *comment); +extern void vorbis_comment_add_tag(vorbis_comment *vc, + const char *tag, const char *contents); +extern char *vorbis_comment_query(vorbis_comment *vc, const char *tag, int count); +extern int vorbis_comment_query_count(vorbis_comment *vc, const char *tag); +extern void vorbis_comment_clear(vorbis_comment *vc); + +extern int vorbis_block_init(vorbis_dsp_state *v, vorbis_block *vb); +extern int vorbis_block_clear(vorbis_block *vb); +extern void vorbis_dsp_clear(vorbis_dsp_state *v); +extern double vorbis_granule_time(vorbis_dsp_state *v, + ogg_int64_t granulepos); + +extern const char *vorbis_version_string(void); + +/* Vorbis PRIMITIVES: analysis/DSP layer ****************************/ + +extern int vorbis_analysis_init(vorbis_dsp_state *v,vorbis_info *vi); +extern int vorbis_commentheader_out(vorbis_comment *vc, ogg_packet *op); +extern int vorbis_analysis_headerout(vorbis_dsp_state *v, + vorbis_comment *vc, + ogg_packet *op, + ogg_packet *op_comm, + ogg_packet *op_code); +extern float **vorbis_analysis_buffer(vorbis_dsp_state *v,int vals); +extern int vorbis_analysis_wrote(vorbis_dsp_state *v,int vals); +extern int vorbis_analysis_blockout(vorbis_dsp_state *v,vorbis_block *vb); +extern int vorbis_analysis(vorbis_block *vb,ogg_packet *op); + +extern int vorbis_bitrate_addblock(vorbis_block *vb); +extern int vorbis_bitrate_flushpacket(vorbis_dsp_state *vd, + ogg_packet *op); + +/* Vorbis PRIMITIVES: synthesis layer *******************************/ +extern int vorbis_synthesis_idheader(ogg_packet *op); +extern int vorbis_synthesis_headerin(vorbis_info *vi,vorbis_comment *vc, + ogg_packet *op); + +extern int vorbis_synthesis_init(vorbis_dsp_state *v,vorbis_info *vi); +extern int vorbis_synthesis_restart(vorbis_dsp_state *v); +extern int vorbis_synthesis(vorbis_block *vb,ogg_packet *op); +extern int vorbis_synthesis_trackonly(vorbis_block *vb,ogg_packet *op); +extern int vorbis_synthesis_blockin(vorbis_dsp_state *v,vorbis_block *vb); +extern int vorbis_synthesis_pcmout(vorbis_dsp_state *v,float ***pcm); +extern int vorbis_synthesis_lapout(vorbis_dsp_state *v,float ***pcm); +extern int vorbis_synthesis_read(vorbis_dsp_state *v,int samples); +extern long vorbis_packet_blocksize(vorbis_info *vi,ogg_packet *op); + +extern int vorbis_synthesis_halfrate(vorbis_info *v,int flag); +extern int vorbis_synthesis_halfrate_p(vorbis_info *v); + +/* Vorbis ERRORS and return codes ***********************************/ + +#define OV_FALSE -1 +#define OV_EOF -2 +#define OV_HOLE -3 + +#define OV_EREAD -128 +#define OV_EFAULT -129 +#define OV_EIMPL -130 +#define OV_EINVAL -131 +#define OV_ENOTVORBIS -132 +#define OV_EBADHEADER -133 +#define OV_EVERSION -134 +#define OV_ENOTAUDIO -135 +#define OV_EBADPACKET -136 +#define OV_EBADLINK -137 +#define OV_ENOSEEK -138 + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif + diff --git a/Src/Plugins/Encoder/enc_vorbis/vorbis/include/vorbis/vorbisenc.h b/Src/Plugins/Encoder/enc_vorbis/vorbis/include/vorbis/vorbisenc.h new file mode 100644 index 00000000..a0a4e757 --- /dev/null +++ b/Src/Plugins/Encoder/enc_vorbis/vorbis/include/vorbis/vorbisenc.h @@ -0,0 +1,112 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2001 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: vorbis encode-engine setup + last mod: $Id: vorbisenc.h,v 1.1 2010/11/09 22:53:50 dromagod Exp $ + + ********************************************************************/ + +#ifndef _OV_ENC_H_ +#define _OV_ENC_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +#include "codec.h" + +extern int vorbis_encode_init(vorbis_info *vi, + long channels, + long rate, + + long max_bitrate, + long nominal_bitrate, + long min_bitrate); + +extern int vorbis_encode_setup_managed(vorbis_info *vi, + long channels, + long rate, + + long max_bitrate, + long nominal_bitrate, + long min_bitrate); + +extern int vorbis_encode_setup_vbr(vorbis_info *vi, + long channels, + long rate, + + float quality /* quality level from 0. (lo) to 1. (hi) */ + ); + +extern int vorbis_encode_init_vbr(vorbis_info *vi, + long channels, + long rate, + + float base_quality /* quality level from 0. (lo) to 1. (hi) */ + ); + +extern int vorbis_encode_setup_init(vorbis_info *vi); + +extern int vorbis_encode_ctl(vorbis_info *vi,int number,void *arg); + + /* deprecated rate management supported only for compatability */ +#define OV_ECTL_RATEMANAGE_GET 0x10 +#define OV_ECTL_RATEMANAGE_SET 0x11 +#define OV_ECTL_RATEMANAGE_AVG 0x12 +#define OV_ECTL_RATEMANAGE_HARD 0x13 + +struct ovectl_ratemanage_arg { + int management_active; + + long bitrate_hard_min; + long bitrate_hard_max; + double bitrate_hard_window; + + long bitrate_av_lo; + long bitrate_av_hi; + double bitrate_av_window; + double bitrate_av_window_center; +}; + + + /* new rate setup */ +#define OV_ECTL_RATEMANAGE2_GET 0x14 +#define OV_ECTL_RATEMANAGE2_SET 0x15 + +struct ovectl_ratemanage2_arg { + int management_active; + + long bitrate_limit_min_kbps; + long bitrate_limit_max_kbps; + long bitrate_limit_reservoir_bits; + double bitrate_limit_reservoir_bias; + + long bitrate_average_kbps; + double bitrate_average_damping; +}; + + + +#define OV_ECTL_LOWPASS_GET 0x20 +#define OV_ECTL_LOWPASS_SET 0x21 + +#define OV_ECTL_IBLOCK_GET 0x30 +#define OV_ECTL_IBLOCK_SET 0x31 + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif + + diff --git a/Src/Plugins/Encoder/enc_vorbis/vorbis/include/vorbis/vorbisfile.h b/Src/Plugins/Encoder/enc_vorbis/vorbis/include/vorbis/vorbisfile.h new file mode 100644 index 00000000..d64fb824 --- /dev/null +++ b/Src/Plugins/Encoder/enc_vorbis/vorbis/include/vorbis/vorbisfile.h @@ -0,0 +1,201 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. * + * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS * + * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * + * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * + * * + * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2007 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: stdio-based convenience library for opening/seeking/decoding + last mod: $Id: vorbisfile.h,v 1.1 2010/11/09 22:53:50 dromagod Exp $ + + ********************************************************************/ + +#ifndef _OV_FILE_H_ +#define _OV_FILE_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ + +#include +#include "codec.h" + +/* The function prototypes for the callbacks are basically the same as for + * the stdio functions fread, fseek, fclose, ftell. + * The one difference is that the FILE * arguments have been replaced with + * a void * - this is to be used as a pointer to whatever internal data these + * functions might need. In the stdio case, it's just a FILE * cast to a void * + * + * If you use other functions, check the docs for these functions and return + * the right values. For seek_func(), you *MUST* return -1 if the stream is + * unseekable + */ +typedef struct { + size_t (*read_func) (void *ptr, size_t size, size_t nmemb, void *datasource); + int (*seek_func) (void *datasource, ogg_int64_t offset, int whence); + int (*close_func) (void *datasource); + long (*tell_func) (void *datasource); +} ov_callbacks; + +/* a few sets of convenient callbacks, especially for use under + * Windows where ov_open_callbacks() should always be used instead of + * ov_open() to avoid problems with incompatable crt.o version linking + * issues. */ + +static int _ov_header_fseek_wrap(FILE *f,ogg_int64_t off,int whence){ + if(f==NULL)return(-1); + +#ifdef __MINGW32__ + return fseeko64(f,off,whence); +#elif defined (_WIN32) + return _fseeki64(f,off,whence); +#else + return fseek(f,off,whence); +#endif +} + +/* These structs below (OV_CALLBACKS_DEFAULT etc) are defined here as + * static data. That means that every file which includes this header + * will get its own copy of these structs whether it uses them or + * not. This is essential on platforms such as Windows on which + * several different versions of stdio support may be linked to by + * different DLLs, and we need to be certain we know which one we're + * using (the same one as the main application). + */ + +static ov_callbacks OV_CALLBACKS_DEFAULT = { + (size_t (*)(void *, size_t, size_t, void *)) fread, + (int (*)(void *, ogg_int64_t, int)) _ov_header_fseek_wrap, + (int (*)(void *)) fclose, + (long (*)(void *)) ftell +}; + +static ov_callbacks OV_CALLBACKS_NOCLOSE = { + (size_t (*)(void *, size_t, size_t, void *)) fread, + (int (*)(void *, ogg_int64_t, int)) _ov_header_fseek_wrap, + (int (*)(void *)) NULL, + (long (*)(void *)) ftell +}; + +static ov_callbacks OV_CALLBACKS_STREAMONLY = { + (size_t (*)(void *, size_t, size_t, void *)) fread, + (int (*)(void *, ogg_int64_t, int)) NULL, + (int (*)(void *)) fclose, + (long (*)(void *)) NULL +}; + +static ov_callbacks OV_CALLBACKS_STREAMONLY_NOCLOSE = { + (size_t (*)(void *, size_t, size_t, void *)) fread, + (int (*)(void *, ogg_int64_t, int)) NULL, + (int (*)(void *)) NULL, + (long (*)(void *)) NULL +}; + +#define NOTOPEN 0 +#define PARTOPEN 1 +#define OPENED 2 +#define STREAMSET 3 +#define INITSET 4 + +typedef struct OggVorbis_File { + void *datasource; /* Pointer to a FILE *, etc. */ + int seekable; + ogg_int64_t offset; + ogg_int64_t end; + ogg_sync_state oy; + + /* If the FILE handle isn't seekable (eg, a pipe), only the current + stream appears */ + int links; + ogg_int64_t *offsets; + ogg_int64_t *dataoffsets; + long *serialnos; + ogg_int64_t *pcmlengths; /* overloaded to maintain binary + compatability; x2 size, stores both + beginning and end values */ + vorbis_info *vi; + vorbis_comment *vc; + + /* Decoding working state local storage */ + ogg_int64_t pcm_offset; + int ready_state; + long current_serialno; + int current_link; + + double bittrack; + double samptrack; + + ogg_stream_state os; /* take physical pages, weld into a logical + stream of packets */ + vorbis_dsp_state vd; /* central working state for the packet->PCM decoder */ + vorbis_block vb; /* local working space for packet->PCM decode */ + + ov_callbacks callbacks; + +} OggVorbis_File; + + +extern int ov_clear(OggVorbis_File *vf); +extern int ov_fopen(char *path,OggVorbis_File *vf); +extern int ov_open(FILE *f,OggVorbis_File *vf,char *initial,long ibytes); +extern int ov_open_callbacks(void *datasource, OggVorbis_File *vf, + char *initial, long ibytes, ov_callbacks callbacks); + +extern int ov_test(FILE *f,OggVorbis_File *vf,char *initial,long ibytes); +extern int ov_test_callbacks(void *datasource, OggVorbis_File *vf, + char *initial, long ibytes, ov_callbacks callbacks); +extern int ov_test_open(OggVorbis_File *vf); + +extern long ov_bitrate(OggVorbis_File *vf,int i); +extern long ov_bitrate_instant(OggVorbis_File *vf); +extern long ov_streams(OggVorbis_File *vf); +extern long ov_seekable(OggVorbis_File *vf); +extern long ov_serialnumber(OggVorbis_File *vf,int i); + +extern ogg_int64_t ov_raw_total(OggVorbis_File *vf,int i); +extern ogg_int64_t ov_pcm_total(OggVorbis_File *vf,int i); +extern double ov_time_total(OggVorbis_File *vf,int i); + +extern int ov_raw_seek(OggVorbis_File *vf,ogg_int64_t pos); +extern int ov_pcm_seek(OggVorbis_File *vf,ogg_int64_t pos); +extern int ov_pcm_seek_page(OggVorbis_File *vf,ogg_int64_t pos); +extern int ov_time_seek(OggVorbis_File *vf,double pos); +extern int ov_time_seek_page(OggVorbis_File *vf,double pos); + +extern int ov_raw_seek_lap(OggVorbis_File *vf,ogg_int64_t pos); +extern int ov_pcm_seek_lap(OggVorbis_File *vf,ogg_int64_t pos); +extern int ov_pcm_seek_page_lap(OggVorbis_File *vf,ogg_int64_t pos); +extern int ov_time_seek_lap(OggVorbis_File *vf,double pos); +extern int ov_time_seek_page_lap(OggVorbis_File *vf,double pos); + +extern ogg_int64_t ov_raw_tell(OggVorbis_File *vf); +extern ogg_int64_t ov_pcm_tell(OggVorbis_File *vf); +extern double ov_time_tell(OggVorbis_File *vf); + +extern vorbis_info *ov_info(OggVorbis_File *vf,int link); +extern vorbis_comment *ov_comment(OggVorbis_File *vf,int link); + +extern long ov_read_float(OggVorbis_File *vf,float ***pcm_channels,int samples, + int *bitstream); +extern long ov_read_filter(OggVorbis_File *vf,char *buffer,int length, + int bigendianp,int word,int sgned,int *bitstream, + void (*filter)(float **pcm,long channels,long samples,void *filter_param),void *filter_param); +extern long ov_read(OggVorbis_File *vf,char *buffer,int length, + int bigendianp,int word,int sgned,int *bitstream); +extern int ov_crosslap(OggVorbis_File *vf1,OggVorbis_File *vf2); + +extern int ov_halfrate(OggVorbis_File *vf,int flag); +extern int ov_halfrate_p(OggVorbis_File *vf); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif + diff --git a/Src/Plugins/Encoder/enc_wav/ACMEncoder.cpp b/Src/Plugins/Encoder/enc_wav/ACMEncoder.cpp new file mode 100644 index 00000000..bbdda7b7 --- /dev/null +++ b/Src/Plugins/Encoder/enc_wav/ACMEncoder.cpp @@ -0,0 +1,265 @@ +#include "ACMEncoder.h" + +#define rev32(X) ((((DWORD)(X)&0xFF)<<24)|(((DWORD)(X)&0xFF00)<<8)|(((DWORD)(X)&0xFF0000)>>8)|(((DWORD)(X)&0xFF000000)>>24)) + +static DWORD FileTell(HANDLE hFile) +{ + return SetFilePointer(hFile, 0, 0, FILE_CURRENT); +} +static void FileAlign(HANDLE hFile) +{ + if (FileTell(hFile)&1) SetFilePointer(hFile, 1, 0, FILE_CURRENT); +} + +#define BUFSIZE 0x20000 + +ACMEncoder::ACMEncoder(int srate, int nch, int bps, ACMConfig *config) +{ + m_did_header = 0; + m_srate = srate; + m_nch = nch; + m_bps = bps; + m_error = 0; + hStream = 0; + hStreamResample = 0; + m_acm_resample_buf = NULL; + m_acm_resample_outbuf = NULL; + m_bytes_done = 0; + m_hlen = 0; + m_nsam = 0; + + m_acm_buf = (unsigned char *)malloc(BUFSIZE); + m_acm_outbuf = (unsigned char *)malloc(BUFSIZE); + m_bytes_inbuf = 0; + m_bytes_outbuf = 0; + m_convert_wfx = config->convert_wfx; + do_header = config->header; + + m_wfx_src.wFormatTag = WAVE_FORMAT_PCM; + m_wfx_src.nChannels = nch; + m_wfx_src.nSamplesPerSec = srate; + m_wfx_src.nAvgBytesPerSec = srate * nch * (bps >> 3); + m_wfx_src.nBlockAlign = nch * (bps >> 3); + m_wfx_src.wBitsPerSample = bps; + m_wfx_src.cbSize = 0; + MMRESULT rs = acmStreamOpen(&hStream, 0, &m_wfx_src, &m_convert_wfx.wfx, 0, 0, 0, ACM_STREAMOPENF_NONREALTIME); + if (rs) + { + // need resampling + WAVEFORMATEX wfx1; + ZeroMemory(&wfx1, sizeof(wfx1)); + wfx1.wFormatTag = WAVE_FORMAT_PCM; + if (acmFormatSuggest(0, &m_convert_wfx.wfx, &wfx1, sizeof(WAVEFORMATEX), ACM_FORMATSUGGESTF_WFORMATTAG)) m_error = 1; + else if (acmStreamOpen(&hStream, 0, &wfx1, &m_convert_wfx.wfx, 0, 0, 0, ACM_STREAMOPENF_NONREALTIME)) m_error = 1; + else if (acmStreamOpen(&hStreamResample, 0, &m_wfx_src, &wfx1, 0, 0, 0, ACM_STREAMOPENF_NONREALTIME)) m_error = 1; + else + { + ZeroMemory(&ahdResample, sizeof(ahdResample)); + ahdResample.cbStruct = sizeof(ahdResample); + ahdResample.pbSrc = m_acm_resample_buf = (unsigned char *)malloc(BUFSIZE); + ahdResample.cbSrcLength = BUFSIZE; + ahdResample.pbDst = m_acm_resample_outbuf = (unsigned char *)malloc(BUFSIZE); + ahdResample.cbDstLength = BUFSIZE; + if (acmStreamPrepareHeader(hStreamResample, &ahdResample, 0)) m_error = 1; + m_bytes_inbuf_resample = 0; + m_bytes_outbuf_resample = 0; + } + } + + if (!hStream) + { + m_error = 1; + return ; + } + + ZeroMemory(&ahd, sizeof(ahd)); + ahd.cbStruct = sizeof(ahd); + ahd.pbSrc = m_acm_buf; + ahd.cbSrcLength = BUFSIZE; + ahd.pbDst = m_acm_outbuf; + ahd.cbDstLength = BUFSIZE; + if (acmStreamPrepareHeader(hStream, &ahd, 0)) m_error = 1; +} + +ACMEncoder::~ACMEncoder() +{ + free(m_acm_buf); + free(m_acm_outbuf); + free(m_acm_resample_buf); + free(m_acm_resample_outbuf); + if (hStream) + { + if (ahd.fdwStatus & ACMSTREAMHEADER_STATUSF_PREPARED) acmStreamUnprepareHeader(hStream, &ahd, 0); + acmStreamClose(hStream, 0); + } + if (hStreamResample) + { + if (ahdResample.fdwStatus & ACMSTREAMHEADER_STATUSF_PREPARED) acmStreamUnprepareHeader(hStreamResample, &ahdResample, 0); + acmStreamClose(hStreamResample, 0); + } +} + +int ACMEncoder::Encode(int framepos, void *in, int in_avail, int *in_used, void *out, int out_avail) +{ + char *pout = (char *)out; + int retval = 0; + + if (!m_did_header && do_header) + { + int s = 4 + 4 + 12 - 4; + + int t; + if (m_convert_wfx.wfx.wFormatTag == WAVE_FORMAT_PCM) t = 0x10; + else t = sizeof(WAVEFORMATEX) + m_convert_wfx.wfx.cbSize; + s += 4 + t; + if (s&1) s++; + + if (m_convert_wfx.wfx.wFormatTag != WAVE_FORMAT_PCM) + s += 12; + + s += 8; + + if (out_avail < s) return 0; + //xx bytes of randomness + m_hlen = s; + m_did_header = 1; + out_avail -= s; + pout += s; + retval = s; + } + + if (!m_bytes_outbuf) + { + if (hStreamResample) + { + if (!m_bytes_outbuf_resample) + { + DWORD flags = ACM_STREAMCONVERTF_BLOCKALIGN; + + int l = min(in_avail, BUFSIZE - m_bytes_inbuf_resample); + if (l < 0) l = 0; + if (l > 0) memcpy(m_acm_resample_buf + m_bytes_inbuf_resample, in, l); + m_bytes_inbuf_resample += l; + *in_used = l; + m_nsam += l; + + ahdResample.cbSrcLength = m_bytes_inbuf_resample; + acmStreamConvert(hStreamResample, &ahdResample, flags); + m_bytes_inbuf_resample -= ahdResample.cbSrcLengthUsed; + memcpy(m_acm_resample_buf, m_acm_resample_buf + ahdResample.cbSrcLengthUsed, m_bytes_inbuf_resample); //memmove + m_bytes_outbuf_resample = ahdResample.cbDstLengthUsed; + } + in = (void*)m_acm_resample_outbuf; + in_avail = m_bytes_outbuf_resample; + m_bytes_outbuf_resample = 0; + in_used = NULL; + } + + DWORD flags = ACM_STREAMCONVERTF_BLOCKALIGN; + + int l = min(in_avail, BUFSIZE - m_bytes_inbuf); + if (l < 0) l = 0; + if (l > 0) memcpy(m_acm_buf + m_bytes_inbuf, in, l); + m_bytes_inbuf += l; + if (in_used) + { + *in_used = l; + m_nsam += l; + } + + if (m_bytes_inbuf) + { + ahd.cbSrcLength = m_bytes_inbuf; + acmStreamConvert(hStream, &ahd, flags); + m_bytes_inbuf -= ahd.cbSrcLengthUsed; + memcpy(m_acm_buf, m_acm_buf + ahd.cbSrcLengthUsed, m_bytes_inbuf); //memmove + m_bytes_outbuf = ahd.cbDstLengthUsed; + m_bytes_done += l; + } + } + if (m_bytes_outbuf) + { + int l = min(out_avail, m_bytes_outbuf); + memcpy(pout, m_acm_outbuf, l); + m_bytes_outbuf -= l; + memcpy(m_acm_outbuf, m_acm_outbuf + l, m_bytes_outbuf); + retval += l; + } + + return retval; +} + +void ACMEncoder::FinishAudio(const wchar_t *filename) +{ + if (!do_header) return ; + + HANDLE fh = CreateFileW(filename, GENERIC_WRITE | GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); + if (fh == INVALID_HANDLE_VALUE) + return; + + int len, i; + const unsigned char ispred1[4] = + { + 0x52 , 0x49 , 0x46 , 0x46 + }; + const unsigned char ispred2[12] = + { + 0x57, 0x41 , 0x56 , 0x45 , 0x66 , 0x6d , 0x74 , 0x20 , 0x10 , 0x0 , 0x0 , 0x0 + }; + + len = m_bytes_done; + DWORD a = 0; + + FileAlign(fh); + + SetFilePointer(fh, 0, 0, FILE_BEGIN); + + WriteFile(fh, ispred1, sizeof(ispred1), &a, NULL); + i = len + (m_hlen) - 8; + if (i&1) i++; + a = 0; WriteFile(fh, &i, 4, &a, NULL); + a = 0; WriteFile(fh, ispred2, sizeof(ispred2) - (hStream ? 4 : 0), &a, NULL); + + int t; + if (m_convert_wfx.wfx.wFormatTag == WAVE_FORMAT_PCM) t = 0x10; + else t = sizeof(WAVEFORMATEX) + m_convert_wfx.wfx.cbSize; + a = 0; WriteFile(fh, &t, 4, &a, 0); + a = 0; WriteFile(fh, &m_convert_wfx.wfx, t, &a, 0); + + FileAlign(fh); + + DWORD fact_ofs = 0; + if (m_convert_wfx.wfx.wFormatTag != WAVE_FORMAT_PCM) + { + t = rev32('fact'); + a = 0; WriteFile(fh, &t, 4, &a, 0); + t = 4; + a = 0; WriteFile(fh, &t, 4, &a, 0); + fact_ofs = FileTell(fh); + SetFilePointer(fh, 4, 0, FILE_CURRENT); + } + + t = rev32('data'); + WriteFile(fh, &t, 4, &a, 0); + DWORD data_ofs = FileTell(fh); + + { + DWORD t, bw = 0; + SetFilePointer(fh, 4, 0, FILE_BEGIN); + t = GetFileSize(fh, 0) - 8; + WriteFile(fh, &t, 4, &bw, 0); + DWORD data_size = GetFileSize(fh, 0) - (data_ofs + 4); + SetFilePointer(fh, data_ofs, 0, FILE_BEGIN); + bw = 0; WriteFile(fh, &data_size, 4, &bw, 0); + if (fact_ofs) + { + SetFilePointer(fh, fact_ofs, 0, FILE_BEGIN); + t = m_nsam / ((m_bps >> 3) * m_nch); + WriteFile(fh, &t, 4, &bw, 0); + } + } + + CloseHandle(fh); +} + +int ACMEncoder::GetLastError() { return m_error; } \ No newline at end of file diff --git a/Src/Plugins/Encoder/enc_wav/ACMEncoder.h b/Src/Plugins/Encoder/enc_wav/ACMEncoder.h new file mode 100644 index 00000000..deb95968 --- /dev/null +++ b/Src/Plugins/Encoder/enc_wav/ACMEncoder.h @@ -0,0 +1,47 @@ +#ifndef NULLSOFT_ENC_ACM_ACMENCODER_H +#define NULLSOFT_ENC_ACM_ACMENCODER_H + +#include +#include +#include +#include "../nsv/enc_if.h" +#include "Config.h" +#include "Finisher.h" + +class ACMEncoder : public AudioCommon +{ +public: + ACMEncoder(int srate, int nch, int bps, ACMConfig *config); + virtual ~ACMEncoder(); + + virtual int Encode(int framepos, void *in, int in_avail, int *in_used, void *out, int out_avail); + void FinishAudio(const wchar_t *filename); + + int GetLastError(); + + int m_did_header; + int m_nch, m_srate, m_bps; + int m_bytes_done; + int m_error; + int m_hlen; + int m_nsam; + + EXT_WFX m_convert_wfx; + + WAVEFORMATEX m_wfx_src; + HACMSTREAM hStream, hStreamResample; + + ACMSTREAMHEADER ahd, ahdResample; + + unsigned char *m_acm_buf, *m_acm_outbuf; + int m_bytes_inbuf, m_bytes_outbuf; + unsigned char *m_acm_resample_buf, *m_acm_resample_outbuf; + int m_bytes_inbuf_resample, m_bytes_outbuf_resample; + + bool do_header; + +}; + + + +#endif \ No newline at end of file diff --git a/Src/Plugins/Encoder/enc_wav/Config.cpp b/Src/Plugins/Encoder/enc_wav/Config.cpp new file mode 100644 index 00000000..bb7974f9 --- /dev/null +++ b/Src/Plugins/Encoder/enc_wav/Config.cpp @@ -0,0 +1,110 @@ +#include +#include +#include +#include "Config.h" +#include "resource.h" +#include + +static void ACM_gettext(HWND hwndDlg, char* tx) +{ + ConfigWnd *wc = (ConfigWnd *)GetWindowLongPtr(hwndDlg,GWLP_USERDATA); + ACMFORMATTAGDETAILSA aftd; + ZeroMemory(&aftd, sizeof(aftd)); + aftd.cbStruct = sizeof(aftd); + aftd.dwFormatTag = wc->cfg.convert_wfx.wfx.wFormatTag; + if (!acmFormatTagDetailsA(0, &aftd, ACM_FORMATTAGDETAILSF_FORMATTAG)) + { + char* p = aftd.szFormatTag; + while (p && *p) *(tx++) = *(p++); + *(tx++) = 13; + *(tx++) = 10; + } + ACMFORMATDETAILSA afd; + ZeroMemory(&afd, sizeof(afd)); + afd.cbStruct = sizeof(afd); + afd.dwFormatTag = wc->cfg.convert_wfx.wfx.wFormatTag; + afd.pwfx = &wc->cfg.convert_wfx.wfx; + afd.cbwfx = sizeof(wc->cfg.convert_wfx); + if (!acmFormatDetailsA(0, &afd, ACM_FORMATDETAILSF_FORMAT)) + { + char* p = afd.szFormat; + while (p && *p) *(tx++) = *(p++); + } + *tx = 0; +} + +static void ACM_choose(HWND hwndDlg, bool pcm) +{ + ConfigWnd *wc = (ConfigWnd *)GetWindowLongPtr(hwndDlg,GWLP_USERDATA); + ACMFORMATCHOOSE afc; + memset(&afc, 0, sizeof(afc)); + afc.cbStruct = sizeof(afc); + afc.fdwStyle = ACMFORMATCHOOSE_STYLEF_INITTOWFXSTRUCT; + afc.pwfx = &wc->cfg.convert_wfx.wfx; + afc.cbwfx = sizeof(wc->cfg.convert_wfx); + + afc.hwndOwner = hwndDlg; + + if (!acmFormatChoose(&afc)) + { + { + char tmp[512]; + StringCchPrintfA(tmp, 512,"%s\x0d\x0a%s", afc.szFormatTag, afc.szFormat); + SetDlgItemTextA(hwndDlg, IDC_FORMAT_DESCRIPTION, tmp); + + StringCchPrintfA(tmp, 512,"%d", wc->cfg.convert_wfx.wfx.cbSize); + WritePrivateProfileStringA("enc_wav","fmtsize", tmp, wc->configfile); + WritePrivateProfileStructA("enc_wav", "fmt", &wc->cfg.convert_wfx, sizeof(wc->cfg.convert_wfx.wfx) + wc->cfg.convert_wfx.wfx.cbSize, wc->configfile); + } + } +} + +INT_PTR WINAPI DlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + ConfigWnd *wc = (ConfigWnd *)GetWindowLongPtr(hwndDlg,GWLP_USERDATA); + switch (uMsg) + { + case WM_INITDIALOG: + { + if (!lParam) // this should NEVER happen + return 0; + + SetWindowLongPtr(hwndDlg,GWLP_USERDATA,(LONG)lParam); + wc=(ConfigWnd*)lParam; + + char tmp[256]; + ACM_gettext(hwndDlg, tmp); + SetDlgItemTextA(hwndDlg, IDC_FORMAT_DESCRIPTION, tmp); + CheckDlgButton(hwndDlg, IDC_HEADER, wc->cfg.header); + CheckDlgButton(hwndDlg, IDC_DO_CONVERT, wc->cfg.convert); + SetDlgItemTextA(hwndDlg, IDC_EXTENSION, wc->cfg.wav_ext); + SendDlgItemMessage(hwndDlg, IDC_EXTENSION, EM_SETLIMITTEXT, 4, 0); + } + break; + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDC_CHOOSE_FORMAT: + ACM_choose(hwndDlg, 0); + break; + case IDC_HEADER: + wc->cfg.header = !!IsDlgButtonChecked(hwndDlg, IDC_HEADER); + WritePrivateProfileStringA("enc_wav", "header", wc->cfg.header?"1":"0", wc->configfile); + break; + case IDC_DO_CONVERT: + wc->cfg.convert = !!IsDlgButtonChecked(hwndDlg, IDC_DO_CONVERT); + WritePrivateProfileStringA("enc_wav", "convert", wc->cfg.convert?"1":"0", wc->configfile); + break; + case IDC_EXTENSION: + if (HIWORD(wParam) == EN_CHANGE) + { + GetDlgItemTextA(hwndDlg, IDC_EXTENSION, wc->cfg.wav_ext, sizeof(wc->cfg.wav_ext)); + WritePrivateProfileStringA("enc_wav", "ext", wc->cfg.wav_ext, wc->configfile); + } + break; + } + break; + + } + return 0; +} \ No newline at end of file diff --git a/Src/Plugins/Encoder/enc_wav/Config.h b/Src/Plugins/Encoder/enc_wav/Config.h new file mode 100644 index 00000000..7ca66652 --- /dev/null +++ b/Src/Plugins/Encoder/enc_wav/Config.h @@ -0,0 +1,31 @@ +#ifndef NULLSOFT_ENC_ACM_CONFIG_H +#define NULLSOFT_ENC_ACM_CONFIG_H + +#include +#include +#include + +#define WFSIZ 0x800 +struct EXT_WFX +{ + WAVEFORMATEX wfx; + BYTE crap[WFSIZ]; +}; + +struct ACMConfig +{ + EXT_WFX convert_wfx; + char wav_ext[32]; + bool header; + bool convert; +}; + +struct ConfigWnd +{ + ACMConfig cfg; + char *configfile; +}; + +INT_PTR WINAPI DlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); + +#endif \ No newline at end of file diff --git a/Src/Plugins/Encoder/enc_wav/Finisher.h b/Src/Plugins/Encoder/enc_wav/Finisher.h new file mode 100644 index 00000000..b81435df --- /dev/null +++ b/Src/Plugins/Encoder/enc_wav/Finisher.h @@ -0,0 +1,10 @@ +#ifndef NULLSOFT_ENC_WAV_FINISHER_H +#define NULLSOFT_ENC_WAV_FINISHER_H + +class AudioCommon : public AudioCoder +{ +public: + virtual void FinishAudio(const wchar_t *filename)=0; +}; + +#endif diff --git a/Src/Plugins/Encoder/enc_wav/WAVEncoder.cpp b/Src/Plugins/Encoder/enc_wav/WAVEncoder.cpp new file mode 100644 index 00000000..374bbf65 --- /dev/null +++ b/Src/Plugins/Encoder/enc_wav/WAVEncoder.cpp @@ -0,0 +1,72 @@ +#include "WAVEncoder.h" + +#define rev32(X) ((((DWORD)(X)&0xFF)<<24)|(((DWORD)(X)&0xFF00)<<8)|(((DWORD)(X)&0xFF0000)>>8)|(((DWORD)(X)&0xFF000000)>>24)) + +WAVEncoder::WAVEncoder(int nch, int srate, int bps, ACMConfig *config) +{ + numBytes = 0; + + inputFormat.wFormatTag = WAVE_FORMAT_PCM; + inputFormat.nChannels = nch; + inputFormat.nSamplesPerSec = srate; + inputFormat.nAvgBytesPerSec = srate * nch * (bps >> 3); + inputFormat.nBlockAlign = nch * (bps >> 3); + inputFormat.wBitsPerSample = bps; + inputFormat.cbSize = 0; + + do_header = config->header; + if (do_header) + first = 44; +} + +int WAVEncoder::Encode(int framepos, void *in, int in_avail, int *in_used, void *out, int out_avail) +{ + if (first) + { + int valid = min(first, out_avail); + first -= valid; + *in_used = 0; + return valid; + } + + int valid = min(in_avail, out_avail); + + memcpy(out, in, valid); + *in_used = valid; + numBytes += valid; + + return valid; +} + +void WAVEncoder::PrepareToFinish() +{} + +void WAVEncoder::FinishAudio(const wchar_t *filename) +{ + if (!do_header) return; + // open old file + HANDLE tempfile = CreateFileW(filename, GENERIC_READ | GENERIC_WRITE, 0, 0, OPEN_EXISTING, 0, 0); + + if (tempfile) + { + // rewrite initial 44 bytes + DWORD bw = 0; + unsigned __int32 t; + t = rev32('RIFF'); + WriteFile(tempfile, &t, 4, &bw, 0); // RIFF (4 bytes) + t = (unsigned __int32)numBytes + 36; + bw = 0;WriteFile(tempfile, &t, 4, &bw, 0); // size of chunk (4 bytes) + t = rev32('WAVE'); + bw = 0;WriteFile(tempfile, &t, 4, &bw, 0); // WAVE (4 bytes) + t = rev32('fmt '); + bw = 0;WriteFile(tempfile, &t, 4, &bw, 0);// fmt (4 bytes) + t = 16; + bw = 0;WriteFile(tempfile, &t, 4, &bw, 0);// size of chunk (4 bytes) + bw = 0;WriteFile(tempfile, &inputFormat, 16, &bw, 0); // write WAVEFORMAT out (16 bytes) + t = rev32('data'); + bw = 0;WriteFile(tempfile, &t, 4, &bw, 0);// data (4 bytes) + bw = 0;WriteFile(tempfile, &numBytes, 4, &bw, 0);// size of chunk (4 bytes) + + CloseHandle(tempfile); + } +} \ No newline at end of file diff --git a/Src/Plugins/Encoder/enc_wav/WAVEncoder.h b/Src/Plugins/Encoder/enc_wav/WAVEncoder.h new file mode 100644 index 00000000..a9867d55 --- /dev/null +++ b/Src/Plugins/Encoder/enc_wav/WAVEncoder.h @@ -0,0 +1,25 @@ +#ifndef NULLSOFT_ENC_WAV_WAVENCODER_H +#define NULLSOFT_ENC_WAV_WAVENCODER_H + + +#include +#include +#include +#include "../nsv/enc_if.h" +#include "Config.h" +#include "Finisher.h" +class WAVEncoder : public AudioCommon +{ +public: + WAVEncoder(int nch, int srate, int bps, ACMConfig *config); + int Encode(int framepos, void *in, int in_avail, int *in_used, void *out, int out_avail); //returns bytes in out + void FinishAudio(const wchar_t *filename); + void PrepareToFinish(); + + WAVEFORMATEX inputFormat; + size_t numBytes; + int first; + bool do_header; +}; + +#endif \ No newline at end of file diff --git a/Src/Plugins/Encoder/enc_wav/enc_wav.rc b/Src/Plugins/Encoder/enc_wav/enc_wav.rc new file mode 100644 index 00000000..794a0598 --- /dev/null +++ b/Src/Plugins/Encoder/enc_wav/enc_wav.rc @@ -0,0 +1,111 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "#include ""version.rc2""\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_CONFIG DIALOGEX 0, 0, 256, 105 +STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_SYSMENU +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "Write RIFF Header (otherwise write RAW data)",IDC_HEADER, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,8,13,248,10 + CONTROL "Convert to format:",IDC_DO_CONVERT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,26,77,10 + PUSHBUTTON "Choose Format",IDC_CHOOSE_FORMAT,7,40,63,14 + EDITTEXT IDC_FORMAT_DESCRIPTION,7,59,240,22,ES_MULTILINE | ES_AUTOHSCROLL | ES_READONLY + LTEXT "Extension:",IDC_STATIC,8,86,35,13,SS_CENTERIMAGE + EDITTEXT IDC_EXTENSION,48,86,40,13,ES_AUTOHSCROLL + GROUPBOX "Wav Encoder Options",IDC_STATIC,0,0,256,105 +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////// +// English (U.K.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENG) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_UK +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + 65535 "{34DF1A2D-7EAD-41ab-B1A7-9AFA6DE2AFF1}" +END + +STRINGTABLE +BEGIN + IDS_ENC_WAV_DESC "WAV Encoder %s" +END + +#endif // English (U.K.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// +#include "version.rc2" + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/Src/Plugins/Encoder/enc_wav/enc_wav.sln b/Src/Plugins/Encoder/enc_wav/enc_wav.sln new file mode 100644 index 00000000..4c9b46ba --- /dev/null +++ b/Src/Plugins/Encoder/enc_wav/enc_wav.sln @@ -0,0 +1,30 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29519.181 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "enc_wav", "enc_wav.vcxproj", "{7B43D768-8CD3-4929-B91E-DEB97F121656}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {7B43D768-8CD3-4929-B91E-DEB97F121656}.Debug|Win32.ActiveCfg = Debug|Win32 + {7B43D768-8CD3-4929-B91E-DEB97F121656}.Debug|Win32.Build.0 = Debug|Win32 + {7B43D768-8CD3-4929-B91E-DEB97F121656}.Debug|x64.ActiveCfg = Debug|x64 + {7B43D768-8CD3-4929-B91E-DEB97F121656}.Debug|x64.Build.0 = Debug|x64 + {7B43D768-8CD3-4929-B91E-DEB97F121656}.Release|Win32.ActiveCfg = Release|Win32 + {7B43D768-8CD3-4929-B91E-DEB97F121656}.Release|Win32.Build.0 = Release|Win32 + {7B43D768-8CD3-4929-B91E-DEB97F121656}.Release|x64.ActiveCfg = Release|x64 + {7B43D768-8CD3-4929-B91E-DEB97F121656}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {ECC95181-336A-4B52-8743-F687749093EA} + EndGlobalSection +EndGlobal diff --git a/Src/Plugins/Encoder/enc_wav/enc_wav.vcxproj b/Src/Plugins/Encoder/enc_wav/enc_wav.vcxproj new file mode 100644 index 00000000..195c8a57 --- /dev/null +++ b/Src/Plugins/Encoder/enc_wav/enc_wav.vcxproj @@ -0,0 +1,252 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {7B43D768-8CD3-4929-B91E-DEB97F121656} + enc_wav + 10.0.19041.0 + + + + DynamicLibrary + v142 + Unicode + + + DynamicLibrary + v142 + Unicode + + + DynamicLibrary + v142 + Unicode + + + DynamicLibrary + v142 + Unicode + + + + + + + + + + + + + + + + + + + false + $(PlatformShortName)_$(Configuration)\ + $(PlatformShortName)_$(Configuration)\ + $(IncludePath) + $(LibraryPath) + + + false + $(PlatformShortName)_$(Configuration)\ + $(PlatformShortName)_$(Configuration)\ + + + false + $(PlatformShortName)_$(Configuration)\ + $(PlatformShortName)_$(Configuration)\ + $(IncludePath) + $(LibraryPath) + + + false + $(PlatformShortName)_$(Configuration)\ + $(PlatformShortName)_$(Configuration)\ + + + false + + + Debug + + + + + + + + + Debug + + + + Disabled + .;..\..\..\Wasabi;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;_USRDLL;ENC_WAV_EXPORTS;%(PreprocessorDefinitions) + false + true + EnableFastChecks + MultiThreadedDebugDLL + Level3 + ProgramDatabase + $(IntDir)$(TargetName).pdb + + + msacm32.lib;shlwapi.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)$(TargetExt) + true + $(IntDir)$(TargetName).pdb + Windows + false + $(IntDir)$(TargetName).lib + MachineX86 + false + %(AdditionalLibraryDirectories) + + + xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ +xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ + Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\' + + + + + Disabled + .;..\..\..\Wasabi;%(AdditionalIncludeDirectories) + WIN64;_DEBUG;_WINDOWS;_USRDLL;ENC_WAV_EXPORTS;%(PreprocessorDefinitions) + false + true + EnableFastChecks + MultiThreadedDebugDLL + Level3 + ProgramDatabase + $(IntDir)$(TargetName).pdb + + + msacm32.lib;shlwapi.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)$(TargetExt) + true + $(IntDir)$(TargetName).pdb + Windows + false + $(IntDir)$(TargetName).lib + false + %(AdditionalLibraryDirectories) + + + xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ +xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ + Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\' + + + + + MinSpace + Size + .;..\..\..\Wasabi;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_WINDOWS;_USRDLL;ENC_WAV_EXPORTS;%(PreprocessorDefinitions) + true + true + MultiThreadedDLL + true + Level3 + None + $(IntDir)$(TargetName).pdb + + + msacm32.lib;shlwapi.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)$(TargetExt) + false + $(IntDir)$(TargetName).pdb + Windows + true + true + false + $(IntDir)$(TargetName).lib + MachineX86 + false + %(AdditionalLibraryDirectories) + + + xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ + Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\' + + + + + MinSpace + Size + .;..\..\..\Wasabi;%(AdditionalIncludeDirectories) + WIN64;NDEBUG;_WINDOWS;_USRDLL;ENC_WAV_EXPORTS;%(PreprocessorDefinitions) + true + true + MultiThreadedDLL + true + Level3 + None + $(IntDir)$(TargetName).pdb + + + msacm32.lib;shlwapi.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)$(TargetExt) + false + $(IntDir)$(TargetName).pdb + Windows + true + true + false + $(IntDir)$(TargetName).lib + false + %(AdditionalLibraryDirectories) + + + xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ + Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\' + + + + + + + + + + + + + + + + + + + + + {3e0bfa8a-b86a-42e9-a33f-ec294f823f7f} + + + + + + \ No newline at end of file diff --git a/Src/Plugins/Encoder/enc_wav/enc_wav.vcxproj.filters b/Src/Plugins/Encoder/enc_wav/enc_wav.vcxproj.filters new file mode 100644 index 00000000..39f5d704 --- /dev/null +++ b/Src/Plugins/Encoder/enc_wav/enc_wav.vcxproj.filters @@ -0,0 +1,50 @@ + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + {ee875982-d629-49cc-a8ae-2ba63dc64a78} + + + {2e191036-3949-43a0-9fce-044245ce82aa} + + + {df114c96-e301-4277-8849-778ec6cf693a} + + + + + Ressource Files + + + \ No newline at end of file diff --git a/Src/Plugins/Encoder/enc_wav/main.cpp b/Src/Plugins/Encoder/enc_wav/main.cpp new file mode 100644 index 00000000..6a0cd5d5 --- /dev/null +++ b/Src/Plugins/Encoder/enc_wav/main.cpp @@ -0,0 +1,176 @@ +#define ENC_VERSION "v1.02a" + +#include "Config.h" +#include "resource.h" +#include "../nsv/enc_if.h" +#include "ACMEncoder.h" +#include "WAVEncoder.h" +#include "../nu/AutoWideFn.h" + +BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) +{ + return TRUE; +} + +// wasabi based services for localisation support +#include +#include "../Agave/Language/api_language.h" +#include "../winamp/wa_ipc.h" + +#include +HWND winampwnd = 0; +api_service *WASABI_API_SVC = 0; +api_language *WASABI_API_LNG = 0; +HINSTANCE WASABI_API_LNG_HINST = 0, WASABI_API_ORIG_HINST = 0; + +static const WAVEFORMATEX wfx_default = +{ + WAVE_FORMAT_PCM, + 2, + 44100, + 44100 * 4, + 4, + 16, + 0 +}; + +static void ReadConfig(ACMConfig *config, char *INI_FILE) +{ + int l = GetPrivateProfileIntA("enc_wav", "fmtsize", 0, INI_FILE); + + EXT_WFX convert_wfx_temp; + if (GetPrivateProfileStructA("enc_wav", "fmt", &convert_wfx_temp, sizeof(WAVEFORMATEX) + l, INI_FILE)) + memcpy(&config->convert_wfx, &convert_wfx_temp, sizeof(config->convert_wfx)); + else + config->convert_wfx.wfx = wfx_default; + + GetPrivateProfileStringA("enc_wav", "ext", "WAV", config->wav_ext, sizeof(config->wav_ext), INI_FILE); + config->header = !!GetPrivateProfileIntA("enc_wav", "header", 1, INI_FILE); + config->convert = !!GetPrivateProfileIntA("enc_wav", "convert", 0, INI_FILE); +} + +static HINSTANCE GetMyInstance() +{ + MEMORY_BASIC_INFORMATION mbi = {0}; + if(VirtualQuery(GetMyInstance, &mbi, sizeof(mbi))) + return (HINSTANCE)mbi.AllocationBase; + return NULL; +} + +void GetLocalisationApiService(void) +{ + if(!WASABI_API_LNG) + { + // loader so that we can get the localisation service api for use + if(!WASABI_API_SVC) + { + WASABI_API_SVC = (api_service*)SendMessage(winampwnd, WM_WA_IPC, 0, IPC_GET_API_SERVICE); + if (WASABI_API_SVC == (api_service*)1) + { + WASABI_API_SVC = NULL; + return; + } + } + + if(!WASABI_API_LNG) + { + waServiceFactory *sf; + sf = WASABI_API_SVC->service_getServiceByGuid(languageApiGUID); + if (sf) WASABI_API_LNG = reinterpret_cast(sf->getInterface()); + } + + // need to have this initialised before we try to do anything with localisation features + WASABI_API_START_LANG(GetMyInstance(),EncWavLangGUID); + } +} + +extern "C" +{ + AudioCoder __declspec(dllexport) *CreateAudio3(int nch, int srate, int bps, unsigned int srct, unsigned int *outt, char *configfile) + { + if (srct == mmioFOURCC('P','C','M',' ')) + { + if (*outt == mmioFOURCC('A','C','M',' ')) + { + ACMConfig config; + ReadConfig(&config, configfile); + if (config.convert) + { + ACMEncoder *encoder = new ACMEncoder(srate, nch, bps, &config); + if (encoder->GetLastError()) + { + delete encoder; + encoder=0; + } + return encoder; + } + else + { + return new WAVEncoder(nch, srate, bps, &config); + } + } + else if (*outt == mmioFOURCC('W','A','V',' ')) + { + ACMConfig config; + ReadConfig(&config, configfile); + return new WAVEncoder(nch, srate, bps, &config); + } + } + return 0; + } + + unsigned int __declspec(dllexport) GetAudioTypes3(int idx, char *desc) + { + switch(idx) + { + case 0: + GetLocalisationApiService(); + StringCchPrintfA(desc, 1024, WASABI_API_LNGSTRING(IDS_ENC_WAV_DESC), ENC_VERSION); + return mmioFOURCC('A','C','M',' '); + } + return 0; + } + + HWND __declspec(dllexport) ConfigAudio3(HWND hwndParent, HINSTANCE hinst, unsigned int outt, char *configfile) + { + if (outt == mmioFOURCC('A', 'C','M',' ')) + { + ConfigWnd configwnd; + ReadConfig(&configwnd.cfg, configfile); + configwnd.configfile = configfile; + GetLocalisationApiService(); + return WASABI_API_CREATEDIALOGPARAMW(IDD_CONFIG, hwndParent, DlgProc, (LPARAM)&configwnd); + } + return 0; + } + + void __declspec(dllexport) FinishAudio3(const char *filename, AudioCoder *coder) + { + ((AudioCommon*)coder)->FinishAudio(AutoWideFn(filename)); + } + + void __declspec(dllexport) FinishAudio3W(const wchar_t *filename, AudioCoder *coder) + { + ((AudioCommon*)coder)->FinishAudio(filename); + } + + int __declspec(dllexport) GetConfigItem(unsigned int outt, char *item, char *data, int len, char *configfile) + { + if (outt==mmioFOURCC('A','C','M',' ')) + { + ACMConfig config; + ReadConfig(&config, configfile); + if (!_stricmp(item, "extension")) + { + lstrcpynA(data, config.wav_ext, len); + return 1; + } + } + return 0; + } + + void __declspec(dllexport) SetWinampHWND(HWND hwnd) + { + winampwnd = hwnd; + } +}; \ No newline at end of file diff --git a/Src/Plugins/Encoder/enc_wav/preferences.cpp b/Src/Plugins/Encoder/enc_wav/preferences.cpp new file mode 100644 index 00000000..2e71d25d --- /dev/null +++ b/Src/Plugins/Encoder/enc_wav/preferences.cpp @@ -0,0 +1 @@ +// TODO: acmFormatChoose \ No newline at end of file diff --git a/Src/Plugins/Encoder/enc_wav/resource.h b/Src/Plugins/Encoder/enc_wav/resource.h new file mode 100644 index 00000000..47345ea7 --- /dev/null +++ b/Src/Plugins/Encoder/enc_wav/resource.h @@ -0,0 +1,23 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by enc_acm.rc +// +#define IDD_CONFIG 101 +#define IDS_ENC_WAV_DESC 104 +#define IDC_FORMAT_DESCRIPTION 1001 +#define IDC_CHOOSE_FORMAT 1002 +#define IDC_EXTENSION 1003 +#define IDC_HEADER 1004 +#define IDC_CHECK1 1005 +#define IDC_DO_CONVERT 1005 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 105 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1006 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/Src/Plugins/Encoder/enc_wav/version.rc2 b/Src/Plugins/Encoder/enc_wav/version.rc2 new file mode 100644 index 00000000..efdcb944 --- /dev/null +++ b/Src/Plugins/Encoder/enc_wav/version.rc2 @@ -0,0 +1,39 @@ + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// +#include "../../../Winamp/buildType.h" +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,2,1 + PRODUCTVERSION WINAMP_PRODUCTVER + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Winamp SA" + VALUE "FileDescription", "Winamp Encoder Plug-in" + VALUE "FileVersion", "1,0,2,1" + VALUE "InternalName", "Nullsoft WAV Encoder" + VALUE "LegalCopyright", "Copyright © 2006-2023 Winamp SA" + VALUE "LegalTrademarks", "Nullsoft and Winamp are trademarks of Winamp SA" + VALUE "OriginalFilename", "enc_wav.dll" + VALUE "ProductName", "Winamp" + VALUE "ProductVersion", STR_WINAMP_PRODUCTVER + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/Src/Plugins/Encoder/enc_wma/ASFErr.h b/Src/Plugins/Encoder/enc_wma/ASFErr.h new file mode 100644 index 00000000..66bb165e --- /dev/null +++ b/Src/Plugins/Encoder/enc_wma/ASFErr.h @@ -0,0 +1,256 @@ +//========================================================================= +// +// THIS SOFTWARE HAS BEEN LICENSED FROM MICROSOFT CORPORATION PURSUANT +// TO THE TERMS OF AN END USER LICENSE AGREEMENT ("EULA"). +// PLEASE REFER TO THE TEXT OF THE EULA TO DETERMINE THE RIGHTS TO USE THE SOFTWARE. +// +// Copyright (C) Microsoft Corporation, 1996 - 1998 All Rights Reserved. +// +//========================================================================= +/////////////////////////////////////////////////////////////////////////// +// +// ASFErr.h - definition of ASF HRESULT codes +// +// Copyright (C) Microsoft Corporation, 1997 - 1998 +// +// This file is generated by the MC tool from ASFErr.mc +// + +#ifndef _ASFERR_H +#define _ASFERR_H + + +#define STATUS_SEVERITY(hr) (((hr) >> 30) & 0x3) + + +/////////////////////////////////////////////////////////////////////////// +// +// Advanced Streaming Format (ASF) Errors (2000 - 2999) +// +// +// Values are 32 bit values layed out as follows: +// +// 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 +// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 +// +---+-+-+-----------------------+-------------------------------+ +// |Sev|C|R| Facility | Code | +// +---+-+-+-----------------------+-------------------------------+ +// +// where +// +// Sev - is the severity code +// +// 00 - Success +// 01 - Informational +// 10 - Warning +// 11 - Error +// +// C - is the Customer code flag +// +// R - is a reserved bit +// +// Facility - is the facility code +// +// Code - is the facility's status code +// +// +// Define the facility codes +// +#define FACILITY_NS 0xD + + +// +// Define the severity codes +// +#define STATUS_SEVERITY_WARNING 0x2 +#define STATUS_SEVERITY_SUCCESS 0x0 +#define STATUS_SEVERITY_INFORMATIONAL 0x1 +#define STATUS_SEVERITY_ERROR 0x3 + + +// +// MessageId: ASF_E_BUFFEROVERRUN +// +// MessageText: +// +// An attempt was made to seek or position past the end of a buffer.%0 +// +#define ASF_E_BUFFEROVERRUN 0xC00D07D0L + +// +// MessageId: ASF_E_BUFFERTOOSMALL +// +// MessageText: +// +// The supplied input or output buffer was too small.%0 +// +#define ASF_E_BUFFERTOOSMALL 0xC00D07D1L + +// +// MessageId: ASF_E_BADLANGUAGEID +// +// MessageText: +// +// The language ID was not found.%0 +// +#define ASF_E_BADLANGUAGEID 0xC00D07D2L + +// +// MessageId: ASF_E_NOPAYLOADLENGTH +// +// MessageText: +// +// The multiple payload packet is missing the payload length.%0 +// +#define ASF_E_NOPAYLOADLENGTH 0xC00D07DBL + +// +// MessageId: ASF_E_TOOMANYPAYLOADS +// +// MessageText: +// +// The packet contains too many payloads.%0 +// +#define ASF_E_TOOMANYPAYLOADS 0xC00D07DCL + +// +// MessageId: ASF_E_PACKETCONTENTTOOLARGE +// +// MessageText: +// +// ASF_E_PACKETCONTENTTOOLARGE +// +#define ASF_E_PACKETCONTENTTOOLARGE 0xC00D07DEL + +// +// MessageId: ASF_E_UNKNOWNPACKETSIZE +// +// MessageText: +// +// Expecting a fixed packet size but min. and max. are not equal.%0 +// +#define ASF_E_UNKNOWNPACKETSIZE 0xC00D07E0L + +// +// MessageId: ASF_E_INVALIDHEADER +// +// MessageText: +// +// ASF_E_INVALIDHEADER +// +#define ASF_E_INVALIDHEADER 0xC00D07E2L + +// +// MessageId: ASF_E_NOCLOCKOBJECT +// +// MessageText: +// +// The object does not have a valid clock object.%0 +// +#define ASF_E_NOCLOCKOBJECT 0xC00D07E6L + +// +// MessageId: ASF_E_UNKNOWNCLOCKTYPE +// +// MessageText: +// +// ASF_E_UNKNOWNCLOCKTYPE +// +#define ASF_E_UNKNOWNCLOCKTYPE 0xC00D07EBL + +// +// MessageId: ASF_E_OPAQUEPACKET +// +// MessageText: +// +// An attempt was made to restore or access an opaque packet.%0 +// +#define ASF_E_OPAQUEPACKET 0xC00D07EDL + +// +// MessageId: ASF_E_WRONGVERSION +// +// MessageText: +// +// ASF_E_WRONGVERSION +// +#define ASF_E_WRONGVERSION 0xC00D07EEL + +// +// MessageId: ASF_E_OVERFLOW +// +// MessageText: +// +// An attempt was made to store a value which was larger than then destination's maximum value.%0 +// +#define ASF_E_OVERFLOW 0xC00D07EFL + +// +// MessageId: ASF_E_NOTFOUND +// +// MessageText: +// +// The object was not found.%0 +// +#define ASF_E_NOTFOUND 0xC00D07F0L + +// +// MessageId: ASF_E_OBJECTTOOBIG +// +// MessageText: +// +// The object is too large to be processed in the requested manner.%0 +// +#define ASF_E_OBJECTTOOBIG 0xC00D07F1L + +// +// MessageId: ASF_E_UNEXPECTEDVALUE +// +// MessageText: +// +// A value was not set as expected.%0 +// +#define ASF_E_UNEXPECTEDVALUE 0xC00D07F2L + +// +// MessageId: ASF_E_INVALIDSTATE +// +// MessageText: +// +// The request is not valid in the object's current state.%0 +// +#define ASF_E_INVALIDSTATE 0xC00D07F3L + +// +// MessageId: ASF_E_NOLIBRARY +// +// MessageText: +// +// This object does not have a valid library pointer; it has not been Init()'ed or it has been Shutdown().%0 +// +#define ASF_E_NOLIBRARY 0xC00D07F4L + + +/////////////////////////////////////////////////////////////////////////// +// +// Advanced Streaming Format (ASF) Success Codes (2000 - 2999) +// + +// +// MessageId: ASF_S_OPAQUEPACKET +// +// MessageText: +// +// ASF_S_OPAQUEPACKET +// +#define ASF_S_OPAQUEPACKET 0x000D07F0L + + +/////////////////////////////////////////////////////////////////////////// +// +// Advanced Streaming Format (ASF) Warnings (2000 - 2999) +// + + +#endif // _ASFERR_H + diff --git a/Src/Plugins/Encoder/enc_wma/AudioCoderWMA.cpp b/Src/Plugins/Encoder/enc_wma/AudioCoderWMA.cpp new file mode 100644 index 00000000..3fa7a7f0 --- /dev/null +++ b/Src/Plugins/Encoder/enc_wma/AudioCoderWMA.cpp @@ -0,0 +1,468 @@ +#include +#include +#include +#include "../nu/ns_wc.h" +#include "resource.h" +#include "wmsdk.h" // for IWMWriterSink + +#include "AudioCoderWMA.h" + +#include +#include + +#include "../nu/AutoLock.h" +#include "../nu/AutoWide.h" +#include "../Winamp/strutil.h" +#include "../Agave/Language/api_language.h" + +/* TODO: implement 2-pass encoding via IWMWriterPreprocess */ + +int config_bitrate, config_samplerate, config_nch; +// New globals for encoder query + +class CustomIndexStatus : public IWMStatusCallback +{ +public: + CustomIndexStatus( HANDLE _done ) : done(_done), IWMStatusCallback(), refcount(1) + {} + + // IUnknown methods +public: + virtual ULONG STDMETHODCALLTYPE AddRef() + { + return ++refcount; + } + + + virtual ULONG STDMETHODCALLTYPE Release() + { + // If we go to zero, who cares? + return --refcount; + } + + + virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void **ppvObject) + { + HRESULT hRetval = E_NOINTERFACE; + if (IID_IWMStatusCallback == iid) + { + *ppvObject = static_cast(this); + hRetval = S_OK; + } + else + { + *ppvObject = NULL; + } + return hRetval; + } + + // IWMStatusCallback methods +public: + HRESULT STDMETHODCALLTYPE OnStatus( WMT_STATUS Status, HRESULT hr, WMT_ATTR_DATATYPE dwType, BYTE* pValue, void* pvContext ) + { + switch ( Status ) + { + case WMT_CLOSED: + // You may want to deal with the HRESULT value passed with the status. + // If you do, you should do it here. + + // Signal the event. + SetEvent(done); + break; + } + return S_OK; + } + +protected: + ULONG refcount; + HANDLE done; +}; + + +// Our custom buffer object, used by the writer sink. + +AudioCoderWMA::AudioCoderWMA(int numchannels, int samplerate, int bitspersamp, configtype *cfg, char *configfile) : AudioCoder() +{ + lastByteCount=0; + writerAdvanced=0; + begin_writing = false; + error = WMA_NO_ERROR; + sink = NULL; + + // Get globals from Winamp.ini config file + config_bitrate = cfg->config_bitrate; + config_samplerate = cfg->config_samplesSec; + config_nch = cfg->config_nch; + + timeunits_per_byte = ( ( (10000000.0) / (double)samplerate ) / (double)numchannels ) / ( (double)bitspersamp / 8.0 ); + //char t[100] = {0}; + //wsprintf(t,"%d", timeunits_per_byte); + //::MessageBox(NULL, t, t, MB_OK); + input_bytecount = 0; + + HRESULT hr = CreateAndConfigureWriter(numchannels, samplerate, bitspersamp, configfile); + + if ( FAILED(hr) ) + { + error = WMA_CANT_CREATE_WRITER; + } +} + +AudioCoderWMA::~AudioCoderWMA() +{ + if (writer) + { + if ( begin_writing ) + { + begin_writing = false; + writer->EndWriting(); + } + writer->Release(); + writer = NULL; + } + if (writerAdvanced) + { + writerAdvanced->Release(); + writerAdvanced=0; + } + if (sink) + { + sink->Release(); + sink=0; + } +} + +int AudioCoderWMA::GetLastError() +{ + return error; +} + +void AudioCoderWMA::PrepareToFinish() +{ + // We don't want to kill the objects here, because there might still be data in the pipeline. + if (writer && begin_writing) + { + begin_writing = false; + // Tell WM that we're done giving it input data. + + writer->EndWriting(); + // TODO: do we have to wait for this to finish? + } +} + +void AudioCoderWMA::OnFinished(const wchar_t *wfname) +{ + // + // Okay, here we need to go back and index the file we just wrote so it's seekable. + // + + // From: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wmform/htm/toconfiguretheindexer.asp + // And: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wmform/htm/toindexanasffile.asp + IWMIndexer* pBaseIndexer = NULL; + IWMIndexer2* pMyIndexer = NULL; + + // Create an indexer. + WMCreateIndexer(&pBaseIndexer); + + // Retrieve an IWMIndexer2 interface pointer for the indexer just created. + pBaseIndexer->QueryInterface(IID_IWMIndexer2, (void **) & pMyIndexer); + + // Release the base indexer. + pBaseIndexer->Release(); + pBaseIndexer = NULL; + + // Configure the indexer to create a timecode-based index. + pMyIndexer->Configure(0, // Stream Number, use all. + WMT_IT_PRESENTATION_TIME, // Indexer type. + NULL, // Index interval, use default. + NULL); // Index type, use default. + + // Create an event for asynchronous calls. + HANDLE done = CreateEvent(NULL, TRUE, FALSE, NULL); + + // Give that to the status object + CustomIndexStatus status( done ); + + // Start the indexer. + pMyIndexer->StartIndexing(tempFilename, &status, NULL); + + // Wait for the indexer to finish. + WaitForSingleObject(done, INFINITE); + + // Release the remaining interface. + pMyIndexer->Release(); + pMyIndexer = NULL; + + // Cleanup + CloseHandle( done ); + DeleteFileW(wfname); + MoveFileW(tempFilename, wfname); + +} + +int AudioCoderWMA::Encode(int framepos, void *in, int in_avail, int *in_used, void *out, int out_avail) +{ + HRESULT hr = S_OK; + int retval = 0; // number of bytes written into "out" + *in_used = 0; // number of bytes read from "in" + if ( !framepos && !in_avail ) + { + int x = 0; + x++; + } + assert(writer); + + // Hopefully, at the end of the stream, we still get called with "Encode" until we return 0? + if (in_avail) + { + // Allocate an INSSBuffer of the proper size to hold all the data. + INSSBuffer* pSample = NULL; + if (FAILED(writer->AllocateSample(in_avail, &pSample))) return -1; + + // Get its internal memory buffer + DWORD newBufferLength; + pSample->GetLength(&newBufferLength); + assert(newBufferLength == in_avail); + + BYTE *pdwBuffer = NULL; + if (FAILED(pSample->GetBuffer(&pdwBuffer))) return -1; + + memcpy(pdwBuffer, in, in_avail); // Send all the available bytes in the input buffer into the IWMWriter, + + pSample->SetLength(in_avail); // Tell the buffer object how much we used + + QWORD timeunits = (QWORD)( (double)input_bytecount * timeunits_per_byte ); // Compute the current timecode + // And stuff it into the writer + hr = writer->WriteSample(0, timeunits, 0, pSample); + if (FAILED(hr)) + { + } + else + { + // Increment the bytecount to be able to calculate the next timecode + input_bytecount += in_avail; + // And tell the host we used up all the available input data. + *in_used = in_avail; + } + // Release immediately + pSample->Release(); + } + + WM_WRITER_STATISTICS stats; + writerAdvanced->GetStatistics(0, &stats); + retval = (int)(stats.qwByteCount - lastByteCount); + retval = min(retval, out_avail); + lastByteCount+=retval; + memset(out, 0, retval); // so we don't write random memory to disk + + return retval; +} + +HRESULT AudioCoderWMA::SelectAndLoadResampler(int numchannels, int samplerate, int bitspersamp) +{ + DWORD inCount = 0; + BOOL success = false; + + //wsprintf(junk,"IN Chan=%d, SRate=%d, BPS=%d", numchannels, samplerate,bitspersamp); + //MessageBox(NULL, junk, "INPUT FMT", MB_OK); + // First get the number of input streams + HRESULT hr = writer->GetInputCount(&inCount); + if(!FAILED(hr)){ + //wsprintf(junk, "Input Count = %d", inCount); + //MessageBox(NULL, junk, "DEBUG", MB_OK); + // Now get the number of input formats we can resample for + DWORD fmtCount = 0; + hr = writer->GetInputFormatCount(0, &fmtCount); + if(!FAILED(hr)){ + //wsprintf(junk, "Format Count = %d", fmtCount); + //MessageBox(NULL, junk, "DEBUG", MB_OK); + // Now cycle through and find the one that matches our input fmt + for(size_t i = 0;i < fmtCount;i++){ + IWMInputMediaProps* pProps = NULL; + hr = writer->GetInputFormat(0, (DWORD)i, &pProps); + if(!FAILED(hr)){ + DWORD cbSize = 0; + // Get the size of the media type structure. + pProps->GetMediaType(NULL, &cbSize); + // Allocate memory for the media type structure. + WM_MEDIA_TYPE* pType = (WM_MEDIA_TYPE*) new BYTE[cbSize]; + if(pType != NULL){ + WAVEFORMATEX* pwave = NULL; + // Get the media type structure. + hr = pProps->GetMediaType(pType, &cbSize); + // Check that the format data is present. + if (pType->cbFormat >= sizeof(WAVEFORMATEX)){ + pwave = (WAVEFORMATEX*)pType->pbFormat; + //wsprintf(junk, "Cnannels = %d, SPerSec = %d, AvgBPS = %d, BPS = %d BALIGN = %d", + // pwave->nChannels, + // pwave->nSamplesPerSec, + // pwave->nAvgBytesPerSec, + // pwave->wBitsPerSample, + // pwave->nBlockAlign); + //MessageBox(NULL, junk, "DEBUG", MB_OK); + } + else{ + break; + } + // Try to match the channels/samplerate/and bits/samp + if((pwave->nChannels == numchannels) && (pwave->nSamplesPerSec == samplerate) && (pwave->wBitsPerSample == bitspersamp)){ + writer->SetInputProps(0, pProps); + success = true; + break; + } + } + } + } + } + } + if(success != 1){ + wchar_t junk[FILETITLE_SIZE] = {0}; + wsprintfW(junk,WASABI_API_LNGSTRINGW(IDS_CANNOT_FIND_INPUT_FORMATTER), + numchannels, samplerate,bitspersamp); + MessageBoxW(NULL, junk, WASABI_API_LNGSTRINGW(IDS_WARNING), MB_OK); + } + if (success) + return S_OK; + else if (FAILED(hr)) // if we have an error code, return it + return hr; + else + return E_FAIL; +} + + +HRESULT AudioCoderWMA::CreateAndConfigureWriter(WORD numchannels, WORD samplerate, WORD bitspersamp, char *configfile) +{ + // First, create the writer. + HRESULT hr = WMCreateWriter( NULL, &writer ); + if ( !FAILED(hr) ) + { + // Create and Configure a stream profile with the given wave limits. + WAVEFORMATEX WaveLimits = + { + WAVE_FORMAT_PCM, + numchannels, + samplerate, + samplerate * numchannels * bitspersamp / (DWORD)8, + numchannels * bitspersamp / (DWORD)8, + bitspersamp, + 0 + }; + IWMProfile* pProfile = NULL; + hr = CreateAndConfigureProfile(&WaveLimits, &pProfile, configfile); + if ( !FAILED(hr) ) + { + // Set the profile into the writer + hr = writer->SetProfile( pProfile ); + if ( !FAILED(hr) ) + { + // Go get the input resampler and load it to the profile + hr = SelectAndLoadResampler(numchannels, samplerate, bitspersamp); + if (!FAILED(hr)) + { + wchar_t tempPath[MAX_PATH] = {0}; + GetTempPathW(MAX_PATH,tempPath); + GetTempFileNameW(tempPath, L"wma", 0, tempFilename); + + // Make the custom data sink object + WMCreateWriterFileSink(&sink); + //sink = new CustomWMWriterSink; + if ( sink ) + { + sink->Open(tempFilename); + HRESULT hr; + // From MSDN: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wmform/htm/addingsinkstothewriter.asp + IWMWriterSink* pSinkBase = NULL; + hr = writer->QueryInterface( IID_IWMWriterAdvanced, (void **) & writerAdvanced ); + if ( !FAILED(hr) ) + { + hr = sink->QueryInterface( IID_IWMWriterSink, (void**) & pSinkBase ); + if ( !FAILED(hr) ) + { + // Stuff the custom data sink into the writer. + hr = writerAdvanced->AddSink(pSinkBase); + if ( !FAILED(hr) ) + { + // And let the writer initialize itself for output. + hr = writer->BeginWriting(); + if ( !FAILED(hr) ) + { + begin_writing = true; + } + } + else + { + error = WMA_CANT_ADD_SINK; + } + } + else + { + error = WMA_CANT_QUERY_SINK_INTERFACE; + } + } + else + { + error = WMA_CANT_QUERY_WRITER_INTERFACE; + } + } + else + { + error = WMA_CANT_MAKE_CUSTOM_SINK; + } + } + } + } + } + + return hr; +} + +HRESULT AudioCoderWMA::CreateAndConfigureProfile(WAVEFORMATEX* pWaveLimits, IWMProfile** ppProfile, char *configfile) +{ + IWMProfileManager* pProfileMgr = NULL; + + // Instantiate a profile manager object. + HRESULT hr = WMCreateProfileManager(&pProfileMgr); + if ( !FAILED(hr) ) + { + /* SAVE + // Create the empty profile. + //hr = pProfileMgr->CreateEmptyProfile(WMT_VER_9_0, ppProfile); + if ( !FAILED(hr) ){ + IWMCodecInfo3 *codecInfo = NULL; + hr = pProfileMgr->QueryInterface(&codecInfo); + if(!FAILED(hr)){ + // Find the proper IWMStreamConfig that matches the WAVEFORMATEX data. + IWMStreamConfig* pStreamConfig = NULL; + //hr = FindAudioFormat(WMMEDIASUBTYPE_WMAudioV2, pProfileMgr, pWaveLimits, config_bitrate * 1000, FALSE, &pStreamConfig); + hr = codecInfo->GetCodecFormat(WMMEDIATYPE_Audio, config_encOffset, config_formatOffset, &pStreamConfig); + if ( !FAILED(hr) ){ + // Config the stream. + // hr = pStreamConfig->SetBitrate( config_bitrate ); + hr = pStreamConfig->SetConnectionName( L"enc_wma" ); + hr = pStreamConfig->SetStreamName( L"enc_wma" ); + hr = pStreamConfig->SetStreamNumber( 1 ); + + // Stuff it into the profile + hr = (*ppProfile)->AddStream( pStreamConfig ); + } + } + } + */ + if ( !FAILED(hr) ){ + // Load the .prx file into the writer + if(configfile == NULL){ + hr = E_FAIL; + } + else{ + wchar_t cstring[4000] = {0}; + GetPrivateProfileStructW(L"audio_wma", L"profile", cstring, sizeof(cstring)/sizeof(*cstring), AutoWide(configfile)); + hr = pProfileMgr->LoadProfileByData(cstring, ppProfile); + if(hr != S_OK){ + hr = E_FAIL; + } + } + } + pProfileMgr->Release(); + } + return hr; +} \ No newline at end of file diff --git a/Src/Plugins/Encoder/enc_wma/AudioCoderWMA.h b/Src/Plugins/Encoder/enc_wma/AudioCoderWMA.h new file mode 100644 index 00000000..0e877a97 --- /dev/null +++ b/Src/Plugins/Encoder/enc_wma/AudioCoderWMA.h @@ -0,0 +1,50 @@ +#ifndef AUDIOCODERWMA_H +#define AUDIOCODERWMA_H + +#include "../nsv/enc_if.h" +#include "main.h" + +class CustomWMWriterSink; + +class AudioCoderWMA : public AudioCoder +{ +public: + AudioCoderWMA(int nch, int srate, int bps, configtype *cfg, char *configfile); + virtual int Encode(int framepos, void *in, int in_avail, int *in_used, void *out, int out_avail); //returns bytes in out + virtual ~AudioCoderWMA(); + int GetLastError(); + void PrepareToFinish(); + void OnFinished(const wchar_t *filename); + HRESULT SelectAndLoadResampler(int numchannels, int samplerate, int bitpersamp); + + HRESULT CreateAndConfigureWriter(WORD nch, WORD srate, WORD bps, char *configfile); + HRESULT CreateAndConfigureProfile(WAVEFORMATEX* pWaveLimits, IWMProfile** ppProfile, char *configfile); + +private: + bool begin_writing; + int error; + IWMWriterFileSink *sink; + IWMWriter *writer; + IWMWriterAdvanced *writerAdvanced; + double timeunits_per_byte; // "100 nanosecond units" -- ie: ( ( (10000000.0) / (double)samplerate ) / (double)numchannels ) / ( (double)bitspersamp/ 8.0 ) + int input_bytecount; + QWORD lastByteCount; + wchar_t tempFilename[MAX_PATH]; + +}; + +enum AudioCoderWMAErrors +{ + WMA_NO_ERROR = 0, + WMA_CANT_FIND_WMSDK = -1, + WMA_CANT_LOAD_CREATOR = -2, + WMA_CANT_CREATE_WRITER = -3, + WMA_CANT_SET_INPUT_FORMAT = -4, + WMA_CANT_SET_OUTPUT_FORMAT = -5, + WMA_CANT_MAKE_CUSTOM_SINK = -6, + WMA_CANT_QUERY_WRITER_INTERFACE = -7, + WMA_CANT_QUERY_SINK_INTERFACE = -8, + WMA_CANT_ADD_SINK = -9, +}; + +#endif//AUDIOCODERWMA_H diff --git a/Src/Plugins/Encoder/enc_wma/config.cpp b/Src/Plugins/Encoder/enc_wma/config.cpp new file mode 100644 index 00000000..a1114f17 --- /dev/null +++ b/Src/Plugins/Encoder/enc_wma/config.cpp @@ -0,0 +1,648 @@ +#include + +// LGIVEN Mods 4-10-05 +#include "main.h" +#include +#include "../nu/AutoWide.h" +#include "../nu/ns_wc.h" +#include "../Agave/Language/api_language.h" +#include +#include + +#define MAX_PASSES 1 // limited to 1pass encoding until we work out some code for 2pass encoding + +// LGIVEN Mods 4-10-05 +void readconfig(char *configfile, configtype *cfg) +{ + cfg->config_bitrate = 0; + cfg->config_bitsSample = 0; + cfg->config_nch = 0; + cfg->config_samplesSec = 0; + cfg->config_encoder = 0; + cfg->config_vbr = 0; + cfg->config_passes = 1; + if (configfile) + { + GetPrivateProfileStructA("audio_wma", "conf", cfg, sizeof(configtype), configfile); + } +} + +void writeconfig(char *configfile, configtype *cfg) +{ + if (configfile) + { + WritePrivateProfileStructA("audio_wma", "conf", cfg, sizeof(configtype), configfile); + } +} + +// New global table for channels,samplerates and bitrates +static EncoderType* encs = NULL; // Pointer to the TABLE with all config data +// Globals store current selections from Config Dialog +// Number of encoders +static int encNumbs = 0; // Total number of encoders installed WMA + +// New routine to read all config info from WMA encoder and load tables + +static BOOL loadWMATables() +{ + IWMProfileManager *profileManager; + IWMProfileManager2 *profileManager2; + IWMCodecInfo3 *codecInfo; + WAVEFORMATEX *pwave; + HRESULT hr; + int legalFormats = 0; + + WMCreateProfileManager(&profileManager); + profileManager->QueryInterface(&profileManager2); + profileManager2->SetSystemProfileVersion(WMT_VER_9_0); + + profileManager->QueryInterface(&codecInfo); + // Get the number of AUDIO Codecs + DWORD numCodecs = 0; + codecInfo->GetCodecInfoCount(WMMEDIATYPE_Audio, &numCodecs); + // If there are no encoders, just return + if (numCodecs == 0) + { + return false; + } + // Allocate structs for codecs and zero them all + encs = (EncoderType *) calloc(numCodecs * 4, sizeof(struct EncoderType)); + if (encs != NULL) + { + encNumbs = numCodecs * 4; + } + else + { + wchar_t titleStr[32] = {0}; + MessageBoxW(NULL, WASABI_API_LNGSTRINGW(IDS_CANNOT_ALLOCATE_MEM), + WASABI_API_LNGSTRINGW_BUF(IDS_WMA_ENCODER_ERROR,titleStr,32), MB_OK); + return false; + } + // Now cycle through the codecs + EncoderType* encp = encs; + for (BOOL isVBR = 0;isVBR != 2;isVBR++) + for (DWORD numPasses = 1;numPasses <= MAX_PASSES;numPasses++) + for (DWORD i = 0;i != numCodecs;i++) + { + wchar_t codecName[5000] = {0}; + DWORD codecNameSize = 5000; + + codecInfo->SetCodecEnumerationSetting(WMMEDIATYPE_Audio, i, g_wszVBREnabled, WMT_TYPE_BOOL, (BYTE *)&isVBR, sizeof(BOOL)); + codecInfo->SetCodecEnumerationSetting(WMMEDIATYPE_Audio, i, g_wszNumPasses, WMT_TYPE_DWORD, (BYTE *)&numPasses, sizeof(DWORD)); + codecInfo->GetCodecName(WMMEDIATYPE_Audio, i, codecName, &codecNameSize); + // Get the number of formats for this codec + DWORD formatCount = 0; + hr = codecInfo->GetCodecFormatCount( WMMEDIATYPE_Audio, i, &formatCount ); + if (FAILED(hr)) + { + continue; + } + else if (formatCount == 0) + { + continue; + } + else + { + // Fill the EncoderType struct and allocate structs for format info + // First allocate the space for all the formatType structs + encp->formats = (formatType *) malloc(formatCount * sizeof(struct formatType)); + if (encp->formats == NULL) + { + wchar_t titleStr[32] = {0}; + MessageBoxW(NULL, WASABI_API_LNGSTRINGW(IDS_CANNOT_ALLOCATE_MEM), + WASABI_API_LNGSTRINGW_BUF(IDS_WMA_ENCODER_ERROR,titleStr,32), MB_OK); + return false; + } + // Now fill the EncoderType struct with name and info + encp->encoderName = _wcsdup(codecName); + encp->numFormats = formatCount; + encp->offset = i; + encp->vbr = isVBR; + encp->numPasses = numPasses; + } + // Now cycle through the formats for this codec + legalFormats = 0; + formatType *fmts = encp->formats; + for (DWORD f = 0;f != formatCount;f++) + { + wchar_t fDesc[5000] = {0}; + DWORD size = 5000; + // Get the config info for this encoding format in string format + IWMStreamConfig *streamConfig; + codecInfo->GetCodecFormatDesc(WMMEDIATYPE_Audio, i, f, &streamConfig, fDesc, &size); + // Now get the config info + IWMMediaProps *props; + streamConfig->QueryInterface(&props); + // Get the bitrate + //DWORD bitRate; + //streamConfig->GetBitrate(&bitRate); + // Get the Media Encoder type + DWORD mediaTypeSize; + props->GetMediaType(0, &mediaTypeSize); + WM_MEDIA_TYPE *mediaType = (WM_MEDIA_TYPE *)new char[mediaTypeSize]; + props->GetMediaType(mediaType, &mediaTypeSize); + // Go get the WAVEFORMATEX Struct from the + if (mediaType->cbFormat >= sizeof(WAVEFORMATEX)) + { + pwave = (WAVEFORMATEX*)mediaType->pbFormat; + if (pwave != NULL) + { + // Check to see if this is an A/V codec format + // If so, do not save it + + /* + if ((pwave->nAvgBytesPerSec / pwave->nBlockAlign) == + ((pwave->nAvgBytesPerSec >= 3995) ? 5 : 3)) + { + delete(mediaType); + props->Release(); + streamConfig->Release(); + continue; + }*/ + + // old way of checking + if ((wcsstr(fDesc, L"A/V")) != NULL) + { + delete[] (mediaType); + props->Release(); + streamConfig->Release(); + continue; + } + // Load the format name + fmts->formatName = _wcsdup(fDesc); + // Load all the format values and the offset + if ((pwave->nAvgBytesPerSec & 0x7FFFFF00) == 0x7FFFFF00) + { + fmts->bitrate = (pwave->nAvgBytesPerSec & 0x000000FF); + fmts->vbr = 1; + } + else + { + fmts->bitrate = (pwave->nAvgBytesPerSec * 8); + fmts->vbr = 0; + } + fmts->bitsSample = pwave->wBitsPerSample; + fmts->nChannels = pwave->nChannels; + fmts->samplesSec = pwave->nSamplesPerSec; + fmts->offset = f; + } + else + { + wchar_t titleStr[32] = {0}; + MessageBoxW(NULL, WASABI_API_LNGSTRINGW(IDS_CANNOT_GET_STRUCTURE), + WASABI_API_LNGSTRINGW_BUF(IDS_WMA_ENCODER_ERROR,titleStr,32), MB_OK); + return false; + } + } + else + { + wchar_t titleStr[32] = {0}; + MessageBoxW(NULL, WASABI_API_LNGSTRINGW(IDS_CANNOT_GET_ENCODER4_INFO), + WASABI_API_LNGSTRINGW_BUF(IDS_WMA_ENCODER_ERROR,titleStr,32), MB_OK); + return false; + } + // Set the media type value in the EncoderType struct on first legal format + if (f == 0) + { + encp->mediaType = mediaType->subtype; + } + // Now point to the next table block and inc the legal formats count + fmts++; + legalFormats++; + // And release the props and streams structs + delete[] (mediaType); + props->Release(); + streamConfig->Release(); + + } + // If there are no legal formats for this codec then skip it + if (legalFormats == 0) + { + delete[] encp->encoderName; + encp->encoderName = NULL; + encp->numFormats = legalFormats; + encp->offset = 0; + } + // Else load number of legal formats and save it + else + { + encp->numFormats = legalFormats; + encp++; + } + } + return true; +} + +static int FindFormatNumber(formatType *formats, int numFormats, configtype *cfg) +{ + for (int i = 0;i < numFormats;i++, formats++) + { + if (formats->bitrate == cfg->config_bitrate + && formats->bitsSample == cfg->config_bitsSample + && formats->nChannels == cfg->config_nch + && formats->samplesSec == cfg->config_samplesSec) + return formats->offset; + } + + return 0; +} + +static VOID dumpProfile(char *configfile, BOOL isVBR, DWORD numPasses, int encNumb, int fmtNumb) +{ + // Create a Profile and dump it + IWMProfileManager *pmgr = NULL; + IWMProfile *prof = NULL; + IWMStreamConfig *sconf = NULL; + IWMCodecInfo3 *cinfo = NULL; + DWORD ssize; + wchar_t errorTitle[128] = {0}; + WASABI_API_LNGSTRINGW_BUF(IDS_WMA_CONFIG_FILE_ERROR,errorTitle,128); + + HRESULT hr = WMCreateProfileManager(&pmgr); + if (!FAILED(hr)) + { + hr = pmgr->CreateEmptyProfile(WMT_VER_9_0, &prof); + if (!FAILED(hr)) + { + hr = pmgr->QueryInterface(&cinfo); + if (!FAILED(hr)) + { + cinfo->SetCodecEnumerationSetting(WMMEDIATYPE_Audio, encNumb, g_wszVBREnabled, WMT_TYPE_BOOL, (BYTE *)&isVBR, sizeof(BOOL)); + cinfo->SetCodecEnumerationSetting(WMMEDIATYPE_Audio, encNumb, g_wszNumPasses, WMT_TYPE_DWORD, (BYTE *)&numPasses, sizeof(DWORD)); + cinfo->GetCodecFormat(WMMEDIATYPE_Audio, encNumb, fmtNumb, &sconf); + sconf->SetConnectionName(L"enc_wma"); + sconf->SetStreamName(L"enc_wma"); + sconf->SetStreamNumber(1); + hr = prof->AddStream(sconf); + if (!FAILED(hr)) + { + hr = pmgr->SaveProfile(prof, NULL, &ssize); + if (!FAILED(hr)) + { + WCHAR* pstring = new WCHAR[ssize]; + if (pstring != NULL) + { + hr = pmgr->SaveProfile(prof, pstring, &ssize); + if (!FAILED(hr)) + { + wchar_t cstring[4000] = {0}; + wcsncpy(cstring, pstring, 4000 - 1); + WritePrivateProfileStructW(L"audio_wma", L"profile", cstring, sizeof(cstring) / sizeof(*cstring), AutoWide(configfile)); + } + else{ MessageBoxW(NULL, WASABI_API_LNGSTRINGW(IDS_SAVE_PROFILE_READ_ERROR), errorTitle, MB_OK); } + } + else{ MessageBoxW(NULL, WASABI_API_LNGSTRINGW(IDS_MEM_ALLOCATION_ERROR), errorTitle, MB_OK); } + } + else{ MessageBoxW(NULL, WASABI_API_LNGSTRINGW(IDS_PROFILE_SAVE_SIZE_ERROR), errorTitle, MB_OK); } + } + else{ MessageBoxW(NULL, WASABI_API_LNGSTRINGW(IDS_CANNOT_READ_AUDIO_STREAM), errorTitle, MB_OK); } + } + else{ MessageBoxW(NULL, WASABI_API_LNGSTRINGW(IDS_CANNOT_GET_CODEC_INFO), errorTitle, MB_OK); } + } + else{ MessageBoxW(NULL, WASABI_API_LNGSTRINGW(IDS_CANNOT_CREATE_A_PROFILE), errorTitle, MB_OK); } + } + else{ MessageBoxW(NULL, WASABI_API_LNGSTRINGW(IDS_CANNOT_CREATE_PROFILE_MANAGER), errorTitle, MB_OK); } + pmgr->Release(); + prof->Release(); + sconf->Release(); + cinfo->Release(); +} + +static bool Has(HWND hwndDlg, int item, int data) +{ + int numChoices = SendDlgItemMessage(hwndDlg, item, CB_GETCOUNT, 0, 0); + for (int i = 0;i < numChoices;i++) + { + if (SendDlgItemMessage(hwndDlg, item, CB_GETITEMDATA, i, 0) == data) + return true; + } + return false; +} + +static int EncodeSampleFormat(int bps, int numChannels, int sampleRate) +{ + // 20 bits sampleRate + assert((sampleRate & 0xFFFFF) == sampleRate); + // 6 bits numChannels + assert((numChannels & 0x3F) == numChannels); + // 6 bits bps + assert((bps & 0x3F) == bps); + + return (sampleRate << 12) | (numChannels << 6) | (bps); +} + +static void DecodeSampleFormat(int data, int &bps, int &numChannels, int &sampleRate) +{ + bps = data & 0x3F; + data >>= 6; + numChannels = data & 0x3F; + data >>= 6; + sampleRate = data; +} + +static int EncodeVBR(BOOL isVBR, DWORD numPasses) +{ + // 1 bits VBR + assert((isVBR & 0x1) == isVBR); + // 15 bits numPasses + assert((numPasses & 0x7FFF) == numPasses); + + return (isVBR << 15) | (numPasses); +} + +static void DecodeVBR(int data, BOOL &isVBR, DWORD &numPasses) +{ + numPasses = data & 0x7FFF; + data >>= 15; + isVBR = data & 0x1; +} + +static void AutoSelect(HWND hwndDlg, int dlgItem) +{ + if (SendDlgItemMessage(hwndDlg, dlgItem, CB_GETCURSEL, 0, 0) == CB_ERR) + SendDlgItemMessage(hwndDlg, dlgItem, CB_SETCURSEL, 0, 0); +} + +static EncoderType *FindEncoder(int encoderNumber, BOOL isVBR, DWORD numPasses) +{ + EncoderType* enc = encs; + for (int i = 0;i < encNumbs;i++, enc++) + { + if (enc->encoderName == NULL) + return 0; //WTF? + if (enc->offset == encoderNumber && enc->vbr == isVBR && enc->numPasses == numPasses) + return enc; + } + return 0; //WTF? +} + +#define MASK_ENCODER 0x1 +#define MASK_VBR 0x2 +#define MASK_SAMPLE_FORMAT 0x4 +#define MASK_BITRATE 0x8 +#define MASK_ALL 0xF + +static void ResetConfig(HWND hwndDlg, EncoderType *encs, configtype *cfg, int mask) +{ + wchar_t buf1[100] = {0}; + EncoderType* enc = encs; + + if (mask & MASK_ENCODER) SendDlgItemMessage(hwndDlg, IDC_ENCODER, CB_RESETCONTENT, 0, 0); + if (mask & MASK_SAMPLE_FORMAT) SendDlgItemMessage(hwndDlg, IDC_SAMPLE_FORMAT, CB_RESETCONTENT, 0, 0); + if (mask & MASK_BITRATE) SendDlgItemMessage(hwndDlg, IDC_BRATE, CB_RESETCONTENT, 0, 0); + if (mask & MASK_VBR) SendDlgItemMessage(hwndDlg, IDC_VBR, CB_RESETCONTENT, 0, 0); + + // reset encoders + int thisVBR = EncodeVBR(cfg->config_vbr, cfg->config_passes); + for (int i = 0;i < encNumbs;i++, enc++) + { + if (enc->encoderName == NULL) + break; + else if ((mask & MASK_ENCODER) && !Has(hwndDlg, IDC_ENCODER, enc->offset)) + { + int newpos = SendDlgItemMessage(hwndDlg, IDC_ENCODER, CB_ADDSTRING, 0, (LPARAM)enc->encoderName); + SendDlgItemMessage(hwndDlg, IDC_ENCODER, CB_SETITEMDATA, newpos, enc->offset); + + if (cfg->config_encoder == enc->offset) + { + SendDlgItemMessage(hwndDlg, IDC_ENCODER, CB_SETCURSEL, newpos, 0); + } + } + int data = EncodeVBR(enc->vbr, enc->numPasses); + if ((mask & MASK_VBR) && cfg->config_encoder == enc->offset && !Has(hwndDlg, IDC_VBR, data)) + { + int newpos = CB_ERR; + if (enc->vbr == FALSE && enc->numPasses == 1) + newpos = SendDlgItemMessageW(hwndDlg, IDC_VBR, CB_ADDSTRING, 0, (LPARAM)WASABI_API_LNGSTRINGW(IDS_CBR)); + else if (enc->vbr == FALSE && enc->numPasses == 2) + newpos = SendDlgItemMessageW(hwndDlg, IDC_VBR, CB_ADDSTRING, 0, (LPARAM)WASABI_API_LNGSTRINGW(IDS_2_PASS_CBR)); + else if (enc->vbr == TRUE && enc->numPasses == 1) + newpos = SendDlgItemMessageW(hwndDlg, IDC_VBR, CB_ADDSTRING, 0, (LPARAM)WASABI_API_LNGSTRINGW(IDS_VBR)); + else if (enc->vbr == TRUE && enc->numPasses == 2) + newpos = SendDlgItemMessageW(hwndDlg, IDC_VBR, CB_ADDSTRING, 0, (LPARAM)WASABI_API_LNGSTRINGW(IDS_ABR)); + + SendDlgItemMessage(hwndDlg, IDC_VBR, CB_SETITEMDATA, newpos, data); + + if (thisVBR == data) + SendDlgItemMessage(hwndDlg, IDC_VBR, CB_SETCURSEL, newpos, 0); + } + } + + AutoSelect(hwndDlg, IDC_ENCODER); + AutoSelect(hwndDlg, IDC_VBR); + int pos = SendDlgItemMessage(hwndDlg, IDC_VBR, CB_GETCURSEL, 0, 0); + int data = SendDlgItemMessage(hwndDlg, IDC_VBR, CB_GETITEMDATA, pos, 0); + DecodeVBR(data, cfg->config_vbr, cfg->config_passes); + + pos = SendDlgItemMessage(hwndDlg, IDC_ENCODER, CB_GETCURSEL, 0, 0); + data = SendDlgItemMessage(hwndDlg, IDC_ENCODER, CB_GETITEMDATA, pos, 0); + cfg->config_encoder = data; + + // Now set up for dialog fill + enc = FindEncoder(cfg->config_encoder, cfg->config_vbr, cfg->config_passes); + + // Fill the current values + formatType *fmt = enc->formats; + + int thisSampleFormat = EncodeSampleFormat(cfg->config_bitsSample, cfg->config_nch, cfg->config_samplesSec); + for (int i = 0;i < enc->numFormats;i++, fmt++) + { + int data = EncodeSampleFormat(fmt->bitsSample, fmt->nChannels, fmt->samplesSec); + // Add channels to list + if ((mask & MASK_SAMPLE_FORMAT) && !Has(hwndDlg, IDC_SAMPLE_FORMAT, data)) + { + if (fmt->nChannels == 1) + wsprintfW(buf1, WASABI_API_LNGSTRINGW(IDS_MONO_INFO), fmt->bitsSample, fmt->samplesSec); + else if (fmt->nChannels == 2) + wsprintfW(buf1, WASABI_API_LNGSTRINGW(IDS_STEREO_INFO), fmt->bitsSample, fmt->samplesSec); + else + wsprintfW(buf1, WASABI_API_LNGSTRINGW(IDS_CHANNELS_INFO), fmt->bitsSample, fmt->nChannels, fmt->samplesSec); + + int newpos; + if (fmt->bitsSample) + newpos = SendDlgItemMessageW(hwndDlg, IDC_SAMPLE_FORMAT, CB_ADDSTRING, 0, (LPARAM)buf1); + else + newpos = SendDlgItemMessageW(hwndDlg, IDC_SAMPLE_FORMAT, CB_ADDSTRING, 0, (LPARAM)buf1 + 8); // skip "0 bits, " + + SendDlgItemMessage(hwndDlg, IDC_SAMPLE_FORMAT, CB_SETITEMDATA, newpos, data); + // Now set current select for number of channels sample + if (thisSampleFormat == data) + SendDlgItemMessage(hwndDlg, IDC_SAMPLE_FORMAT, CB_SETCURSEL, newpos, 0); + } + } + + if (SendDlgItemMessage(hwndDlg, IDC_SAMPLE_FORMAT, CB_GETCURSEL, 0, 0) == CB_ERR) + { + int num = SendDlgItemMessage(hwndDlg, IDC_SAMPLE_FORMAT, CB_GETCOUNT, 0, 0); + int defaultSampleFormat = EncodeSampleFormat(16, 2, 44100); + for (int i = 0;i < num;i++) + { + int data = SendDlgItemMessage(hwndDlg, IDC_SAMPLE_FORMAT, CB_GETITEMDATA, i, 0); + if (data == defaultSampleFormat) + SendDlgItemMessage(hwndDlg, IDC_SAMPLE_FORMAT, CB_SETCURSEL, i, 0); + } + } + + AutoSelect(hwndDlg, IDC_SAMPLE_FORMAT); + pos = SendDlgItemMessage(hwndDlg, IDC_SAMPLE_FORMAT, CB_GETCURSEL, 0, 0); + data = SendDlgItemMessage(hwndDlg, IDC_SAMPLE_FORMAT, CB_GETITEMDATA, pos, 0); + DecodeSampleFormat(data, cfg->config_bitsSample, cfg->config_nch, cfg->config_samplesSec); + + thisSampleFormat = EncodeSampleFormat(cfg->config_bitsSample, cfg->config_nch, cfg->config_samplesSec); + + // Next Show the Bitrates + fmt = enc->formats; + for (int i = 0;i < enc->numFormats;i++, fmt++) + { + int data = EncodeSampleFormat(fmt->bitsSample, fmt->nChannels, fmt->samplesSec); + if (thisSampleFormat == data) + { + if ((mask & MASK_BITRATE) && !Has(hwndDlg, IDC_BRATE, fmt->bitrate)) + { + if (fmt->vbr) + SetDlgItemTextW(hwndDlg, IDC_STATIC_BITRATE, WASABI_API_LNGSTRINGW(IDS_QUALITY)); + else + SetDlgItemTextW(hwndDlg, IDC_STATIC_BITRATE, WASABI_API_LNGSTRINGW(IDS_BITRATE)); + + wsprintfW(buf1, L"%d", fmt->bitrate); + int newpos = SendDlgItemMessageW(hwndDlg, IDC_BRATE, CB_ADDSTRING, 0, (LPARAM)buf1); + SendDlgItemMessage(hwndDlg, IDC_BRATE, CB_SETITEMDATA, newpos, fmt->bitrate); + // Set the current bit rate + if (cfg->config_bitrate == fmt->bitrate) + { + SendDlgItemMessage(hwndDlg, IDC_BRATE, CB_SETCURSEL, newpos, 0); + } + } + } + } + + if (SendDlgItemMessage(hwndDlg, IDC_BRATE, CB_GETCURSEL, 0, 0) == CB_ERR) + { + int num = SendDlgItemMessage(hwndDlg, IDC_BRATE, CB_GETCOUNT, 0, 0); + + for (int i = 0;i < num;i++) + { + int data = SendDlgItemMessage(hwndDlg, IDC_BRATE, CB_GETITEMDATA, i, 0); + if (data == 50 || (data / 1000 == 128)) + SendDlgItemMessage(hwndDlg, IDC_BRATE, CB_SETCURSEL, i, 0); + } + } + + AutoSelect(hwndDlg, IDC_BRATE); + + pos = SendDlgItemMessage(hwndDlg, IDC_BRATE, CB_GETCURSEL, 0, 0); + data = SendDlgItemMessage(hwndDlg, IDC_BRATE, CB_GETITEMDATA, pos, 0); + cfg->config_bitrate = data; +} + +BOOL CALLBACK ConfigProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + configwndrec *wc = NULL; + if (uMsg == WM_INITDIALOG) + { + // LGIVEN Mod 4-10-05 +#if defined(_WIN64) + SetWindowLong(hwndDlg, GWLP_USERDATA, lParam); +#else + SetWindowLong(hwndDlg, GWL_USERDATA, lParam); +#endif + if (lParam) + { + // Get the saved params + wc = (configwndrec*)lParam; + + loadWMATables(); + ResetConfig(hwndDlg, encs , &wc->cfg, MASK_ALL); + } + return 1; + } + if (uMsg == WM_DESTROY) + { +#if defined(_WIN64) + wc = (configwndrec*)SetWindowLong(hwndDlg, GWLP_USERDATA, 0); +#else + wc = (configwndrec*)SetWindowLong(hwndDlg, GWL_USERDATA, 0); +#endif + if (wc) + { + EncoderType *encoder=FindEncoder(wc->cfg.config_encoder,wc->cfg.config_vbr, wc->cfg.config_passes); + int formatNumber = FindFormatNumber(encoder->formats, encoder->numFormats, &wc->cfg); + // Dump the profile in WMA format + dumpProfile(wc->configfile, wc->cfg.config_vbr, wc->cfg.config_passes, wc->cfg.config_encoder, formatNumber); + // Write it to config file + writeconfig(wc->configfile, &wc->cfg); + free(wc->configfile); + free(wc); + } + return 0; + } + if (uMsg == WM_COMMAND) + { + switch (LOWORD(wParam)) + { + case IDC_VBR: + if ((HIWORD(wParam) == CBN_SELCHANGE)) + { +#if defined(_WIN64) + wc = (configwndrec*)GetWindowLong(hwndDlg, GWLP_USERDATA); +#else + wc = (configwndrec*)GetWindowLong(hwndDlg, GWL_USERDATA); +#endif + int pos = SendDlgItemMessage(hwndDlg, IDC_VBR, CB_GETCURSEL, 0, 0); + int data = SendDlgItemMessage(hwndDlg, IDC_VBR, CB_GETITEMDATA, pos, 0); + DecodeVBR(data, wc->cfg.config_vbr, wc->cfg.config_passes); + ResetConfig(hwndDlg, encs, &wc->cfg, MASK_BITRATE | MASK_SAMPLE_FORMAT); + } + break; + + case IDC_SAMPLE_FORMAT: + if ((HIWORD(wParam) == CBN_SELCHANGE)) + { +#if defined(_WIN64) + wc = (configwndrec*)GetWindowLong(hwndDlg, GWLP_USERDATA); +#else + wc = (configwndrec*)GetWindowLong(hwndDlg, GWL_USERDATA); +#endif + int pos = SendDlgItemMessage(hwndDlg, IDC_SAMPLE_FORMAT, CB_GETCURSEL, 0, 0); + int data = SendDlgItemMessage(hwndDlg, IDC_SAMPLE_FORMAT, CB_GETITEMDATA, pos, 0); + DecodeSampleFormat(data, wc->cfg.config_bitsSample, wc->cfg.config_nch, wc->cfg.config_samplesSec); + ResetConfig(hwndDlg, encs, &wc->cfg, MASK_BITRATE); + } + break; + + case IDC_BRATE: + if ((HIWORD(wParam) == CBN_SELCHANGE)) + { +#if defined(_WIN64) + wc = (configwndrec*)GetWindowLong(hwndDlg, GWLP_USERDATA); +#else + wc = (configwndrec*)GetWindowLong(hwndDlg, GWL_USERDATA); +#endif + int pos = SendDlgItemMessage(hwndDlg, IDC_BRATE, CB_GETCURSEL, 0, 0); + int data = SendDlgItemMessage(hwndDlg, IDC_BRATE, CB_GETITEMDATA, pos, 0); + wc->cfg.config_bitrate = data; + } + break; + + case IDC_ENCODER: + if ((HIWORD(wParam) == CBN_SELCHANGE)) + { +#if defined(_WIN64) + wc = (configwndrec*)GetWindowLong(hwndDlg, GWLP_USERDATA); +#else + wc = (configwndrec*)GetWindowLong(hwndDlg, GWL_USERDATA); +#endif + if (wc) + { + int pos = SendDlgItemMessage(hwndDlg, IDC_ENCODER, CB_GETCURSEL, 0, 0); + int data = SendDlgItemMessage(hwndDlg, IDC_ENCODER, CB_GETITEMDATA, pos, 0); + wc->cfg.config_encoder = data; + ResetConfig(hwndDlg, encs, &wc->cfg, MASK_VBR | MASK_SAMPLE_FORMAT | MASK_BITRATE); + } + } + break; + + } + } + return 0; +} \ No newline at end of file diff --git a/Src/Plugins/Encoder/enc_wma/enc_wma.rc b/Src/Plugins/Encoder/enc_wma/enc_wma.rc new file mode 100644 index 00000000..6b81c4a4 --- /dev/null +++ b/Src/Plugins/Encoder/enc_wma/enc_wma.rc @@ -0,0 +1,128 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_DIALOG1 DIALOGEX 0, 0, 256, 167 +STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD +FONT 8, "MS Shell Dlg", 0, 0, 0x0 +BEGIN + GROUPBOX "Windows Media Audio",IDC_STATIC,0,0,256,167 + COMBOBOX IDC_ENCODER,18,13,145,148,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + LTEXT "Encoder Format",IDC_STATIC,166,13,60,12,SS_CENTERIMAGE + COMBOBOX IDC_SAMPLE_FORMAT,17,30,145,133,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + LTEXT "Sample Format",IDC_STATIC,166,30,71,12,SS_CENTERIMAGE + COMBOBOX IDC_VBR,18,48,59,67,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_BRATE,86,48,77,116,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + LTEXT "Bitrate (Bits/Sec)",IDC_STATIC_BITRATE,165,48,54,12,SS_CENTERIMAGE + CTEXT "Portions utilize Microsoft Windows Media Technologies.\nCopyright © 1992-2019 Microsoft Corporation. All Rights Reserved.",IDC_STATIC,14,144,227,18 +END + + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "#include ""version.rc2""\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + 65535 "{33BC12FD-E7F7-42ec-8FE3-2D8BD3A977C2}" +END + +STRINGTABLE +BEGIN + IDS_CANNOT_FIND_INPUT_FORMATTER + "Cannot find input formatter for:\n\nChannels = %d, Sample Rate = %d, Bits/Sample = %d\n\nNo resampler available for this input format" + IDS_WARNING "WARNING" + IDS_CANNOT_ALLOCATE_MEM "Cannot allocate memory for WMA encoder format info" + IDS_WMA_ENCODER_ERROR "WMA Encoder Error" + IDS_CANNOT_GET_STRUCTURE "Cannot get structure with format details" + IDS_CANNOT_GET_ENCODER4_INFO "Cannot get detail info for Encoder4 format" + IDS_SAVE_PROFILE_READ_ERROR "Save profile read error" + IDS_MEM_ALLOCATION_ERROR "Memory allocation error" + IDS_PROFILE_SAVE_SIZE_ERROR "Profile save size get error" + IDS_CANNOT_READ_AUDIO_STREAM "Cannot create audio stream" + IDS_CANNOT_GET_CODEC_INFO "Cannot get codec interface" + IDS_CANNOT_CREATE_A_PROFILE "Cannot create a profile" + IDS_CANNOT_CREATE_PROFILE_MANAGER "Cannot create a profile manager" + IDS_WMA_CONFIG_FILE_ERROR "WMA Config File Error" +END + +STRINGTABLE +BEGIN + IDS_CBR "CBR" + IDS_2_PASS_CBR "2-pass CBR" + IDS_VBR "VBR" + IDS_ABR "ABR" + IDS_MONO_INFO "%d bits, mono, %d Hz" + IDS_STEREO_INFO "%d bits, stereo, %d Hz" + IDS_CHANNELS_INFO "%d bits, %d channels, %d Hz" + IDS_QUALITY "Quality (0-100)" + IDS_BITRATE "Bitrate (Bits/Sec)" + IDS_ENC_WMA_DESC "WMA Encoder %s" +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// +#include "version.rc2" + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/Src/Plugins/Encoder/enc_wma/enc_wma.sln b/Src/Plugins/Encoder/enc_wma/enc_wma.sln new file mode 100644 index 00000000..6eb2c7ca --- /dev/null +++ b/Src/Plugins/Encoder/enc_wma/enc_wma.sln @@ -0,0 +1,30 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29613.14 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "enc_wma", "enc_wma.vcxproj", "{5F4B0989-B35B-40C0-BAA1-E7058377B398}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {5F4B0989-B35B-40C0-BAA1-E7058377B398}.Debug|Win32.ActiveCfg = Debug|Win32 + {5F4B0989-B35B-40C0-BAA1-E7058377B398}.Debug|Win32.Build.0 = Debug|Win32 + {5F4B0989-B35B-40C0-BAA1-E7058377B398}.Debug|x64.ActiveCfg = Debug|x64 + {5F4B0989-B35B-40C0-BAA1-E7058377B398}.Debug|x64.Build.0 = Debug|x64 + {5F4B0989-B35B-40C0-BAA1-E7058377B398}.Release|Win32.ActiveCfg = Release|Win32 + {5F4B0989-B35B-40C0-BAA1-E7058377B398}.Release|Win32.Build.0 = Release|Win32 + {5F4B0989-B35B-40C0-BAA1-E7058377B398}.Release|x64.ActiveCfg = Release|x64 + {5F4B0989-B35B-40C0-BAA1-E7058377B398}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {424261B8-8392-4100-A876-CD8560D5D1AC} + EndGlobalSection +EndGlobal diff --git a/Src/Plugins/Encoder/enc_wma/enc_wma.vcxproj b/Src/Plugins/Encoder/enc_wma/enc_wma.vcxproj new file mode 100644 index 00000000..c0580d2c --- /dev/null +++ b/Src/Plugins/Encoder/enc_wma/enc_wma.vcxproj @@ -0,0 +1,276 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {5F4B0989-B35B-40C0-BAA1-E7058377B398} + enc_wma + 10.0.19041.0 + + + + DynamicLibrary + v142 + Unicode + + + DynamicLibrary + v142 + Unicode + + + DynamicLibrary + v142 + Unicode + + + DynamicLibrary + v142 + Unicode + + + + + + + + + + + + + + + + + + + false + $(PlatformShortName)_$(Configuration)\ + $(PlatformShortName)_$(Configuration)\ + $(IncludePath) + $(LibraryPath) + + + false + $(PlatformShortName)_$(Configuration)\ + $(PlatformShortName)_$(Configuration)\ + + + false + $(PlatformShortName)_$(Configuration)\ + $(PlatformShortName)_$(Configuration)\ + $(IncludePath) + $(LibraryPath) + + + false + $(PlatformShortName)_$(Configuration)\ + $(PlatformShortName)_$(Configuration)\ + + + false + + + Debug + + + + + + + + + Debug + + + + Disabled + .;..\..\..\Wasabi;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;_USRDLL;NSV_CODER_WMA_EXPORTS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + false + true + EnableFastChecks + MultiThreadedDebugDLL + true + Level3 + ProgramDatabase + Default + 4996;%(DisableSpecificWarnings) + $(IntDir)$(TargetName).pdb + + + _DEBUG;%(PreprocessorDefinitions) + 0x0409 + + + wmvcore.lib;shlwapi.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)$(TargetExt) + true + $(IntDir)$(TargetName).pdb + false + $(IntDir)$(TargetName).lib + MachineX86 + false + %(AdditionalLibraryDirectories) + + + xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ +xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ + Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\' + + + + + Disabled + .;..\..\..\Wasabi;%(AdditionalIncludeDirectories) + WIN64;_DEBUG;_WINDOWS;_USRDLL;NSV_CODER_WMA_EXPORTS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + false + true + EnableFastChecks + MultiThreadedDebugDLL + true + Level3 + ProgramDatabase + Default + 4244;4312;4996;%(DisableSpecificWarnings) + $(IntDir)$(TargetName).pdb + + + _DEBUG;%(PreprocessorDefinitions) + 0x0409 + + + wmvcore.lib;shlwapi.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)$(TargetExt) + true + $(IntDir)$(TargetName).pdb + false + $(IntDir)$(TargetName).lib + false + %(AdditionalLibraryDirectories) + + + xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ +xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ + Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\' + + + + + MinSpace + OnlyExplicitInline + Size + .;..\..\..\Wasabi;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_WINDOWS;_USRDLL;NSV_CODER_WMA_EXPORTS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + true + true + MultiThreadedDLL + true + Level3 + None + Default + 4996;%(DisableSpecificWarnings) + $(IntDir)$(TargetName).pdb + + + NDEBUG;%(PreprocessorDefinitions) + 0x0409 + + + /ignore:4210 %(AdditionalOptions) + wmvcore.lib;shlwapi.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)$(TargetExt) + false + $(IntDir)$(TargetName).pdb + true + true + false + $(IntDir)$(TargetName).lib + MachineX86 + false + %(AdditionalLibraryDirectories) + + + xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ + Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\' + + + + + MinSpace + OnlyExplicitInline + Size + .;..\..\..\Wasabi;%(AdditionalIncludeDirectories) + WIN64;NDEBUG;_WINDOWS;_USRDLL;NSV_CODER_WMA_EXPORTS;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + true + true + MultiThreadedDLL + true + Level3 + None + Default + 4244;4312;4996;%(DisableSpecificWarnings) + $(IntDir)$(TargetName).pdb + + + NDEBUG;%(PreprocessorDefinitions) + 0x0409 + + + /ignore:4210 %(AdditionalOptions) + wmvcore.lib;shlwapi.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)$(TargetExt) + false + $(IntDir)$(TargetName).pdb + true + true + false + $(IntDir)$(TargetName).lib + false + %(AdditionalLibraryDirectories) + + + xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ + Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\' + + + + + + + + + + + + + + + + + + + {3e0bfa8a-b86a-42e9-a33f-ec294f823f7f} + + + + + + \ No newline at end of file diff --git a/Src/Plugins/Encoder/enc_wma/enc_wma.vcxproj.filters b/Src/Plugins/Encoder/enc_wma/enc_wma.vcxproj.filters new file mode 100644 index 00000000..6b65e74e --- /dev/null +++ b/Src/Plugins/Encoder/enc_wma/enc_wma.vcxproj.filters @@ -0,0 +1,44 @@ + + + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + {497dbf34-c784-481d-bc15-343f4d25c508} + + + {a60f8eac-325c-466b-8a17-69b1064e57fb} + + + {33266828-89ee-4119-b784-03c8edaad9a1} + + + + + Ressource Files + + + \ No newline at end of file diff --git a/Src/Plugins/Encoder/enc_wma/main.cpp b/Src/Plugins/Encoder/enc_wma/main.cpp new file mode 100644 index 00000000..451304a1 --- /dev/null +++ b/Src/Plugins/Encoder/enc_wma/main.cpp @@ -0,0 +1,212 @@ +/* +** nsv_coder_lame: main.cpp - LAME mp3 encoder plug-in +** (requires lame_enc.dll) +** +** Copyright (C) 2001-2006 Nullsoft, Inc. +** +** This software is provided 'as-is', without any express or implied warranty. +** In no event will the authors be held liable for any damages arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, including commercial +** applications, and to alter it and redistribute it freely, subject to the following restrictions: +** 1. The origin of this software must not be misrepresented; you must not claim that you wrote the +** original software. If you use this software in a product, an acknowledgment in the product +** documentation would be appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be misrepresented as +** being the original software. +** 3. This notice may not be removed or altered from any source distribution. +*/ + +#define ENC_VERSION "v1.23" + +#include +#include +#include + +#include +#include + +#include "AudioCoderWMA.h" +#include "../nu/AutoWideFn.h" + +// wasabi based services for localisation support +#include +#include "../Agave/Language/api_language.h" +#include "../winamp/wa_ipc.h" + +#include + +HWND winampwnd = 0; +api_service *WASABI_API_SVC = 0; +api_language *WASABI_API_LNG = 0; +HINSTANCE WASABI_API_LNG_HINST = 0, WASABI_API_ORIG_HINST = 0; + +BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) +{ + return TRUE; +} + +int getwrittentime(); + +HINSTANCE g_hinst; + +int g_srate, g_numchan, g_bps; +volatile int writtentime, w_offset; + +// LGIVEN Mod 4-10-05 +void readconfig(char *configfile, configtype *cfg); +void writeconfig(char *configfile, configtype *cfg); + +static HINSTANCE GetMyInstance() +{ + MEMORY_BASIC_INFORMATION mbi = {0}; + if(VirtualQuery(GetMyInstance, &mbi, sizeof(mbi))) + return (HINSTANCE)mbi.AllocationBase; + return NULL; +} + +void GetLocalisationApiService(void) +{ + if(!WASABI_API_LNG) + { + // loader so that we can get the localisation service api for use + if(!WASABI_API_SVC) + { + WASABI_API_SVC = (api_service*)SendMessage(winampwnd, WM_WA_IPC, 0, IPC_GET_API_SERVICE); + if (WASABI_API_SVC == (api_service*)1) + { + WASABI_API_SVC = NULL; + return; + } + } + + if(!WASABI_API_LNG) + { + waServiceFactory *sf; + sf = WASABI_API_SVC->service_getServiceByGuid(languageApiGUID); + if (sf) WASABI_API_LNG = reinterpret_cast(sf->getInterface()); + } + + // need to have this initialised before we try to do anything with localisation features + WASABI_API_START_LANG(GetMyInstance(),EncWMALangGUID); + } +} + + +// ================================================================== +// +// Published enc_wma methods. +// +// ================================================================== +#include + +extern "C" +{ + + unsigned int __declspec(dllexport) GetAudioTypes3(int idx, char *desc) + { + if ( idx == 0 ) + { + GetLocalisationApiService(); + StringCchPrintfA(desc, 1024, WASABI_API_LNGSTRING(IDS_ENC_WMA_DESC), ENC_VERSION); + return mmioFOURCC('W', 'M', 'A', ' '); + } + return 0; + } + + AudioCoder __declspec(dllexport) *CreateAudio3(int nch, int srate, int bps, unsigned int srct, unsigned int *outt, char *configfile) + { + if (srct == mmioFOURCC('P', 'C', 'M', ' ') && *outt == mmioFOURCC('W', 'M', 'A', ' ')) + { + GetLocalisationApiService(); + configtype cfg; + readconfig(configfile, &cfg); + + AudioCoderWMA *t = new AudioCoderWMA(nch, srate, bps, &cfg, configfile); + + if ( t && t->GetLastError()) + { + delete t; + return NULL; + } + else return t; + } + return NULL; + } + + void __declspec(dllexport) FinishAudio3(const char *filename, AudioCoder *coder) + { + ((AudioCoderWMA*)coder)->OnFinished(AutoWideFn(filename)); + } + + void __declspec(dllexport) FinishAudio3W(const wchar_t *filename, AudioCoder *coder) + { + ((AudioCoderWMA*)coder)->OnFinished(filename); + } + + void __declspec(dllexport) PrepareToFinish(const char *filename, AudioCoder *coder) + { + ((AudioCoderWMA*)coder)->PrepareToFinish(); + } + + void __declspec(dllexport) PrepareToFinishW(const wchar_t *filename, AudioCoder *coder) + { + ((AudioCoderWMA*)coder)->PrepareToFinish(); + } + + HWND __declspec(dllexport) ConfigAudio3(HWND hwndParent, HINSTANCE hinst, unsigned int outt, char *configfile) + { + if (outt == mmioFOURCC('W', 'M', 'A', ' ')) + { + configwndrec *wr = (configwndrec*)malloc(sizeof(configwndrec)); + if (configfile) + { + wr->configfile = _strdup(configfile); + } + else + { + wr->configfile = 0; + } + readconfig(configfile, &wr->cfg); + GetLocalisationApiService(); + return WASABI_API_CREATEDIALOGPARAMW(IDD_DIALOG1, hwndParent, ConfigProc, (LPARAM)wr); + } + return NULL; + } + + int __declspec(dllexport) SetConfigItem(unsigned int outt, char *item, char *data, char *configfile) + { + if (outt == mmioFOURCC('W', 'M', 'A', ' ')) + { + configtype cfg; + readconfig(configfile, &cfg); + if (!lstrcmpiA(item, "bitrate")) + { + //cfg.config_bitrate = atoi(data) * 1000; + } + writeconfig(configfile, &cfg); + return 1; + } + return 0; + } + + int __declspec(dllexport) GetConfigItem(unsigned int outt, char *item, char *data, int len, char *configfile) + { + if (outt == mmioFOURCC('W', 'M', 'A', ' ')) + { + configtype cfg; + readconfig(configfile, &cfg); + if (!lstrcmpiA(item, "bitrate")) + { + StringCchPrintfA(data, len, "%d", cfg.config_bitrate / 1000); + } + return 1; + } + return 0; + } + + void __declspec(dllexport) SetWinampHWND(HWND hwnd) + { + winampwnd = hwnd; + } +}; \ No newline at end of file diff --git a/Src/Plugins/Encoder/enc_wma/main.h b/Src/Plugins/Encoder/enc_wma/main.h new file mode 100644 index 00000000..460fb03e --- /dev/null +++ b/Src/Plugins/Encoder/enc_wma/main.h @@ -0,0 +1,55 @@ +#include +#include + +#include "../nsv/enc_if.h" +#include "resource.h" + +// LGIVEN Mods 4-25-05 +// Config info saved in Winamp.ini [enc_wma]---conf=xxxxxxxxxxx +typedef struct +{ + int config_nch; // Number of channels of encoder/fmt selected + int config_bitrate; // Bitrate of encoder/fmt selected + int config_bitsSample; // Bits/Sample of encoder/fmt selected + int config_samplesSec; // Sample rate of encoder/fmt selected + int config_encoder; // Encoder offset in table in Config Dialog + BOOL config_vbr; // VBR or not + DWORD config_passes; // number of passes (1 or 2) +} +configtype; + +typedef struct +{ + configtype cfg; // config type struct + char *configfile; // Name of config file (...\Winamp.ini) +} +configwndrec; + +// Data table values in Config Dialog +// One of these for each format + +struct formatType +{ + wchar_t *formatName; // Format Name (for display) + int offset; // offset in WMEncoder for this Encoder + int nChannels; // number of channels + int bitsSample; // Bits per sample + int samplesSec; // Samples per sec + int bitrate; // Bitrate value + int vbr; +}; + +// One of these for each encoder +struct EncoderType +{ + wchar_t *encoderName; // Encoder name (for display) + int offset; // Offset in WMEncoder + int numFormats; // Number of formats in WMEncoder for this encoder + struct _GUID mediaType; // Media type GUID + BOOL vbr; + DWORD numPasses; + formatType* formats; +}; + + +BOOL CALLBACK ConfigProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); diff --git a/Src/Plugins/Encoder/enc_wma/nserror.h b/Src/Plugins/Encoder/enc_wma/nserror.h new file mode 100644 index 00000000..84d844a4 --- /dev/null +++ b/Src/Plugins/Encoder/enc_wma/nserror.h @@ -0,0 +1,1631 @@ +/*++ + +Copyright (C) Microsoft Corporation, 1992 - 1999 + +Module Name: + + nserror.mc + +Abstract: + + Definitions for NetShow events. + +Author: + + +Revision History: + +Notes: + + This file is used by the MC tool to generate the nserror.h file + + Add new Ids ONLY in the sections marked **New** + +--*/ + +#ifndef _NSERROR_H +#define _NSERROR_H + + +#define STATUS_SEVERITY(hr) (((hr) >> 30) & 0x3) + + +///////////////////////////////////////////////////////////////////////// +// +// NETSHOW Success Events +// +///////////////////////////////////////////////////////////////////////// + +// +// Values are 32 bit values layed out as follows: +// +// 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 +// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 +// +---+-+-+-----------------------+-------------------------------+ +// |Sev|C|R| Facility | Code | +// +---+-+-+-----------------------+-------------------------------+ +// +// where +// +// Sev - is the severity code +// +// 00 - Success +// 01 - Informational +// 10 - Warning +// 11 - Error +// +// C - is the Customer code flag +// +// R - is a reserved bit +// +// Facility - is the facility code +// +// Code - is the facility's status code +// +// +// Define the facility codes +// +#define FACILITY_NS_WIN32 0x7 +#define FACILITY_NS 0xD + + +// +// Define the severity codes +// +#define STATUS_SEVERITY_WARNING 0x2 +#define STATUS_SEVERITY_SUCCESS 0x0 +#define STATUS_SEVERITY_INFORMATIONAL 0x1 +#define STATUS_SEVERITY_ERROR 0x3 + + +// +// MessageId: NS_S_CALLPENDING +// +// MessageText: +// +// The requested operation is pending completion.%0 +// +#define NS_S_CALLPENDING 0x000D0000L + +// +// MessageId: NS_S_CALLABORTED +// +// MessageText: +// +// The requested operation was aborted by the client.%0 +// +#define NS_S_CALLABORTED 0x000D0001L + +// +// MessageId: NS_S_STREAM_TRUNCATED +// +// MessageText: +// +// The stream was purposefully stopped before completion.%0 +// +#define NS_S_STREAM_TRUNCATED 0x000D0002L + + +///////////////////////////////////////////////////////////////////////// +// +// NETSHOW Warning Events +// +///////////////////////////////////////////////////////////////////////// + +// +// MessageId: NS_W_SERVER_BANDWIDTH_LIMIT +// +// MessageText: +// +// The maximum filebitrate value specified is greater than the server's configured maximum bandwidth.%0 +// +#define NS_W_SERVER_BANDWIDTH_LIMIT 0x800D0003L + +// +// MessageId: NS_W_FILE_BANDWIDTH_LIMIT +// +// MessageText: +// +// The maximum bandwidth value specified is less than the maximum filebitrate.%0 +// +#define NS_W_FILE_BANDWIDTH_LIMIT 0x800D0004L + + +///////////////////////////////////////////////////////////////////////// +// +// NETSHOW Error Events +// +///////////////////////////////////////////////////////////////////////// + +// +// MessageId: NS_E_NOCONNECTION +// +// MessageText: +// +// There is no connection established with the NetShow server. The operation failed.%0 +// +#define NS_E_NOCONNECTION 0xC00D0005L + +// +// MessageId: NS_E_CANNOTCONNECT +// +// MessageText: +// +// Unable to establish a connection to the server.%0 +// +#define NS_E_CANNOTCONNECT 0xC00D0006L + +// +// MessageId: NS_E_CANNOTDESTROYTITLE +// +// MessageText: +// +// Unable to destroy the title.%0 +// +#define NS_E_CANNOTDESTROYTITLE 0xC00D0007L + +// +// MessageId: NS_E_CANNOTRENAMETITLE +// +// MessageText: +// +// Unable to rename the title.%0 +// +#define NS_E_CANNOTRENAMETITLE 0xC00D0008L + +// +// MessageId: NS_E_CANNOTOFFLINEDISK +// +// MessageText: +// +// Unable to offline disk.%0 +// +#define NS_E_CANNOTOFFLINEDISK 0xC00D0009L + +// +// MessageId: NS_E_CANNOTONLINEDISK +// +// MessageText: +// +// Unable to online disk.%0 +// +#define NS_E_CANNOTONLINEDISK 0xC00D000AL + +// +// MessageId: NS_E_NOREGISTEREDWALKER +// +// MessageText: +// +// There is no file parser registered for this type of file.%0 +// +#define NS_E_NOREGISTEREDWALKER 0xC00D000BL + +// +// MessageId: NS_E_NOFUNNEL +// +// MessageText: +// +// There is no data connection established.%0 +// +#define NS_E_NOFUNNEL 0xC00D000CL + +// +// MessageId: NS_E_NO_LOCALPLAY +// +// MessageText: +// +// Failed to load the local play DLL.%0 +// +#define NS_E_NO_LOCALPLAY 0xC00D000DL + +// +// MessageId: NS_E_NETWORK_BUSY +// +// MessageText: +// +// The network is busy.%0 +// +#define NS_E_NETWORK_BUSY 0xC00D000EL + +// +// MessageId: NS_E_TOO_MANY_SESS +// +// MessageText: +// +// The server session limit was exceeded.%0 +// +#define NS_E_TOO_MANY_SESS 0xC00D000FL + +// +// MessageId: NS_E_ALREADY_CONNECTED +// +// MessageText: +// +// The network connection already exists.%0 +// +#define NS_E_ALREADY_CONNECTED 0xC00D0010L + +// +// MessageId: NS_E_INVALID_INDEX +// +// MessageText: +// +// Index %1 is invalid.%0 +// +#define NS_E_INVALID_INDEX 0xC00D0011L + +// +// MessageId: NS_E_PROTOCOL_MISMATCH +// +// MessageText: +// +// There is no protocol or protocol version supported by both the client and the server.%0 +// +#define NS_E_PROTOCOL_MISMATCH 0xC00D0012L + +// +// MessageId: NS_E_TIMEOUT +// +// MessageText: +// +// There was no timely response from server.%0 +// +#define NS_E_TIMEOUT 0xC00D0013L + +// +// MessageId: NS_E_NET_WRITE +// +// MessageText: +// +// Error writing to the network.%0 +// +#define NS_E_NET_WRITE 0xC00D0014L + +// +// MessageId: NS_E_NET_READ +// +// MessageText: +// +// Error reading from the network.%0 +// +#define NS_E_NET_READ 0xC00D0015L + +// +// MessageId: NS_E_DISK_WRITE +// +// MessageText: +// +// Error writing to a disk.%0 +// +#define NS_E_DISK_WRITE 0xC00D0016L + +// +// MessageId: NS_E_DISK_READ +// +// MessageText: +// +// Error reading from a disk.%0 +// +#define NS_E_DISK_READ 0xC00D0017L + +// +// MessageId: NS_E_FILE_WRITE +// +// MessageText: +// +// Error writing to a file.%0 +// +#define NS_E_FILE_WRITE 0xC00D0018L + +// +// MessageId: NS_E_FILE_READ +// +// MessageText: +// +// Error reading from a file.%0 +// +#define NS_E_FILE_READ 0xC00D0019L + +// +// MessageId: NS_E_FILE_NOT_FOUND +// +// MessageText: +// +// The system cannot find the file specified.%0 +// +#define NS_E_FILE_NOT_FOUND 0xC00D001AL + +// +// MessageId: NS_E_FILE_EXISTS +// +// MessageText: +// +// The file already exists.%0 +// +#define NS_E_FILE_EXISTS 0xC00D001BL + +// +// MessageId: NS_E_INVALID_NAME +// +// MessageText: +// +// The file name, directory name, or volume label syntax is incorrect.%0 +// +#define NS_E_INVALID_NAME 0xC00D001CL + +// +// MessageId: NS_E_FILE_OPEN_FAILED +// +// MessageText: +// +// Failed to open a file.%0 +// +#define NS_E_FILE_OPEN_FAILED 0xC00D001DL + +// +// MessageId: NS_E_FILE_ALLOCATION_FAILED +// +// MessageText: +// +// Unable to allocate a file.%0 +// +#define NS_E_FILE_ALLOCATION_FAILED 0xC00D001EL + +// +// MessageId: NS_E_FILE_INIT_FAILED +// +// MessageText: +// +// Unable to initialize a file.%0 +// +#define NS_E_FILE_INIT_FAILED 0xC00D001FL + +// +// MessageId: NS_E_FILE_PLAY_FAILED +// +// MessageText: +// +// Unable to play a file.%0 +// +#define NS_E_FILE_PLAY_FAILED 0xC00D0020L + +// +// MessageId: NS_E_SET_DISK_UID_FAILED +// +// MessageText: +// +// Could not set the disk UID.%0 +// +#define NS_E_SET_DISK_UID_FAILED 0xC00D0021L + +// +// MessageId: NS_E_INDUCED +// +// MessageText: +// +// An error was induced for testing purposes.%0 +// +#define NS_E_INDUCED 0xC00D0022L + +// +// MessageId: NS_E_CCLINK_DOWN +// +// MessageText: +// +// Two Content Servers failed to communicate.%0 +// +#define NS_E_CCLINK_DOWN 0xC00D0023L + +// +// MessageId: NS_E_INTERNAL +// +// MessageText: +// +// An unknown error occurred.%0 +// +#define NS_E_INTERNAL 0xC00D0024L + +// +// MessageId: NS_E_BUSY +// +// MessageText: +// +// The requested resource is in use.%0 +// +#define NS_E_BUSY 0xC00D0025L + +// +// MessageId: NS_E_UNRECOGNIZED_STREAM_TYPE +// +// MessageText: +// +// The specified stream type is not recognized.%0 +// +#define NS_E_UNRECOGNIZED_STREAM_TYPE 0xC00D0026L + +// +// MessageId: NS_E_NETWORK_SERVICE_FAILURE +// +// MessageText: +// +// The network service provider failed.%0 +// +#define NS_E_NETWORK_SERVICE_FAILURE 0xC00D0027L + +// +// MessageId: NS_E_NETWORK_RESOURCE_FAILURE +// +// MessageText: +// +// An attempt to acquire a network resource failed.%0 +// +#define NS_E_NETWORK_RESOURCE_FAILURE 0xC00D0028L + +// +// MessageId: NS_E_CONNECTION_FAILURE +// +// MessageText: +// +// The network connection has failed.%0 +// +#define NS_E_CONNECTION_FAILURE 0xC00D0029L + +// +// MessageId: NS_E_SHUTDOWN +// +// MessageText: +// +// The session is being terminated locally.%0 +// +#define NS_E_SHUTDOWN 0xC00D002AL + +// +// MessageId: NS_E_INVALID_REQUEST +// +// MessageText: +// +// The request is invalid in the current state.%0 +// +#define NS_E_INVALID_REQUEST 0xC00D002BL + +// +// MessageId: NS_E_INSUFFICIENT_BANDWIDTH +// +// MessageText: +// +// There is insufficient bandwidth available to fulfill the request.%0 +// +#define NS_E_INSUFFICIENT_BANDWIDTH 0xC00D002CL + +// +// MessageId: NS_E_NOT_REBUILDING +// +// MessageText: +// +// The disk is not rebuilding.%0 +// +#define NS_E_NOT_REBUILDING 0xC00D002DL + +// +// MessageId: NS_E_LATE_OPERATION +// +// MessageText: +// +// An operation requested for a particular time could not be carried out on schedule.%0 +// +#define NS_E_LATE_OPERATION 0xC00D002EL + +// +// MessageId: NS_E_INVALID_DATA +// +// MessageText: +// +// Invalid or corrupt data was encountered.%0 +// +#define NS_E_INVALID_DATA 0xC00D002FL + +// +// MessageId: NS_E_FILE_BANDWIDTH_LIMIT +// +// MessageText: +// +// The bandwidth required to stream a file is higher than the maximum file bandwidth allowed on the server.%0 +// +#define NS_E_FILE_BANDWIDTH_LIMIT 0xC00D0030L + +// +// MessageId: NS_E_OPEN_FILE_LIMIT +// +// MessageText: +// +// The client cannot have any more files open simultaneously.%0 +// +#define NS_E_OPEN_FILE_LIMIT 0xC00D0031L + +// +// MessageId: NS_E_BAD_CONTROL_DATA +// +// MessageText: +// +// The server received invalid data from the client on the control connection.%0 +// +#define NS_E_BAD_CONTROL_DATA 0xC00D0032L + +// +// MessageId: NS_E_NO_STREAM +// +// MessageText: +// +// There is no stream available.%0 +// +#define NS_E_NO_STREAM 0xC00D0033L + +// +// MessageId: NS_E_STREAM_END +// +// MessageText: +// +// There is no more data in the stream.%0 +// +#define NS_E_STREAM_END 0xC00D0034L + +// +// MessageId: NS_E_SERVER_NOT_FOUND +// +// MessageText: +// +// The specified server could not be found.%0 +// +#define NS_E_SERVER_NOT_FOUND 0xC00D0035L + +// +// MessageId: NS_E_DUPLICATE_NAME +// +// MessageText: +// +// The specified name is already in use. +// +#define NS_E_DUPLICATE_NAME 0xC00D0036L + +// +// MessageId: NS_E_DUPLICATE_ADDRESS +// +// MessageText: +// +// The specified address is already in use. +// +#define NS_E_DUPLICATE_ADDRESS 0xC00D0037L + +// +// MessageId: NS_E_BAD_MULTICAST_ADDRESS +// +// MessageText: +// +// The specified address is not a valid multicast address. +// +#define NS_E_BAD_MULTICAST_ADDRESS 0xC00D0038L + +// +// MessageId: NS_E_BAD_ADAPTER_ADDRESS +// +// MessageText: +// +// The specified adapter address is invalid. +// +#define NS_E_BAD_ADAPTER_ADDRESS 0xC00D0039L + +// +// MessageId: NS_E_BAD_DELIVERY_MODE +// +// MessageText: +// +// The specified delivery mode is invalid. +// +#define NS_E_BAD_DELIVERY_MODE 0xC00D003AL + +// +// MessageId: NS_E_INVALID_CHANNEL +// +// MessageText: +// +// The specified station does not exist. +// +#define NS_E_INVALID_CHANNEL 0xC00D003BL + +// +// MessageId: NS_E_INVALID_STREAM +// +// MessageText: +// +// The specified stream does not exist. +// +#define NS_E_INVALID_STREAM 0xC00D003CL + +// +// MessageId: NS_E_INVALID_ARCHIVE +// +// MessageText: +// +// The specified archive could not be opened. +// +#define NS_E_INVALID_ARCHIVE 0xC00D003DL + +// +// MessageId: NS_E_NOTITLES +// +// MessageText: +// +// The system cannot find any titles on the server.%0 +// +#define NS_E_NOTITLES 0xC00D003EL + +// +// MessageId: NS_E_INVALID_CLIENT +// +// MessageText: +// +// The system cannot find the client specified.%0 +// +#define NS_E_INVALID_CLIENT 0xC00D003FL + +// +// MessageId: NS_E_INVALID_BLACKHOLE_ADDRESS +// +// MessageText: +// +// The Blackhole Address is not initialized.%0 +// +#define NS_E_INVALID_BLACKHOLE_ADDRESS 0xC00D0040L + +// +// MessageId: NS_E_INCOMPATIBLE_FORMAT +// +// MessageText: +// +// The station does not support the stream format. +// +#define NS_E_INCOMPATIBLE_FORMAT 0xC00D0041L + +// +// MessageId: NS_E_INVALID_KEY +// +// MessageText: +// +// The specified key is not valid. +// +#define NS_E_INVALID_KEY 0xC00D0042L + +// +// MessageId: NS_E_INVALID_PORT +// +// MessageText: +// +// The specified port is not valid. +// +#define NS_E_INVALID_PORT 0xC00D0043L + +// +// MessageId: NS_E_INVALID_TTL +// +// MessageText: +// +// The specified TTL is not valid. +// +#define NS_E_INVALID_TTL 0xC00D0044L + +// +// MessageId: NS_E_STRIDE_REFUSED +// +// MessageText: +// +// The request to fast forward or rewind could not be fulfilled. +// +#define NS_E_STRIDE_REFUSED 0xC00D0045L + +// +// IMmsAutoServer Errors +// +// +// MessageId: NS_E_MMSAUTOSERVER_CANTFINDWALKER +// +// MessageText: +// +// Unable to load the appropriate file parser.%0 +// +#define NS_E_MMSAUTOSERVER_CANTFINDWALKER 0xC00D0046L + +// +// MessageId: NS_E_MAX_BITRATE +// +// MessageText: +// +// Cannot exceed the maximum bandwidth limit.%0 +// +#define NS_E_MAX_BITRATE 0xC00D0047L + +// +// MessageId: NS_E_LOGFILEPERIOD +// +// MessageText: +// +// Invalid value for LogFilePeriod.%0 +// +#define NS_E_LOGFILEPERIOD 0xC00D0048L + +// +// MessageId: NS_E_MAX_CLIENTS +// +// MessageText: +// +// Cannot exceed the maximum client limit.%0 +// +// +#define NS_E_MAX_CLIENTS 0xC00D0049L + +// +// MessageId: NS_E_LOG_FILE_SIZE +// +// MessageText: +// +// Log File Size too small.%0 +// +// +#define NS_E_LOG_FILE_SIZE 0xC00D004AL + +// +// MessageId: NS_E_MAX_FILERATE +// +// MessageText: +// +// Cannot exceed the maximum file rate.%0 +// +#define NS_E_MAX_FILERATE 0xC00D004BL + +// +// File Walker Errors +// +// +// MessageId: NS_E_WALKER_UNKNOWN +// +// MessageText: +// +// Unknown file type.%0 +// +#define NS_E_WALKER_UNKNOWN 0xC00D004CL + +// +// MessageId: NS_E_WALKER_SERVER +// +// MessageText: +// +// The specified file, %1, cannot be loaded onto the specified server, %2.%0 +// +#define NS_E_WALKER_SERVER 0xC00D004DL + +// +// MessageId: NS_E_WALKER_USAGE +// +// MessageText: +// +// There was a usage error with file parser.%0 +// +#define NS_E_WALKER_USAGE 0xC00D004EL + + +///////////////////////////////////////////////////////////////////////// +// +// NETSHOW Monitor Events +// +///////////////////////////////////////////////////////////////////////// + + + // Tiger Events + + // %1 is the tiger name + +// +// MessageId: NS_I_TIGER_START +// +// MessageText: +// +// The Title Server %1 is running.%0 +// +#define NS_I_TIGER_START 0x400D004FL + +// +// MessageId: NS_E_TIGER_FAIL +// +// MessageText: +// +// The Title Server %1 has failed.%0 +// +#define NS_E_TIGER_FAIL 0xC00D0050L + + + // Cub Events + + // %1 is the cub ID + // %2 is the cub name + +// +// MessageId: NS_I_CUB_START +// +// MessageText: +// +// Content Server %1 (%2) is starting.%0 +// +#define NS_I_CUB_START 0x400D0051L + +// +// MessageId: NS_I_CUB_RUNNING +// +// MessageText: +// +// Content Server %1 (%2) is running.%0 +// +#define NS_I_CUB_RUNNING 0x400D0052L + +// +// MessageId: NS_E_CUB_FAIL +// +// MessageText: +// +// Content Server %1 (%2) has failed.%0 +// +#define NS_E_CUB_FAIL 0xC00D0053L + + + // Disk Events + + // %1 is the tiger disk ID + // %2 is the device name + // %3 is the cub ID +// +// MessageId: NS_I_DISK_START +// +// MessageText: +// +// Disk %1 ( %2 ) on Content Server %3, is running.%0 +// +#define NS_I_DISK_START 0x400D0054L + +// +// MessageId: NS_E_DISK_FAIL +// +// MessageText: +// +// Disk %1 ( %2 ) on Content Server %3, has failed.%0 +// +#define NS_E_DISK_FAIL 0xC00D0055L + +// +// MessageId: NS_I_DISK_REBUILD_STARTED +// +// MessageText: +// +// Started rebuilding disk %1 ( %2 ) on Content Server %3.%0 +// +#define NS_I_DISK_REBUILD_STARTED 0x400D0056L + +// +// MessageId: NS_I_DISK_REBUILD_FINISHED +// +// MessageText: +// +// Finished rebuilding disk %1 ( %2 ) on Content Server %3.%0 +// +#define NS_I_DISK_REBUILD_FINISHED 0x400D0057L + +// +// MessageId: NS_I_DISK_REBUILD_ABORTED +// +// MessageText: +// +// Aborted rebuilding disk %1 ( %2 ) on Content Server %3.%0 +// +#define NS_I_DISK_REBUILD_ABORTED 0x400D0058L + + + // Admin Events + +// +// MessageId: NS_I_LIMIT_FUNNELS +// +// MessageText: +// +// A NetShow administrator at network location %1 set the data stream limit to %2 streams.%0 +// +#define NS_I_LIMIT_FUNNELS 0x400D0059L + +// +// MessageId: NS_I_START_DISK +// +// MessageText: +// +// A NetShow administrator at network location %1 started disk %2.%0 +// +#define NS_I_START_DISK 0x400D005AL + +// +// MessageId: NS_I_STOP_DISK +// +// MessageText: +// +// A NetShow administrator at network location %1 stopped disk %2.%0 +// +#define NS_I_STOP_DISK 0x400D005BL + +// +// MessageId: NS_I_STOP_CUB +// +// MessageText: +// +// A NetShow administrator at network location %1 stopped Content Server %2.%0 +// +#define NS_I_STOP_CUB 0x400D005CL + +// +// MessageId: NS_I_KILL_VIEWER +// +// MessageText: +// +// A NetShow administrator at network location %1 disconnected viewer %2 from the system.%0 +// +#define NS_I_KILL_VIEWER 0x400D005DL + +// +// MessageId: NS_I_REBUILD_DISK +// +// MessageText: +// +// A NetShow administrator at network location %1 started rebuilding disk %2.%0 +// +#define NS_I_REBUILD_DISK 0x400D005EL + +// +// MessageId: NS_W_UNKNOWN_EVENT +// +// MessageText: +// +// Unknown %1 event encountered.%0 +// +#define NS_W_UNKNOWN_EVENT 0x800D005FL + + + // Alerts + +// +// MessageId: NS_E_MAX_FUNNELS_ALERT +// +// MessageText: +// +// The NetShow data stream limit of %1 streams was reached.%0 +// +#define NS_E_MAX_FUNNELS_ALERT 0xC00D0060L + +// +// MessageId: NS_E_ALLOCATE_FILE_FAIL +// +// MessageText: +// +// The NetShow Video Server was unable to allocate a %1 block file named %2.%0 +// +#define NS_E_ALLOCATE_FILE_FAIL 0xC00D0061L + +// +// MessageId: NS_E_PAGING_ERROR +// +// MessageText: +// +// A Content Server was unable to page a block.%0 +// +#define NS_E_PAGING_ERROR 0xC00D0062L + +// +// MessageId: NS_E_BAD_BLOCK0_VERSION +// +// MessageText: +// +// Disk %1 has unrecognized control block version %2.%0 +// +#define NS_E_BAD_BLOCK0_VERSION 0xC00D0063L + +// +// MessageId: NS_E_BAD_DISK_UID +// +// MessageText: +// +// Disk %1 has incorrect uid %2.%0 +// +#define NS_E_BAD_DISK_UID 0xC00D0064L + +// +// MessageId: NS_E_BAD_FSMAJOR_VERSION +// +// MessageText: +// +// Disk %1 has unsupported file system major version %2.%0 +// +#define NS_E_BAD_FSMAJOR_VERSION 0xC00D0065L + +// +// MessageId: NS_E_BAD_STAMPNUMBER +// +// MessageText: +// +// Disk %1 has bad stamp number in control block.%0 +// +#define NS_E_BAD_STAMPNUMBER 0xC00D0066L + +// +// MessageId: NS_E_PARTIALLY_REBUILT_DISK +// +// MessageText: +// +// Disk %1 is partially reconstructed.%0 +// +#define NS_E_PARTIALLY_REBUILT_DISK 0xC00D0067L + +// +// MessageId: NS_E_ENACTPLAN_GIVEUP +// +// MessageText: +// +// EnactPlan gives up.%0 +// +#define NS_E_ENACTPLAN_GIVEUP 0xC00D0068L + + + // MCMADM warnings/errors + +// +// MessageId: MCMADM_I_NO_EVENTS +// +// MessageText: +// +// Event initialization failed, there will be no MCM events.%0 +// +#define MCMADM_I_NO_EVENTS 0x400D0069L + +// +// MessageId: MCMADM_E_REGKEY_NOT_FOUND +// +// MessageText: +// +// The key was not found in the registry.%0 +// +#define MCMADM_E_REGKEY_NOT_FOUND 0xC00D006AL + +// +// MessageId: NS_E_NO_FORMATS +// +// MessageText: +// +// No stream formats were found in an NSC file.%0 +// +#define NS_E_NO_FORMATS 0xC00D006BL + +// +// MessageId: NS_E_NO_REFERENCES +// +// MessageText: +// +// No reference URLs were found in an ASX file.%0 +// +#define NS_E_NO_REFERENCES 0xC00D006CL + +// +// MessageId: NS_E_WAVE_OPEN +// +// MessageText: +// +// Error opening wave device, the device might be in use.%0 +// +#define NS_E_WAVE_OPEN 0xC00D006DL + +// +// MessageId: NS_I_LOGGING_FAILED +// +// MessageText: +// +// The logging operation failed. +// +#define NS_I_LOGGING_FAILED 0x400D006EL + +// +// MessageId: NS_E_CANNOTCONNECTEVENTS +// +// MessageText: +// +// Unable to establish a connection to the NetShow event monitor service.%0 +// +#define NS_E_CANNOTCONNECTEVENTS 0xC00D006FL + +// +// MessageId: NS_I_LIMIT_BANDWIDTH +// +// MessageText: +// +// A NetShow administrator at network location %1 set the maximum bandwidth limit to %2 bps.%0 +// +#define NS_I_LIMIT_BANDWIDTH 0x400D0070L + +// +// MessageId: NS_E_NOTHING_TO_DO +// +// MessageText: +// +// NS_E_NOTHING_TO_DO +// +#define NS_E_NOTHING_TO_DO 0xC00D07F1L + +// +// MessageId: NS_E_NO_MULTICAST +// +// MessageText: +// +// NS_E_NO_MULTICAST +// +#define NS_E_NO_MULTICAST 0xC00D07F2L + + +///////////////////////////////////////////////////////////////////////// +// +// **New** NETSHOW Error Events +// +// IdRange = 200..399 +// +///////////////////////////////////////////////////////////////////////// + +// +// MessageId: NS_E_MONITOR_GIVEUP +// +// MessageText: +// +// Netshow Events Monitor is not operational and has been disconnected.%0 +// +#define NS_E_MONITOR_GIVEUP 0xC00D00C8L + +// +// MessageId: NS_E_REMIRRORED_DISK +// +// MessageText: +// +// Disk %1 is remirrored.%0 +// +#define NS_E_REMIRRORED_DISK 0xC00D00C9L + +// +// MessageId: NS_E_INSUFFICIENT_DATA +// +// MessageText: +// +// Insufficient data found.%0 +// +#define NS_E_INSUFFICIENT_DATA 0xC00D00CAL + +// +// MessageId: NS_E_ASSERT +// +// MessageText: +// +// %1 failed in file %2 line %3.%0 +// +#define NS_E_ASSERT 0xC00D00CBL + +// +// MessageId: NS_E_BAD_ADAPTER_NAME +// +// MessageText: +// +// The specified adapter name is invalid.%0 +// +#define NS_E_BAD_ADAPTER_NAME 0xC00D00CCL + +// +// MessageId: NS_E_NOT_LICENSED +// +// MessageText: +// +// The application is not licensed for this feature.%0 +// +#define NS_E_NOT_LICENSED 0xC00D00CDL + +// +// MessageId: NS_E_NO_SERVER_CONTACT +// +// MessageText: +// +// Unable to contact the server.%0 +// +#define NS_E_NO_SERVER_CONTACT 0xC00D00CEL + +// +// MessageId: NS_E_TOO_MANY_TITLES +// +// MessageText: +// +// Maximum number of titles exceeded.%0 +// +#define NS_E_TOO_MANY_TITLES 0xC00D00CFL + +// +// MessageId: NS_E_TITLE_SIZE_EXCEEDED +// +// MessageText: +// +// Maximum size of a title exceeded.%0 +// +#define NS_E_TITLE_SIZE_EXCEEDED 0xC00D00D0L + +// +// MessageId: NS_E_UDP_DISABLED +// +// MessageText: +// +// UDP protocol not enabled. Not trying %1!ls!.%0 +// +#define NS_E_UDP_DISABLED 0xC00D00D1L + +// +// MessageId: NS_E_TCP_DISABLED +// +// MessageText: +// +// TCP protocol not enabled. Not trying %1!ls!.%0 +// +#define NS_E_TCP_DISABLED 0xC00D00D2L + +// +// MessageId: NS_E_HTTP_DISABLED +// +// MessageText: +// +// HTTP protocol not enabled. Not trying %1!ls!.%0 +// +#define NS_E_HTTP_DISABLED 0xC00D00D3L + +// +// MessageId: NS_E_LICENSE_EXPIRED +// +// MessageText: +// +// The product license has expired.%0 +// +#define NS_E_LICENSE_EXPIRED 0xC00D00D4L + +// +// MessageId: NS_E_TITLE_BITRATE +// +// MessageText: +// +// Source file exceeds the per title maximum bitrate. See NetShow Theater documentation for more information.%0 +// +#define NS_E_TITLE_BITRATE 0xC00D00D5L + +// +// MessageId: NS_E_EMPTY_PROGRAM_NAME +// +// MessageText: +// +// The program name cannot be empty.%0 +// +#define NS_E_EMPTY_PROGRAM_NAME 0xC00D00D6L + +// +// MessageId: NS_E_MISSING_CHANNEL +// +// MessageText: +// +// Station %1 does not exist.%0 +// +#define NS_E_MISSING_CHANNEL 0xC00D00D7L + +// +// MessageId: NS_E_NO_CHANNELS +// +// MessageText: +// +// You need to define at least one station before this operation can complete.%0 +// +#define NS_E_NO_CHANNELS 0xC00D00D8L + + +///////////////////////////////////////////////////////////////////////// +// +// **New** NETSHOW Monitor Events +// +// IdRange = 400..599 +// +// Admin Events: +// +// Alerts: +// +// Title Server: +// %1 is the Title Server name +// +// Content Server: +// %1 is the Content Server ID +// %2 is the Content Server name +// %3 is the Peer Content Server name (optional) +// +// Disks: +// %1 is the Title Server disk ID +// %2 is the device name +// %3 is the Content Server ID +// +;///////////////////////////////////////////////////////////////////////// + +// +// MessageId: NS_E_CUB_FAIL_LINK +// +// MessageText: +// +// Content Server %1 (%2) has failed its link to Content Server %3.%0 +// +#define NS_E_CUB_FAIL_LINK 0xC00D0190L + +// +// MessageId: NS_I_CUB_UNFAIL_LINK +// +// MessageText: +// +// Content Server %1 (%2) has established its link to Content Server %3.%0 +// +#define NS_I_CUB_UNFAIL_LINK 0x400D0191L + +// +// MessageId: NS_E_BAD_CUB_UID +// +// MessageText: +// +// Content Server %1 (%2) has incorrect uid %3.%0 +// +#define NS_E_BAD_CUB_UID 0xC00D0192L + +// +// MessageId: NS_I_RESTRIPE_START +// +// MessageText: +// +// Restripe operation has started.%0 +// +#define NS_I_RESTRIPE_START 0x400D0193L + +// +// MessageId: NS_I_RESTRIPE_DONE +// +// MessageText: +// +// Restripe operation has completed.%0 +// +#define NS_I_RESTRIPE_DONE 0x400D0194L + +// +// MessageId: NS_E_GLITCH_MODE +// +// MessageText: +// +// Server unreliable because multiple components failed.%0 +// +#define NS_E_GLITCH_MODE 0xC00D0195L + +// +// MessageId: NS_I_RESTRIPE_DISK_OUT +// +// MessageText: +// +// Content disk %1 (%2) on Content Server %3 has been restriped out.%0 +// +#define NS_I_RESTRIPE_DISK_OUT 0x400D0196L + +// +// MessageId: NS_I_RESTRIPE_CUB_OUT +// +// MessageText: +// +// Content server %1 (%2) has been restriped out.%0 +// +#define NS_I_RESTRIPE_CUB_OUT 0x400D0197L + +// +// MessageId: NS_I_DISK_STOP +// +// MessageText: +// +// Disk %1 ( %2 ) on Content Server %3, has been offlined.%0 +// +#define NS_I_DISK_STOP 0x400D0198L + +// +// MessageId: NS_I_CATATONIC_FAILURE +// +// MessageText: +// +// Disk %1 ( %2 ) on Content Server %3, will be failed because it is catatonic.%0 +// +#define NS_I_CATATONIC_FAILURE 0x800D0199L + +// +// MessageId: NS_I_CATATONIC_AUTO_UNFAIL +// +// MessageText: +// +// Disk %1 ( %2 ) on Content Server %3, auto online from catatonic state.%0 +// +#define NS_I_CATATONIC_AUTO_UNFAIL 0x800D019AL + +// +// MessageId: NS_E_NO_MEDIA_PROTOCOL +// +// MessageText: +// +// Content Server %1 (%2) is unable to communicate with the Media System Network Protocol.%0 +// +#define NS_E_NO_MEDIA_PROTOCOL 0xC00D019BL + + + +///////////////////////////////////////////////////////////////////////// +// +// **New** NETSHOW IMmsAutoServer Errors +// +// IdRange = 600..799 +// +///////////////////////////////////////////////////////////////////////// + +// +// MessageId: NS_E_INITIAL +// +// MessageText: +// +// Placeholder.%0 +// +#define NS_E_INITIAL 0xC00D0258L + + +///////////////////////////////////////////////////////////////////////// +// +// **New** MCMADM warnings/errors +// +// IdRange = 1000..1199 +// +///////////////////////////////////////////////////////////////////////// + +// +// MessageId: MCMADM_E_INITIAL +// +// MessageText: +// +// Placeholder.%0 +// +#define MCMADM_E_INITIAL 0xC00D03E8L + + +// +// Advanced Streaming Format (ASF) codes occupy MessageIds 2000-2999 +// +// See ASFErr.mc for more details - please do not define any symbols +// in that range in this file. +// + + +///////////////////////////////////////////////////////////////////////// +// +// Windows Media Audio SDK Errors +// +// IdRange = 3000-3199 +// +///////////////////////////////////////////////////////////////////////// + +// +// MessageId: NS_E_INVALID_INPUT_FORMAT +// +// MessageText: +// +// The input audio format must be a valid, PCM audio format.%0 +// +#define NS_E_INVALID_INPUT_FORMAT 0xC00D0BB8L + +// +// MessageId: NS_E_MSAUDIO_NOT_INSTALLED +// +// MessageText: +// +// The MSAudio codec is not installed on this system.%0 +// +#define NS_E_MSAUDIO_NOT_INSTALLED 0xC00D0BB9L + +// +// MessageId: NS_E_UNEXPECTED_MSAUDIO_ERROR +// +// MessageText: +// +// An unexpected error occured with the MSAudio codec.%0 +// +#define NS_E_UNEXPECTED_MSAUDIO_ERROR 0xC00D0BBAL + +// +// MessageId: NS_E_INVALID_OUTPUT_FORMAT +// +// MessageText: +// +// The MSAudio codec does not support the specified output format.%0 +// +#define NS_E_INVALID_OUTPUT_FORMAT 0xC00D0BBBL + +// +// MessageId: NS_E_NOT_CONFIGURED +// +// MessageText: +// +// The object must be fully configured before audio samples can be processed.%0 +// +#define NS_E_NOT_CONFIGURED 0xC00D0BBCL + +// +// MessageId: NS_E_PROTECTED_CONTENT +// +// MessageText: +// +// The content is protected and cannot be opened at this time.%0 +// +#define NS_E_PROTECTED_CONTENT 0xC00D0BBDL + +// +// MessageId: NS_E_LICENSE_REQUIRED +// +// MessageText: +// +// A playback license is required to open this content.%0 +// +#define NS_E_LICENSE_REQUIRED 0xC00D0BBEL + +// +// MessageId: NS_E_TAMPERED_CONTENT +// +// MessageText: +// +// This content has been tampered with and cannot be opened.%0 +// +#define NS_E_TAMPERED_CONTENT 0xC00D0BBFL + +// +// MessageId: NS_E_LICENSE_OUTOFDATE +// +// MessageText: +// +// The license is to open this content has expired.%0 +// +#define NS_E_LICENSE_OUTOFDATE 0xC00D0BC0L + +// +// MessageId: NS_E_LICENSE_INCORRECT_RIGHTS +// +// MessageText: +// +// The requested rights prevent the content from being opened.%0 +// +#define NS_E_LICENSE_INCORRECT_RIGHTS 0xC00D0BC1L + + +///////////////////////////////////////////////////////////////////////// +// +// **New** NETSHOW Warning Events +// +// IdRange = 10000 +// +///////////////////////////////////////////////////////////////////////// + +// +// MessageId: NS_W_INITIAL +// +// MessageText: +// +// Placeholder.%0 +// +#define NS_W_INITIAL 0x800D2710L + + +#endif _NSERROR_H + diff --git a/Src/Plugins/Encoder/enc_wma/resource.h b/Src/Plugins/Encoder/enc_wma/resource.h new file mode 100644 index 00000000..61332778 --- /dev/null +++ b/Src/Plugins/Encoder/enc_wma/resource.h @@ -0,0 +1,57 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Script1.rc +// +#define IDS_CANNOT_FIND_INPUT_FORMATTER 0 +#define IDS_WARNING 1 +#define IDOK2 2 +#define IDS_CANNOT_ALLOCATE_MEM 2 +#define IDS_WMA_ENCODER_ERROR 3 +#define IDS_CANNOT_GET_STRUCTURE 4 +#define IDS_CANNOT_GET_ENCODER4_INFO 5 +#define IDS_SAVE_PROFILE_READ_ERROR 6 +#define IDS_MEM_ALLOCATION_ERROR 7 +#define IDS_PROFILE_SAVE_SIZE_ERROR 8 +#define IDS_CANNOT_READ_AUDIO_STREAM 9 +#define IDS_CANNOT_GET_CODEC_INFO 10 +#define IDS_CANNOT_CREATE_A_PROFILE 11 +#define IDS_CANNOT_CREATE_PROFILE_MANAGER 12 +#define IDS_WMA_CONFIG_FILE_ERROR 13 +#define IDS_CBR 16 +#define IDS_2_PASS_CBR 17 +#define IDS_VBR 18 +#define IDS_ABR 19 +#define IDS_MONO_INFO 20 +#define IDS_STEREO_INFO 21 +#define IDS_CHANNELS_INFO 22 +#define IDS_QUALITY 23 +#define IDS_BITRATE 24 +#define IDS_ENC_WMA_DESC 25 +#define IDD_DIALOG1 101 +#define IDC_DIR 1000 +#define IDC_SRATE 1001 +#define IDC_BRATE 1002 +#define IDC_NCH 1003 +#define IDC_PROFILE 1004 +#define IDC_ABRATE 1005 +#define IDC_CBR 1005 +#define IDC_ENCODER 1006 +#define IDC_BPSAMPLE 1007 +#define IDC_VBR 1008 +#define IDC_1PASS 1009 +#define IDC_RADIO4 1010 +#define IDC_2PASS 1010 +#define IDC_STATIC_BITRATE 1011 +#define IDC_SAMPLE_FORMAT 1012 +#define IDC_COMBO1 1013 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 105 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1014 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/Src/Plugins/Encoder/enc_wma/version.rc2 b/Src/Plugins/Encoder/enc_wma/version.rc2 new file mode 100644 index 00000000..d3cf0a16 --- /dev/null +++ b/Src/Plugins/Encoder/enc_wma/version.rc2 @@ -0,0 +1,39 @@ + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// +#include "../../../Winamp/buildType.h" +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,23,0,0 + PRODUCTVERSION WINAMP_PRODUCTVER + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Winamp SA" + VALUE "FileDescription", "Winamp Encoder Plug-in" + VALUE "FileVersion", "1,23,0,0" + VALUE "InternalName", "Nullsoft WMA Encoder" + VALUE "LegalCopyright", "Copyright © 2006-2023 Winamp SA" + VALUE "LegalTrademarks", "Nullsoft and Winamp are trademarks of Winamp SA" + VALUE "OriginalFilename", "enc_wma.dll" + VALUE "ProductName", "Winamp" + VALUE "ProductVersion", STR_WINAMP_PRODUCTVER + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/Src/Plugins/Encoder/enc_wma/wmaudiosdk.h b/Src/Plugins/Encoder/enc_wma/wmaudiosdk.h new file mode 100644 index 00000000..99199d9d --- /dev/null +++ b/Src/Plugins/Encoder/enc_wma/wmaudiosdk.h @@ -0,0 +1,1009 @@ +/* this ALWAYS GENERATED file contains the definitions for the interfaces */ + + +/* File created by MIDL compiler version 3.01.75 */ +/* at Mon Aug 09 13:10:31 1999 + */ +/* Compiler settings for .\wmaudiosdk.idl: + Oicf (OptLev=i2), W1, Zp8, env=Win32, ms_ext, c_ext + error checks: none +*/ +//@@MIDL_FILE_HEADING( ) +#include "rpc.h" +#include "rpcndr.h" +#ifndef COM_NO_WINDOWS_H +#include "windows.h" +#include "ole2.h" +#endif /*COM_NO_WINDOWS_H*/ + +#ifndef __wmaudiosdk_h__ +#define __wmaudiosdk_h__ + +#ifdef __cplusplus +extern "C"{ +#endif + +/* Forward Declarations */ + +#ifndef __IWMAudioWriter_FWD_DEFINED__ +#define __IWMAudioWriter_FWD_DEFINED__ +typedef interface IWMAudioWriter IWMAudioWriter; +#endif /* __IWMAudioWriter_FWD_DEFINED__ */ + + +#ifndef __IWMAudioReader_FWD_DEFINED__ +#define __IWMAudioReader_FWD_DEFINED__ +typedef interface IWMAudioReader IWMAudioReader; +#endif /* __IWMAudioReader_FWD_DEFINED__ */ + + +#ifndef __IWMAudioReadCallback_FWD_DEFINED__ +#define __IWMAudioReadCallback_FWD_DEFINED__ +typedef interface IWMAudioReadCallback IWMAudioReadCallback; +#endif /* __IWMAudioReadCallback_FWD_DEFINED__ */ + + +#ifndef __IWMAudioInfo_FWD_DEFINED__ +#define __IWMAudioInfo_FWD_DEFINED__ +typedef interface IWMAudioInfo IWMAudioInfo; +#endif /* __IWMAudioInfo_FWD_DEFINED__ */ + + +/* header files for imported files */ +#include "oaidl.h" +#include "ocidl.h" + +void __RPC_FAR * __RPC_USER MIDL_user_allocate(size_t); +void __RPC_USER MIDL_user_free( void __RPC_FAR * ); + +/**************************************** + * Generated header for interface: __MIDL_itf_wmaudiosdk_0000 + * at Mon Aug 09 13:10:31 1999 + * using MIDL 3.01.75 + ****************************************/ +/* [local] */ + + +//========================================================================= +// +// THIS SOFTWARE HAS BEEN LICENSED FROM MICROSOFT CORPORATION PURSUANT +// TO THE TERMS OF AN END USER LICENSE AGREEMENT ("EULA"). +// PLEASE REFER TO THE TEXT OF THE EULA TO DETERMINE THE RIGHTS TO USE THE SOFTWARE. +// +// Copyright (C) Microsoft Corporation, 1996 - 1999 All Rights Reserved. +// +//========================================================================= +typedef struct tWAVEFORMATEX WAVEFORMATEX; + + + + +#include "nserror.h" +#include "asferr.h" +EXTERN_GUID( IID_IWMAudioWriter, 0x1A5636F1, 0xDB5E, 0x11d2, 0x9D, 0x41, 0x00, 0x60, 0x08, 0x31, 0x78, 0xAF ); +EXTERN_GUID( IID_IWMAudioReader, 0x1A5636F2, 0xDB5E, 0x11d2, 0x9D, 0x41, 0x00, 0x60, 0x08, 0x31, 0x78, 0xAF ); +EXTERN_GUID( IID_IWMAudioReadCallback, 0x1A5636F3, 0xDB5E, 0x11d2, 0x9D, 0x41, 0x00, 0x60, 0x08, 0x31, 0x78, 0xAF ); +EXTERN_GUID( IID_IWMAudioInfo, 0xaa139f0, 0xf6a8, 0x11d2, 0x97, 0xf7, 0x0, 0xa0, 0xc9, 0x5e, 0xa8, 0x50 ); +#define WMT_SAMPLE_MUSIC 0 +#define WMT_SAMPLE_SPEECH 0xFFFFFFFF +//////////////////////////////////////////////////////////////// +// +// These are the special case attributes that give information +// about the ASF file. +// +static const DWORD g_dwWMASpecialAttributes = 7; +static const WCHAR *g_wszWMADuration = L"Duration"; +static const WCHAR *g_wszWMABitrate = L"Bitrate"; +static const WCHAR *g_wszWMASeekable = L"Seekable"; +static const WCHAR *g_wszWMABroadcast = L"Broadcast"; +static const WCHAR *g_wszWMAProtected = L"Is_Protected"; +static const WCHAR *g_wszWMATrusted = L"Is_Trusted"; +static const WCHAR *g_wszWMASignature_Name = L"Signature_Name"; + +//////////////////////////////////////////////////////////////// +// +// The content description object supports 5 basic attributes. +// +static const DWORD g_dwWMAContentAttributes = 5; +static const WCHAR *g_wszWMATitle = L"Title"; +static const WCHAR *g_wszWMAAuthor = L"Author"; +static const WCHAR *g_wszWMADescription = L"Description"; +static const WCHAR *g_wszWMARating = L"Rating"; +static const WCHAR *g_wszWMACopyright = L"Copyright"; + +//////////////////////////////////////////////////////////////// +// +// These attributes are used to set DRM properties on an ASF. +// +static const WCHAR *g_wszWMAUse_DRM = L"Use_DRM"; +static const WCHAR *g_wszWMADRM_Flags = L"DRM_Flags"; +static const WCHAR *g_wszWMADRM_Level = L"DRM_Level"; + +//////////////////////////////////////////////////////////////// +// +// These are the additional attributes defined in the ASF attribute +// namespace that gives information about the content in the ASF file. +// +static const WCHAR *g_wszWMAAlbumTitle = L"WM/AlbumTitle"; +static const WCHAR *g_wszWMATrack = L"WM/Track"; +static const WCHAR *g_wszWMAPromotionURL = L"WM/PromotionURL"; +static const WCHAR *g_wszWMAAlbumCoverURL = L"WM/AlbumCoverURL"; +static const WCHAR *g_wszWMAGenre = L"WM/Genre"; +static const WCHAR *g_wszWMAYear = L"WM/Year"; + +HRESULT STDMETHODCALLTYPE WMAudioCreateWriter( LPCWSTR pszFilename, IWMAudioWriter **ppIWMAudioWriter ); +HRESULT STDMETHODCALLTYPE WMAudioCreateReader( LPCWSTR pszFilename, IWMAudioReadCallback *pIWMReadCallback, IWMAudioReader **ppIWMAudioReader, void *pvReserved ); +HRESULT STDMETHODCALLTYPE WMAudioCreateInfo( LPCWSTR pszFilename, IWMAudioInfo **ppIWMAudioInfo ); + +/* + + // Already defined in wmsdk.h + +typedef +enum WMT_STATUS + { WMT_ERROR = 0, + WMT_BUFFERING_START = 1, + WMT_BUFFERING_STOP = 2, + WMT_EOF = 3, + WMT_LOCATING = 4, + WMT_CONNECTING = 5, + WMT_NO_RIGHTS = 6, + WMT_MISSING_CODEC = 7 + } WMT_STATUS; + +typedef +enum WMT_ATTR_DATATYPE + { WMT_TYPE_DWORD = 0, + WMT_TYPE_STRING = 1, + WMT_TYPE_BINARY = 2, + WMT_TYPE_BOOL = 3 + } WMT_ATTR_DATATYPE; + +typedef +enum WMT_RIGHTS + { WMT_RIGHT_PLAYBACK = 0x1, + WMT_RIGHT_COPY_TO_PORTABLE = 0x2, + WMT_RIGHT_COPY_TO_CD = 0x8 + } WMT_RIGHTS; + +typedef +enum WMT_AUDIO_OPTIONS + { WMT_OPTION_DEFAULT = 0 + } WMT_AUDIO_OPTIONS; +*/ + + +extern RPC_IF_HANDLE __MIDL_itf_wmaudiosdk_0000_v0_0_c_ifspec; +extern RPC_IF_HANDLE __MIDL_itf_wmaudiosdk_0000_v0_0_s_ifspec; + +#ifndef __IWMAudioWriter_INTERFACE_DEFINED__ +#define __IWMAudioWriter_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IWMAudioWriter + * at Mon Aug 09 13:10:31 1999 + * using MIDL 3.01.75 + ****************************************/ +/* [local][unique][helpstring][uuid][object] */ + + + +EXTERN_C const IID IID_IWMAudioWriter; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface DECLSPEC_UUID("1A5636F1-DB5E-11d2-9D41-0060083178AF") + IWMAudioWriter : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE SetAttribute( + /* [in] */ LPCWSTR pszName, + /* [in] */ WMT_ATTR_DATATYPE Type, + /* [in] */ const BYTE __RPC_FAR *pValue, + /* [in] */ WORD cbLength) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetInputFormat( + /* [in] */ const WAVEFORMATEX __RPC_FAR *pWfx) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetOutputFormat( + /* [in] */ DWORD dwBitrate, + /* [in] */ DWORD dwSampleRate, + /* [in] */ DWORD dwNumChannels, + /* [in] */ DWORD dwAudioOptions) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetOutputFormat( + /* [out] */ DWORD __RPC_FAR *pdwBitrate, + /* [out] */ DWORD __RPC_FAR *pdwSampleRate, + /* [out] */ DWORD __RPC_FAR *pdwNumChannels, + /* [out] */ DWORD __RPC_FAR *pdwAudioOptions) = 0; + + virtual HRESULT STDMETHODCALLTYPE WriteSample( + /* [in] */ const BYTE __RPC_FAR *pData, + /* [in] */ DWORD cbData) = 0; + + virtual HRESULT STDMETHODCALLTYPE Flush( void) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMAudioWriterVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IWMAudioWriter __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IWMAudioWriter __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IWMAudioWriter __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *SetAttribute )( + IWMAudioWriter __RPC_FAR * This, + /* [in] */ LPCWSTR pszName, + /* [in] */ WMT_ATTR_DATATYPE Type, + /* [in] */ const BYTE __RPC_FAR *pValue, + /* [in] */ WORD cbLength); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *SetInputFormat )( + IWMAudioWriter __RPC_FAR * This, + /* [in] */ const WAVEFORMATEX __RPC_FAR *pWfx); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *SetOutputFormat )( + IWMAudioWriter __RPC_FAR * This, + /* [in] */ DWORD dwBitrate, + /* [in] */ DWORD dwSampleRate, + /* [in] */ DWORD dwNumChannels, + /* [in] */ DWORD dwAudioOptions); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetOutputFormat )( + IWMAudioWriter __RPC_FAR * This, + /* [out] */ DWORD __RPC_FAR *pdwBitrate, + /* [out] */ DWORD __RPC_FAR *pdwSampleRate, + /* [out] */ DWORD __RPC_FAR *pdwNumChannels, + /* [out] */ DWORD __RPC_FAR *pdwAudioOptions); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *WriteSample )( + IWMAudioWriter __RPC_FAR * This, + /* [in] */ const BYTE __RPC_FAR *pData, + /* [in] */ DWORD cbData); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Flush )( + IWMAudioWriter __RPC_FAR * This); + + END_INTERFACE + } IWMAudioWriterVtbl; + + interface IWMAudioWriter + { + CONST_VTBL struct IWMAudioWriterVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMAudioWriter_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMAudioWriter_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMAudioWriter_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMAudioWriter_SetAttribute(This,pszName,Type,pValue,cbLength) \ + (This)->lpVtbl -> SetAttribute(This,pszName,Type,pValue,cbLength) + +#define IWMAudioWriter_SetInputFormat(This,pWfx) \ + (This)->lpVtbl -> SetInputFormat(This,pWfx) + +#define IWMAudioWriter_SetOutputFormat(This,dwBitrate,dwSampleRate,dwNumChannels,dwAudioOptions) \ + (This)->lpVtbl -> SetOutputFormat(This,dwBitrate,dwSampleRate,dwNumChannels,dwAudioOptions) + +#define IWMAudioWriter_GetOutputFormat(This,pdwBitrate,pdwSampleRate,pdwNumChannels,pdwAudioOptions) \ + (This)->lpVtbl -> GetOutputFormat(This,pdwBitrate,pdwSampleRate,pdwNumChannels,pdwAudioOptions) + +#define IWMAudioWriter_WriteSample(This,pData,cbData) \ + (This)->lpVtbl -> WriteSample(This,pData,cbData) + +#define IWMAudioWriter_Flush(This) \ + (This)->lpVtbl -> Flush(This) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMAudioWriter_SetAttribute_Proxy( + IWMAudioWriter __RPC_FAR * This, + /* [in] */ LPCWSTR pszName, + /* [in] */ WMT_ATTR_DATATYPE Type, + /* [in] */ const BYTE __RPC_FAR *pValue, + /* [in] */ WORD cbLength); + + +void __RPC_STUB IWMAudioWriter_SetAttribute_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMAudioWriter_SetInputFormat_Proxy( + IWMAudioWriter __RPC_FAR * This, + /* [in] */ const WAVEFORMATEX __RPC_FAR *pWfx); + + +void __RPC_STUB IWMAudioWriter_SetInputFormat_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMAudioWriter_SetOutputFormat_Proxy( + IWMAudioWriter __RPC_FAR * This, + /* [in] */ DWORD dwBitrate, + /* [in] */ DWORD dwSampleRate, + /* [in] */ DWORD dwNumChannels, + /* [in] */ DWORD dwAudioOptions); + + +void __RPC_STUB IWMAudioWriter_SetOutputFormat_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMAudioWriter_GetOutputFormat_Proxy( + IWMAudioWriter __RPC_FAR * This, + /* [out] */ DWORD __RPC_FAR *pdwBitrate, + /* [out] */ DWORD __RPC_FAR *pdwSampleRate, + /* [out] */ DWORD __RPC_FAR *pdwNumChannels, + /* [out] */ DWORD __RPC_FAR *pdwAudioOptions); + + +void __RPC_STUB IWMAudioWriter_GetOutputFormat_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMAudioWriter_WriteSample_Proxy( + IWMAudioWriter __RPC_FAR * This, + /* [in] */ const BYTE __RPC_FAR *pData, + /* [in] */ DWORD cbData); + + +void __RPC_STUB IWMAudioWriter_WriteSample_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMAudioWriter_Flush_Proxy( + IWMAudioWriter __RPC_FAR * This); + + +void __RPC_STUB IWMAudioWriter_Flush_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMAudioWriter_INTERFACE_DEFINED__ */ + + +#ifndef __IWMAudioReader_INTERFACE_DEFINED__ +#define __IWMAudioReader_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IWMAudioReader + * at Mon Aug 09 13:10:31 1999 + * using MIDL 3.01.75 + ****************************************/ +/* [local][unique][helpstring][uuid][object] */ + + + +EXTERN_C const IID IID_IWMAudioReader; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface DECLSPEC_UUID("1A5636F2-DB5E-11d2-9D41-0060083178AF") + IWMAudioReader : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE GetAttributeByName( + /* [in] */ LPCWSTR pszName, + /* [out] */ WMT_ATTR_DATATYPE __RPC_FAR *pType, + /* [out] */ BYTE __RPC_FAR *pValue, + /* [out][in] */ WORD __RPC_FAR *pcbLength) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetAttributeCount( + /* [out] */ WORD __RPC_FAR *pcAttributes) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetAttributeByIndex( + /* [in] */ WORD wIndex, + /* [out] */ WCHAR __RPC_FAR *pwszName, + /* [out][in] */ WORD __RPC_FAR *pcbNameLen, + /* [out] */ WMT_ATTR_DATATYPE __RPC_FAR *pType, + /* [out] */ BYTE __RPC_FAR *pValue, + /* [out][in] */ WORD __RPC_FAR *pcbLength) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetOutputFormat( + /* [out] */ WAVEFORMATEX __RPC_FAR *pWfx, + /* [in] */ DWORD cbSize) = 0; + + virtual HRESULT STDMETHODCALLTYPE Start( void) = 0; + + virtual HRESULT STDMETHODCALLTYPE Stop( void) = 0; + + virtual HRESULT STDMETHODCALLTYPE Seek( + DWORD dwMsTime) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMAudioReaderVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IWMAudioReader __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IWMAudioReader __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IWMAudioReader __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetAttributeByName )( + IWMAudioReader __RPC_FAR * This, + /* [in] */ LPCWSTR pszName, + /* [out] */ WMT_ATTR_DATATYPE __RPC_FAR *pType, + /* [out] */ BYTE __RPC_FAR *pValue, + /* [out][in] */ WORD __RPC_FAR *pcbLength); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetAttributeCount )( + IWMAudioReader __RPC_FAR * This, + /* [out] */ WORD __RPC_FAR *pcAttributes); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetAttributeByIndex )( + IWMAudioReader __RPC_FAR * This, + /* [in] */ WORD wIndex, + /* [out] */ WCHAR __RPC_FAR *pwszName, + /* [out][in] */ WORD __RPC_FAR *pcbNameLen, + /* [out] */ WMT_ATTR_DATATYPE __RPC_FAR *pType, + /* [out] */ BYTE __RPC_FAR *pValue, + /* [out][in] */ WORD __RPC_FAR *pcbLength); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetOutputFormat )( + IWMAudioReader __RPC_FAR * This, + /* [out] */ WAVEFORMATEX __RPC_FAR *pWfx, + /* [in] */ DWORD cbSize); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Start )( + IWMAudioReader __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Stop )( + IWMAudioReader __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Seek )( + IWMAudioReader __RPC_FAR * This, + DWORD dwMsTime); + + END_INTERFACE + } IWMAudioReaderVtbl; + + interface IWMAudioReader + { + CONST_VTBL struct IWMAudioReaderVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMAudioReader_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMAudioReader_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMAudioReader_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMAudioReader_GetAttributeByName(This,pszName,pType,pValue,pcbLength) \ + (This)->lpVtbl -> GetAttributeByName(This,pszName,pType,pValue,pcbLength) + +#define IWMAudioReader_GetAttributeCount(This,pcAttributes) \ + (This)->lpVtbl -> GetAttributeCount(This,pcAttributes) + +#define IWMAudioReader_GetAttributeByIndex(This,wIndex,pwszName,pcbNameLen,pType,pValue,pcbLength) \ + (This)->lpVtbl -> GetAttributeByIndex(This,wIndex,pwszName,pcbNameLen,pType,pValue,pcbLength) + +#define IWMAudioReader_GetOutputFormat(This,pWfx,cbSize) \ + (This)->lpVtbl -> GetOutputFormat(This,pWfx,cbSize) + +#define IWMAudioReader_Start(This) \ + (This)->lpVtbl -> Start(This) + +#define IWMAudioReader_Stop(This) \ + (This)->lpVtbl -> Stop(This) + +#define IWMAudioReader_Seek(This,dwMsTime) \ + (This)->lpVtbl -> Seek(This,dwMsTime) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMAudioReader_GetAttributeByName_Proxy( + IWMAudioReader __RPC_FAR * This, + /* [in] */ LPCWSTR pszName, + /* [out] */ WMT_ATTR_DATATYPE __RPC_FAR *pType, + /* [out] */ BYTE __RPC_FAR *pValue, + /* [out][in] */ WORD __RPC_FAR *pcbLength); + + +void __RPC_STUB IWMAudioReader_GetAttributeByName_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMAudioReader_GetAttributeCount_Proxy( + IWMAudioReader __RPC_FAR * This, + /* [out] */ WORD __RPC_FAR *pcAttributes); + + +void __RPC_STUB IWMAudioReader_GetAttributeCount_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMAudioReader_GetAttributeByIndex_Proxy( + IWMAudioReader __RPC_FAR * This, + /* [in] */ WORD wIndex, + /* [out] */ WCHAR __RPC_FAR *pwszName, + /* [out][in] */ WORD __RPC_FAR *pcbNameLen, + /* [out] */ WMT_ATTR_DATATYPE __RPC_FAR *pType, + /* [out] */ BYTE __RPC_FAR *pValue, + /* [out][in] */ WORD __RPC_FAR *pcbLength); + + +void __RPC_STUB IWMAudioReader_GetAttributeByIndex_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMAudioReader_GetOutputFormat_Proxy( + IWMAudioReader __RPC_FAR * This, + /* [out] */ WAVEFORMATEX __RPC_FAR *pWfx, + /* [in] */ DWORD cbSize); + + +void __RPC_STUB IWMAudioReader_GetOutputFormat_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMAudioReader_Start_Proxy( + IWMAudioReader __RPC_FAR * This); + + +void __RPC_STUB IWMAudioReader_Start_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMAudioReader_Stop_Proxy( + IWMAudioReader __RPC_FAR * This); + + +void __RPC_STUB IWMAudioReader_Stop_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMAudioReader_Seek_Proxy( + IWMAudioReader __RPC_FAR * This, + DWORD dwMsTime); + + +void __RPC_STUB IWMAudioReader_Seek_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMAudioReader_INTERFACE_DEFINED__ */ + + +#ifndef __IWMAudioReadCallback_INTERFACE_DEFINED__ +#define __IWMAudioReadCallback_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IWMAudioReadCallback + * at Mon Aug 09 13:10:31 1999 + * using MIDL 3.01.75 + ****************************************/ +/* [local][unique][helpstring][uuid][object] */ + + + +EXTERN_C const IID IID_IWMAudioReadCallback; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface DECLSPEC_UUID("1A5636F3-DB5E-11d2-9D41-0060083178AF") + IWMAudioReadCallback : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE OnSample( + /* [in] */ const BYTE __RPC_FAR *pData, + /* [in] */ DWORD cbData, + /* [in] */ DWORD dwMsTime) = 0; + + virtual HRESULT STDMETHODCALLTYPE OnStatus( + /* [in] */ WMT_STATUS Status, + /* [in] */ HRESULT hr, + /* [in] */ const VARIANT __RPC_FAR *pParam) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMAudioReadCallbackVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IWMAudioReadCallback __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IWMAudioReadCallback __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IWMAudioReadCallback __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnSample )( + IWMAudioReadCallback __RPC_FAR * This, + /* [in] */ const BYTE __RPC_FAR *pData, + /* [in] */ DWORD cbData, + /* [in] */ DWORD dwMsTime); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *OnStatus )( + IWMAudioReadCallback __RPC_FAR * This, + /* [in] */ WMT_STATUS Status, + /* [in] */ HRESULT hr, + /* [in] */ const VARIANT __RPC_FAR *pParam); + + END_INTERFACE + } IWMAudioReadCallbackVtbl; + + interface IWMAudioReadCallback + { + CONST_VTBL struct IWMAudioReadCallbackVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMAudioReadCallback_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMAudioReadCallback_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMAudioReadCallback_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMAudioReadCallback_OnSample(This,pData,cbData,dwMsTime) \ + (This)->lpVtbl -> OnSample(This,pData,cbData,dwMsTime) + +#define IWMAudioReadCallback_OnStatus(This,Status,hr,pParam) \ + (This)->lpVtbl -> OnStatus(This,Status,hr,pParam) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMAudioReadCallback_OnSample_Proxy( + IWMAudioReadCallback __RPC_FAR * This, + /* [in] */ const BYTE __RPC_FAR *pData, + /* [in] */ DWORD cbData, + /* [in] */ DWORD dwMsTime); + + +void __RPC_STUB IWMAudioReadCallback_OnSample_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMAudioReadCallback_OnStatus_Proxy( + IWMAudioReadCallback __RPC_FAR * This, + /* [in] */ WMT_STATUS Status, + /* [in] */ HRESULT hr, + /* [in] */ const VARIANT __RPC_FAR *pParam); + + +void __RPC_STUB IWMAudioReadCallback_OnStatus_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMAudioReadCallback_INTERFACE_DEFINED__ */ + + +#ifndef __IWMAudioInfo_INTERFACE_DEFINED__ +#define __IWMAudioInfo_INTERFACE_DEFINED__ + +/**************************************** + * Generated header for interface: IWMAudioInfo + * at Mon Aug 09 13:10:31 1999 + * using MIDL 3.01.75 + ****************************************/ +/* [local][unique][helpstring][uuid][object] */ + + + +EXTERN_C const IID IID_IWMAudioInfo; + +#if defined(__cplusplus) && !defined(CINTERFACE) + + interface DECLSPEC_UUID("0AA139F0-F6A8-11d2-97F7-00A0C95EA850") + IWMAudioInfo : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE GetAttributeCount( + /* [out] */ WORD __RPC_FAR *pcAttributes) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetAttributeByIndex( + /* [in] */ WORD wIndex, + /* [out] */ WCHAR __RPC_FAR *pwszName, + /* [out][in] */ WORD __RPC_FAR *pcbNameLen, + /* [out] */ WMT_ATTR_DATATYPE __RPC_FAR *pType, + /* [out] */ BYTE __RPC_FAR *pValue, + /* [out][in] */ WORD __RPC_FAR *pcbLength) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetAttributeByName( + /* [in] */ LPCWSTR pszName, + /* [out] */ WMT_ATTR_DATATYPE __RPC_FAR *pType, + /* [out] */ BYTE __RPC_FAR *pValue, + /* [out][in] */ WORD __RPC_FAR *pcbLength) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetAttribute( + /* [in] */ LPCWSTR pszName, + /* [in] */ WMT_ATTR_DATATYPE Type, + /* [in] */ const BYTE __RPC_FAR *pValue, + /* [in] */ WORD cbLength) = 0; + + virtual HRESULT STDMETHODCALLTYPE Close( void) = 0; + + }; + +#else /* C style interface */ + + typedef struct IWMAudioInfoVtbl + { + BEGIN_INTERFACE + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *QueryInterface )( + IWMAudioInfo __RPC_FAR * This, + /* [in] */ REFIID riid, + /* [iid_is][out] */ void __RPC_FAR *__RPC_FAR *ppvObject); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *AddRef )( + IWMAudioInfo __RPC_FAR * This); + + ULONG ( STDMETHODCALLTYPE __RPC_FAR *Release )( + IWMAudioInfo __RPC_FAR * This); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetAttributeCount )( + IWMAudioInfo __RPC_FAR * This, + /* [out] */ WORD __RPC_FAR *pcAttributes); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetAttributeByIndex )( + IWMAudioInfo __RPC_FAR * This, + /* [in] */ WORD wIndex, + /* [out] */ WCHAR __RPC_FAR *pwszName, + /* [out][in] */ WORD __RPC_FAR *pcbNameLen, + /* [out] */ WMT_ATTR_DATATYPE __RPC_FAR *pType, + /* [out] */ BYTE __RPC_FAR *pValue, + /* [out][in] */ WORD __RPC_FAR *pcbLength); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *GetAttributeByName )( + IWMAudioInfo __RPC_FAR * This, + /* [in] */ LPCWSTR pszName, + /* [out] */ WMT_ATTR_DATATYPE __RPC_FAR *pType, + /* [out] */ BYTE __RPC_FAR *pValue, + /* [out][in] */ WORD __RPC_FAR *pcbLength); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *SetAttribute )( + IWMAudioInfo __RPC_FAR * This, + /* [in] */ LPCWSTR pszName, + /* [in] */ WMT_ATTR_DATATYPE Type, + /* [in] */ const BYTE __RPC_FAR *pValue, + /* [in] */ WORD cbLength); + + HRESULT ( STDMETHODCALLTYPE __RPC_FAR *Close )( + IWMAudioInfo __RPC_FAR * This); + + END_INTERFACE + } IWMAudioInfoVtbl; + + interface IWMAudioInfo + { + CONST_VTBL struct IWMAudioInfoVtbl __RPC_FAR *lpVtbl; + }; + + + +#ifdef COBJMACROS + + +#define IWMAudioInfo_QueryInterface(This,riid,ppvObject) \ + (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) + +#define IWMAudioInfo_AddRef(This) \ + (This)->lpVtbl -> AddRef(This) + +#define IWMAudioInfo_Release(This) \ + (This)->lpVtbl -> Release(This) + + +#define IWMAudioInfo_GetAttributeCount(This,pcAttributes) \ + (This)->lpVtbl -> GetAttributeCount(This,pcAttributes) + +#define IWMAudioInfo_GetAttributeByIndex(This,wIndex,pwszName,pcbNameLen,pType,pValue,pcbLength) \ + (This)->lpVtbl -> GetAttributeByIndex(This,wIndex,pwszName,pcbNameLen,pType,pValue,pcbLength) + +#define IWMAudioInfo_GetAttributeByName(This,pszName,pType,pValue,pcbLength) \ + (This)->lpVtbl -> GetAttributeByName(This,pszName,pType,pValue,pcbLength) + +#define IWMAudioInfo_SetAttribute(This,pszName,Type,pValue,cbLength) \ + (This)->lpVtbl -> SetAttribute(This,pszName,Type,pValue,cbLength) + +#define IWMAudioInfo_Close(This) \ + (This)->lpVtbl -> Close(This) + +#endif /* COBJMACROS */ + + +#endif /* C style interface */ + + + +HRESULT STDMETHODCALLTYPE IWMAudioInfo_GetAttributeCount_Proxy( + IWMAudioInfo __RPC_FAR * This, + /* [out] */ WORD __RPC_FAR *pcAttributes); + + +void __RPC_STUB IWMAudioInfo_GetAttributeCount_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMAudioInfo_GetAttributeByIndex_Proxy( + IWMAudioInfo __RPC_FAR * This, + /* [in] */ WORD wIndex, + /* [out] */ WCHAR __RPC_FAR *pwszName, + /* [out][in] */ WORD __RPC_FAR *pcbNameLen, + /* [out] */ WMT_ATTR_DATATYPE __RPC_FAR *pType, + /* [out] */ BYTE __RPC_FAR *pValue, + /* [out][in] */ WORD __RPC_FAR *pcbLength); + + +void __RPC_STUB IWMAudioInfo_GetAttributeByIndex_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMAudioInfo_GetAttributeByName_Proxy( + IWMAudioInfo __RPC_FAR * This, + /* [in] */ LPCWSTR pszName, + /* [out] */ WMT_ATTR_DATATYPE __RPC_FAR *pType, + /* [out] */ BYTE __RPC_FAR *pValue, + /* [out][in] */ WORD __RPC_FAR *pcbLength); + + +void __RPC_STUB IWMAudioInfo_GetAttributeByName_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMAudioInfo_SetAttribute_Proxy( + IWMAudioInfo __RPC_FAR * This, + /* [in] */ LPCWSTR pszName, + /* [in] */ WMT_ATTR_DATATYPE Type, + /* [in] */ const BYTE __RPC_FAR *pValue, + /* [in] */ WORD cbLength); + + +void __RPC_STUB IWMAudioInfo_SetAttribute_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + +HRESULT STDMETHODCALLTYPE IWMAudioInfo_Close_Proxy( + IWMAudioInfo __RPC_FAR * This); + + +void __RPC_STUB IWMAudioInfo_Close_Stub( + IRpcStubBuffer *This, + IRpcChannelBuffer *_pRpcChannelBuffer, + PRPC_MESSAGE _pRpcMessage, + DWORD *_pdwStubPhase); + + + +#endif /* __IWMAudioInfo_INTERFACE_DEFINED__ */ + + +/* Additional Prototypes for ALL interfaces */ + +/* end of Additional Prototypes */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/Src/Plugins/General/gen_crasher/ExceptionHandler.cpp b/Src/Plugins/General/gen_crasher/ExceptionHandler.cpp new file mode 100644 index 00000000..ae1c2399 --- /dev/null +++ b/Src/Plugins/General/gen_crasher/ExceptionHandler.cpp @@ -0,0 +1,814 @@ +// ExceptionHandler.cpp Version 1.4 +// +// Copyright © 1998 Bruce Dawson +// +// This source file contains the exception handler for recording error +// information after crashes. See ExceptionHandler.h for information +// on how to hook it in. +// +// Author: Bruce Dawson +// brucedawson@cygnus-software.com +// +// Modified by: Hans Dietrich +// hdietrich2@hotmail.com +// +// Version 1.4: - Added invocation of XCrashReport.exe +// +// Version 1.3: - Added minidump output +// +// Version 1.1: - reformatted output for XP-like error report +// - added ascii output to stack dump +// +// A paper by the original author can be found at: +// http://www.cygnus-software.com/papers/release_debugging.html +// +/////////////////////////////////////////////////////////////////////////////// + +// Disable warnings generated by the Windows header files. +#pragma warning(disable : 4514) +#pragma warning(disable : 4201) + +#define _WIN32_WINDOWS 0x0500 // for IsDebuggerPresent + +#include "windows.h" +#include +#include "GetWinVer.h" +#include "miniversion.h" +#include "../nu/ns_wc.h" + +#include "minidump.h" +#include ".\settings.h" +#include "api__gen_crasher.h" + +extern char *winampVersion; +extern Settings settings; + +#ifndef _countof +#define _countof(array) (sizeof(array)/sizeof(array[0])) +#endif + +const int NumCodeBytes = 16; // Number of code bytes to record. +const int MaxStackDump = 3072; // Maximum number of DWORDS in stack dumps. +const int StackColumns = 4; // Number of columns in stack dump. + +#define ONEK 1024 +#define SIXTYFOURK (64*ONEK) +#define ONEM (ONEK*ONEK) +#define ONEG (ONEK*ONEK*ONEK) + + +/////////////////////////////////////////////////////////////////////////////// +// lstrrchr (avoid the C Runtime ) +static TCHAR * lstrrchr(LPCTSTR string, int ch) +{ + TCHAR *start = (TCHAR *)string; + + while (string && *string++) /* find end of string */ + ; + /* search towards front */ + while (--string != start && *string != (TCHAR) ch) + ; + + if (*string == (TCHAR) ch) /* char found ? */ + return (TCHAR *)string; + + return NULL; +} + +/////////////////////////////////////////////////////////////////////////////// +// hprintf behaves similarly to printf, with a few vital differences. +// It uses wvsprintf to do the formatting, which is a system routine, +// thus avoiding C run time interactions. For similar reasons it +// uses WriteFile rather than fwrite. +// The one limitation that this imposes is that wvsprintf, and +// therefore hprintf, cannot handle floating point numbers. + +// Too many calls to WriteFile can take a long time, causing +// confusing delays when programs crash. Therefore I implemented +// a simple buffering scheme for hprintf + +#define HPRINTF_BUFFER_SIZE (8*1024) // must be at least 2048 +static wchar_t hprintf_buffer[HPRINTF_BUFFER_SIZE]; // wvsprintf never prints more than one K. +static int hprintf_index = 0; + +/////////////////////////////////////////////////////////////////////////////// +// hflush +static void hflush(HANDLE LogFile) +{ + if (hprintf_index > 0) + { + DWORD NumBytes = 0; + WriteFile(LogFile, hprintf_buffer, lstrlenW(hprintf_buffer)*2, &NumBytes, 0); + hprintf_index = 0; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// hprintf +static void hprintf(HANDLE LogFile, const wchar_t *Format, ...) +{ + if (hprintf_index > (HPRINTF_BUFFER_SIZE-1024)) + { + DWORD NumBytes = 0; + WriteFile(LogFile, hprintf_buffer, lstrlen(hprintf_buffer)*2, &NumBytes, 0); + hprintf_index = 0; + } + + va_list arglist; + va_start( arglist, Format); + hprintf_index += vswprintf(&hprintf_buffer[hprintf_index], Format, arglist); + va_end( arglist); +} + +#include + +/////////////////////////////////////////////////////////////////////////////// +// DumpMiniDump +static BOOL DumpMiniDump(HANDLE hFile, PEXCEPTION_POINTERS excpInfo) +{ + if (excpInfo == NULL) + { + // Generate exception to get proper context in dump + __try + { + //OutputDebugString(_T("raising exception\r\n")); + RaiseException(EXCEPTION_BREAKPOINT, 0, 0, NULL); + } + __except(DumpMiniDump(hFile, GetExceptionInformation()), EXCEPTION_CONTINUE_EXECUTION) + { + } + } + else + { + //OutputDebugString(_T("writing minidump\r\n")); + MINIDUMP_EXCEPTION_INFORMATION eInfo = {0}; + eInfo.ThreadId = GetCurrentThreadId(); + eInfo.ExceptionPointers = excpInfo; + eInfo.ClientPointers = FALSE; + + // try to load dbghelpdll + HMODULE hm = NULL; + // first from app folder + wchar_t szDbgHelpPath[_MAX_PATH] = {0}; + + if (GetModuleFileNameW( NULL, szDbgHelpPath, _MAX_PATH )) + { + wchar_t *pSlash = wcsrchr( szDbgHelpPath, L'\\' ); + if (pSlash) + { + StringCchCopy( pSlash+1, _MAX_PATH, L"dbghelp.dll"); + hm = LoadLibraryW( szDbgHelpPath ); + } + } + if (!hm) + { + // load any version we can + hm = LoadLibraryW(L"dbghelp.dll"); + } + + if (hm) + { + BOOL (WINAPI* MiniDumpWriteDump)( + HANDLE hProcess, + DWORD ProcessId, + HANDLE hFile, + MINIDUMP_TYPE DumpType, + PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, + PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, + PMINIDUMP_CALLBACK_INFORMATION CallbackParam + ) = NULL; + //OutputDebugString(_T("Found dbghelp.dll, searching for MiniDumpWriteDump\r\n")); + *(FARPROC*)&MiniDumpWriteDump = GetProcAddress(hm, "MiniDumpWriteDump"); + if (MiniDumpWriteDump) + { + //OutputDebugString(_T("Calling MiniDumpWriteDump\r\n")); + BOOL ret = MiniDumpWriteDump( + GetCurrentProcess(), + GetCurrentProcessId(), + hFile, + (MINIDUMP_TYPE)settings.dumpType, + excpInfo ? &eInfo : NULL, + NULL, + NULL); + //OutputDebugString(_T("MiniDumpWriteDump finished\r\n")); + if (!ret) + { + DWORD le = GetLastError(); + wchar_t tmp[256] = {0}; + StringCchPrintfW(tmp, 256, L"call failed with error code: %d", le); + //OutputDebugString(tmp); + } + return ret; + } + } + } + return FALSE; +} + +/////////////////////////////////////////////////////////////////////////////// +// FormatTime +// +// Format the specified FILETIME to output in a human readable format, +// without using the C run time. +static void FormatTime(LPTSTR output, FILETIME TimeToPrint) +{ + output[0] = _T('\0'); + WORD Date, Time; + if (FileTimeToLocalFileTime(&TimeToPrint, &TimeToPrint) && + FileTimeToDosDateTime(&TimeToPrint, &Date, &Time)) + { + StringCchPrintf(output, 100, _T("%d/%d/%d %02d:%02d:%02d"), + (Date / 32) & 15, Date & 31, (Date / 512) + 1980, + (Time >> 11), (Time >> 5) & 0x3F, (Time & 0x1F) * 2); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// DumpModuleInfo +// +// Print information about a code module (DLL or EXE) such as its size, +// location, time stamp, etc. +static bool DumpModuleInfo(HANDLE LogFile, HINSTANCE ModuleHandle, int nModuleNo) +{ + bool rc = false; + wchar_t szModName[MAX_PATH*2] = {0}; + __try + { + if (GetModuleFileName(ModuleHandle, szModName, MAX_PATH*2) > 0) + { + // If GetModuleFileName returns greater than zero then this must + // be a valid code module address. Therefore we can try to walk + // our way through its structures to find the link time stamp. + IMAGE_DOS_HEADER *DosHeader = (IMAGE_DOS_HEADER*)ModuleHandle; + if (IMAGE_DOS_SIGNATURE != DosHeader->e_magic) + return false; + + IMAGE_NT_HEADERS *NTHeader = (IMAGE_NT_HEADERS*)((char *)DosHeader + + DosHeader->e_lfanew); + if (IMAGE_NT_SIGNATURE != NTHeader->Signature) + return false; + + // open the code module file so that we can get its file date and size + HANDLE ModuleFile = CreateFile(szModName, GENERIC_READ, + FILE_SHARE_READ, 0, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, 0); + + TCHAR TimeBuffer[100] = {0}; + DWORD FileSize = 0; + if (ModuleFile != INVALID_HANDLE_VALUE) + { + FileSize = GetFileSize(ModuleFile, 0); + FILETIME LastWriteTime; + if (GetFileTime(ModuleFile, 0, 0, &LastWriteTime)) + { + FormatTime(TimeBuffer, LastWriteTime); + } + CloseHandle(ModuleFile); + } + hprintf(LogFile, _T("Module %d\r\n"), nModuleNo); + hprintf(LogFile, _T("%s\r\n"), szModName); + hprintf(LogFile, _T("Image Base: 0x%08x Image Size: 0x%08x\r\n"), + NTHeader->OptionalHeader.ImageBase, + NTHeader->OptionalHeader.SizeOfImage), + + hprintf(LogFile, _T("Checksum: 0x%08x Time Stamp: 0x%08x\r\n"), + NTHeader->OptionalHeader.CheckSum, + NTHeader->FileHeader.TimeDateStamp); + + hprintf(LogFile, _T("File Size: %-10d File Time: %s\r\n"), + FileSize, TimeBuffer); + + hprintf(LogFile, _T("Version Information:\r\n")); + + CMiniVersion ver(szModName); + TCHAR szBuf[200] = {0}; + WORD dwBuf[4] = {0}; + + ver.GetCompanyName(szBuf, _countof(szBuf)-1); + hprintf(LogFile, _T(" Company: %s\r\n"), szBuf); + + ver.GetProductName(szBuf, _countof(szBuf)-1); + hprintf(LogFile, _T(" Product: %s\r\n"), szBuf); + + ver.GetFileDescription(szBuf, _countof(szBuf)-1); + hprintf(LogFile, _T(" FileDesc: %s\r\n"), szBuf); + + ver.GetFileVersion(dwBuf); + hprintf(LogFile, _T(" FileVer: %d.%d.%d.%d\r\n"), + dwBuf[0], dwBuf[1], dwBuf[2], dwBuf[3]); + + ver.GetProductVersion(dwBuf); + hprintf(LogFile, _T(" ProdVer: %d.%d.%d.%d\r\n"), + dwBuf[0], dwBuf[1], dwBuf[2], dwBuf[3]); + + ver.Release(); + + hprintf(LogFile, _T("\r\n")); + + rc = true; + } + } + // Handle any exceptions by continuing from this point. + __except(EXCEPTION_EXECUTE_HANDLER) + { + //OutputDebugString(L"DumpModuleInfo exception"); + } + return rc; +} + +/////////////////////////////////////////////////////////////////////////////// +// DumpModuleList +// +// Scan memory looking for code modules (DLLs or EXEs). VirtualQuery is used +// to find all the blocks of address space that were reserved or committed, +// and ShowModuleInfo will display module information if they are code +// modules. +static void DumpModuleList(HANDLE LogFile) +{ + SYSTEM_INFO SystemInfo; + GetSystemInfo(&SystemInfo); + + //OutputDebugString(L"Dumping modules list"); + const size_t PageSize = SystemInfo.dwPageSize; + + // Set NumPages to the number of pages in the 4GByte address space, + // while being careful to avoid overflowing ints + const size_t NumPages = 4 * size_t(ONEG / PageSize); + size_t pageNum = 0; + void *LastAllocationBase = 0; + + int nModuleNo = 1; + + while (pageNum < NumPages) + { + MEMORY_BASIC_INFORMATION MemInfo; + if (VirtualQuery((void *)(pageNum * PageSize), &MemInfo, sizeof(MemInfo))) + { + if (MemInfo.RegionSize > 0) + { + // Adjust the page number to skip over this block of memory + pageNum += MemInfo.RegionSize / PageSize; + if (MemInfo.State == MEM_COMMIT && MemInfo.AllocationBase > LastAllocationBase) + { + // Look for new blocks of committed memory, and try + // recording their module names - this will fail + // gracefully if they aren't code modules + LastAllocationBase = MemInfo.AllocationBase; + + if (DumpModuleInfo(LogFile, (HINSTANCE)LastAllocationBase, nModuleNo)) + { + nModuleNo++; + } + } + } + else + pageNum += SIXTYFOURK / PageSize; + } + else + pageNum += SIXTYFOURK / PageSize; + + // If VirtualQuery fails we advance by 64K because that is the + // granularity of address space doled out by VirtualAlloc() + } +} + +/////////////////////////////////////////////////////////////////////////////// +// DumpSystemInformation +// +// Record information about the user's system, such as processor type, amount +// of memory, etc. +static void DumpSystemInformation(HANDLE LogFile) +{ + FILETIME CurrentTime; + GetSystemTimeAsFileTime(&CurrentTime); + TCHAR szTimeBuffer[100] = {0}; + FormatTime(szTimeBuffer, CurrentTime); + + hprintf(LogFile, _T("Error occurred at %s.\r\n"), szTimeBuffer); + + TCHAR szModuleName[MAX_PATH*2] = {0}; + if (GetModuleFileName(0, szModuleName, _countof(szModuleName)-2) <= 0) + StringCbCopy(szModuleName, sizeof(szModuleName), _T("Unknown")); + + TCHAR szUserName[200] = {0}; + DWORD UserNameSize = _countof(szUserName)-2; + if (!GetUserName(szUserName, &UserNameSize)) + StringCbCopy(szUserName, sizeof(szUserName), _T("Unknown")); + + hprintf(LogFile, _T("%s, run by %s.\r\n"), szModuleName, szUserName); + + // print out operating system + TCHAR szWinVer[50] = {0}, szMajorMinorBuild[50] = {0}; + int nWinVer = 0; + GetWinVer(szWinVer, &nWinVer, szMajorMinorBuild); + hprintf(LogFile, _T("Operating system: %s (%s).\r\n"), + szWinVer, szMajorMinorBuild); + + SYSTEM_INFO SystemInfo; + GetSystemInfo(&SystemInfo); + hprintf(LogFile, _T("%d processor(s), type %d.\r\n"), + SystemInfo.dwNumberOfProcessors, SystemInfo.dwProcessorType); + + MEMORYSTATUS MemInfo; + MemInfo.dwLength = sizeof(MemInfo); + GlobalMemoryStatus(&MemInfo); + + // Print out info on memory, rounded up. + hprintf(LogFile, _T("%d%% memory in use.\r\n"), MemInfo.dwMemoryLoad); + hprintf(LogFile, _T("%d MBytes physical memory.\r\n"), (MemInfo.dwTotalPhys + + ONEM - 1) / ONEM); + hprintf(LogFile, _T("%d MBytes physical memory free.\r\n"), + (MemInfo.dwAvailPhys + ONEM - 1) / ONEM); + hprintf(LogFile, _T("%d MBytes paging file.\r\n"), (MemInfo.dwTotalPageFile + + ONEM - 1) / ONEM); + hprintf(LogFile, _T("%d MBytes paging file free.\r\n"), + (MemInfo.dwAvailPageFile + ONEM - 1) / ONEM); + hprintf(LogFile, _T("%d MBytes user address space.\r\n"), + (MemInfo.dwTotalVirtual + ONEM - 1) / ONEM); + hprintf(LogFile, _T("%d MBytes user address space free.\r\n"), + (MemInfo.dwAvailVirtual + ONEM - 1) / ONEM); +} + +/////////////////////////////////////////////////////////////////////////////// +// GetExceptionDescription +// +// Translate the exception code into something human readable +static const TCHAR *GetExceptionDescription(DWORD ExceptionCode) +{ + struct ExceptionNames + { + DWORD ExceptionCode; + TCHAR * ExceptionName; + }; + +#if 0 // from winnt.h +#define STATUS_WAIT_0 ((DWORD )0x00000000L) +#define STATUS_ABANDONED_WAIT_0 ((DWORD )0x00000080L) +#define STATUS_USER_APC ((DWORD )0x000000C0L) +#define STATUS_TIMEOUT ((DWORD )0x00000102L) +#define STATUS_PENDING ((DWORD )0x00000103L) +#define STATUS_SEGMENT_NOTIFICATION ((DWORD )0x40000005L) +#define STATUS_GUARD_PAGE_VIOLATION ((DWORD )0x80000001L) +#define STATUS_DATATYPE_MISALIGNMENT ((DWORD )0x80000002L) +#define STATUS_BREAKPOINT ((DWORD )0x80000003L) +#define STATUS_SINGLE_STEP ((DWORD )0x80000004L) +#define STATUS_ACCESS_VIOLATION ((DWORD )0xC0000005L) +#define STATUS_IN_PAGE_ERROR ((DWORD )0xC0000006L) +#define STATUS_INVALID_HANDLE ((DWORD )0xC0000008L) +#define STATUS_NO_MEMORY ((DWORD )0xC0000017L) +#define STATUS_ILLEGAL_INSTRUCTION ((DWORD )0xC000001DL) +#define STATUS_NONCONTINUABLE_EXCEPTION ((DWORD )0xC0000025L) +#define STATUS_INVALID_DISPOSITION ((DWORD )0xC0000026L) +#define STATUS_ARRAY_BOUNDS_EXCEEDED ((DWORD )0xC000008CL) +#define STATUS_FLOAT_DENORMAL_OPERAND ((DWORD )0xC000008DL) +#define STATUS_FLOAT_DIVIDE_BY_ZERO ((DWORD )0xC000008EL) +#define STATUS_FLOAT_INEXACT_RESULT ((DWORD )0xC000008FL) +#define STATUS_FLOAT_INVALID_OPERATION ((DWORD )0xC0000090L) +#define STATUS_FLOAT_OVERFLOW ((DWORD )0xC0000091L) +#define STATUS_FLOAT_STACK_CHECK ((DWORD )0xC0000092L) +#define STATUS_FLOAT_UNDERFLOW ((DWORD )0xC0000093L) +#define STATUS_INTEGER_DIVIDE_BY_ZERO ((DWORD )0xC0000094L) +#define STATUS_INTEGER_OVERFLOW ((DWORD )0xC0000095L) +#define STATUS_PRIVILEGED_INSTRUCTION ((DWORD )0xC0000096L) +#define STATUS_STACK_OVERFLOW ((DWORD )0xC00000FDL) +#define STATUS_CONTROL_C_EXIT ((DWORD )0xC000013AL) +#define STATUS_FLOAT_MULTIPLE_FAULTS ((DWORD )0xC00002B4L) +#define STATUS_FLOAT_MULTIPLE_TRAPS ((DWORD )0xC00002B5L) +#define STATUS_ILLEGAL_VLM_REFERENCE ((DWORD )0xC00002C0L) +#endif + + ExceptionNames ExceptionMap[] = + { + {0x40010005, _T("a Control-C")}, + {0x40010008, _T("a Control-Break")}, + {0x80000002, _T("a Datatype Misalignment")}, + {0x80000003, _T("a Breakpoint")}, + {0xc0000005, _T("an Access Violation")}, + {0xc0000006, _T("an In Page Error")}, + {0xc0000017, _T("a No Memory")}, + {0xc000001d, _T("an Illegal Instruction")}, + {0xc0000025, _T("a Noncontinuable Exception")}, + {0xc0000026, _T("an Invalid Disposition")}, + {0xc000008c, _T("a Array Bounds Exceeded")}, + {0xc000008d, _T("a Float Denormal Operand")}, + {0xc000008e, _T("a Float Divide by Zero")}, + {0xc000008f, _T("a Float Inexact Result")}, + {0xc0000090, _T("a Float Invalid Operation")}, + {0xc0000091, _T("a Float Overflow")}, + {0xc0000092, _T("a Float Stack Check")}, + {0xc0000093, _T("a Float Underflow")}, + {0xc0000094, _T("an Integer Divide by Zero")}, + {0xc0000095, _T("an Integer Overflow")}, + {0xc0000096, _T("a Privileged Instruction")}, + {0xc00000fD, _T("a Stack Overflow")}, + {0xc0000142, _T("a DLL Initialization Failed")}, + {0xe06d7363, _T("a Microsoft C++ Exception")}, + }; + + for (int i = 0; i < _countof(ExceptionMap); i++) + if (ExceptionCode == ExceptionMap[i].ExceptionCode) + return ExceptionMap[i].ExceptionName; + + return _T("an Unknown exception type"); +} + +/////////////////////////////////////////////////////////////////////////////// +// GetFilePart +static TCHAR * GetFilePart(LPCTSTR source) +{ + TCHAR *result = lstrrchr(source, _T('\\')); + if (result) + result++; + else + result = (TCHAR *)source; + return result; +} + +#ifdef _M_IX86 +/////////////////////////////////////////////////////////////////////////////// +// DumpStack +static void DumpStack(HANDLE LogFile, DWORD *pStack) +{ + hprintf(LogFile, _T("\r\n\r\nStack:\r\n")); + + __try + { + // Esp contains the bottom of the stack, or at least the bottom of + // the currently used area. + DWORD* pStackTop; + + __asm + { + // Load the top (highest address) of the stack from the + // thread information block. It will be found there in + // Win9x and Windows NT. + mov eax, fs:[4] + mov pStackTop, eax + } + + if (pStackTop > pStack + MaxStackDump) + pStackTop = pStack + MaxStackDump; + + int Count = 0; + + DWORD* pStackStart = pStack; + + int nDwordsPrinted = 0; + + while (pStack + 1 <= pStackTop) + { + if ((Count % StackColumns) == 0) + { + pStackStart = pStack; + nDwordsPrinted = 0; + hprintf(LogFile, _T("0x%08x: "), pStack); + } + hprintf(LogFile, _T("%08x "), pStack); + if ((++Count % StackColumns) == 0 || pStack + 2 > pStackTop) + { + nDwordsPrinted++; + int n = nDwordsPrinted; + while (n < 4) + { + hprintf(LogFile, _T(" ")); + n++; + } + + for (int i = 0; i < nDwordsPrinted; i++) + { + DWORD dwStack = *pStackStart; + for (int j = 0; j < 4; j++) + { + char c = (char)(dwStack & 0xFF); + if (c < 0x20 || c > 0x7E) + c = '.'; +#ifdef _UNICODE + WCHAR w = (WCHAR)c; + hprintf(LogFile, _T("%c"), w); +#else + hprintf(LogFile, _T("%c"), c); +#endif + dwStack = dwStack >> 8; + } + pStackStart++; + } + + hprintf(LogFile, _T("\r\n")); + } + else + { + // hprintf(LogFile, _T("%08x "), *pStack); + nDwordsPrinted++; + } + pStack++; + } + hprintf(LogFile, _T("\r\n")); + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + hprintf(LogFile, _T("Exception encountered during stack dump.\r\n")); + } +} + + +/////////////////////////////////////////////////////////////////////////////// +// DumpRegisters +static void DumpRegisters(HANDLE LogFile, PCONTEXT Context) +{ + // Print out the register values in an XP error window compatible format. + hprintf(LogFile, _T("\r\n")); + hprintf(LogFile, _T("Context:\r\n")); + hprintf(LogFile, _T("EDI: 0x%08x ESI: 0x%08x EAX: 0x%08x\r\n"), + Context->Edi, Context->Esi, Context->Eax); + hprintf(LogFile, _T("EBX: 0x%08x ECX: 0x%08x EDX: 0x%08x\r\n"), + Context->Ebx, Context->Ecx, Context->Edx); + hprintf(LogFile, _T("EIP: 0x%08x EBP: 0x%08x SegCs: 0x%08x\r\n"), + Context->Eip, Context->Ebp, Context->SegCs); + hprintf(LogFile, _T("EFlags: 0x%08x ESP: 0x%08x SegSs: 0x%08x\r\n"), + Context->EFlags, Context->Esp, Context->SegSs); +} + +#endif + +BOOL CreateLog(PEXCEPTION_POINTERS pExceptPtrs, LPCWSTR lpszMessage) +{ + HANDLE hLogFile = CreateFile(settings.logPath, GENERIC_WRITE, 0, 0, + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH, 0); + + if (hLogFile == INVALID_HANDLE_VALUE) + { + //OutputDebugString(_T("Error creating exception report\r\n")); + return FALSE; + } + + // add BOM + WORD wBOM = 0xFEFF; + DWORD num = 0; + WriteFile(hLogFile, &wBOM, sizeof(WORD), &num, NULL); + // Append to the error log + SetFilePointer(hLogFile, 0, 0, FILE_END); + + wchar_t line[1024] = {0}; + wchar_t msgBody[4*1024] = {0}; + wchar_t winampVersionWide[1024] = {0}; + MultiByteToWideCharSZ(CP_ACP, 0, winampVersion, -1, winampVersionWide, 1024); + StringCchPrintf(line, 1024, L"Winamp client version: %s\r\n", winampVersionWide); + StringCchCopy(msgBody, 4*1024, line); + + PEXCEPTION_RECORD Exception = pExceptPtrs->ExceptionRecord; + PCONTEXT Context = pExceptPtrs->ContextRecord; + + TCHAR szCrashModulePathName[MAX_PATH*2] = {0}; + + TCHAR *pszCrashModuleFileName = _T("Unknown"); + + #ifdef _M_IX86 + MEMORY_BASIC_INFORMATION MemInfo; + + // VirtualQuery can be used to get the allocation base associated with a + // code address, which is the same as the ModuleHandle. This can be used + // to get the filename of the module that the crash happened in. + + if (VirtualQuery((void*)Context->Eip, &MemInfo, sizeof(MemInfo)) && + (GetModuleFileName((HINSTANCE)MemInfo.AllocationBase, + szCrashModulePathName, + sizeof(szCrashModulePathName)-2) > 0)) + { + //OutputDebugString(szCrashModulePathName); + pszCrashModuleFileName = GetFilePart(szCrashModulePathName); + } + #endif + + // Print out the beginning of the error log in a Win95 error window + // compatible format. + TCHAR szModuleName[MAX_PATH*2] = {0}; + if (GetModuleFileName(0, szModuleName, _countof(szModuleName)-2) <= 0) + StringCbCopy(szModuleName, sizeof(szModuleName), _T("Unknown")); + + TCHAR *pszFilePart = GetFilePart(szModuleName); + + // Extract the file name portion and remove it's file extension + TCHAR szFileName[MAX_PATH*2] = {0}; + StringCbCopy(szFileName, sizeof(szFileName), pszFilePart); + TCHAR *lastperiod = lstrrchr(szFileName, _T('.')); + if (lastperiod) + lastperiod[0] = 0; + + #ifdef _M_IX86 + StringCchPrintf(line, 1024, L"%s caused %s (0x%08x) \r\nin module %s at %04x:%08x.\r\n\r\n", + szFileName, GetExceptionDescription(Exception->ExceptionCode), + Exception->ExceptionCode, + pszCrashModuleFileName, Context->SegCs, Context->Eip); + #endif + StringCchCat(msgBody, 4*1024, line); + + StringCchPrintf(line, 1024, L"Exception handler called in %s.\r\n", lpszMessage); + StringCchCat(msgBody, 4*1024, line); + + hprintf(hLogFile, L"%s", msgBody); + wchar_t *p = msgBody, *end = msgBody + wcslen(msgBody); + while(p != end) + { + if (*p == L'\r') *p = 1; + if (*p == L'\n') *p = 2; + p++; + + } + settings.WriteBody(msgBody); + + if (settings.logSystem) + { + DumpSystemInformation(hLogFile); + + // If the exception was an access violation, print out some additional + // information, to the error log and the debugger. + if (Exception->ExceptionCode == STATUS_ACCESS_VIOLATION && + Exception->NumberParameters >= 2) + { + TCHAR szDebugMessage[1000] = {0}; + const TCHAR* readwrite = _T("Read from"); + if (Exception->ExceptionInformation[0]) + readwrite = _T("Write to"); + StringCchPrintf(szDebugMessage, 1000, _T("%s location %08x caused an access violation.\r\n"), + readwrite, Exception->ExceptionInformation[1]); + hprintf(hLogFile, _T("%s"), szDebugMessage); + } + } + if (settings.logRegistry) + { + #ifdef _M_IX86 + DumpRegisters(hLogFile, Context); + #endif + + // Print out the bytes of code at the instruction pointer. Since the + // crash may have been caused by an instruction pointer that was bad, + // this code needs to be wrapped in an exception handler, in case there + // is no memory to read. If the dereferencing of code[] fails, the + // exception handler will print '??'. + #ifdef _M_IX86 + hprintf(hLogFile, _T("\r\nBytes at CS:EIP:\r\n")); + BYTE * code = (BYTE *)Context->Eip; + for (int codebyte = 0; codebyte < NumCodeBytes; codebyte++) + { + __try + { + hprintf(hLogFile, _T("%02x "), code[codebyte]); + + } + __except(EXCEPTION_EXECUTE_HANDLER) + { + hprintf(hLogFile, _T("?? ")); + } + } + #endif + } + if (settings.logStack) + { + // Time to print part or all of the stack to the error log. This allows + // us to figure out the call stack, parameters, local variables, etc. + + // Esp contains the bottom of the stack, or at least the bottom of + // the currently used area + + #ifdef _M_IX86 + DWORD* pStack = (DWORD *)Context->Esp; + DumpStack(hLogFile, pStack); + #endif + } + if (settings.logModule) + { + DumpModuleList(hLogFile); + } + + hprintf(hLogFile, _T("\r\n===== [end of log file] =====\r\n")); + hflush(hLogFile); + CloseHandle(hLogFile); + return TRUE; +} + +BOOL CreateDump(PEXCEPTION_POINTERS pExceptPtrs) +{ + BOOL retCode = FALSE; + + // Create the file + //OutputDebugString(_T("CreateFile: ")); + //OutputDebugString(settings.dumpPath); + HANDLE hMiniDumpFile = CreateFile( + settings.dumpPath, + GENERIC_WRITE, + 0, + NULL, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH, + NULL); + + // Write the minidump to the file + if (hMiniDumpFile != INVALID_HANDLE_VALUE) + { + retCode = DumpMiniDump(hMiniDumpFile, pExceptPtrs); + // Close file + CloseHandle(hMiniDumpFile); + if (!retCode) DeleteFile(settings.dumpPath); + } + return retCode; +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_crasher/ExceptionHandler.h b/Src/Plugins/General/gen_crasher/ExceptionHandler.h new file mode 100644 index 00000000..5b4a2ce2 --- /dev/null +++ b/Src/Plugins/General/gen_crasher/ExceptionHandler.h @@ -0,0 +1,33 @@ +// ExceptionHandler.h Version 1.1 +// +// Copyright © 1998 Bruce Dawson +// +// Author: Bruce Dawson +// brucedawson@cygnus-software.com +// +// Modified by: Hans Dietrich +// hdietrich2@hotmail.com +// +// A paper by the original author can be found at: +// http://www.cygnus-software.com/papers/release_debugging.html +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef EXCEPTIONHANDLER_H +#define EXCEPTIONHANDLER_H + +BOOL CreateLog(PEXCEPTION_POINTERS pExceptPtrs, LPCWSTR lpszMessage); +BOOL CreateDump(PEXCEPTION_POINTERS pExceptPtrs); + +// We forward declare PEXCEPTION_POINTERS so that the function +// prototype doesn't needlessly require windows.h. +#ifdef __cplusplus +extern "C" +{ +#endif + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/Src/Plugins/General/gen_crasher/GetWinVer.cpp b/Src/Plugins/General/gen_crasher/GetWinVer.cpp new file mode 100644 index 00000000..7434d73e --- /dev/null +++ b/Src/Plugins/General/gen_crasher/GetWinVer.cpp @@ -0,0 +1,195 @@ +// GetWinVer.cpp Version 1.1 +// +// Copyright (C) 2001-2003 Hans Dietrich +// +// This software is released into the public domain. +// You are free to use it in any way you like, except +// that you may not sell this source code. +// +// This software is provided "as is" with no expressed +// or implied warranty. I accept no liability for any +// damage or loss of business that this software may cause. +// +/////////////////////////////////////////////////////////////////////////////// + +//#include "tchar.h" +#include "GetWinVer.h" + +// from winbase.h +#ifndef VER_PLATFORM_WIN32s +#define VER_PLATFORM_WIN32s 0 +#endif +#ifndef VER_PLATFORM_WIN32_WINDOWS +#define VER_PLATFORM_WIN32_WINDOWS 1 +#endif +#ifndef VER_PLATFORM_WIN32_NT +#define VER_PLATFORM_WIN32_NT 2 +#endif +#ifndef VER_PLATFORM_WIN32_CE +#define VER_PLATFORM_WIN32_CE 3 +#endif + +/* + This table has been assembled from Usenet postings, personal + observations, and reading other people's code. Please feel + free to add to it or correct it. + + + dwPlatFormID dwMajorVersion dwMinorVersion dwBuildNumber +95 1 4 0 950 +95 SP1 1 4 0 >950 && <=1080 +95 OSR2 1 4 <10 >1080 +98 1 4 10 1998 +98 SP1 1 4 10 >1998 && <2183 +98 SE 1 4 10 >=2183 +ME 1 4 90 3000 + +NT 3.51 2 3 51 +NT 4 2 4 0 1381 +2000 2 5 0 2195 +XP 2 5 1 2600 +2003 Server 2 5 2 3790 +VISTA 2 6 0 6000 +7 2 6 1 7600 +8 2 6 2 9200 +8.1 2 6 3 9600 +10 2 10 0 10240 +11 2 11 0 22000 + +CE 3 + +*/ + + +/////////////////////////////////////////////////////////////////////////////// +// GetWinVer +BOOL GetWinVer(LPWSTR pszVersion, int *nVersion, LPWSTR pszMajorMinorBuild) +{ + if (!pszVersion || !nVersion || !pszMajorMinorBuild) + return FALSE; + lstrcpy(pszVersion, WUNKNOWNSTR); + *nVersion = WUNKNOWN; + + DWORD (WINAPI *RtlGetVersion)(LPOSVERSIONINFOEXW); + OSVERSIONINFOEXW osinfo; + *(FARPROC*)&RtlGetVersion = GetProcAddress(GetModuleHandleW(L"ntdll"), "RtlGetVersion"); + if (!RtlGetVersion) { + return FALSE; + } + osinfo.dwOSVersionInfoSize = sizeof(osinfo); + if (RtlGetVersion(&osinfo)) { + return FALSE; + } + + DWORD dwPlatformId = osinfo.dwPlatformId; + DWORD dwMajorVersion = osinfo.dwMajorVersion; + DWORD dwMinorVersion = osinfo.dwMinorVersion; + DWORD dwBuildNumber = osinfo.dwBuildNumber & 0xFFFF; // Win 95 needs this + + wsprintfW(pszMajorMinorBuild, L"%u.%u.%u", dwMajorVersion, dwMinorVersion, dwBuildNumber); + + if ((dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) && (dwMajorVersion == 4)) + { + if ((dwMinorVersion < 10) && (dwBuildNumber == 950)) + { + lstrcpy(pszVersion, W95STR); + *nVersion = W95; + } + else if ((dwMinorVersion < 10) && + ((dwBuildNumber > 950) && (dwBuildNumber <= 1080))) + { + lstrcpy(pszVersion, W95SP1STR); + *nVersion = W95SP1; + } + else if ((dwMinorVersion < 10) && (dwBuildNumber > 1080)) + { + lstrcpy(pszVersion, W95OSR2STR); + *nVersion = W95OSR2; + } + else if ((dwMinorVersion == 10) && (dwBuildNumber == 1998)) + { + lstrcpy(pszVersion, W98STR); + *nVersion = W98; + } + else if ((dwMinorVersion == 10) && + ((dwBuildNumber > 1998) && (dwBuildNumber < 2183))) + { + lstrcpy(pszVersion, W98SP1STR); + *nVersion = W98SP1; + } + else if ((dwMinorVersion == 10) && (dwBuildNumber >= 2183)) + { + lstrcpy(pszVersion, W98SESTR); + *nVersion = W98SE; + } + else if (dwMinorVersion == 90) + { + lstrcpy(pszVersion, WMESTR); + *nVersion = WME; + } + } + else if (dwPlatformId == VER_PLATFORM_WIN32_NT) + { + if ((dwMajorVersion == 3) && (dwMinorVersion == 51)) + { + lstrcpy(pszVersion, WNT351STR); + *nVersion = WNT351; + } + else if ((dwMajorVersion == 4) && (dwMinorVersion == 0)) + { + lstrcpy(pszVersion, WNT4STR); + *nVersion = WNT4; + } + else if ((dwMajorVersion == 5) && (dwMinorVersion == 0)) + { + lstrcpy(pszVersion, W2KSTR); + *nVersion = W2K; + } + else if ((dwMajorVersion == 5) && (dwMinorVersion == 1)) + { + lstrcpy(pszVersion, WXPSTR); + *nVersion = WXP; + } + else if ((dwMajorVersion == 5) && (dwMinorVersion == 2)) + { + lstrcpy(pszVersion, W2003SERVERSTR); + *nVersion = W2003SERVER; + } + else if ((dwMajorVersion == 6) && (dwMinorVersion == 0)) + { + lstrcpy(pszVersion, WVSTR); + *nVersion = WV; + } + else if ((dwMajorVersion == 6) && (dwMinorVersion == 1)) + { + lstrcpy(pszVersion, W7STR); + *nVersion = W7; + } + else if ((dwMajorVersion == 6) && (dwMinorVersion == 2)) + { + lstrcpy(pszVersion, W8STR); + *nVersion = W8; + } + else if ((dwMajorVersion == 6) && (dwMinorVersion == 3)) + { + lstrcpy(pszVersion, W81STR); + *nVersion = W81; + } + else if ((dwMajorVersion == 10) && (dwMinorVersion == 0) && (dwBuildNumber < 22000)) + { + lstrcpy(pszVersion, W10STR); + *nVersion = W10; + } + else if ((dwMajorVersion == 10) && (dwMinorVersion == 0) && (dwBuildNumber >= 22000)) + { + lstrcpy(pszVersion, W11STR); + *nVersion = W11; + } + } + else if (dwPlatformId == VER_PLATFORM_WIN32_CE) + { + lstrcpy(pszVersion, WCESTR); + *nVersion = WCE; + } + return TRUE; +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_crasher/GetWinVer.h b/Src/Plugins/General/gen_crasher/GetWinVer.h new file mode 100644 index 00000000..120a7653 --- /dev/null +++ b/Src/Plugins/General/gen_crasher/GetWinVer.h @@ -0,0 +1,76 @@ +// GetWinVer.h Version 1.1 +// +// Copyright (C) 2001-2003 Hans Dietrich +// +// This software is released into the public domain. +// You are free to use it in any way you like, except +// that you may not sell this source code. +// +// This software is provided "as is" with no expressed +// or implied warranty. I accept no liability for any +// damage or loss of business that this software may cause. +// +/////////////////////////////////////////////////////////////////////////////// +#include + +#ifndef GETWINVER_H +#define GETWINVER_H + +#define WUNKNOWNSTR L"Windows [Unknown version]" + +#define W95STR L"Windows 95" +#define W95SP1STR L"Windows 95 SP1" +#define W95OSR2STR L"Windows 95 OSR2" +#define W98STR L"Windows 98" +#define W98SP1STR L"Windows 98 SP1" +#define W98SESTR L"Windows 98 SE" +#define WMESTR L"Windows ME" + +#define WNT351STR L"Windows NT 3.51" +#define WNT4STR L"Windows NT 4" +#define W2KSTR L"Windows 2000" +#define WXPSTR L"Windows XP" +#define W2003SERVERSTR L"Windows 2003 Server" +#define WVSTR L"Windows Vista" +#define W7STR L"Windows 7" +#define W8STR L"Windows 8" +#define W81STR L"Windows 8.1" +#define W10STR L"Windows 10" +#define W11STR L"Windows 11" + +#define WCESTR L"Windows CE" + + +#define WUNKNOWN 0 +#define W9XFIRST 1 +#define W95 1 +#define W95SP1 2 +#define W95OSR2 3 +#define W98 4 +#define W98SP1 5 +#define W98SE 6 +#define WME 7 +#define W9XLAST 99 + +#define WNTFIRST 101 +#define WNT351 101 +#define WNT4 102 +#define W2K 103 +#define WXP 104 +#define W2003SERVER 105 +#define WV 106 +#define W7 107 +#define W8 108 +#define W81 109 +#define W10 110 +#define W11 111 + +#define WNTLAST 199 + +#define WCEFIRST 201 +#define WCE 201 +#define WCELAST 299 + +BOOL GetWinVer(LPWSTR pszVersion, int *nVersion, LPWSTR pszMajorMinorBuild); + +#endif //GETWINVER_H diff --git a/Src/Plugins/General/gen_crasher/MiniVersion.cpp b/Src/Plugins/General/gen_crasher/MiniVersion.cpp new file mode 100644 index 00000000..ece52f26 --- /dev/null +++ b/Src/Plugins/General/gen_crasher/MiniVersion.cpp @@ -0,0 +1,267 @@ +// MiniVersion.cpp Version 1.1 +// +// Author: Hans Dietrich +// hdietrich2@hotmail.com +// +// This software is released into the public domain. +// You are free to use it in any way you like, except +// that you may not sell this source code. +// +// This software is provided "as is" with no expressed +// or implied warranty. I accept no liability for any +// damage or loss of business that this software may cause. +// +/////////////////////////////////////////////////////////////////////////////// + +#include "MiniVersion.h" +#include + +/////////////////////////////////////////////////////////////////////////////// +// ctor +CMiniVersion::CMiniVersion(LPCTSTR lpszPath) +{ + ZeroMemory(m_szPath, sizeof(m_szPath)); + + if (lpszPath && lpszPath[0] != 0) + { + lstrcpyn(m_szPath, lpszPath, sizeof(m_szPath)-1); + } + else + { + } + + m_pData = NULL; + m_dwHandle = 0; + + for (int i = 0; i < 4; i++) + { + m_wFileVersion[i] = 0; + m_wProductVersion[i] = 0; + } + + m_dwFileFlags = 0; + m_dwFileOS = 0; + m_dwFileType = 0; + m_dwFileSubtype = 0; + + ZeroMemory(m_szCompanyName, sizeof(m_szCompanyName)); + ZeroMemory(m_szProductName, sizeof(m_szProductName)); + ZeroMemory(m_szFileDescription, sizeof(m_szFileDescription)); + + Init(); +} + +/////////////////////////////////////////////////////////////////////////////// +// Init +BOOL CMiniVersion::Init() +{ + DWORD dwHandle; + DWORD dwSize; + BOOL rc; + + dwSize = ::GetFileVersionInfoSize((LPCTSTR)m_szPath, &dwHandle); + if (dwSize == 0) + return FALSE; + + m_pData = new BYTE [dwSize + 1]; + ZeroMemory(m_pData, dwSize+1); + + rc = ::GetFileVersionInfo((LPCTSTR)m_szPath, dwHandle, dwSize, m_pData); + if (!rc) + return FALSE; + + // get fixed info + + VS_FIXEDFILEINFO FixedInfo; + + if (GetFixedInfo(FixedInfo)) + { + m_wFileVersion[0] = HIWORD(FixedInfo.dwFileVersionMS); + m_wFileVersion[1] = LOWORD(FixedInfo.dwFileVersionMS); + m_wFileVersion[2] = HIWORD(FixedInfo.dwFileVersionLS); + m_wFileVersion[3] = LOWORD(FixedInfo.dwFileVersionLS); + + m_wProductVersion[0] = HIWORD(FixedInfo.dwProductVersionMS); + m_wProductVersion[1] = LOWORD(FixedInfo.dwProductVersionMS); + m_wProductVersion[2] = HIWORD(FixedInfo.dwProductVersionLS); + m_wProductVersion[3] = LOWORD(FixedInfo.dwProductVersionLS); + + m_dwFileFlags = FixedInfo.dwFileFlags; + m_dwFileOS = FixedInfo.dwFileOS; + m_dwFileType = FixedInfo.dwFileType; + m_dwFileSubtype = FixedInfo.dwFileSubtype; + } + else + return FALSE; + + // get string info + + GetStringInfo(_T("CompanyName"), m_szCompanyName, MAX_PATH*2); + GetStringInfo(_T("FileDescription"), m_szFileDescription, MAX_PATH*2); + GetStringInfo(_T("ProductName"), m_szProductName, MAX_PATH*2); + + return TRUE; +} + +/////////////////////////////////////////////////////////////////////////////// +// Release +void CMiniVersion::Release() +{ + // do this manually, because we can't use objects requiring + // a dtor within an exception handler + if (m_pData) + delete [] m_pData; + m_pData = NULL; +} + +/////////////////////////////////////////////////////////////////////////////// +// GetFileVersion +BOOL CMiniVersion::GetFileVersion(WORD * pwVersion) +{ + for (int i = 0; i < 4; i++) + *pwVersion++ = m_wFileVersion[i]; + return TRUE; +} + +/////////////////////////////////////////////////////////////////////////////// +// GetProductVersion +BOOL CMiniVersion::GetProductVersion(WORD * pwVersion) +{ + for (int i = 0; i < 4; i++) + *pwVersion++ = m_wProductVersion[i]; + return TRUE; +} + +/////////////////////////////////////////////////////////////////////////////// +// GetFileFlags +BOOL CMiniVersion::GetFileFlags(DWORD& rdwFlags) +{ + rdwFlags = m_dwFileFlags; + return TRUE; +} + +/////////////////////////////////////////////////////////////////////////////// +// GetFileOS +BOOL CMiniVersion::GetFileOS(DWORD& rdwOS) +{ + rdwOS = m_dwFileOS; + return TRUE; +} + +/////////////////////////////////////////////////////////////////////////////// +// GetFileType +BOOL CMiniVersion::GetFileType(DWORD& rdwType) +{ + rdwType = m_dwFileType; + return TRUE; +} + +/////////////////////////////////////////////////////////////////////////////// +// GetFileSubtype +BOOL CMiniVersion::GetFileSubtype(DWORD& rdwType) +{ + rdwType = m_dwFileSubtype; + return TRUE; +} + +/////////////////////////////////////////////////////////////////////////////// +// GetCompanyName +BOOL CMiniVersion::GetCompanyName(LPTSTR lpszCompanyName, int nSize) +{ + if (!lpszCompanyName) + return FALSE; + ZeroMemory(lpszCompanyName, nSize); + lstrcpyn(lpszCompanyName, m_szCompanyName, nSize-1); + return TRUE; +} + +/////////////////////////////////////////////////////////////////////////////// +// GetFileDescription +BOOL CMiniVersion::GetFileDescription(LPTSTR lpszFileDescription, int nSize) +{ + if (!lpszFileDescription) + return FALSE; + ZeroMemory(lpszFileDescription, nSize); + lstrcpyn(lpszFileDescription, m_szFileDescription, nSize-1); + return TRUE; +} + +/////////////////////////////////////////////////////////////////////////////// +// GetProductName +BOOL CMiniVersion::GetProductName(LPTSTR lpszProductName, int nSize) +{ + if (!lpszProductName) + return FALSE; + ZeroMemory(lpszProductName, nSize); + lstrcpyn(lpszProductName, m_szProductName, nSize-1); + return TRUE; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +// +// protected methods +// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + + +/////////////////////////////////////////////////////////////////////////////// +// GetFixedInfo +BOOL CMiniVersion::GetFixedInfo(VS_FIXEDFILEINFO& rFixedInfo) +{ + BOOL rc; + UINT nLength; + VS_FIXEDFILEINFO *pFixedInfo = NULL; + + if (!m_pData) + return FALSE; + + if (m_pData) + rc = ::VerQueryValue(m_pData, _T("\\"), (void **) &pFixedInfo, &nLength); + else + rc = FALSE; + + if (rc) + memcpy (&rFixedInfo, pFixedInfo, sizeof (VS_FIXEDFILEINFO)); + + return rc; +} + +/////////////////////////////////////////////////////////////////////////////// +// GetStringInfo +BOOL CMiniVersion::GetStringInfo(LPCTSTR lpszKey, LPTSTR lpszReturnValue, unsigned int cchBuffer) +{ + BOOL rc; + DWORD *pdwTranslation; + UINT nLength; + LPTSTR lpszValue; + + if (m_pData == NULL) + return FALSE; + + if (!lpszReturnValue) + return FALSE; + + if (!lpszKey) + return FALSE; + + *lpszReturnValue = 0; + + rc = ::VerQueryValue(m_pData, _T("\\VarFileInfo\\Translation"), + (void**) &pdwTranslation, &nLength); + if (!rc) + return FALSE; + + TCHAR szKey[2000] = {0}; + StringCchPrintf(szKey, 2000, _T("\\StringFileInfo\\%04x%04x\\%s"), LOWORD (*pdwTranslation), HIWORD (*pdwTranslation), lpszKey); + + rc = ::VerQueryValue(m_pData, szKey, (void**) &lpszValue, &nLength); + + if (!rc) + return FALSE; + + StringCchCopy(lpszReturnValue,cchBuffer, lpszValue); + + return TRUE; +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_crasher/MiniVersion.h b/Src/Plugins/General/gen_crasher/MiniVersion.h new file mode 100644 index 00000000..4a44d97e --- /dev/null +++ b/Src/Plugins/General/gen_crasher/MiniVersion.h @@ -0,0 +1,69 @@ +// MiniVersion.h Version 1.1 +// +// Author: Hans Dietrich +// hdietrich2@hotmail.com +// +// This software is released into the public domain. +// You are free to use it in any way you like, except +// that you may not sell this source code. +// +// This software is provided "as is" with no expressed +// or implied warranty. I accept no liability for any +// damage or loss of business that this software may cause. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef MINIVERSION_H +#define MINIVERSION_H + +#include +#include + +class CMiniVersion +{ +// constructors +public: + CMiniVersion(LPCTSTR lpszPath = NULL); + BOOL Init(); + void Release(); + +// operations +public: + +// attributes +public: + // fixed info + BOOL GetFileVersion(WORD *pwVersion); + BOOL GetProductVersion(WORD* pwVersion); + BOOL GetFileFlags(DWORD& rdwFlags); + BOOL GetFileOS(DWORD& rdwOS); + BOOL GetFileType(DWORD& rdwType); + BOOL GetFileSubtype(DWORD& rdwType); + + // string info + BOOL GetCompanyName(LPTSTR lpszCompanyName, int nSize); + BOOL GetFileDescription(LPTSTR lpszFileDescription, int nSize); + BOOL GetProductName(LPTSTR lpszProductName, int nSize); + +// implementation +protected: + BOOL GetFixedInfo(VS_FIXEDFILEINFO& rFixedInfo); + BOOL GetStringInfo(LPCTSTR lpszKey, LPTSTR lpszValue, unsigned int cchBuffer); + + BYTE* m_pData; + DWORD m_dwHandle; + WORD m_wFileVersion[4]; + WORD m_wProductVersion[4]; + DWORD m_dwFileFlags; + DWORD m_dwFileOS; + DWORD m_dwFileType; + DWORD m_dwFileSubtype; + + TCHAR m_szPath[MAX_PATH*2]; + TCHAR m_szCompanyName[MAX_PATH*2]; + TCHAR m_szProductName[MAX_PATH*2]; + TCHAR m_szFileDescription[MAX_PATH*2]; +}; + +/////////////////////////////////////////////////////////////////////////////// +#endif \ No newline at end of file diff --git a/Src/Plugins/General/gen_crasher/ReadMe.txt b/Src/Plugins/General/gen_crasher/ReadMe.txt new file mode 100644 index 00000000..69bf0a16 --- /dev/null +++ b/Src/Plugins/General/gen_crasher/ReadMe.txt @@ -0,0 +1,3 @@ +gen_crasher ver 1.0b + + diff --git a/Src/Plugins/General/gen_crasher/api__gen_crasher.h b/Src/Plugins/General/gen_crasher/api__gen_crasher.h new file mode 100644 index 00000000..50e0e6fc --- /dev/null +++ b/Src/Plugins/General/gen_crasher/api__gen_crasher.h @@ -0,0 +1,20 @@ +#ifndef NULLSOFT_APIH +#define NULLSOFT_APIH + +#include +extern api_service *serviceManager; +#define WASABI_API_SVC serviceManager + + +#include +#define WASABI_API_APP applicationApi + + +#include +#define WASABI_API_SYSCB sysCallbackApi + +#include + +#include "../Agave/Language/api_language.h" + +#endif \ No newline at end of file diff --git a/Src/Plugins/General/gen_crasher/config.cpp b/Src/Plugins/General/gen_crasher/config.cpp new file mode 100644 index 00000000..9196bdd1 --- /dev/null +++ b/Src/Plugins/General/gen_crasher/config.cpp @@ -0,0 +1,308 @@ +#include +#include "..\..\..\nu\ns_wc.h" +#include "config.h" +#include + +/////////////////// +/// +/// UNICODE VERSION +/// +///// +ConfigW::ConfigW() +{ + emptyBOM = FALSE; + buff[0] = 0; + buffA = NULL; + fileName = NULL; + defSection = NULL; +} + +ConfigW::ConfigW(const wchar_t *ini, const wchar_t *section) +{ + emptyBOM = FALSE; + buff[0] = 0; + buffA = NULL; + fileName = NULL; + defSection = NULL; + if (SetIniFile(ini)) SetSection(section); +} + +ConfigW::~ConfigW() +{ + if (fileName) free(fileName); + if (defSection) free(defSection); + if (buffA) free(buffA); + if (emptyBOM) RemoveEmptyFile(); +} + +void ConfigW::Flush(void) +{ + return; +} + +BOOL ConfigW::Write(const wchar_t *section, const wchar_t *name, const wchar_t *value) +{ + return (fileName) ? WritePrivateProfileString(section, name, value, fileName) : FALSE; +} + +BOOL ConfigW::Write(const wchar_t *section, const wchar_t *name, double value) +{ + wchar_t tmp[48] = {0}; + if (S_OK != StringCchPrintf(tmp, 48, L"%g", value)) return FALSE; + return Write(section, name, tmp); +} + +BOOL ConfigW::Write(const wchar_t *section, const wchar_t *name, long long value) +{ + wchar_t tmp[32] = {0}; + if (S_OK != StringCchPrintf(tmp, 32, L"%I64d", value)) return FALSE; + return Write(section,name, tmp); +} + +BOOL ConfigW::Write(const wchar_t *section, const wchar_t *name, int value) +{ + wchar_t tmp[16] = {0}; + if (S_OK != StringCchPrintf(tmp, 16, L"%d", value)) return FALSE; + return Write(section,name, tmp); +} + +BOOL ConfigW::Write(const wchar_t *section, const wchar_t *name, const char *value) +{ + int len = (int)strlen(value) + 1; + wchar_t *tmp = (wchar_t*)malloc(len*sizeof(wchar_t)); + if (!tmp) return FALSE; + + BOOL ret = FALSE; + if (MultiByteToWideCharSZ(CP_ACP, 0, value, -1, tmp, len)) + { + ret = Write(section,name, tmp); + } + free(tmp); + return ret; +} + +BOOL ConfigW::Write(const wchar_t *name, int value) +{ + if(!defSection) return FALSE; + return Write(defSection, name, value); +} + +BOOL ConfigW::Write(const wchar_t *name, long long value) +{ + if(!defSection) return FALSE; + return Write(defSection, name, value); +} + +BOOL ConfigW::Write(const wchar_t *name, double value) +{ + if(!defSection) return FALSE; + return Write(defSection, name, value); +} + +BOOL ConfigW::Write(const wchar_t *name, const wchar_t *value) +{ + if(!defSection) return FALSE; + return Write(defSection, name, value); +} + +BOOL ConfigW::Write(const wchar_t *name, const char value) +{ + if(!defSection) return FALSE; + return Write(defSection, name, value); +} + +int ConfigW::ReadInt(const wchar_t *section, const wchar_t *name, int defvalue) +{ + if (!fileName) return defvalue; + return GetPrivateProfileInt(section, name, defvalue, fileName); +} + +long long ConfigW::ReadInt64(const wchar_t *section, const wchar_t *name, long long defvalue) +{ + if (!fileName) return defvalue; + wchar_t tmp[32] = {0}; + if (S_OK != StringCchPrintf(tmp, 32, L"%I64d", defvalue)) return defvalue; + const wchar_t *ret = ReadStringW(section, name, tmp); + return _wtoi64(ret); +} + +double ConfigW::ReadDouble(const wchar_t *section, const wchar_t *name, double defvalue) +{ + if (!fileName) return defvalue; + wchar_t tmp[32] = {0}; + if (S_OK != StringCchPrintf(tmp, 32, L"%g", defvalue)) return defvalue; + const wchar_t *ret = ReadStringW(section, name, tmp); + return _wtof(ret); +} + +const char* ConfigW::ReadStringA(const wchar_t *section, const wchar_t *name, const char *defvalue) +{ + if (buffA) free(buffA); + if (!fileName) return defvalue; + + int len = (int)strlen(defvalue) + 1; + wchar_t *tmp = (wchar_t*)malloc(len *sizeof(wchar_t)); + if (!tmp) return defvalue; + + if (!MultiByteToWideCharSZ(CP_ACP, 0, defvalue, -1, tmp, len)) + { + free(tmp); + return defvalue; + } + const wchar_t *ret = ReadStringW(section, name, tmp); + if (!ret || lstrcmp(ret, tmp) == 0) + { + free(tmp); + return defvalue; + } + free(tmp); + + len = (int)lstrlen(ret) + 1; + + buffA = (char*)malloc(len*sizeof(char)); + if (!buffA) return defvalue; + if (WideCharToMultiByteSZ(CP_ACP, 0, ret, -1, buffA, len, NULL, NULL)) + { + free(buffA); + buffA = NULL; + return defvalue; + } + return buffA; +} + +const wchar_t* ConfigW::ReadStringW(const wchar_t *section, const wchar_t *name, const wchar_t *defvalue) +{ + if (!fileName) return defvalue; + static wchar_t def[] = L"_$~$_"; + buff[0] = 0; + int len = GetPrivateProfileString(section, name, def, buff, BUFF_SIZE, fileName); + if (!len || !lstrcmp(def,buff)) return defvalue; + buff[BUFF_SIZE-1]=0; + return buff; +} + +int ConfigW::ReadInt(const wchar_t *name, int defvalue) +{ + if(!defSection) return defvalue; + return ReadInt(defSection, name, defvalue); +} + +long long ConfigW::ReadInt64(const wchar_t *name, long long defvalue) +{ + if(!defSection) return defvalue; + return ReadInt64(defSection, name, defvalue); +} + +double ConfigW::ReadDouble(const wchar_t *name, double defvalue) +{ + if(!defSection) return defvalue; + return ReadDouble(defSection, name, defvalue); +} + +const char* ConfigW::ReadStringA(const wchar_t *name, const char *defvalue) +{ + if(!defSection) return defvalue; + return ReadStringA(defSection, name, defvalue); +} + +const wchar_t* ConfigW::ReadStringW(const wchar_t *name, const wchar_t *defvalue) +{ + if(!defSection) return defvalue; + return ReadStringW(defSection, name, defvalue); +} + +BOOL ConfigW::SetSection(const wchar_t *section) +{ + if (defSection) + { + free(defSection); + defSection = NULL; + } + size_t len; + len = lstrlen(section) + 1; + defSection = (wchar_t*)malloc(len*sizeof(wchar_t)); + if (NULL == defSection) + { + return FALSE; + } + + if (S_OK != StringCchCopy(defSection, len, section)) + { + free(defSection); + defSection = NULL; + return FALSE; + } + return TRUE; +} + +BOOL ConfigW::SetIniFile(const wchar_t *file) +{ + if (fileName) + { + if (emptyBOM) RemoveEmptyFile(); + free(fileName); + fileName = NULL; + } + size_t len; + len = lstrlen(file) + 1; + fileName = (wchar_t*)malloc(len*sizeof(wchar_t)); + if (NULL == fileName) return FALSE; + + if (S_OK != StringCchCopy(fileName, len, file)) + { + free(fileName); + fileName = NULL; + return FALSE; + } + if (fileName) CreateFileWithBOM(); + return TRUE; +} + +const wchar_t* ConfigW::GetSection(void) +{ + return defSection; +} + +const wchar_t* ConfigW::GetFile(void) +{ + return fileName; +} + +void ConfigW::CreateFileWithBOM(void) +{ + // benski> this doesn't seem to be working on win9x + HANDLE hFile = CreateFile(fileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (INVALID_HANDLE_VALUE == hFile) + { + WORD wBOM = 0xFEFF; + DWORD num = 0; + hFile = CreateFile(fileName, GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); + WriteFile(hFile, &wBOM, sizeof(WORD), &num, NULL); + emptyBOM = TRUE; + } + CloseHandle(hFile); +} + +void ConfigW::RemoveEmptyFile(void) +{ + emptyBOM = FALSE; + HANDLE hFile = CreateFile(fileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (INVALID_HANDLE_VALUE == hFile) return; + DWORD fsize; + fsize = GetFileSize(hFile, NULL); + CloseHandle(hFile); + if (fsize == 2) DeleteFile(fileName); + return; +} + +BOOL ConfigW::IsFileExist(void) +{ + HANDLE hFile = CreateFile(fileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + BOOL exist = (INVALID_HANDLE_VALUE != hFile); + if (exist) + { + exist = (GetFileSize(hFile, NULL) != 2); + CloseHandle(hFile); + } + return exist; +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_crasher/config.h b/Src/Plugins/General/gen_crasher/config.h new file mode 100644 index 00000000..1016b570 --- /dev/null +++ b/Src/Plugins/General/gen_crasher/config.h @@ -0,0 +1,62 @@ +#ifndef NULLSOFT_CONFIG_H_ +#define NULLSOFT_CONFIG_H_ + +#include +#include +#include +#include +#include + +#define BUFF_SIZE 8192 + +class ConfigW +{ +public: + ConfigW(); + ConfigW(const wchar_t *ini, const wchar_t *section); + ~ConfigW(); + +public: + void Flush(void); + + BOOL Write(const wchar_t *name, double value); + BOOL Write(const wchar_t *section, const wchar_t *name, double value); + BOOL Write(const wchar_t *name, long long value); + BOOL Write(const wchar_t *section, const wchar_t *name, long long value); + BOOL Write(const wchar_t *name, int value); + BOOL Write(const wchar_t *section, const wchar_t *name, int value); + BOOL Write(const wchar_t *name, const wchar_t *value); + BOOL Write(const wchar_t *section, const wchar_t *name, const wchar_t *value); + BOOL Write(const wchar_t *name, const char value); + BOOL Write(const wchar_t *section, const wchar_t *name, const char *value); + + int ReadInt(const wchar_t *name, int defvalue); + long long ReadInt64(const wchar_t *name, long long defvalue); + double ReadDouble(const wchar_t *name, double defvalue); + const char* ReadStringA(const wchar_t *name, const char *defvalue); + const wchar_t* ReadStringW(const wchar_t *name, const wchar_t *defvalue); + int ReadInt(const wchar_t *section, const wchar_t *name, int defvalue); + long long ReadInt64(const wchar_t *section, const wchar_t *name, long long defvalue); + double ReadDouble(const wchar_t *section, const wchar_t *name, double defvalue); + const char* ReadStringA(const wchar_t *section, const wchar_t *name, const char *defvalue); + const wchar_t* ReadStringW(const wchar_t *section, const wchar_t *name, const wchar_t *defvalue); + + BOOL SetSection(const wchar_t *section); + BOOL SetIniFile(const wchar_t *file); + BOOL IsFileExist(void); + + const wchar_t* GetSection(void); + const wchar_t* GetFile(void); +private: + HANDLE CreateFileHandle(); + void CreateFileWithBOM(void); + void RemoveEmptyFile(void); +private: + BOOL emptyBOM; + wchar_t buff[BUFF_SIZE]; + char *buffA; + wchar_t *fileName; + wchar_t *defSection; +}; + +#endif //NULLSOFT_CONFIG_H_ \ No newline at end of file diff --git a/Src/Plugins/General/gen_crasher/configDlg.cpp b/Src/Plugins/General/gen_crasher/configDlg.cpp new file mode 100644 index 00000000..5e845425 --- /dev/null +++ b/Src/Plugins/General/gen_crasher/configDlg.cpp @@ -0,0 +1,457 @@ +#include ".\configdlg.h" +#include ".\smtpdlg.h" +#include ".\resource.h" +#include +#include ".\miniVersion.h" +#include ".\getwinver.h" +#include ".\settings.h" +#include ".\minidump.h" +#include ".\main.h" +#include +#include "api__gen_crasher.h" + +extern Settings settings; + +BOOL CALLBACK ConfigDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + static wchar_t *path; + + switch (uMsg) + { + case WM_INITDIALOG: + { + HWND hwCombo = GetDlgItem(hwndDlg, IDC_CMB_DMPTYPE); + + // detect windows version + wchar_t strBuff[2048] = {0}, winVer[32] = {0}, build[32] = {0}; + int nWinVer = 0; + GetWinVer(winVer, &nWinVer, build); + StringCchPrintf(strBuff, 2048, L"%s (%s)", winVer, build); + SetWindowText(GetDlgItem(hwndDlg, IDC_LBL_OSVERSION), strBuff); + + // discover dbghlp.dll + HMODULE hm = NULL; + // first in app folder + if (GetModuleFileName( NULL, strBuff, _MAX_PATH )) + { + wchar_t *pSlash = wcsrchr( strBuff, L'\\' ); + if (pSlash) + { + StringCchCopy(pSlash+1, 2048 - (pSlash + 1 - strBuff), L"dbghelp.dll" ); + hm = LoadLibraryW( strBuff ); + } + } + if (!hm) + { + // load any version we can + hm = LoadLibraryW(L"dbghelp.dll"); + } + + if (hm) + { + GetModuleFileName(hm, strBuff, 2048); + SetWindowText(GetDlgItem(hwndDlg, IDC_LBL_DLLPATH), strBuff); + // try to get dll version + CMiniVersion ver(strBuff); + if(ver.Init()) + { + WORD dwBuf[4] = {0}; + ver.GetProductVersion(dwBuf); + StringCchPrintf(strBuff, 2048, L"%d.%d.%d.%d", dwBuf[0], dwBuf[1], dwBuf[2], dwBuf[3]); + } + else + { + WASABI_API_LNGSTRINGW_BUF(IDS_UNABLE_TO_LOAD, strBuff, 128); + } + ver.Release(); + + BOOL (WINAPI* MiniDumpWriteDump)( + HANDLE hProcess, + DWORD ProcessId, + HANDLE hFile, + MINIDUMP_TYPE DumpType, + PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, + PMINIDUMP_EXCEPTION_INFORMATION UserStreamParam, + PMINIDUMP_EXCEPTION_INFORMATION CallbackParam + ) = NULL; + *(FARPROC*)&MiniDumpWriteDump = GetProcAddress(hm, "MiniDumpWriteDump"); + + wchar_t temp[256] = {0}; + StringCchPrintfW(temp, 256, L"%s [%s]", strBuff, WASABI_API_LNGSTRINGW(MiniDumpWriteDump ? IDS_LOADED_OK : IDS_UNABLE_TO_LOAD)); + SetDlgItemText(hwndDlg, IDC_LBL_DLLVERSION, temp); + + FreeLibrary(hm); + } + else + { + SetWindowText(GetDlgItem(hwndDlg, IDC_LBL_DLLPATH), WASABI_API_LNGSTRINGW(IDS_NOT_FOUND)); + wchar_t temp[256] = {0}, temp2[128] = {0}; + StringCchPrintfW(temp, 256, L"%s [%s]", WASABI_API_LNGSTRINGW(IDS_UNKNOWN), WASABI_API_LNGSTRINGW_BUF(IDS_UNABLE_TO_LOAD, temp2, 128)); + SetWindowText(GetDlgItem(hwndDlg, IDC_LBL_DLLVERSION), temp); + } + // set combobox with values + WPARAM pos; + pos = SendMessage(hwCombo, CB_ADDSTRING, 0, (LPARAM) L"MiniDumpNormal"); + SendMessage(hwCombo, CB_SETITEMDATA, pos, 0x00000000); + pos = SendMessage(hwCombo, CB_ADDSTRING, 0, (LPARAM) L"MiniDumpWithDataSegs"); + SendMessage(hwCombo, CB_SETITEMDATA, pos, 0x00000001); + pos = SendMessage(hwCombo, CB_ADDSTRING, 0, (LPARAM) L"MiniDumpWithFullMemory"); + SendMessage(hwCombo, CB_SETITEMDATA, pos, 0x00000002); + pos = SendMessage(hwCombo, CB_ADDSTRING, 0, (LPARAM) L"MiniDumpWithHandleData (Not Supported: Windows Me/98/95)"); + SendMessage(hwCombo, CB_SETITEMDATA, pos, 0x00000004); + pos = SendMessage(hwCombo, CB_ADDSTRING, 0, (LPARAM) L"MiniDumpFilterMemory"); + SendMessage(hwCombo, CB_SETITEMDATA, pos, 0x00000008); + pos = SendMessage(hwCombo, CB_ADDSTRING, 0, (LPARAM) L"MiniDumpScanMemory"); + SendMessage(hwCombo, CB_SETITEMDATA, pos, 0x00000010); + pos = SendMessage(hwCombo, CB_ADDSTRING, 0, (LPARAM) L"MiniDumpWithUnloadedModules (Not Supported: DbgHelp 5.1 and earlier)"); + SendMessage(hwCombo, CB_SETITEMDATA, pos, 0x00000020); + pos = SendMessage(hwCombo, CB_ADDSTRING, 0, (LPARAM) L"MiniDumpWithIndirectlyReferencedMemory (Not Supported: DbgHelp 5.1 and earlier)"); + SendMessage(hwCombo, CB_SETITEMDATA, pos, 0x00000040); + pos = SendMessage(hwCombo, CB_ADDSTRING, 0, (LPARAM) L"MiniDumpFilterModulePaths (Not Supported: DbgHelp 5.1 and earlier)"); + SendMessage(hwCombo, CB_SETITEMDATA, pos, 0x00000080); + pos = SendMessage(hwCombo, CB_ADDSTRING, 0, (LPARAM) L"MiniDumpWithProcessThreadData (Not Supported: DbgHelp 5.1 and earlier)"); + SendMessage(hwCombo, CB_SETITEMDATA, pos, 0x00000100); + pos = SendMessage(hwCombo, CB_ADDSTRING, 0, (LPARAM) L"MiniDumpWithPrivateReadWriteMemory (Not Supported: DbgHelp 5.1 and earlier)"); + SendMessage(hwCombo, CB_SETITEMDATA, pos, 0x00000200); + pos = SendMessage(hwCombo, CB_ADDSTRING, 0, (LPARAM) L"MiniDumpWithoutOptionalData (Not Supported: DbgHelp 6.1 and earlier)"); + SendMessage(hwCombo, CB_SETITEMDATA, pos, 0x00000400); + pos = SendMessage(hwCombo, CB_ADDSTRING, 0, (LPARAM) L"MiniDumpWithFullMemoryInfo (Not Supported: DbgHelp 6.1 and earlier)"); + SendMessage(hwCombo, CB_SETITEMDATA, pos, 0x00000800); + pos = SendMessage(hwCombo, CB_ADDSTRING, 0, (LPARAM) L"MiniDumpWithThreadInfo (Not Supported: DbgHelp 6.1 and earlier)"); + SendMessage(hwCombo, CB_SETITEMDATA, pos, 0x00001000); + pos = SendMessage(hwCombo, CB_ADDSTRING, 0, (LPARAM) L"MiniDumpWithCodeSegs (Not Supported: DbgHelp 6.1 and earlier)"); + SendMessage(hwCombo, CB_SETITEMDATA, pos, 0x00002000); + + // read settings + settings.Load(); + CheckDlgButton(hwndDlg, IDC_CHK_CREATELOG, settings.createLOG); + CheckDlgButton(hwndDlg, IDC_CHK_CREATEDMP, settings.createDMP); + CheckDlgButton(hwndDlg, IDC_CHK_RESTART, settings.autoRestart); + CheckDlgButton(hwndDlg, IDC_CHK_SILENT, settings.silentMode); + CheckDlgButton(hwndDlg, IDC_CHK_SEND, settings.sendData); + CheckDlgButton(hwndDlg, IDC_CHK_COMPRESS, settings.zipData); + CheckDlgButton(hwndDlg, IDC_RB_USECLIENT, settings.sendByClient); + CheckDlgButton(hwndDlg, IDC_RB_USESMTP, settings.sendBySMTP); + + CreatePathFromFullName(&path, settings.zipPath); + SetWindowText(GetDlgItem(hwndDlg, IDC_EDT_PATH), path); + + SetWindowText(GetDlgItem(hwndDlg, IDC_EDT_ZIPNAME), GetFileName(settings.zipPath)); + + SelectComboBoxItem(hwCombo, settings.dumpType); + SetWindowText(GetDlgItem(hwndDlg, IDC_EDT_DMPPATH), GetFileName(settings.dumpPath)); + + CheckDlgButton(hwndDlg, IDC_CHK_LOGSYSTEM, settings.logSystem); + CheckDlgButton(hwndDlg, IDC_CHK_LOGREGISTRY, settings.logRegistry); + CheckDlgButton(hwndDlg, IDC_CHK_LOGSTACK, settings.logStack); + CheckDlgButton(hwndDlg, IDC_CHK_LOGMODULE, settings.logModule); + SetWindowText(GetDlgItem(hwndDlg, IDC_EDT_LOGPATH), GetFileName(settings.logPath)); + + UpdateSend(hwndDlg, settings.sendData); + UpdateZip(hwndDlg, settings.zipData); + UpdateCreateDmp(hwndDlg, settings.createDMP); + UpdateCreateLog(hwndDlg, settings.createLOG); + } + break; + case WM_DESTROY: + { + wchar_t buf[1024] = {0}; + int len, pathlen; + + HWND hwCombo; + settings.createLOG = (SendMessage(GetDlgItem(hwndDlg, IDC_CHK_CREATELOG), BM_GETCHECK, 0,0) == BST_CHECKED); + settings.createDMP = (SendMessage(GetDlgItem(hwndDlg, IDC_CHK_CREATEDMP), BM_GETCHECK, 0,0) == BST_CHECKED); + settings.autoRestart = (SendMessage(GetDlgItem(hwndDlg, IDC_CHK_RESTART), BM_GETCHECK, 0,0) == BST_CHECKED); + settings.silentMode = (SendMessage(GetDlgItem(hwndDlg, IDC_CHK_SILENT), BM_GETCHECK, 0,0) == BST_CHECKED); + settings.sendData = (SendMessage(GetDlgItem(hwndDlg, IDC_CHK_SEND), BM_GETCHECK, 0,0) == BST_CHECKED); + settings.sendByClient = (SendMessage(GetDlgItem(hwndDlg, IDC_RB_USECLIENT), BM_GETCHECK, 0,0) == BST_CHECKED); + settings.sendBySMTP = !settings.sendByClient; + settings.zipData = (SendMessage(GetDlgItem(hwndDlg, IDC_CHK_COMPRESS), BM_GETCHECK, 0,0) == BST_CHECKED); + + len = GetWindowText(GetDlgItem(hwndDlg, IDC_EDT_PATH), buf, 1024); + if (path) free(path); + path = NULL; + if (len) + { + int cpyLen; + path = (wchar_t*)malloc((len +1)*2); + cpyLen = (buf[len-1] == L'\\') ? len -1: len; + StringCchCopyN(path, len + 1, buf, cpyLen); + } + pathlen = (path) ? (int)lstrlen(path) + 1 : 0; + + if (settings.zipPath) free(settings.zipPath); + settings.zipPath = NULL; + len = GetWindowText(GetDlgItem(hwndDlg, IDC_EDT_ZIPNAME), buf, 1024); + if (len) + { + if (pathlen) + { + len += pathlen; + settings.zipPath = (wchar_t*)malloc((len + 1)*2); + StringCchPrintf(settings.zipPath, len+1, L"%s\\%s", path, buf); + } + else + { + settings.zipPath = (wchar_t*)malloc((len + 1)*2); + StringCchCopy(settings.zipPath, len+1, buf); + } + } + else // because path is based on the zipPath just write path + { + if (pathlen) + { + len = pathlen; + settings.zipPath = (wchar_t*)malloc((len + 1)*2); + StringCchPrintf(settings.zipPath, len+1, L"%s\\", path); + } + } + + hwCombo = GetDlgItem(hwndDlg, IDC_CMB_DMPTYPE); + settings.dumpType = (int) SendMessage(hwCombo, CB_GETITEMDATA, SendMessage(hwCombo, CB_GETCURSEL, 0, 0), 0); + + if (settings.dumpPath) free(settings.dumpPath); + settings.dumpPath = NULL; + len = GetWindowText(GetDlgItem(hwndDlg, IDC_EDT_DMPNAME), buf, 1024); + if (len) + { + if (pathlen) + { + len += pathlen; + settings.dumpPath = (wchar_t*)malloc((len + 1)*2); + StringCchPrintf(settings.dumpPath, len+1, L"%s\\%s", path, buf); + } + else + { + settings.dumpPath = (wchar_t*)malloc((len + 1)*2); + StringCchCopy(settings.dumpPath, len+1, buf); + } + } + + settings.logSystem = (SendMessage(GetDlgItem(hwndDlg, IDC_CHK_LOGSYSTEM), BM_GETCHECK, 0,0) == BST_CHECKED); + settings.logRegistry = (SendMessage(GetDlgItem(hwndDlg, IDC_CHK_LOGREGISTRY), BM_GETCHECK, 0,0) == BST_CHECKED); + settings.logStack = (SendMessage(GetDlgItem(hwndDlg, IDC_CHK_LOGSTACK), BM_GETCHECK, 0,0) == BST_CHECKED); + settings.logModule = (SendMessage(GetDlgItem(hwndDlg, IDC_CHK_LOGMODULE), BM_GETCHECK, 0,0) == BST_CHECKED); + + if (settings.logPath) free(settings.logPath); + settings.logPath = NULL; + len = GetWindowText(GetDlgItem(hwndDlg, IDC_EDT_LOGNAME), buf, 1024); + if (len) + { + if (pathlen) + { + len += pathlen; + settings.logPath = (wchar_t*)malloc((len + 1)*2); + StringCchPrintf(settings.logPath, len+1, L"%s\\%s", path, buf); + } + else + { + settings.logPath = (wchar_t*)malloc((len + 1)*2); + StringCchCopy(settings.logPath, len+1, buf); + } + } + + if (!settings.Save()) + { + wchar_t title[32] = {0}; + MessageBox(NULL, WASABI_API_LNGSTRINGW(IDS_UNABLE_TO_SAVE_SETTINGS), + WASABI_API_LNGSTRINGW_BUF(IDS_SAVE_ERROR,title,32), MB_OK); + } + + if(path) free(path); + path = NULL; + break; + } + case WM_COMMAND: + int ctrl = LOWORD(wParam); + switch (ctrl) + { + case IDC_CHK_CREATELOG: + case IDC_CHK_CREATEDMP: + case IDC_CHK_SEND: + case IDC_CHK_COMPRESS: + if (HIWORD(wParam) == BN_CLICKED) + { + BOOL enabled = (SendMessage((HWND) lParam, BM_GETCHECK, 0,0) == BST_CHECKED); + if (ctrl == IDC_CHK_CREATELOG) UpdateCreateLog(hwndDlg, enabled); + else if (ctrl == IDC_CHK_CREATEDMP) UpdateCreateDmp(hwndDlg, enabled); + else if (ctrl == IDC_CHK_SEND) UpdateSend(hwndDlg, enabled); + else if (ctrl == IDC_CHK_COMPRESS) UpdateZip(hwndDlg, enabled); + } + break; + case IDC_RB_USESMTP: + case IDC_RB_USECLIENT: + if (HIWORD(wParam) == BN_CLICKED) UpdateSendType(hwndDlg, (ctrl == IDC_RB_USESMTP)); + break; + case IDC_BTN_PATH: + { + wchar_t szFile[2048] = {0}; // buffer for file name + GetWindowText(GetDlgItem(hwndDlg, IDC_EDT_PATH), szFile, 2048); + if (OpenFolderDialog(NULL, szFile))SetWindowText(GetDlgItem(hwndDlg, IDC_EDT_PATH), szFile); + } + break; + case IDC_BTN_SMTP: + WASABI_API_DIALOGBOXW(IDD_DLG_SMTP, hwndDlg, (DLGPROC)smtpDlgProc); + break; + } + break; + } + //settings.smtpUser; + return FALSE; +} + +int SelectComboBoxItem(const HWND hwCombo, int data) +{ + int count = (int) SendMessage(hwCombo, CB_GETCOUNT , 0, 0); + for (int i = 0; i < count; i++) + { + if (data == (int) SendMessage(hwCombo, CB_GETITEMDATA, i, 0)) + return (int) SendMessage(hwCombo, CB_SETCURSEL, i, 0); + } + return -1; +} + +void UpdateZip(HWND hwndDlg, BOOL enabled) +{ + EnableWindow(GetDlgItem(hwndDlg, IDC_GRP_ZIP), enabled); + EnableWindow(GetDlgItem(hwndDlg, IDC_LBL_ZIPNAME), enabled); + EnableWindow(GetDlgItem(hwndDlg, IDC_EDT_ZIPNAME), enabled); +} + +void UpdateSendType(HWND hwndDlg, BOOL enabled) +{ + EnableWindow(GetDlgItem(hwndDlg, IDC_BTN_SMTP), enabled); +} + +void UpdateSend(HWND hwndDlg, BOOL enabled) +{ + EnableWindow(GetDlgItem(hwndDlg, IDC_RB_USECLIENT), enabled); + EnableWindow(GetDlgItem(hwndDlg, IDC_RB_USESMTP), enabled); + BOOL stmpPressed = (SendMessage(GetDlgItem(hwndDlg,IDC_RB_USESMTP), BM_GETCHECK, 0,0) == BST_CHECKED); + EnableWindow(GetDlgItem(hwndDlg, IDC_BTN_SMTP), enabled && stmpPressed); +} + +void UpdateCreateDmp(HWND hwndDlg, BOOL enabled) +{ + EnableWindow(GetDlgItem(hwndDlg, IDC_LBL_OSVERSION_CAPTION), enabled); + EnableWindow(GetDlgItem(hwndDlg, IDC_LBL_DLLPATH_CAPTION), enabled); + EnableWindow(GetDlgItem(hwndDlg, IDC_LBL_DLLVERSION_CAPTION), enabled); + EnableWindow(GetDlgItem(hwndDlg, IDC_LBL_OSVERSION), enabled); + EnableWindow(GetDlgItem(hwndDlg, IDC_LBL_DLLPATH), enabled); + EnableWindow(GetDlgItem(hwndDlg, IDC_LBL_DLLVERSION), enabled); + EnableWindow(GetDlgItem(hwndDlg, IDC_LBL_DMPTYPE), enabled); + EnableWindow(GetDlgItem(hwndDlg, IDC_CMB_DMPTYPE), enabled); + EnableWindow(GetDlgItem(hwndDlg, IDC_LBL_DMPNAME), enabled); + EnableWindow(GetDlgItem(hwndDlg, IDC_EDT_DMPNAME), enabled); +} + +void UpdateCreateLog(HWND hwndDlg, BOOL enabled) +{ + EnableWindow(GetDlgItem(hwndDlg, IDC_GRP_LOG), enabled); + EnableWindow(GetDlgItem(hwndDlg, IDC_CHK_LOGSYSTEM), enabled); + EnableWindow(GetDlgItem(hwndDlg, IDC_CHK_LOGREGISTRY), enabled); + EnableWindow(GetDlgItem(hwndDlg, IDC_CHK_LOGSTACK), enabled); + EnableWindow(GetDlgItem(hwndDlg, IDC_CHK_LOGMODULE), enabled); + EnableWindow(GetDlgItem(hwndDlg, IDC_LBL_LOGNAME), enabled); + EnableWindow(GetDlgItem(hwndDlg, IDC_EDT_LOGNAME), enabled); +} + +BOOL CALLBACK browseEnumProc(HWND hwnd, LPARAM lParam) +{ + wchar_t cl[32] = {0}; + GetClassNameW(hwnd, cl, ARRAYSIZE(cl)); + if (!lstrcmpiW(cl, WC_TREEVIEW)) + { + PostMessage(hwnd, TVM_ENSUREVISIBLE, 0, (LPARAM)TreeView_GetSelection(hwnd)); + return FALSE; + } + + return TRUE; +} + +static int CALLBACK WINAPI BrowseCallbackProc( HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData) +{ + switch (uMsg) + { + case BFFM_INITIALIZED: + { + //SetWindowText(hwnd, getString(IDS_P_SELECT_LANGDIR,NULL,0)); + SendMessage(hwnd, BFFM_SETSELECTION, 1, (LPARAM)lpData); + + // this is not nice but it fixes the selection not working correctly on all OSes + EnumChildWindows(hwnd, browseEnumProc, 0); + } + return 0; + } + return 0; +} + +BOOL OpenFolderDialog(HWND parent, LPWSTR pathBuffer) +{ + if (!pathBuffer) return FALSE; + + // Return value for the function + BOOL ret = TRUE; + + // Set up the params + BROWSEINFO browseInfo = {0}; + browseInfo.hwndOwner = parent; + browseInfo.lpfn = BrowseCallbackProc; + browseInfo.lParam = (LPARAM)pathBuffer; + browseInfo.lpszTitle = WASABI_API_LNGSTRINGW(IDS_SELECT_FOLDER_FOR_ERROR_INFO); + browseInfo.ulFlags = BIF_NEWDIALOGSTYLE; + + // Show the dialog + LPITEMIDLIST itemIDList = SHBrowseForFolder(&browseInfo); + + // Did user press cancel? + if (!itemIDList) + ret = FALSE; + + // Is everything so far? + if (ret != FALSE) + { + // Get the path from the returned ITEMIDLIST + if (!SHGetPathFromIDList(itemIDList, pathBuffer)) + ret = FALSE; + + // Now we need to free the ITEMIDLIST the shell allocated + LPMALLOC shellMalloc; + HRESULT hr; + + // Get pointer to the shell's malloc interface + hr = SHGetMalloc(&shellMalloc); + + // Did it work? + if (SUCCEEDED(hr)) + { + shellMalloc->Free(itemIDList); + shellMalloc->Release(); + ret = TRUE; + } + } + return ret; +} + +void CreatePathFromFullName(wchar_t **path, const wchar_t *fullname) +{ + if (*path) free(*path); + *path = NULL; + if (!fullname) return; + const wchar_t *end = wcsrchr(fullname, L'\\'); + if (!end || end == fullname) return; + int len = (int)(end - fullname + 1); + if (len == 3) len++; + + *path = (wchar_t*)malloc(len*2); + StringCchCopyN(*path, len, fullname, len -1); +} + +const wchar_t* GetFileName(const wchar_t *fullname) +{ + if (!fullname) return NULL; + const wchar_t *start = wcsrchr(fullname, L'\\'); + if (start && start != fullname) start = CharNext(start); + + return start; +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_crasher/configDlg.h b/Src/Plugins/General/gen_crasher/configDlg.h new file mode 100644 index 00000000..5c3ef4df --- /dev/null +++ b/Src/Plugins/General/gen_crasher/configDlg.h @@ -0,0 +1,15 @@ +#pragma once +#include + +int SelectComboBoxItem(const HWND hwCombo, int data); +BOOL OpenFolderDialog(HWND parent, LPWSTR pathBuffer); +BOOL CALLBACK ConfigDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); + +void UpdateSendType(HWND hwndDlg, BOOL enabled); +void UpdateSend(HWND hwndDlg, BOOL enabled); +void UpdateCreateDmp(HWND hwndDlg, BOOL enabled); +void UpdateCreateLog(HWND hwndDlg, BOOL enabled); +void UpdateZip(HWND hwndDlg, BOOL enabled); + +void CreatePathFromFullName(wchar_t **path, const wchar_t *fullname); +const wchar_t* GetFileName(const wchar_t *fullname); \ No newline at end of file diff --git a/Src/Plugins/General/gen_crasher/crashDlg.cpp b/Src/Plugins/General/gen_crasher/crashDlg.cpp new file mode 100644 index 00000000..d74bee75 --- /dev/null +++ b/Src/Plugins/General/gen_crasher/crashDlg.cpp @@ -0,0 +1,101 @@ +#include ".\crashdlg.h" +#include ".\configdlg.h" +#include ".\resource.h" +#include ".\settings.h" +#include "exceptionhandler.h" +#include + +extern Settings settings; +extern PEXCEPTION_POINTERS gExceptionInfo; + +BOOL CALLBACK CrashDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + UNREFERENCED_PARAMETER(lParam); + switch (uMsg) + { + case WM_INITDIALOG: + { + // as we're loading things, make sure we've got a decent icon size to use in the second usage + HICON hIcon = (HICON)LoadImage(GetModuleHandle(NULL),MAKEINTRESOURCE(102),IMAGE_ICON,48,48,LR_SHARED); + SetClassLongPtr(hwndDlg, GCLP_HICON, (LONG_PTR)hIcon); + + HWND hwndPrg = GetDlgItem(hwndDlg, IDC_PRG_COLLECT); + SendMessage(hwndPrg, PBM_SETRANGE, 0, MAKELPARAM(0,100)); + SendMessage(hwndPrg, PBM_SETPOS, 0, 0); + + // this will make sure that we've got the logo shown even when using a localised version + SendDlgItemMessage(hwndDlg,IDC_BMP_LOGO,STM_SETIMAGE,IMAGE_ICON,(LPARAM)hIcon); + + SetDlgItemText(hwndDlg, IDC_LBL_STEP, L"Analyzing settings..."); + settings.ClearTempData(); + + wchar_t waPath[2*_MAX_PATH] = {0}; + if (GetModuleFileName( NULL, waPath, 2*_MAX_PATH )) + { + settings.WriteWinamp(waPath); + } + + SetTimer(hwndDlg, 123, 1000, NULL); + break; + } + case WM_TIMER: + if (wParam == 123) + { + KillTimer(hwndDlg,wParam); + HWND hwndPrg = GetDlgItem(hwndDlg, IDC_PRG_COLLECT); + SetDlgItemText(hwndDlg, IDC_LBL_STEP, L"Generating log file..."); + SendMessage(hwndPrg, PBM_SETPOS, 30, 0); + UpdateWindow(hwndDlg); + if (settings.createLOG) settings.WriteLogCollectResult(CreateLog(gExceptionInfo, L"Winamp")); + SetDlgItemText(hwndDlg, IDC_LBL_STEP, L"Generating dump file..."); + SendMessage(hwndPrg, PBM_SETPOS, 50, 0); + UpdateWindow(hwndDlg); + if (settings.createDMP) settings.WriteDmpCollectResult(CreateDump(gExceptionInfo)); + SetDlgItemText(hwndDlg, IDC_LBL_STEP, L"Starting error reporter..."); + SendMessage(hwndPrg, PBM_SETPOS, 90, 0); + UpdateWindow(hwndDlg); + STARTUPINFO si = {0}; + si.cb = sizeof(si); + si.dwFlags = STARTF_USESHOWWINDOW; + si.wShowWindow = SW_SHOW; + + PROCESS_INFORMATION pi = {0}; + wchar_t reporter[512] = {0}, waPlugPath[MAX_PATH] = {0}, cmd[512] = {0}, *waPath = 0; + GetModuleFileName( NULL, waPlugPath, MAX_PATH); + CreatePathFromFullName(&waPath, waPlugPath); + StringCchPrintf(reporter, 512, L"%s\\reporter.exe", waPath); + StringCchPrintf(cmd, 512, L" \"%s\"", settings.GetPath()); + + if (CreateProcess( + reporter, // name of executable module + cmd, // command line string + NULL, // process attributes + NULL, // thread attributes + FALSE, // handle inheritance option + 0, // creation flags + NULL, // new environment block + NULL, // current directory name + &si, // startup information + &pi)) // process information + { + SetDlgItemText(hwndDlg, IDC_LBL_STEP, L"Done."); + SetTimer(hwndDlg, 126, 200, NULL); + } + else + { + SetDlgItemText(hwndDlg, IDC_LBL_STEP, L"Error. Unable to run reporter."); + SetTimer(hwndDlg, 126, 3000, NULL); + } + + SendMessage(hwndPrg, PBM_SETPOS, 100, 0); + UpdateWindow(hwndDlg); + } + else if (wParam == 126) + { + KillTimer(hwndDlg,wParam); + DestroyWindow(hwndDlg); + } + break; + } + return FALSE; +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_crasher/crashDlg.h b/Src/Plugins/General/gen_crasher/crashDlg.h new file mode 100644 index 00000000..eb92f027 --- /dev/null +++ b/Src/Plugins/General/gen_crasher/crashDlg.h @@ -0,0 +1,5 @@ +#pragma once +#include +#include + +BOOL CALLBACK CrashDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); \ No newline at end of file diff --git a/Src/Plugins/General/gen_crasher/feedback/email/SendEmail.cpp b/Src/Plugins/General/gen_crasher/feedback/email/SendEmail.cpp new file mode 100644 index 00000000..20e2d435 --- /dev/null +++ b/Src/Plugins/General/gen_crasher/feedback/email/SendEmail.cpp @@ -0,0 +1,364 @@ +// SendEmail.cpp Version 1.0 +// +// Author: Hans Dietrich +// hdietrich2@hotmail.com +// +// This software is released into the public domain. +// You are free to use it in any way you like, except +// that you may not sell this source code. +// +// This software is provided "as is" with no expressed +// or implied warranty. I accept no liability for any +// damage or loss of business that this software may cause. +// +/////////////////////////////////////////////////////////////////////////////// + +// Notes on compiling: this does not use MFC. Set precompiled header +// option to "Not using precompiled headers". +// +// This code assumes that a default mail client has been set up. + +#include ".\sendemail.h" +#include +#include +#pragma warning(disable: 4228) +#include +#pragma warning(default: 4228) + +#pragma warning(disable:4127) // for _ASSERTE + +#ifndef _countof +#define _countof(array) (sizeof(array)/sizeof(array[0])) +#endif + + +BOOL SendEmail(HWND hWnd, // parent window, must not be NULL + LPCTSTR lpszTo, // must NOT be NULL or empty + LPCTSTR lpszToName, // may be NULL + LPCTSTR lpszSubject, // may be NULL + LPCTSTR lpszMessage, // may be NULL + LPCTSTR lpszAttachment) // may be NULL +{ + + _ASSERTE(lpszTo && lpszTo[0] != _T('\0')); + if (lpszTo == NULL || lpszTo[0] == _T('\0')) + return FALSE; + + // ===== LOAD MAPI DLL ===== + + HMODULE hMapi = ::LoadLibraryA("MAPI32.DLL"); + + _ASSERTE(hMapi); + if (hMapi == NULL) + { + ::MessageBox(NULL, + _T("Failed to load MAPI32.DLL."), + _T("CrashRep"), + MB_OK|MB_ICONSTOP); + return FALSE; + } + + // get proc address for MAPISendMail + ULONG (PASCAL *lpfnSendMail)(ULONG, ULONG, MapiMessage*, FLAGS, ULONG); + (FARPROC&)lpfnSendMail = GetProcAddress(hMapi, "MAPISendMail"); + _ASSERTE(lpfnSendMail); + if (lpfnSendMail == NULL) + { + ::MessageBox(NULL, + _T("Invalid MAPI32.DLL, cannot find MAPISendMail."), + _T("CrashRep"), + MB_OK|MB_ICONSTOP); + ::FreeLibrary(hMapi); + return FALSE; + } + + // ===== SET UP MAPI STRUCTS ===== + + // ===== file description (for the attachment) ===== + + MapiFileDesc fileDesc; + memset(&fileDesc, 0, sizeof(fileDesc)); + + // ----- attachment path + TCHAR szTempName[_MAX_PATH*2]; + memset(szTempName, 0, sizeof(szTempName)); + if (lpszAttachment && lpszAttachment[0] != _T('\0')) + lstrcpyn(szTempName, lpszAttachment, _countof(szTempName)-2); + +#ifdef _UNICODE + char szTempNameA[_MAX_PATH*2]; + memset(szTempNameA, 0, sizeof(szTempNameA)); + _wcstombsz(szTempNameA, szTempName, _countof(szTempNameA)-2); +#endif + + // ----- attachment title + TCHAR szTitle[_MAX_PATH*2]; + memset(szTitle, 0, sizeof(szTitle)); + if (lpszAttachment && lpszAttachment[0] != _T('\0')) + lstrcpyn(szTitle, GetFilePart(lpszAttachment), _countof(szTitle)-2); + +#ifdef _UNICODE + char szTitleA[_MAX_PATH*2]; + memset(szTitleA, 0, sizeof(szTitleA)); + _wcstombsz(szTitleA, szTitle, _countof(szTitleA)-2); +#endif + + fileDesc.nPosition = (ULONG)-1; +#ifdef _UNICODE + fileDesc.lpszPathName = szTempNameA; + fileDesc.lpszFileName = szTitleA; +#else + fileDesc.lpszPathName = szTempName; + fileDesc.lpszFileName = szTitle; +#endif + + // ===== recipient ===== + + MapiRecipDesc recip; + memset(&recip, 0, sizeof(recip)); + + // ----- name + TCHAR szRecipName[_MAX_PATH*2]; + memset(szRecipName, 0, sizeof(szRecipName)); + if (lpszToName && lpszToName[0] != _T('\0')) + lstrcpyn(szRecipName, lpszToName, _countof(szRecipName)-2); +#ifdef _UNICODE + char szRecipNameA[_MAX_PATH*2]; + memset(szRecipNameA, 0, sizeof(szRecipNameA)); + _wcstombsz(szRecipNameA, szRecipName, _countof(szRecipNameA)-2); +#endif + + if (lpszToName && lpszToName[0] != _T('\0')) + { +#ifdef _UNICODE + recip.lpszName = szRecipNameA; +#else + recip.lpszName = szRecipName; +#endif + } + + // ----- address + TCHAR szAddress[_MAX_PATH*2]; + memset(szAddress, 0, sizeof(szAddress)); + lstrcpyn(szAddress, lpszTo, _countof(szAddress)-2); +#ifdef _UNICODE + char szAddressA[_MAX_PATH*2]; + memset(szAddressA, 0, sizeof(szAddressA)); + _wcstombsz(szAddressA, szAddress, _countof(szAddressA)-2); +#endif + +#ifdef _UNICODE + recip.lpszAddress = szAddressA; +#else + recip.lpszAddress = szAddress; +#endif + + recip.ulRecipClass = MAPI_TO; + + // ===== message ===== + + MapiMessage message; + memset(&message, 0, sizeof(message)); + + // ----- recipient + message.nRecipCount = 1; + message.lpRecips = &recip; + + // ----- attachment + if (lpszAttachment && lpszAttachment[0] != _T('\0')) + { + message.nFileCount = 1; + message.lpFiles = &fileDesc; + } + + // ----- subject + TCHAR szSubject[_MAX_PATH*2]; + memset(szSubject, 0, sizeof(szSubject)); + if (lpszSubject && lpszSubject[0] != _T('\0')) + lstrcpyn(szSubject, lpszSubject, _countof(szSubject)-2); +#ifdef _UNICODE + char szSubjectA[_MAX_PATH*2]; + memset(szSubjectA, 0, sizeof(szSubjectA)); + _wcstombsz(szSubjectA, szSubject, _countof(szSubjectA)-2); +#endif + + if (lpszSubject && lpszSubject[0] != _T('\0')) + { +#ifdef _UNICODE + message.lpszSubject = szSubjectA; +#else + message.lpszSubject = szSubject; +#endif + } + + // ----- message + // message may be large, so allocate buffer + TCHAR *pszMessage = NULL; + int nMessageSize = 0; + if (lpszMessage) + { + nMessageSize = lstrlen(lpszMessage); + if (nMessageSize > 0) + { + pszMessage = new TCHAR [nMessageSize + 10]; + _ASSERTE(pszMessage); + memset(pszMessage, 0, nMessageSize + 10); + lstrcpy(pszMessage, lpszMessage); + } + } + + char *pszMessageA = NULL; +#ifdef _UNICODE + if (nMessageSize > 0) + { + pszMessageA = new char [nMessageSize + 10]; + _ASSERTE(pszMessageA); + memset(pszMessageA, 0, nMessageSize + 10); + } + _wcstombsz(pszMessageA, pszMessage, nMessageSize+2); +#endif + + if (nMessageSize > 0) + { +#ifdef _UNICODE + message.lpszNoteText = pszMessageA; +#else + message.lpszNoteText = pszMessage; +#endif + } + + + // ===== SETUP FINISHED, READY TO SEND ===== + + + // some extra precautions are required to use MAPISendMail as it + // tends to enable the parent window in between dialogs (after + // the login dialog, but before the send note dialog). + + ::SetCapture(hWnd); + ::SetFocus(NULL); + ::EnableWindow(hWnd, FALSE); + + ULONG nError = lpfnSendMail(0, + (LPARAM)hWnd, + &message, + MAPI_LOGON_UI | MAPI_DIALOG, + 0); + +#ifdef _DEBUG + TCHAR *cp = NULL; + switch (nError) + { + case SUCCESS_SUCCESS: cp = _T("SUCCESS_SUCCESS"); break; + case MAPI_E_USER_ABORT: cp = _T("MAPI_E_USER_ABORT"); break; + case MAPI_E_FAILURE: cp = _T("MAPI_E_FAILURE"); break; + case MAPI_E_LOGON_FAILURE: cp = _T("MAPI_E_LOGON_FAILURE"); break; + case MAPI_E_DISK_FULL: cp = _T("MAPI_E_DISK_FULL"); break; + case MAPI_E_INSUFFICIENT_MEMORY: cp = _T("MAPI_E_INSUFFICIENT_MEMORY"); break; + case MAPI_E_ACCESS_DENIED: cp = _T("MAPI_E_ACCESS_DENIED"); break; + case MAPI_E_TOO_MANY_SESSIONS: cp = _T("MAPI_E_TOO_MANY_SESSIONS"); break; + case MAPI_E_TOO_MANY_FILES: cp = _T("MAPI_E_TOO_MANY_FILES"); break; + case MAPI_E_TOO_MANY_RECIPIENTS: cp = _T("MAPI_E_TOO_MANY_RECIPIENTS"); break; + case MAPI_E_ATTACHMENT_NOT_FOUND: cp = _T("MAPI_E_ATTACHMENT_NOT_FOUND"); break; + case MAPI_E_ATTACHMENT_OPEN_FAILURE: cp = _T("MAPI_E_ATTACHMENT_OPEN_FAILURE"); break; + case MAPI_E_ATTACHMENT_WRITE_FAILURE: cp = _T("MAPI_E_ATTACHMENT_WRITE_FAILURE"); break; + case MAPI_E_UNKNOWN_RECIPIENT: cp = _T("MAPI_E_UNKNOWN_RECIPIENT"); break; + case MAPI_E_BAD_RECIPTYPE: cp = _T("MAPI_E_BAD_RECIPTYPE"); break; + case MAPI_E_NO_MESSAGES: cp = _T("MAPI_E_NO_MESSAGES"); break; + case MAPI_E_INVALID_MESSAGE: cp = _T("MAPI_E_INVALID_MESSAGE"); break; + case MAPI_E_TEXT_TOO_LARGE: cp = _T("MAPI_E_TEXT_TOO_LARGE"); break; + case MAPI_E_INVALID_SESSION: cp = _T("MAPI_E_INVALID_SESSION"); break; + case MAPI_E_TYPE_NOT_SUPPORTED: cp = _T("MAPI_E_TYPE_NOT_SUPPORTED"); break; + case MAPI_E_AMBIGUOUS_RECIPIENT: cp = _T("MAPI_E_AMBIGUOUS_RECIPIENT"); break; + case MAPI_E_MESSAGE_IN_USE: cp = _T("MAPI_E_MESSAGE_IN_USE"); break; + case MAPI_E_NETWORK_FAILURE: cp = _T("MAPI_E_NETWORK_FAILURE"); break; + case MAPI_E_INVALID_EDITFIELDS: cp = _T("MAPI_E_INVALID_EDITFIELDS"); break; + case MAPI_E_INVALID_RECIPS: cp = _T("MAPI_E_INVALID_RECIPS"); break; + case MAPI_E_NOT_SUPPORTED: cp = _T("MAPI_E_NOT_SUPPORTED"); break; + default: cp = _T("unknown error"); break; + } + + if (nError == SUCCESS_SUCCESS) + { + OutputDebugString(_T("MAPISendMail ok\r\n")); + } + else + { + OutputDebugString(L"ERROR - MAPISendMail failed: "); + OutputDebugString(cp); + OutputDebugString(L"\r\n"); + + } +#endif // _DEBUG + + + // ===== SEND COMPLETE, CLEAN UP ===== + + // after returning from the MAPISendMail call, the window must be + // re-enabled and focus returned to the frame to undo the workaround + // done before the MAPI call. + + ::ReleaseCapture(); + ::EnableWindow(hWnd, TRUE); + ::SetActiveWindow(NULL); + ::SetActiveWindow(hWnd); + ::SetFocus(hWnd); + + if (pszMessage) + delete [] pszMessage; + pszMessage = NULL; + + if (pszMessageA) + delete [] pszMessageA; + pszMessageA = NULL; + + if (hMapi) + ::FreeLibrary(hMapi); + hMapi = NULL; + + BOOL bRet = TRUE; + if (nError != SUCCESS_SUCCESS) // && + //nError != MAPI_USER_ABORT && + //nError != MAPI_E_LOGIN_FAILURE) + { + bRet = FALSE; + } + + return bRet; +} + +const wchar_t* GetFilePart(LPCWSTR lpszFile) +{ + const wchar_t *result = wcsrchr(lpszFile, _T('\\')); + if (result) + result++; + else + result = (wchar_t *) lpszFile; + return result; +} + +int xwcstombsz(char* mbstr, const wchar_t* wcstr, size_t count) +{ + if (count == 0 && mbstr != NULL) + return 0; + + int result = ::WideCharToMultiByte(CP_ACP, 0, wcstr, -1, + mbstr, (int)count, NULL, NULL); + _ASSERTE(mbstr == NULL || result <= (int)count); + if (result > 0) + mbstr[result-1] = 0; + return result; +} + +int xmbstowcsz(wchar_t* wcstr, const char* mbstr, size_t count) +{ + if (count == 0 && wcstr != NULL) + return 0; + + int result = ::MultiByteToWideChar(CP_ACP, 0, mbstr, -1, + wcstr, (int)count); + _ASSERTE(wcstr == NULL || result <= (int)count); + if (result > 0) + wcstr[result-1] = 0; + return result; +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_crasher/feedback/email/SendEmail.h b/Src/Plugins/General/gen_crasher/feedback/email/SendEmail.h new file mode 100644 index 00000000..ce1c523b --- /dev/null +++ b/Src/Plugins/General/gen_crasher/feedback/email/SendEmail.h @@ -0,0 +1,38 @@ +// SendEmail.h Version 1.0 +// +// Author: Hans Dietrich +// hdietrich2@hotmail.com +// +// This software is released into the public domain. +// You are free to use it in any way you like, except +// that you may not sell this source code. +// +// This software is provided "as is" with no expressed +// or implied warranty. I accept no liability for any +// damage or loss of business that this software may cause. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef SENDEMAIL_H +#define SENDEMAIL_H + +#include +#include + +const wchar_t* GetFilePart(LPCWSTR lpszFile); + +BOOL SendEmail(HWND hWnd, // parent window, must not be NULL + LPCTSTR lpszTo, // must NOT be NULL or empty + LPCTSTR lpszToName, // may be NULL + LPCTSTR lpszSubject, // may be NULL + LPCTSTR lpszMessage, // may be NULL + LPCTSTR lpszAttachment); // may be NULL + + +#define _wcstombsz xwcstombsz +#define _mbstowcsz xmbstowcsz + +int xwcstombsz(char* mbstr, const wchar_t* wcstr, size_t count); +int xmbstowcsz(wchar_t* wcstr, const char* mbstr, size_t count); + +#endif //SENDEMAIL_H \ No newline at end of file diff --git a/Src/Plugins/General/gen_crasher/feedback/feedback.rc b/Src/Plugins/General/gen_crasher/feedback/feedback.rc new file mode 100644 index 00000000..0e02c7e1 --- /dev/null +++ b/Src/Plugins/General/gen_crasher/feedback/feedback.rc @@ -0,0 +1,119 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#define APSTUDIO_HIDDEN_SYMBOLS +#include "windows.h" +#undef APSTUDIO_HIDDEN_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +ICON_XP ICON "..\\..\\..\\..\\Winamp\\resource\\WinampIcon.ico" + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_DLG_SILENT DIALOGEX 0, 0, 187, 42 +STYLE DS_SYSMODAL | DS_SETFONT | DS_SETFOREGROUND | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +EXSTYLE WS_EX_APPWINDOW +CAPTION "Winamp Error Reporter" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + ICON ICON_XP,IDC_STATIC,8,6,21,20,SS_REALSIZEIMAGE + LTEXT "",IDC_LBL_STEP,48,6,127,10 + CONTROL "",IDC_PRG_COLLECT,"msctls_progress32",WS_BORDER,49,19,127,9 + PUSHBUTTON "&Open Report Folder...",IDC_BUTTON1,47,21,87,13,NOT WS_VISIBLE + DEFPUSHBUTTON "&Close",IDC_BUTTON2,138,21,39,13,NOT WS_VISIBLE +END + + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#define APSTUDIO_HIDDEN_SYMBOLS\r\n" + "#include ""windows.h""\r\n" + "#undef APSTUDIO_HIDDEN_SYMBOLS\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "#include ""version.rc2""\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_DLG_SILENT, DIALOG + BEGIN + BOTTOMMARGIN, 39 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// RT_MANIFEST +// +//The manifest is commented out because it is no longer needed (it was need for VC6 files) +// +//1 RT_MANIFEST "manifest.xml" +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// +#include "version.rc2" + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/Src/Plugins/General/gen_crasher/feedback/feedback.sln b/Src/Plugins/General/gen_crasher/feedback/feedback.sln new file mode 100644 index 00000000..dad7acb5 --- /dev/null +++ b/Src/Plugins/General/gen_crasher/feedback/feedback.sln @@ -0,0 +1,30 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29424.173 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "feedback", "feedback.vcxproj", "{A845D04C-A95E-424C-BFA8-D7706DBA78BF}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A845D04C-A95E-424C-BFA8-D7706DBA78BF}.Debug|Win32.ActiveCfg = Debug|Win32 + {A845D04C-A95E-424C-BFA8-D7706DBA78BF}.Debug|Win32.Build.0 = Debug|Win32 + {A845D04C-A95E-424C-BFA8-D7706DBA78BF}.Debug|x64.ActiveCfg = Debug|x64 + {A845D04C-A95E-424C-BFA8-D7706DBA78BF}.Debug|x64.Build.0 = Debug|x64 + {A845D04C-A95E-424C-BFA8-D7706DBA78BF}.Release|Win32.ActiveCfg = Release|Win32 + {A845D04C-A95E-424C-BFA8-D7706DBA78BF}.Release|Win32.Build.0 = Release|Win32 + {A845D04C-A95E-424C-BFA8-D7706DBA78BF}.Release|x64.ActiveCfg = Release|x64 + {A845D04C-A95E-424C-BFA8-D7706DBA78BF}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {2C383520-1961-4F68-B42C-3172B6341FB8} + EndGlobalSection +EndGlobal diff --git a/Src/Plugins/General/gen_crasher/feedback/feedback.vcproj b/Src/Plugins/General/gen_crasher/feedback/feedback.vcproj new file mode 100644 index 00000000..a8a31b82 --- /dev/null +++ b/Src/Plugins/General/gen_crasher/feedback/feedback.vcproj @@ -0,0 +1,321 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Src/Plugins/General/gen_crasher/feedback/feedback.vcxproj b/Src/Plugins/General/gen_crasher/feedback/feedback.vcxproj new file mode 100644 index 00000000..63bcab20 --- /dev/null +++ b/Src/Plugins/General/gen_crasher/feedback/feedback.vcxproj @@ -0,0 +1,253 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {A845D04C-A95E-424C-BFA8-D7706DBA78BF} + feedback + 10.0.19041.0 + + + + Application + v142 + Unicode + + + Application + v142 + Unicode + + + Application + v142 + Unicode + + + Application + v142 + Unicode + + + + + + + + + + + + + + + + + + + false + $(PlatformShortName)_$(Configuration)\ + $(PlatformShortName)_$(Configuration)\ + reporter + $(IncludePath) + $(LibraryPath) + + + false + $(PlatformShortName)_$(Configuration)\ + $(PlatformShortName)_$(Configuration)\ + reporter + + + false + $(PlatformShortName)_$(Configuration)\ + $(PlatformShortName)_$(Configuration)\ + reporter + $(IncludePath) + $(LibraryPath) + + + false + $(PlatformShortName)_$(Configuration)\ + $(PlatformShortName)_$(Configuration)\ + reporter + + + false + + + Debug + + + + Disabled + .;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;_CRT_SECURE_NO_WARNINGS;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions) + false + true + EnableFastChecks + MultiThreadedDebug + Level3 + ProgramDatabase + 4091;4244;%(DisableSpecificWarnings) + $(IntDir)$(TargetName).pdb + + + comctl32.lib;rpcrt4.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)$(TargetExt) + true + $(IntDir)$(TargetName).pdb + Windows + false + MachineX86 + false + %(AdditionalLibraryDirectories) + + + xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\ +xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\ + Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\' + + + + + Disabled + .;%(AdditionalIncludeDirectories) + WIN64;_DEBUG;_WINDOWS;_CRT_SECURE_NO_WARNINGS;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions) + false + true + EnableFastChecks + MultiThreadedDebug + Level3 + ProgramDatabase + 4091;4244;%(DisableSpecificWarnings) + $(IntDir)$(TargetName).pdb + + + comctl32.lib;rpcrt4.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)$(TargetExt) + true + $(IntDir)$(TargetName).pdb + Windows + false + false + %(AdditionalLibraryDirectories) + + + xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\ +xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\ + Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\' + + + + + MinSpace + Size + .;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_WINDOWS;_CRT_SECURE_NO_WARNINGS;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions) + true + true + MultiThreaded + false + Level3 + None + 4091;4244;%(DisableSpecificWarnings) + $(IntDir)$(TargetName).pdb + + + comctl32.lib;rpcrt4.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)$(TargetExt) + false + $(IntDir)$(TargetName).pdb + Windows + true + true + false + MachineX86 + false + %(AdditionalLibraryDirectories) + + + xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\ + Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\' + + + + + MinSpace + Size + .;%(AdditionalIncludeDirectories) + WIN64;NDEBUG;_WINDOWS;_CRT_SECURE_NO_WARNINGS;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions) + true + true + MultiThreaded + false + Level3 + None + 4091;4244;%(DisableSpecificWarnings) + $(IntDir)$(TargetName).pdb + + + comctl32.lib;rpcrt4.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)$(TargetExt) + false + $(IntDir)$(TargetName).pdb + Windows + true + true + false + false + %(AdditionalLibraryDirectories) + + + xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\ + Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\' + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Src/Plugins/General/gen_crasher/feedback/feedback.vcxproj.filters b/Src/Plugins/General/gen_crasher/feedback/feedback.vcxproj.filters new file mode 100644 index 00000000..f2609acf --- /dev/null +++ b/Src/Plugins/General/gen_crasher/feedback/feedback.vcxproj.filters @@ -0,0 +1,79 @@ + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + {de803cce-7a8d-46da-97a8-cb794b2ef154} + + + {6cea30eb-0534-44ec-af7c-340d8047d962} + + + {a281ea66-f75c-47a9-90b7-61a031b4ea22} + + + + + Ressource Files + + + + + Ressource Files + + + \ No newline at end of file diff --git a/Src/Plugins/General/gen_crasher/feedback/main.cpp b/Src/Plugins/General/gen_crasher/feedback/main.cpp new file mode 100644 index 00000000..b425fd15 --- /dev/null +++ b/Src/Plugins/General/gen_crasher/feedback/main.cpp @@ -0,0 +1,79 @@ +// feedback.cpp : Defines the entry point for the application. +// +#include ".\main.h" +#include +#include +Settings settings; + +int WINAPI wWinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpszCmdLine, int nCmdShow) +{ + wchar_t *argv[2] = {0}; + int argc = 0; + if (lpszCmdLine && wcslen(lpszCmdLine) >0) argc = ParseCommandLine(lpszCmdLine, NULL); + if (argc != 1) + { + MSGBOXPARAMSW msgbx = {sizeof(MSGBOXPARAMS),0}; + msgbx.lpszText = L"Winamp Error Reporter\nCopyright © 2005-2014 Winamp SA"; + msgbx.lpszCaption = L"About..."; + msgbx.lpszIcon = MAKEINTRESOURCEW(ICON_XP); + msgbx.hInstance = GetModuleHandle(0); + msgbx.dwStyle = MB_USERICON; + MessageBoxIndirectW(&msgbx); + return 0; + } + + ParseCommandLine(lpszCmdLine, argv); + settings.SetPath(argv[0]); + + if (!settings.Load()) + { + MessageBox(NULL, L"Unable to load settings.", L"Error", MB_OK); + return 0; + } + + INITCOMMONCONTROLSEX icex = {sizeof(icex), ICC_WIN95_CLASSES}; + InitCommonControlsEx(&icex); + DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_DLG_SILENT), NULL, (DLGPROC)SilentDlgProc, (LPARAM)hInstance); + return 0; +} + +static int ParseCommandLine(wchar_t *cmdline, wchar_t **argv) +{ + wchar_t *bufp; + int argc; + argc = 0; + for ( bufp = cmdline; *bufp; ) + { + /* Skip leading whitespace */ + while ( isspace(*bufp) ) ++bufp; + /* Skip over argument */ + if ( *bufp == L'"' ) + { + ++bufp; + if ( *bufp ) + { + if ( argv ) argv[argc] = bufp; + ++argc; + } + /* Skip over word */ + while ( *bufp && (*bufp != L'"') ) ++bufp; + } + else + { + if ( *bufp ) + { + if ( argv ) argv[argc] = bufp; + ++argc; + } + /* Skip over word */ + while ( *bufp && ! isspace(*bufp) ) ++bufp; + } + if ( *bufp ) + { + if ( argv ) *bufp = L'\0'; + ++bufp; + } + } + if ( argv ) argv[argc] = NULL; + return(argc); +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_crasher/feedback/main.h b/Src/Plugins/General/gen_crasher/feedback/main.h new file mode 100644 index 00000000..bd2363fd --- /dev/null +++ b/Src/Plugins/General/gen_crasher/feedback/main.h @@ -0,0 +1,19 @@ +#ifndef FEEDBACK_H +#define FEEDBACK_H + +#include +#include "resource.h" + +#include "..\settings.h" + +extern Settings settings; + +static int ParseCommandLine(wchar_t *cmdline, wchar_t **argv); + +BOOL CALLBACK SilentDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); + +BOOL ZipData(void); +BOOL SendData(HWND hwnd); +BOOL Restart(void); + +#endif FEEDBACK_H \ No newline at end of file diff --git a/Src/Plugins/General/gen_crasher/feedback/manifest.xml b/Src/Plugins/General/gen_crasher/feedback/manifest.xml new file mode 100644 index 00000000..651ac743 --- /dev/null +++ b/Src/Plugins/General/gen_crasher/feedback/manifest.xml @@ -0,0 +1,36 @@ + + + + Nullsoft Error Reporter + + + + + + + + + + + + + + + true + + + \ No newline at end of file diff --git a/Src/Plugins/General/gen_crasher/feedback/operations.cpp b/Src/Plugins/General/gen_crasher/feedback/operations.cpp new file mode 100644 index 00000000..5919c690 --- /dev/null +++ b/Src/Plugins/General/gen_crasher/feedback/operations.cpp @@ -0,0 +1,163 @@ +#include ".\main.h" +#include ".\xzip\xzip.h" +#include ".\smtp\smtp.h" +#include ".\email\sendemail.h" +#include + +const wchar_t* GetFileName(const wchar_t *fullname) +{ + if (!fullname) return NULL; + const wchar_t *start = wcsrchr(fullname, L'\\'); + if (start && start != fullname) start = CharNext(start); + + return start; +} + +BOOL ZipData(void) +{ + BOOL retCode = FALSE; + HZIP hz = CreateZip(settings.zipPath, 0, ZIP_FILENAME); + if (hz) + { + retCode = TRUE; + if (settings.createLOG && settings.ReadLogCollectResult()) retCode = (ZR_OK == ZipAdd(hz, GetFileName(settings.logPath), settings.logPath, 0, ZIP_FILENAME)); + if (retCode && settings.createDMP && settings.ReadDmpCollectResult()) retCode = (ZR_OK == ZipAdd(hz, GetFileName(settings.dumpPath), settings.dumpPath, 0, ZIP_FILENAME)); + } + CloseZip(hz); + return retCode; +} + +LPCTSTR BuildSubjectString(LPCTSTR subject) +{ + static wchar_t subject_str[512] = {L"Winamp Error Report"}; + wchar_t uid_str[64] = {0}, path[MAX_PATH] = {0}; + if (GetModuleFileName(0, path, MAX_PATH)) + { + PathRemoveFileSpec(path); + wchar_t *p = path + wcslen(path) - 1; + while(p && *p && *p != L'\\') + { + p = CharPrev(path, p); + } + if (p) *p = 0; + PathAppend(path, L"winamp.exe"); + + HKEY hkey = NULL; + if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Nullsoft\\Winamp", 0, 0, 0, KEY_READ, NULL, &hkey, NULL) == ERROR_SUCCESS) + { + DWORD s = 512, t = REG_SZ; + if (RegQueryValueEx(hkey, path, 0, &t, (LPBYTE)uid_str, &s) != ERROR_SUCCESS || t != REG_SZ) uid_str[0] = 0; + RegCloseKey(hkey); + } + } + + // if it fails then we'll need to make something... + if (!uid_str[0]) + { + GUID guid; + UuidCreate(&guid); + StringCbPrintf(uid_str, ARRAYSIZE(uid_str), L"%08X%04X%04X%02X%02X%02X%02X%02X%02X%02X%02X", + (int)guid.Data1, (int)guid.Data2, (int)guid.Data3, (int)guid.Data4[0], + (int)guid.Data4[1], (int)guid.Data4[2], (int)guid.Data4[3], + (int)guid.Data4[4], (int)guid.Data4[5], (int)guid.Data4[6], (int)guid.Data4[7]); + } + + if (StringCchPrintfW(subject_str, ARRAYSIZE(subject_str), L"%s [%s]", subject, uid_str) == S_OK) + return subject_str; + else + return subject; +} + +BOOL SendData(HWND hwnd) +{ + BOOL retCode = FALSE; + const wchar_t *subject = L"Winamp Error Report"; + const wchar_t *senderName = L"Winamp Error Reporter"; + const wchar_t *recipientAddress = L"bug@winamp.com"; + const wchar_t *recipientName = L"Nullsoft Bug Reporting"; + wchar_t *msgInfo = _wcsdup(settings.ReadBody()); + + wchar_t *p = msgInfo, *end = p + wcslen(msgInfo); + while(p != end) + { + if (*p == 1) *p = L'\r'; + if (*p == 2) *p = L'\n'; + p++; + } + + if (settings.sendBySMTP) + { + CSmtp smtp; + CSmtpMessage msg; + CSmtpMessageBody body; + CSmtpAttachment attach; + + msg.Subject = BuildSubjectString(subject); + + // Who the message is from + msg.Sender.Name = senderName; + msg.Sender.Address = settings.smtpAddress; + msg.Recipient.Address = recipientAddress; + msg.Recipient.Name = recipientName; + if(settings.zipData ) + { + attach.FileName = settings.zipPath; + msg.Attachments.Add(attach); + } + else + { + if (settings.createLOG && settings.ReadLogCollectResult()) attach.FileName = settings.logPath; + msg.Attachments.Add(attach); + if (settings.createDMP && settings.ReadDmpCollectResult()) attach.FileName = settings.dumpPath; + msg.Attachments.Add(attach); + } + + // smtp.m_wSmtpPort = settings.smtpPort; - not working for some reasons + if (settings.smtpAuth) + { + smtp.m_strUser = settings.smtpUser; + smtp.m_strPass = settings.smtpPwd;; + } + + body = L"This message was generated by Winamp Error Reporter v1.09.\r\nPlease check attachments for viruses.\r\n"; + body.Data.append(L"\r\n"); + body.Data.append(msgInfo); + + msg.Message.Add(body); + retCode = smtp.Connect(settings.smtpServer); + if ( retCode ) + { + // Send the message and close the connection afterwards + retCode = (smtp.SendMessage(msg) == 0); + smtp.Close(); + } + } + else if(settings.sendByClient) + { + retCode = SendEmail(hwnd, recipientAddress, recipientName, BuildSubjectString(subject), msgInfo, settings.zipPath); + } + + return retCode; +} + +BOOL Restart(void) +{ + STARTUPINFO si = {0}; + si.cb = sizeof(si); + si.dwFlags = STARTF_USESHOWWINDOW; + si.wShowWindow = SW_SHOW; + + PROCESS_INFORMATION pi = {0}; + return CreateProcess( + settings.ReadWinamp(), // name of executable module + NULL, // command line string + NULL, // process attributes + NULL, // thread attributes + FALSE, // handle inheritance option + 0, // creation flags + NULL, // new environment block + NULL, // current directory name + &si, // startup information + &pi // process information + ); +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_crasher/feedback/resource.h b/Src/Plugins/General/gen_crasher/feedback/resource.h new file mode 100644 index 00000000..4c564828 --- /dev/null +++ b/Src/Plugins/General/gen_crasher/feedback/resource.h @@ -0,0 +1,29 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by feedback.rc +// +#define IDC_MYICON 2 +#define IDD_DLG_SILENT 102 +#define IDS_APP_TITLE 103 +#define IDM_ABOUT 104 +#define IDM_EXIT 105 +#define ICON_XP 108 +#define IDR_MAINFRAME 128 +#define IDR_RT_MANIFEST1 133 +#define IDC_PRG_COLLECT 1000 +#define IDC_LBL_STEP 1001 +#define IDC_BUTTON1 1003 +#define IDC_BUTTON2 1004 +#define IDC_STATIC -1 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NO_MFC 1 +#define _APS_NEXT_RESOURCE_VALUE 134 +#define _APS_NEXT_COMMAND_VALUE 32771 +#define _APS_NEXT_CONTROL_VALUE 1005 +#define _APS_NEXT_SYMED_VALUE 110 +#endif +#endif diff --git a/Src/Plugins/General/gen_crasher/feedback/silent.cpp b/Src/Plugins/General/gen_crasher/feedback/silent.cpp new file mode 100644 index 00000000..3d756e70 --- /dev/null +++ b/Src/Plugins/General/gen_crasher/feedback/silent.cpp @@ -0,0 +1,134 @@ +#include ".\main.h" +#include +#include +#include "resource.h" + +BOOL CALLBACK SilentDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch(uMsg) + { + case WM_INITDIALOG: + { + HICON hIcon = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(ICON_XP)); + SetClassLongPtr(hwndDlg, GCLP_HICON, (LONG_PTR)hIcon); + + HWND hwndPrg = GetDlgItem(hwndDlg, IDC_PRG_COLLECT); + SendMessage(hwndPrg, PBM_SETRANGE, 0, MAKELPARAM(0,100)); + SendMessage(hwndPrg, PBM_SETPOS, 0, 0); + SetDlgItemText(hwndDlg, IDC_LBL_STEP, L"Starting reporter..."); + ShowWindow(GetDlgItem(hwndDlg, IDC_BUTTON1), SW_HIDE); + ShowWindow(GetDlgItem(hwndDlg, IDC_BUTTON2), SW_HIDE); + UpdateWindow(hwndDlg); + + if ((settings.createLOG && !settings.ReadLogCollectResult()) && + (settings.createDMP && !settings.ReadDmpCollectResult()) ) + { + SetDlgItemText(hwndDlg, IDC_LBL_STEP, L"Error. Data was not generated."); + SendMessage(hwndPrg, PBM_SETPOS, 100, 0); + UpdateWindow(hwndDlg); + SetTimer(hwndDlg, 126, 2000, NULL); + break; + } + SetTimer(hwndDlg, 123, 500, NULL); + break; + } + case WM_COMMAND: + switch(LOWORD(wParam)) + { + case IDC_BUTTON1: + { + BOOL ret = FALSE; + wchar_t file[MAX_PATH] = {0}; + lstrcpyn(file, settings.zipPath, MAX_PATH); + + LPSHELLFOLDER pDesktopFolder = 0; + if(SUCCEEDED(SHGetDesktopFolder(&pDesktopFolder))) + { + LPITEMIDLIST filepidl = 0; + HRESULT hr = pDesktopFolder->ParseDisplayName(NULL,0,file,0,&filepidl,0); + if(FAILED(hr)){ pDesktopFolder->Release(); ret = FALSE; } + else + { + if(SUCCEEDED(SHOpenFolderAndSelectItems(filepidl,0,NULL,NULL))){ + ret = TRUE; + } + } + } + + if (ret == FALSE) + { + SetDlgItemText(hwndDlg, IDC_LBL_STEP, L"Error. Unable to locate crash report."); + UpdateWindow(hwndDlg); + } + } + break; + case IDCANCEL: + case IDC_BUTTON2: + SetTimer(hwndDlg, 126, 1, NULL); + break; + } + break; + case WM_TIMER: + if (wParam == 123) + { + KillTimer(hwndDlg, wParam); + HWND hwndPrg; + hwndPrg = GetDlgItem(hwndDlg, IDC_PRG_COLLECT); + SendMessage(hwndPrg, PBM_SETPOS, 20, 0); + if (settings.zipData) + { + SetDlgItemText(hwndDlg, IDC_LBL_STEP, L"Packing results..."); + if(!ZipData()) + { + SetDlgItemText(hwndDlg, IDC_LBL_STEP, L"Error. Unable to pack results."); + SendMessage(hwndPrg, PBM_SETPOS, 100, 0); + UpdateWindow(hwndDlg); + SetTimer(hwndDlg, 126, 2000, NULL); + break; + } + } + SendMessage(hwndPrg, PBM_SETPOS, 40, 0); + UpdateWindow(hwndDlg); + if (settings.sendData) + { + SetDlgItemText(hwndDlg, IDC_LBL_STEP, L"Sending results..."); + UpdateWindow(hwndDlg); + if(!SendData(hwndDlg)) + { + SetDlgItemText(hwndDlg, IDC_LBL_STEP, L"Error. Unable to send crash report."); + SendMessage(hwndPrg, PBM_SETPOS, 100, 0); + ShowWindow(GetDlgItem(hwndDlg, IDC_BUTTON1), SW_SHOW); + ShowWindow(GetDlgItem(hwndDlg, IDC_BUTTON2), SW_SHOW); + ShowWindow(GetDlgItem(hwndDlg, IDC_PRG_COLLECT), SW_HIDE); + UpdateWindow(hwndDlg); + break; + } + } + if (settings.autoRestart) + { + SetDlgItemText(hwndDlg, IDC_LBL_STEP, L"Restarting Winamp..."); + SendMessage(hwndPrg, PBM_SETPOS, 80, 0); + UpdateWindow(hwndDlg); + if(!Restart()) + { + SetDlgItemText(hwndDlg, IDC_LBL_STEP, L"Error. Unable to restart Winamp."); + SendMessage(hwndPrg, PBM_SETPOS, 100, 0); + UpdateWindow(hwndDlg); + SetTimer(hwndDlg, 126, 2000, NULL); + break; + } + } + SetDlgItemText(hwndDlg, IDC_LBL_STEP, L"Done."); + SendMessage(hwndPrg, PBM_SETPOS, 100, 0); + UpdateWindow(hwndDlg); + SetTimer(hwndDlg, 126, 1000, NULL); + } + else if (wParam == 126) + { + KillTimer(hwndDlg, wParam); + EndDialog(hwndDlg, TRUE); + } + break; + } + return FALSE; +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_crasher/feedback/smtp/Base64.cpp b/Src/Plugins/General/gen_crasher/feedback/smtp/Base64.cpp new file mode 100644 index 00000000..fb95336e --- /dev/null +++ b/Src/Plugins/General/gen_crasher/feedback/smtp/Base64.cpp @@ -0,0 +1,320 @@ +// CBase64.cpp: implementation of the CBase64 class. +// +////////////////////////////////////////////////////////////////////// + +#include "Base64.h" + +// Digits... +static char Base64Digits[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +BOOL CBase64::m_Init = FALSE; +char CBase64::m_DecodeTable[256]; + +#ifndef PAGESIZE +#define PAGESIZE 4096 +#endif + +#ifndef ROUNDTOPAGE +#define ROUNDTOPAGE(a) (((a/4096)+1)*4096) +#endif + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction +////////////////////////////////////////////////////////////////////// + +CBase64::CBase64() + : m_pDBuffer(NULL), + m_pEBuffer(NULL), + m_nDBufLen(0), + m_nEBufLen(0), + m_nDDataLen(0), + m_nEDataLen(0) +{ + +} + +CBase64::~CBase64() +{ + if(m_pDBuffer != NULL) + delete [] m_pDBuffer; + + if(m_pEBuffer != NULL) + delete [] m_pEBuffer; +} + +LPCSTR CBase64::DecodedMessage() const +{ + return (LPCSTR) m_pDBuffer; +} + +LPCSTR CBase64::EncodedMessage() const +{ + return (LPCSTR) m_pEBuffer; +} + +void CBase64::AllocEncode(DWORD nSize) +{ + if(m_nEBufLen < nSize) + { + if(m_pEBuffer != NULL) + delete [] m_pEBuffer; + + m_nEBufLen = ROUNDTOPAGE(nSize); + m_pEBuffer = new BYTE[m_nEBufLen]; + } + + ::ZeroMemory(m_pEBuffer, m_nEBufLen); + m_nEDataLen = 0; +} + +void CBase64::AllocDecode(DWORD nSize) +{ + if(m_nDBufLen < nSize) + { + if(m_pDBuffer != NULL) + delete [] m_pDBuffer; + + m_nDBufLen = ROUNDTOPAGE(nSize); + m_pDBuffer = new BYTE[m_nDBufLen]; + } + + ::ZeroMemory(m_pDBuffer, m_nDBufLen); + m_nDDataLen = 0; +} + +void CBase64::SetEncodeBuffer(const PBYTE pBuffer, DWORD nBufLen) +{ + DWORD i = 0; + + AllocEncode(nBufLen); + while(i < nBufLen) + { + if(!_IsBadMimeChar(pBuffer[i])) + { + m_pEBuffer[m_nEDataLen] = pBuffer[i]; + m_nEDataLen++; + } + + i++; + } +} + +void CBase64::SetDecodeBuffer(const PBYTE pBuffer, DWORD nBufLen) +{ + AllocDecode(nBufLen); + ::CopyMemory(m_pDBuffer, pBuffer, nBufLen); + m_nDDataLen = nBufLen; +} + +void CBase64::Encode(const PBYTE pBuffer, DWORD nBufLen) +{ + SetDecodeBuffer(pBuffer, nBufLen); + AllocEncode(nBufLen * 2); + + TempBucket Raw; + DWORD nIndex = 0; + + while((nIndex + 3) <= nBufLen) + { + Raw.Clear(); + ::CopyMemory(&Raw, m_pDBuffer + nIndex, 3); + Raw.nSize = 3; + _EncodeToBuffer(Raw, m_pEBuffer + m_nEDataLen); + nIndex += 3; + m_nEDataLen += 4; + } + + if(nBufLen > nIndex) + { + Raw.Clear(); + Raw.nSize = (BYTE) (nBufLen - nIndex); + ::CopyMemory(&Raw, m_pDBuffer + nIndex, nBufLen - nIndex); + _EncodeToBuffer(Raw, m_pEBuffer + m_nEDataLen); + m_nEDataLen += 4; + } +} + +void CBase64::Encode(LPCSTR szMessage) +{ + if(szMessage != NULL) + CBase64::Encode((const PBYTE)szMessage, lstrlenA(szMessage)); +} + +void CBase64::Decode(const PBYTE pBuffer, DWORD dwBufLen) +{ + if(!CBase64::m_Init) + _Init(); + + SetEncodeBuffer(pBuffer, dwBufLen); + + AllocDecode(dwBufLen); + + TempBucket Raw; + + DWORD nIndex = 0; + + while((nIndex + 4) <= m_nEDataLen) + { + Raw.Clear(); + Raw.nData[0] = CBase64::m_DecodeTable[m_pEBuffer[nIndex]]; + Raw.nData[1] = CBase64::m_DecodeTable[m_pEBuffer[nIndex + 1]]; + Raw.nData[2] = CBase64::m_DecodeTable[m_pEBuffer[nIndex + 2]]; + Raw.nData[3] = CBase64::m_DecodeTable[m_pEBuffer[nIndex + 3]]; + + if(Raw.nData[2] == 255) + Raw.nData[2] = 0; + if(Raw.nData[3] == 255) + Raw.nData[3] = 0; + + Raw.nSize = 4; + _DecodeToBuffer(Raw, m_pDBuffer + m_nDDataLen); + nIndex += 4; + m_nDDataLen += 3; + } + + // If nIndex < m_nEDataLen, then we got a decode message without padding. + // We may want to throw some kind of warning here, but we are still required + // to handle the decoding as if it was properly padded. + if(nIndex < m_nEDataLen) + { + Raw.Clear(); + for(DWORD i = nIndex; i < m_nEDataLen; i++) + { + Raw.nData[i - nIndex] = CBase64::m_DecodeTable[m_pEBuffer[i]]; + Raw.nSize++; + if(Raw.nData[i - nIndex] == 255) + Raw.nData[i - nIndex] = 0; + } + + _DecodeToBuffer(Raw, m_pDBuffer + m_nDDataLen); + m_nDDataLen += (m_nEDataLen - nIndex); + } +} + +void CBase64::Decode(LPCSTR szMessage) +{ + if(szMessage != NULL) + CBase64::Decode((const PBYTE)szMessage, lstrlenA(szMessage)); +} + +DWORD CBase64::_DecodeToBuffer(const TempBucket &Decode, PBYTE pBuffer) +{ + TempBucket Data; + DWORD nCount = 0; + + _DecodeRaw(Data, Decode); + + for(int i = 0; i < 3; i++) + { + pBuffer[i] = Data.nData[i]; + if(pBuffer[i] != 255) + nCount++; + } + + return nCount; +} + + +void CBase64::_EncodeToBuffer(const TempBucket &Decode, PBYTE pBuffer) +{ + TempBucket Data; + + _EncodeRaw(Data, Decode); + + for(int i = 0; i < 4; i++) + pBuffer[i] = Base64Digits[Data.nData[i]]; + + switch(Decode.nSize) + { + case 1: + pBuffer[2] = '='; + case 2: + pBuffer[3] = '='; + } +} + +void CBase64::_DecodeRaw(TempBucket &Data, const TempBucket &Decode) +{ + BYTE nTemp; + + Data.nData[0] = Decode.nData[0]; + Data.nData[0] <<= 2; + + nTemp = Decode.nData[1]; + nTemp >>= 4; + nTemp &= 0x03; + Data.nData[0] |= nTemp; + + Data.nData[1] = Decode.nData[1]; + Data.nData[1] <<= 4; + + nTemp = Decode.nData[2]; + nTemp >>= 2; + nTemp &= 0x0F; + Data.nData[1] |= nTemp; + + Data.nData[2] = Decode.nData[2]; + Data.nData[2] <<= 6; + nTemp = Decode.nData[3]; + nTemp &= 0x3F; + Data.nData[2] |= nTemp; +} + +void CBase64::_EncodeRaw(TempBucket &Data, const TempBucket &Decode) +{ + BYTE nTemp; + + Data.nData[0] = Decode.nData[0]; + Data.nData[0] >>= 2; + + Data.nData[1] = Decode.nData[0]; + Data.nData[1] <<= 4; + nTemp = Decode.nData[1]; + nTemp >>= 4; + Data.nData[1] |= nTemp; + Data.nData[1] &= 0x3F; + + Data.nData[2] = Decode.nData[1]; + Data.nData[2] <<= 2; + + nTemp = Decode.nData[2]; + nTemp >>= 6; + + Data.nData[2] |= nTemp; + Data.nData[2] &= 0x3F; + + Data.nData[3] = Decode.nData[2]; + Data.nData[3] &= 0x3F; +} + +BOOL CBase64::_IsBadMimeChar(BYTE nData) +{ + switch(nData) + { + case '\r': case '\n': case '\t': case ' ' : + case '\b': case '\a': case '\f': case '\v': + return TRUE; + default: + return FALSE; + } +} + +void CBase64::_Init() +{ // Initialize Decoding table. + + int i; + + for(i = 0; i < 256; i++) + CBase64::m_DecodeTable[i] = -2; + + for(i = 0; i < 64; i++) + { + CBase64::m_DecodeTable[Base64Digits[i]] = (CHAR)i; + CBase64::m_DecodeTable[Base64Digits[i]|0x80] = (CHAR)i; + } + + CBase64::m_DecodeTable['='] = -1; + CBase64::m_DecodeTable['='|0x80] = -1; + + CBase64::m_Init = TRUE; +} diff --git a/Src/Plugins/General/gen_crasher/feedback/smtp/Base64.h b/Src/Plugins/General/gen_crasher/feedback/smtp/Base64.h new file mode 100644 index 00000000..4e5d3924 --- /dev/null +++ b/Src/Plugins/General/gen_crasher/feedback/smtp/Base64.h @@ -0,0 +1,63 @@ +// CBase64.h: interface for the CBase64 class. +// +////////////////////////////////////////////////////////////////////// + +#if !defined(AFX_CBase64_H__B2E45717_0625_11D2_A80A_00C04FB6794C__INCLUDED_) +#define AFX_CBase64_H__B2E45717_0625_11D2_A80A_00C04FB6794C__INCLUDED_ + +#include + +#define lCONTEXT char +#define PlCONTEXT lCONTEXT* + + + +class CBase64 +{ + // Internal bucket class. + class TempBucket + { + public: + BYTE nData[4]; + BYTE nSize; + void Clear() { ::ZeroMemory(nData, 4); nSize = 0; }; + }; + + PBYTE m_pDBuffer; + PBYTE m_pEBuffer; + DWORD m_nDBufLen; + DWORD m_nEBufLen; + DWORD m_nDDataLen; + DWORD m_nEDataLen; + +public: + CBase64(); + virtual ~CBase64(); + +public: + virtual void Encode(const PBYTE, DWORD); + virtual void Decode(const PBYTE, DWORD); + virtual void Encode(LPCSTR sMessage); + virtual void Decode(LPCSTR sMessage); + + virtual LPCSTR DecodedMessage() const; + virtual LPCSTR EncodedMessage() const; + + virtual void AllocEncode(DWORD); + virtual void AllocDecode(DWORD); + virtual void SetEncodeBuffer(const PBYTE pBuffer, DWORD nBufLen); + virtual void SetDecodeBuffer(const PBYTE pBuffer, DWORD nBufLen); + +protected: + virtual void _EncodeToBuffer(const TempBucket &Decode, PBYTE pBuffer); + virtual ULONG _DecodeToBuffer(const TempBucket &Decode, PBYTE pBuffer); + virtual void _EncodeRaw(TempBucket &, const TempBucket &); + virtual void _DecodeRaw(TempBucket &, const TempBucket &); + virtual BOOL _IsBadMimeChar(BYTE); + + static char m_DecodeTable[256]; + static BOOL m_Init; + void _Init(); +}; + +#endif // !defined(AFX_CBase64_H__B2E45717_0625_11D2_A80A_00C04FB6794C__INCLUDED_) diff --git a/Src/Plugins/General/gen_crasher/feedback/smtp/Smtp.cpp b/Src/Plugins/General/gen_crasher/feedback/smtp/Smtp.cpp new file mode 100644 index 00000000..97e319e1 --- /dev/null +++ b/Src/Plugins/General/gen_crasher/feedback/smtp/Smtp.cpp @@ -0,0 +1,1468 @@ +////////////////////////////////////////////////////////////////////// +/* + Smtp.cpp: implementation of the CSmtp and CSmtpMessage classes + + Written by Robert Simpson (robert@blackcastlesoft.com) + Created 11/1/2000 + Version 1.7 -- Last Modified 06/18/2001 + + 1.7 - Modified the code that gets the GMT offset and the code that + parses the date/time as per Noa Karsten's suggestions on + codeguru. + - Added an FD_ZERO(&set) to the last part of SendCmd(), since + I use the set twice and only zero it out once. Submitted by + Marc Allen. + - Removed the requirement that a message have a body and/or an + attachment. This allows for sending messages with only a + subject line. Submitted by Marc Allen. + 1.6 - Apparently older versions of the STL do not have the clear() + method for basic_string's. I modified the code to use + erase() instead. + - Added #include to the smtp.h file, which will + allow any app to use these classes without problems. + 1.5 - Guess I should have checked EncodeQuotedPrintable() as well, + since it did the same thing BreakMessage() did in adding an + extranneous CRLF to the end of any text it processed. Fixed. + 1.4 - BreakMesage() added an extranneous CRLF to the end of any + text it processed, which is now fixed. Certainly not a big + deal, but it caused text attachments to not be 100% identical + to the original. + 1.3 - Added a new class, CSmtpMimePart, to which the CSmtpAttachment + and CSmtpMessageBody classes inherit. This was done for + future expansion. CSmtpMimePart has a new ContentId string + value for optionally assigning a unique content ID value to + body parts and attachments. This was done to support the + multipart/related enhancement + - Support for multipart/related messages, which can be used + for sending html messages with embedded images. + - Modifed CSmtpMessage, adding a new MimeType member variable + so the user can specify a certain type of MIME format to use + when coding the message. + - Fixed a bug where multipart/alternative messages with multiple + message bodies were not properly processed when attachments + were also included in the message. + - Some small optimizations during the CSmtpMessage::Parse routine + + 1.2 - Vastly improved the time it takes to break a message, + which was dog slow with large attachments. My bad. + - Added another overridable, SmtpProgress() which is + called during the CSmtp::SendCmd() function when there + is a large quantity of data being sent over the wire. + Added CMD_BLOCK_SIZE to support the above new feature + - Added support for UNICODE + - Added the CSmtpAttachment class for better control and + expandability for attachments. + - Added alternative implementations for CSmtp::SendMessage + which make it easier to send simple messages via a single + function call. + - Added a constructor to CSmtpAddress for assigning default + values during initialization. + - Added a #pragma comment(lib,"wsock32.lib") to the smtp.h + file so existing projects don't have to have their linker + options modified. + + 1.1 - Rearranged the headers so they are written out as: + From,To,Subject,Date,MimeVersion, + followed by all remaining headers + - Modified the class to support multipart/alternative with + multiple message bodies. + + Note that CSimpleMap does not sort the key values, and CSmtp + takes advantage of this by writing the headers out in the reverse + order of how they will be parsed before being sent to the SMTP + server. If you modify the code to use std::map or any other map + class, the headers may be alphabetized by key, which may cause + some mail clients to show the headers in the body of the message + or cause other undesirable results when viewing the message. +*/ +////////////////////////////////////////////////////////////////////// + +#include "Smtp.h" + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction for CSmtpMessageBody +////////////////////////////////////////////////////////////////////// +CSmtpMessageBody::CSmtpMessageBody(LPCTSTR pszBody, LPCTSTR pszEncoding, LPCTSTR pszCharset, EncodingEnum encode) +{ + + // Set the default message encoding method + // To transfer html messages, make Encoding = _T("text/html") + if (pszEncoding) Encoding = pszEncoding; + if (pszCharset) Charset = pszCharset; + if (pszBody) Data = pszBody; + TransferEncoding = encode; +} + +const CSmtpMessageBody& CSmtpMessageBody::operator=(LPCTSTR pszBody) +{ + Data = pszBody; + return *this; +} + +const CSmtpMessageBody& CSmtpMessageBody::operator=(const String& strBody) +{ + Data = strBody; + return *this; +} + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction for CSmtpAttachment +////////////////////////////////////////////////////////////////////// +CSmtpAttachment::CSmtpAttachment(LPCTSTR pszFilename, LPCTSTR pszAltName, BOOL bIsInline, LPCTSTR pszEncoding, LPCTSTR pszCharset, EncodingEnum encode) +{ + if (pszFilename) FileName = pszFilename; + if (pszAltName) AltName = pszAltName; + if (pszEncoding) Encoding = pszEncoding; + if (pszCharset) Charset = pszCharset; + TransferEncoding = encode; + Inline = bIsInline; +} + +const CSmtpAttachment& CSmtpAttachment::operator=(LPCTSTR pszFilename) +{ + FileName = pszFilename; + return *this; +} + +const CSmtpAttachment& CSmtpAttachment::operator=(const String& strFilename) +{ + FileName = strFilename; + return *this; +} + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction for CSmtpAddress +////////////////////////////////////////////////////////////////////// +CSmtpAddress::CSmtpAddress(LPCTSTR pszAddress, LPCTSTR pszName) +{ + if (pszAddress) Address = pszAddress; + if (pszName) Name = pszName; +} + +const CSmtpAddress& CSmtpAddress::operator=(LPCTSTR pszAddress) +{ + Address = pszAddress; + return *this; +} + +const CSmtpAddress& CSmtpAddress::operator=(const String& strAddress) +{ + Address = strAddress; + return *this; +} + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction for CSmtpMessage +////////////////////////////////////////////////////////////////////// +CSmtpMessage::CSmtpMessage() +{ + TIME_ZONE_INFORMATION tzi; + DWORD dwRet; + long Offset; + + // Get local time and timezone offset + GetLocalTime(&Timestamp); + GMTOffset = 0; + dwRet = GetTimeZoneInformation(&tzi); + Offset = tzi.Bias; + if (dwRet == TIME_ZONE_ID_STANDARD) Offset += tzi.StandardBias; + if (dwRet == TIME_ZONE_ID_DAYLIGHT) Offset += tzi.DaylightBias; + GMTOffset = -((Offset / 60) * 100 + (Offset % 60)); + + MimeType = mimeGuess; +} + +// Write all the headers to the e-mail message. +// This is done just before sending it, when we're sure the user wants it to go out. +void CSmtpMessage::CommitHeaders() +{ + TCHAR szTime[64] = {0}; + TCHAR szDate[64] = {0}; + TCHAR szOut[1024] = {0}; + String strHeader; + String strValue; + int n; + + // Assign a few standard headers to the message + strHeader = _T("X-Priority"); + strValue = _T("3 (Normal)"); + // Only add the key if it doesn't exist already in the headers map + if (Headers.FindKey(strHeader) == -1) Headers.Add(strHeader,strValue); + + strHeader = _T("X-MSMail-Priority"); + strValue = _T("Normal"); + if (Headers.FindKey(strHeader) == -1) Headers.Add(strHeader,strValue); + + strHeader = _T("X-Mailer"); + strValue = _T("ATL CSmtp Class Mailer by Robert Simpson (robert@blackcastlesoft.com)"); + if (Headers.FindKey(strHeader) == -1) Headers.Add(strHeader,strValue); + + strHeader = _T("Importance"); + strValue = _T("Normal"); + if (Headers.FindKey(strHeader) == -1) Headers.Add(strHeader,strValue); + + // Get the time/date stamp and GMT offset for the Date header. + GetDateFormat(MAKELCID(LANG_ENGLISH, SORT_DEFAULT),0,&Timestamp,_T("ddd, d MMM yyyy"),szDate,64); + GetTimeFormat(MAKELCID(LANG_ENGLISH, SORT_DEFAULT),0,&Timestamp,_T("H:mm:ss"),szTime,64); + + // Add the date/time stamp to the message headers + wsprintf(szOut,_T("%s %s %c%4.4d"),szDate,szTime,(GMTOffset>0)?'+':'-',GMTOffset); + + strHeader = _T("Date"); + strValue = szOut; + Headers.Remove(strHeader); + Headers.Add(strHeader,strValue); + + // Write out the subject header + strHeader = _T("Subject"); + strValue = Subject; + Headers.Remove(strHeader); + Headers.Add(strHeader,strValue); + + // Write out the TO header + strValue.erase(); + strHeader = _T("To"); + if (Recipient.Name.length()) + { + wsprintf(szOut,_T("\"%s\" "),Recipient.Name.c_str()); + strValue += szOut; + } + if (Recipient.Address.length()) + { + wsprintf(szOut,_T("<%s>"),Recipient.Address.c_str()); + strValue += szOut; + } + // Write out all the CC'd names + for (n = 0;n < CC.GetSize();n++) + { + if (strValue.length()) strValue += _T(",\r\n\t"); + if (CC[n].Name.length()) + { + wsprintf(szOut,_T("\"%s\" "),CC[n].Name.c_str()); + strValue += szOut; + } + wsprintf(szOut,_T("<%s>"),CC[n].Address.c_str()); + strValue += szOut; + } + Headers.Remove(strHeader); + Headers.Add(strHeader,strValue); + + // Write out the FROM header + strValue.erase(); + strHeader = _T("From"); + if (Sender.Name.length()) + { + wsprintf(szOut,_T("\"%s\" "),Sender.Name.c_str()); + strValue += szOut; + } + wsprintf(szOut,_T("<%s>"),Sender.Address.c_str()); + strValue += szOut; + Headers.Remove(strHeader); + Headers.Add(strHeader,strValue); +} + +// Parse a message into a single string +void CSmtpMessage::Parse(String& strDest) +{ + String strHeader; + String strValue; + String strTemp; + String strBoundary; + String strInnerBoundary; + TCHAR szOut[1024]; + int n; + + strDest.erase(); + // Get a count of the sections to see if this will be a multipart message + n = Message.GetSize(); + n += Attachments.GetSize(); + + // Remove this header in case the message is being reused + strHeader = _T("Content-Type"); + Headers.Remove(strHeader); + + // If we have more than one section, then this is a multipart MIME message + if (n > 1) + { + wsprintf(szOut,_T("CSmtpMsgPart123X456_000_%8.8X"),GetTickCount()); + strBoundary = szOut; + + lstrcpy(szOut,_T("multipart/")); + + if (MimeType == mimeGuess) + { + if (Attachments.GetSize() == 0) MimeType = mimeAlternative; + else MimeType = mimeMixed; + } + switch(MimeType) + { + case mimeAlternative: + lstrcat(szOut,_T("alternative")); + break; + case mimeMixed: + lstrcat(szOut,_T("mixed")); + break; + case mimeRelated: + lstrcat(szOut,_T("related")); + break; + } + lstrcat(szOut,_T(";\r\n\tboundary=\"")); + lstrcat(szOut,strBoundary.c_str()); + lstrcat(szOut,_T("\"")); + + strValue = szOut; + Headers.Add(strHeader,strValue); + } + + strHeader = _T("MIME-Version"); + strValue = MIME_VERSION; + Headers.Remove(strHeader); + Headers.Add(strHeader,strValue); + + // Remove any message ID in the header and replace it with this message ID, if it exists + strHeader = _T("Message-ID"); + Headers.Remove(strHeader); + if (MessageId.length()) + { + wsprintf(szOut,_T("<%s>"),MessageId.c_str()); + strValue = szOut; + Headers.Add(strHeader,strValue); + } + + // Finalize the message headers + CommitHeaders(); + + // Write out all the message headers -- done backwards on purpose! + for (n = Headers.GetSize();n > 0;n--) + { + wsprintf(szOut,_T("%s: %s\r\n"),Headers.GetKeyAt(n-1).c_str(),Headers.GetValueAt(n-1).c_str()); + strDest += szOut; + } + if (strBoundary.length()) + { + wsprintf(szOut,_T("\r\n%s\r\n"),MULTIPART_MESSAGE); + strDest += szOut; + } + + // If we have attachments and multiple message bodies, create a new multipart section + // This is done so we can display our multipart/alternative section separate from the + // main multipart/mixed environment, and support both attachments and alternative bodies. + if (Attachments.GetSize() && Message.GetSize() > 1 && strBoundary.length()) + { + wsprintf(szOut,_T("CSmtpMsgPart123X456_001_%8.8X"),GetTickCount()); + strInnerBoundary = szOut; + + wsprintf(szOut,_T("\r\n--%s\r\nContent-Type: multipart/alternative;\r\n\tboundary=\"%s\"\r\n"),strBoundary.c_str(),strInnerBoundary.c_str()); + strDest += szOut; + } + + for (n = 0;n < Message.GetSize();n++) + { + // If we're multipart, then write the boundary line + if (strBoundary.length() || strInnerBoundary.length()) + { + strDest += _T("\r\n--"); + // If we have an inner boundary, write that one. Otherwise write the outer one + if (strInnerBoundary.length()) strDest += strInnerBoundary; + else strDest += strBoundary; + strDest += _T("\r\n"); + } + strValue.erase(); + strDest += _T("Content-Type: "); + strDest += Message[n].Encoding; + // Include the character set if the message is text + if (_tcsnicmp(Message[n].Encoding.c_str(),_T("text/"),5) == 0) + { + wsprintf(szOut,_T(";\r\n\tcharset=\"%s\""),Message[n].Charset.c_str()); + strDest += szOut; + } + strDest += _T("\r\n"); + + // Encode the message + strValue = Message[n].Data; + EncodeMessage(Message[n].TransferEncoding,strValue,strTemp); + + // Write out the encoding method used and the encoded message + strDest += _T("Content-Transfer-Encoding: "); + strDest += strTemp; + + // If the message body part has a content ID, write it out + if (Message[n].ContentId.length()) + { + wsprintf(szOut,_T("\r\nContent-ID: <%s>"),Message[n].ContentId.c_str()); + strDest += szOut; + } + strDest += _T("\r\n\r\n"); + strDest += strValue; + } + + // If we have multiple message bodies, write out the trailing inner end sequence + if (strInnerBoundary.length()) + { + wsprintf(szOut,_T("\r\n--%s--\r\n"),strInnerBoundary.c_str()); + strDest += szOut; + } + + // Process any attachments + for (n = 0;n < Attachments.GetSize();n++) + { + DWORD dwBytes = 0; + CRegKey cKey; + TCHAR szFilename[MAX_PATH] = {0}; + + // Get the filename of the attachment + strValue = Attachments[n].FileName; + + // Open the file + lstrcpy(szFilename,strValue.c_str()); + HANDLE hFile = CreateFile(szFilename,GENERIC_READ,0,NULL,OPEN_EXISTING,0,NULL); + if (hFile != INVALID_HANDLE_VALUE) + { + // Get the size of the file, allocate the memory and read the contents. + DWORD dwSize = GetFileSize(hFile,NULL); + LPBYTE pData = (LPBYTE)malloc(dwSize + 1); + ZeroMemory(pData,dwSize+1); + + if (ReadFile(hFile,pData,dwSize,&dwBytes,NULL)) + { + // Write out our boundary marker + if (strBoundary.length()) + { + wsprintf(szOut,_T("\r\n--%s\r\n"),strBoundary.c_str()); + strDest += szOut; + } + + // If no alternate name is supplied, strip the path to get the base filename + LPTSTR pszFile; + if (!Attachments[n].AltName.length()) + { + // Strip the path from the filename + pszFile = _tcsrchr(szFilename,'\\'); + if (!pszFile) pszFile = szFilename; + else pszFile ++; + } + else pszFile = (LPTSTR)Attachments[n].AltName.c_str(); + + // Set the content type for the attachment. + TCHAR szType[MAX_PATH] = {0}; + lstrcpy(szType,_T("application/octet-stream")); + + // Check the registry for a content type that overrides the above default + LPTSTR pszExt = _tcschr(pszFile,'.'); + if (pszExt) + { + if (!cKey.Open(HKEY_CLASSES_ROOT,pszExt,KEY_READ)) + { + DWORD dwSize = MAX_PATH; + cKey.QueryValue(_T("Content Type"), NULL, szType, &dwSize); + cKey.Close(); + } + } + + // If the attachment has a specific encoding method, use it instead + if (Attachments[n].Encoding.length()) + lstrcpy(szType,Attachments[n].Encoding.c_str()); + + // Write out the content type and attachment types to the message + wsprintf(szOut,_T("Content-Type: %s"),szType); + strDest += szOut; + // If the content type is text, write the charset + if (_tcsnicmp(szType,_T("text/"),5) == 0) + { + wsprintf(szOut,_T(";\r\n\tcharset=\"%s\""),Attachments[n].Charset.c_str()); + strDest += szOut; + } + wsprintf(szOut,_T(";\r\n\tname=\"%s\"\r\n"),pszFile); + strDest += szOut; + + // Encode the attachment + EncodeMessage(Attachments[n].TransferEncoding,strValue,strTemp,pData,dwSize); + + // Write out the transfer encoding method + wsprintf(szOut,_T("Content-Transfer-Encoding: %s\r\n"),strTemp.c_str()); + strDest += szOut; + + // Write out the attachment's disposition + strDest += _T("Content-Disposition: "); + + if (Attachments[n].Inline) strDest += _T("inline"); + else strDest += _T("attachment"); + + strDest += _T(";\r\n\tfilename=\""); + strDest += pszFile; + + // If the attachment has a content ID, write it out + if (Attachments[n].ContentId.length()) + { + wsprintf(szOut,_T("\r\nContent-ID: <%s>"),Attachments[n].ContentId.c_str()); + strDest += szOut; + } + strDest += _T("\r\n\r\n"); + + // Write out the encoded attachment + strDest += strValue; + strTemp.erase(); + strValue.erase(); + } + // Close the file and clear the temp buffer + CloseHandle(hFile); + free(pData); + } + } + + // If we are multipart, write out the trailing end sequence + if (strBoundary.length()) + { + wsprintf(szOut,_T("\r\n--%s--\r\n"),strBoundary.c_str()); + strDest += szOut; + } +} + +// Parses text into quoted-printable lines. +// See RFC 1521 for full details on how this works. +void CSmtpMessage::EncodeQuotedPrintable(String& strDest, String& strSrc) +{ + String strTemp; + String strTemp2; + LPTSTR pszTok1; + LPTSTR pszTok2; + TCHAR szSub[16]; + TCHAR ch; + int n; + + strDest.erase(); + if (!strSrc.length()) return; + + // Change = signs and non-printable characters to =XX + pszTok1 = (LPTSTR)strSrc.c_str(); + pszTok2 = pszTok1; + do + { + if (*pszTok2 == '=' || *pszTok2 > 126 || + (*pszTok2 < 32 && (*pszTok2 != '\r' && *pszTok2 != '\n' && *pszTok2 != '\t'))) + { + ch = *pszTok2; + *pszTok2 = 0; + strTemp += pszTok1; + *pszTok2 = ch; + wsprintf(szSub,_T("=%2.2X"),(BYTE)*pszTok2); + strTemp += szSub; + pszTok1 = pszTok2 + 1; + } + pszTok2 ++; + } while (pszTok2 && *pszTok2); + + // Append anything left after the search + if (_tcslen(pszTok1)) strTemp += pszTok1; + + pszTok1 = (LPTSTR)strTemp.c_str(); + while (pszTok1) + { + pszTok2 = _tcschr(pszTok1,'\r'); + if (pszTok2) *pszTok2 = 0; + while (1) + { + if (_tcslen(pszTok1) > 76) + { + n = 75; // Breaking at the 75th character + if (pszTok1[n-1] == '=') n -= 1; // If the last character is an =, don't break the line there + else if (pszTok1[n-2] == '=') n -= 2; // If we're breaking in the middle of a = sequence, back up! + + // Append the first section of the line to the total string + ch = pszTok1[n]; + pszTok1[n] = 0; + strDest += pszTok1; + pszTok1[n] = ch; + strDest += _T("=\r\n"); + pszTok1 += n; + } + else // Line is less than or equal to 76 characters + { + n = (int)_tcslen(pszTok1); // If we have some trailing data, process it. + if (n) + { + if (pszTok1[n-1] == ' ' || pszTok1[n-1] == '\t') // Last character is a space or tab + { + wsprintf(szSub,_T("=%2.2X"),(BYTE)pszTok1[n-1]); + // Replace the last character with an =XX sequence + pszTok1[n-1] = 0; + strTemp2 = pszTok1; + strTemp2 += szSub; + // Since the string may now be larger than 76 characters, we have to reprocess the line + pszTok1 = (LPTSTR)strTemp2.c_str(); + } + else // Last character is not a space or tab + { + strDest += pszTok1; + if (pszTok2) strDest += _T("\r\n"); + break; // Exit the loop which processes this line, and move to the next line + } + } + else + { + if (pszTok2) strDest += _T("\r\n"); + break; // Move to the next line + } + } + } + if (pszTok2) + { + *pszTok2 = '\r'; + pszTok2 ++; + if (*pszTok2 == '\n') pszTok2 ++; + } + pszTok1 = pszTok2; + } +} + +// Breaks a message's lines into a maximum of 76 characters +// Does some semi-intelligent wordwrapping to ensure the text is broken properly. +// If a line contains no break characters, it is forcibly truncated at the 76th char +void CSmtpMessage::BreakMessage(String& strDest, String& strSrc, int nLength) +{ + String strTemp = strSrc; + LPTSTR pszTok1; + LPTSTR pszTok2; + LPTSTR pszBreak; + LPTSTR pszBreaks = _T(" -;.,?!"); + TCHAR ch; + int nLen; + + strDest.erase(); + if (!strSrc.length()) return; + + nLen = (int)strTemp.length(); + nLen += (nLen / 60) * 2; + + strDest.reserve(nLen); + + // Process each line one at a time + pszTok1 = (LPTSTR)strTemp.c_str(); + while (pszTok1) + { + pszTok2 = _tcschr(pszTok1,'\r'); + if (pszTok2) *pszTok2 = 0; + + BOOL bNoBreaks = (!_tcspbrk(pszTok1,pszBreaks)); + nLen = (int)_tcslen(pszTok1); + while (nLen > nLength) + { + // Start at the 76th character, and move backwards until we hit a break character + pszBreak = &pszTok1[nLength - 1]; + + // If there are no break characters in the string, skip the backward search for them! + if (!bNoBreaks) + { + while (!_tcschr(pszBreaks,*pszBreak) && pszBreak > pszTok1) + pszBreak--; + } + pszBreak ++; + ch = *pszBreak; + *pszBreak = 0; + strDest += pszTok1; + + strDest += _T("\r\n"); + *pszBreak = ch; + + nLen -= (int)(pszBreak - pszTok1); + // Advance the search to the next segment of text after the break + pszTok1 = pszBreak; + } + strDest += pszTok1; + if (pszTok2) + { + strDest += _T("\r\n"); + *pszTok2 = '\r'; + pszTok2 ++; + if (*pszTok2 == '\n') pszTok2 ++; + } + pszTok1 = pszTok2; + } +} + +// Makes the message into a 7bit stream +void CSmtpMessage::Make7Bit(String& strDest, String& strSrc) +{ + LPTSTR pszTok; + + strDest = strSrc; + + pszTok = (LPTSTR)strDest.c_str(); + do + { + // Replace any characters above 126 with a ? character + if (*pszTok > 126 || *pszTok < 0) + *pszTok = '?'; + pszTok ++; + } while (pszTok && *pszTok); +} + +// Encodes a message or binary stream into a properly-formatted message +// Takes care of breaking the message into 76-byte lines of text, encoding to +// Base64, quoted-printable and etc. +void CSmtpMessage::EncodeMessage(EncodingEnum code, String& strMsg, String& strMethod, LPBYTE pByte, DWORD dwSize) +{ + String strTemp; + LPTSTR pszTok1; + LPTSTR pszTok2; + LPSTR pszBuffer = NULL; + DWORD dwStart = GetTickCount(); + + if (!pByte) + { + pszBuffer = (LPSTR)malloc(strMsg.length() + 1); + _T2A(pszBuffer,strMsg.c_str()); + pByte = (LPBYTE)pszBuffer; + dwSize = (DWORD)strMsg.length(); + } + + // Guess the encoding scheme if we have to + if (code == encodeGuess) code = GuessEncoding(pByte, dwSize); + + switch(code) + { + case encodeQuotedPrintable: + strMethod = _T("quoted-printable"); + + pszTok1 = (LPTSTR)malloc((dwSize+1) * sizeof(TCHAR)); + _A2T(pszTok1,(LPSTR)pByte); + strMsg = pszTok1; + free(pszTok1); + + EncodeQuotedPrintable(strTemp, strMsg); + break; + case encodeBase64: + strMethod = _T("base64"); + { + CBase64 cvt; + cvt.Encode(pByte, dwSize); + LPSTR pszTemp = (LPSTR)cvt.EncodedMessage(); + pszTok1 = (LPTSTR)malloc((lstrlenA(pszTemp)+1) * sizeof(TCHAR)); + _A2T(pszTok1,pszTemp); + } + strMsg = pszTok1; + free(pszTok1); + + BreakMessage(strTemp, strMsg); + break; + case encode7Bit: + strMethod = _T("7bit"); + + pszTok1 = (LPTSTR)malloc((dwSize+1) * sizeof(TCHAR)); + _A2T(pszTok1,(LPSTR)pByte); + strMsg = pszTok1; + free(pszTok1); + + Make7Bit(strTemp, strMsg); + strMsg = strTemp; + BreakMessage(strTemp, strMsg); + break; + case encode8Bit: + strMethod = _T("8bit"); + + pszTok1 = (LPTSTR)malloc((dwSize+1) * sizeof(TCHAR)); + _A2T(pszTok1,(LPSTR)pByte); + strMsg = pszTok1; + free(pszTok1); + + BreakMessage(strTemp, strMsg); + break; + } + + if (pszBuffer) free(pszBuffer); + + strMsg.erase(); + + // Parse the message text, replacing CRLF. sequences with CRLF.. sequences + pszTok1 = (LPTSTR)strTemp.c_str(); + do + { + pszTok2 = _tcsstr(pszTok1,_T("\r\n.")); + if (pszTok2) + { + *pszTok2 = 0; + strMsg += pszTok1; + *pszTok2 = '\r'; + strMsg += _T("\r\n.."); + pszTok1 = pszTok2 + 3; + } + } while (pszTok2); + strMsg += pszTok1; + + TCHAR szOut[MAX_PATH] = {0}; + wsprintf(szOut,_T("Encoding took %dms\n"),GetTickCount() - dwStart); + OutputDebugString(szOut); +} + +// Makes a best-guess of the proper encoding to use for this stream of bytes +// It does this by counting the # of lines, the # of 8bit bytes and the number +// of 7bit bytes. It also records the line and the count of lines over +// 76 characters. +// If the stream is 90% or higher 7bit, it uses a text encoding method. If the stream +// is all at or under 76 characters, it uses 7bit or 8bit, depending on the content. +// If the lines are longer than 76 characters, use quoted printable. +// If the stream is under 90% 7bit characters, use base64 encoding. +EncodingEnum CSmtpMessage::GuessEncoding(LPBYTE pByte, DWORD dwLen) +{ + int n7Bit = 0; + int n8Bit = 0; + int nLineStart = 0; + int nLinesOver76 = 0; + int nLines = 0; + DWORD n; + + // Count the content type, byte by byte + for (n = 0;n < dwLen; n++) + { + if (pByte[n] > 126 || (pByte[n] < 32 && pByte[n] != '\t' && pByte[n] != '\r' && pByte[n] != '\n')) + n8Bit ++; + else n7Bit ++; + + // New line? If so, record the line size + if (pByte[n] == '\r') + { + nLines ++; + nLineStart = (n - nLineStart) - 1; + if (nLineStart > 76) nLinesOver76 ++; + nLineStart = n + 1; + } + } + // Determine if it is mostly 7bit data + if ((n7Bit * 100) / dwLen > 89) + { + // At least 90% text, so use a text-base encoding scheme + if (!nLinesOver76) + { + if (!n8Bit) return encode7Bit; + else return encode8Bit; + } + else return encodeQuotedPrintable; + } + return encodeBase64; +} + +////////////////////////////////////////////////////////////////////// +// Construction/Destruction for CSmtp +////////////////////////////////////////////////////////////////////// +CSmtp::CSmtp() +{ + LPSERVENT pEnt; + + m_bExtensions = TRUE; // Use ESMTP if possible + m_dwCmdTimeout = 30; // Default to 30 second timeout + m_hSocket = INVALID_SOCKET; + m_bConnected = m_bUsingExtensions = FALSE; + + // Try and get the SMTP service entry by name + pEnt = getservbyname("SMTP","tcp"); + if (pEnt) m_wSmtpPort = pEnt->s_port; + else m_wSmtpPort = htons(25); + +} + +CSmtp::~CSmtp() +{ + // Make sure any open connections are shut down + Close(); +} + +// Connects to a SMTP server. Returns TRUE if successfully connected, or FALSE otherwise. +BOOL CSmtp::Connect(LPTSTR pszServer) +{ + SOCKADDR_IN addr; + int nRet; + CHAR szHost[MAX_PATH] = {0}; + + _T2A(szHost,pszServer); + // Shut down any active connection + Close(); +// test + WORD wVersionRequested; + WSADATA wsaData; + wVersionRequested = MAKEWORD( 2, 1 ); + WSAStartup( wVersionRequested, &wsaData ); +// end test + // Resolve the hostname + addr.sin_family = AF_INET; + addr.sin_port = m_wSmtpPort; + addr.sin_addr.s_addr = inet_addr(szHost); + + if (addr.sin_addr.s_addr == INADDR_NONE) + { + + LPHOSTENT pHost = gethostbyname(szHost); + if (!pHost) + { + return FALSE; + } + + addr.sin_addr.s_addr = *(LPDWORD)pHost->h_addr; + } + + + + // Create a socket + m_hSocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); + if (m_hSocket == INVALID_SOCKET) return FALSE; + + // Connect to the host + if (connect(m_hSocket,(LPSOCKADDR)&addr,sizeof(addr)) == SOCKET_ERROR) + { + Close(); + return FALSE; + } + + // Get the initial response string + nRet = SendCmd(NULL); + if (nRet != 220) + { + RaiseError(nRet); + Close(); + return FALSE; + } + + // Send a HELLO message to the SMTP server + if (SendHello()) + { + Close(); + return FALSE; + } + + return TRUE; +} + +// Closes any active SMTP sessions and shuts down the socket. +void CSmtp::Close() +{ + if (m_hSocket != INVALID_SOCKET) + { + // If we're connected to a server, tell them we're quitting + if (m_bConnected) SendQuitCmd(); + // Shutdown and close the socket + shutdown(m_hSocket,2); + closesocket(m_hSocket); + } + m_hSocket = INVALID_SOCKET; +} + +// Send a command to the SMTP server and wait for a response +int CSmtp::SendCmd(LPTSTR pszCmd) +{ + USES_CONVERSION; + FD_SET set; + TIMEVAL tv; + int nRet = 0; + DWORD dwTick; + CHAR szResult[CMD_RESPONSE_SIZE] = {0}; + LPSTR pszPos; + LPSTR pszTok; + BOOL bReportProgress = FALSE; + LPSTR pszBuff; + + ZeroMemory(szResult,CMD_RESPONSE_SIZE); + FD_ZERO(&set); + + // If we have a command to send, then send it. + if (pszCmd) + { + pszBuff = (LPSTR)malloc(lstrlen(pszCmd)+1); + _T2A(pszBuff,pszCmd); + + // Make sure the input buffer is clear before sending + nRet = 1; + while (nRet > 0) + { + FD_SET(m_hSocket,&set); + tv.tv_sec = 0; + tv.tv_usec = 0; + nRet = select(1,&set,NULL,NULL,&tv); + if (nRet == 1) nRet = recv(m_hSocket,szResult,CMD_RESPONSE_SIZE,0); + } + DWORD dwPosition = 0; + DWORD dwLen = lstrlen(pszCmd); + if (dwLen > CMD_BLOCK_SIZE) bReportProgress = TRUE; + + while (dwLen != dwPosition) + { + DWORD dwMax = min(CMD_BLOCK_SIZE,dwLen - dwPosition); + nRet = send(m_hSocket,&pszBuff[dwPosition],dwMax,0); + if (nRet == SOCKET_ERROR) + { + free(pszBuff); + return nRet; + } + dwPosition += dwMax; + if (bReportProgress) + { + if (!SmtpProgress(pszBuff,dwPosition,dwLen)) + { + free(pszBuff); + return -1; + } + } + } + // Wait for the CMD to finish being sent + FD_ZERO(&set); + FD_SET(m_hSocket,&set); + nRet = select(1,NULL,&set,NULL,NULL); + free(pszBuff); + } + + // Prepare to receive a response + ZeroMemory(szResult,CMD_RESPONSE_SIZE); + pszPos = szResult; + // Wait for the specified timeout for a full response string + dwTick = GetTickCount(); + while (GetTickCount() - dwTick < (m_dwCmdTimeout * 1000)) + { + FD_SET(m_hSocket,&set); + + tv.tv_sec = m_dwCmdTimeout - ((GetTickCount() - dwTick) / 1000); + tv.tv_usec = 0; + + // Check the socket for readability + nRet = select(1,&set,NULL,NULL,&tv); + if (nRet == SOCKET_ERROR) break; + + // If the socket has data, read it. + if (nRet == 1) + { + nRet = recv(m_hSocket,pszPos,CMD_RESPONSE_SIZE - (int)(pszPos - szResult),0); + // Treats a graceful shutdown as an error + if (nRet == 0) nRet = SOCKET_ERROR; + if (nRet == SOCKET_ERROR) break; + + // Add the data to the total response string & check for a LF + pszPos += nRet; + pszTok = strrchr(szResult,'\n'); + if (pszTok) + { + // Truncate CRLF combination and exit our wait loop + pszTok --; + pszTok[0] = 0; + break; + } + } + } + // Assign the response string + m_strResult = A2CT(szResult); + + // Evaluate the numeric response code + if (nRet && nRet != SOCKET_ERROR) + { + szResult[3] = 0; + nRet = atoi(szResult); + SmtpCommandResponse(pszCmd, nRet, (LPTSTR)m_strResult.c_str()); + } + else nRet = -1; + + return nRet; +} + +// Placeholder function -- overridable +// This function is called when the SMTP server gives us an unexpected error +// The value is the SMTP server's numeric error response, and the +// is the descriptive error text +// +// may be NULL if the server failed to respond before the timeout! +// will be -1 if a catastrophic failure occurred. +// +// Return 0, or nError. The return value is currently ignored. +int CSmtp::SmtpError(int /*nError*/, LPTSTR pszErr) +{ +#ifdef _DEBUG + if (pszErr) + { + OutputDebugString(_T("SmtpError: ")); + OutputDebugString(pszErr); + OutputDebugString(_T("\n")); + } +#endif + return 0; +} + +// Placeholder function -- overridable +// Currently the only warning condition that this class is designed for is +// an authentication failure. In that case, will be 535, +// which is the RFC error for authentication failure. If authentication +// fails, you can override this function to prompt the user for a new +// username and password. Change the and member +// variables and return TRUE to retry authentication. +// +// may be NULL if the server did not respond in time! +// +// Return FALSE to abort authentication, or TRUE to retry. +int CSmtp::SmtpWarning(int /*nWarning*/, LPTSTR pszWarning) +{ +#ifdef _DEBUG + if (pszWarning) + { + OutputDebugString(_T("SmtpWarning: ")); + OutputDebugString(pszWarning); + OutputDebugString(_T("\n")); + } +#endif + return 0; +} + +// Placeholder function -- overridable +// This is an informational callback only, and provides a means to inform +// the caller as the SMTP session progresses. +// ALWAYS check for NULL values on and before performing +// any actions! +// will be -1 if a catastrophic failure occurred, but that will +// be raised in the SmtpError() event later on during processing. +void CSmtp::SmtpCommandResponse(LPTSTR pszCmd, int /*nResponse*/, LPTSTR pszResponse) +{ +#ifdef _DEBUG + if (pszCmd) + { + TCHAR szOut[MAX_PATH+1] = {0}; + OutputDebugString(_T("SmtpCommand : ")); + while (lstrlen(pszCmd) > MAX_PATH) + { + lstrcpyn(szOut,pszCmd,MAX_PATH+1); + OutputDebugString(szOut); + Sleep(100); + pszCmd += MAX_PATH; + } + OutputDebugString(pszCmd); + } + OutputDebugString(_T("SmtpResponse: ")); + OutputDebugString(pszResponse); + OutputDebugString(_T("\n")); +#endif +} + +// Placeholder function -- overridable +// This is a progress callback to indicate that data is being sent over the wire +// and that the operation may take some time. +// Return TRUE to continue sending, or FALSE to abort the transfer +BOOL CSmtp::SmtpProgress(LPSTR /*pszBuffer*/, DWORD /*dwBytesSent*/, DWORD /*dwBytesTotal*/) +{ + return TRUE; // Continue sending the data +} + +// Raises a SmtpError() condition +int CSmtp::RaiseError(int nError) +{ + // If the error code is -1, something catastrophic happened + // so we're effectively not connected to any SMTP server. + if (nError == -1) m_bConnected = FALSE; + return SmtpError(nError, (LPTSTR)m_strResult.c_str()); +} + +// Warnings are recoverable errors that we may be able to continue working with +int CSmtp::RaiseWarning(int nWarning) +{ + return SmtpWarning(nWarning, (LPTSTR)m_strResult.c_str()); +} + +// E-Mail's a message +// Returns 0 if successful, -1 if an internal error occurred, or a positive +// error value if the SMTP server gave an error or failure response. +int CSmtp::SendMessage(CSmtpMessage &msg) +{ + int nRet; + int n; +// int nRecipients = 0; + int nRecipientCount = 0; + + // Check if we have a sender + if (!msg.Sender.Address.length()) return -1; + + // Check if we have recipients + if (!msg.Recipient.Address.length() && !msg.CC.GetSize()) return -1; + + // Check if we have a message body or attachments + // *** Commented out to remove the requirement that a message have a body or attachments + // if (!msg.Message.GetSize() && !msg.Attachments.GetSize()) return -1; + + // Send the sender's address + nRet = SendFrom((LPTSTR)msg.Sender.Address.c_str()); + if (nRet) return nRet; + // If we have a recipient, send it + nRecipientCount = 0; // Count of recipients + if (msg.Recipient.Address.length()) + { + nRet = SendTo((LPTSTR)msg.Recipient.Address.c_str()); + if (!nRet) nRecipientCount ++; + } + + // If we have any CC's, send those. + for (n = 0;n < msg.CC.GetSize();n++) + { + nRet = SendTo((LPTSTR)msg.CC[n].Address.c_str()); + if (!nRet) nRecipientCount ++; + } + + // If we have any bcc's, send those. + for (n = 0;n < msg.BCC.GetSize();n++) + { + nRet = SendTo((LPTSTR)msg.BCC[n].Address.c_str()); + if (!nRet) nRecipientCount ++; + } + // If we failed on all recipients, we must abort. + if (!nRecipientCount) + RaiseError(nRet); + else + nRet = SendData(msg); + + return nRet; +} + +// Simplified way to send a message. +// can be either an LPTSTR containing NULL terminated strings, in which +// case should be zero, or can be an LPTSTR * +// containing an array of LPTSTR's, in which case should equal the +// number of strings in the array. +int CSmtp::SendMessage(CSmtpAddress &addrFrom, CSmtpAddress &addrTo, LPCTSTR pszSubject, LPTSTR pszMessage, LPVOID pvAttachments, DWORD dwAttachmentCount) +{ + CSmtpMessage message; + CSmtpMessageBody body; + CSmtpAttachment attach; + + body = pszMessage; + + message.Sender = addrFrom; + message.Recipient = addrTo; + message.Message.Add(body); + message.Subject = pszSubject; + + // If the attachment count is zero, but the pvAttachments variable is not NULL, + // assume that the ppvAttachments variable is a string value containing NULL terminated + // strings. A double NULL ends the list. + // Example: LPTSTR pszAttachments = "foo.exe\0bar.zip\0autoexec.bat\0\0"; + if (!dwAttachmentCount && pvAttachments) + { + LPTSTR pszAttachments = (LPTSTR)pvAttachments; + while (lstrlen(pszAttachments)) + { + attach.FileName = pszAttachments; + message.Attachments.Add(attach); + pszAttachments = &pszAttachments[lstrlen(pszAttachments)]; + } + } + + // dwAttachmentCount is not zero, so assume pvAttachments is an array of LPTSTR's + // Example: LPTSTR *ppszAttachments = {"foo.exe","bar.exe","autoexec.bat"}; + if (pvAttachments && dwAttachmentCount) + { + LPTSTR *ppszAttachments = (LPTSTR *)pvAttachments; + while (dwAttachmentCount-- && ppszAttachments) + { + attach.FileName = ppszAttachments[dwAttachmentCount]; + message.Attachments.Add(attach); + } + } + return SendMessage(message); +} + +// Yet an even simpler method for sending a message +// and should be e-mail addresses with no decorations +// Example: "foo@bar.com" +// and are described above in the alternative +// version of this function +int CSmtp::SendMessage(LPTSTR pszAddrFrom, LPTSTR pszAddrTo, LPTSTR pszSubject, LPTSTR pszMessage, LPVOID pvAttachments, DWORD dwAttachmentCount) +{ + CSmtpAddress addrFrom(pszAddrFrom); + CSmtpAddress addrTo(pszAddrTo); + + return SendMessage(addrFrom,addrTo,pszSubject,pszMessage,pvAttachments,dwAttachmentCount); +} + +// Tell the SMTP server we're quitting +// Returns 0 if successful, or a positive +// error value if the SMTP server gave an error or failure response. +int CSmtp::SendQuitCmd() +{ + int nRet; + + if (!m_bConnected) return 0; + + nRet = SendCmd(_T("QUIT\r\n")); + if (nRet != 221) RaiseError(nRet); + + m_bConnected = FALSE; + + return (nRet == 221) ? 0:nRet; +} + +// Initiate a conversation with the SMTP server +// Returns 0 if successful, or a positive +// error value if the SMTP server gave an error or failure response. +int CSmtp::SendHello() +{ + int nRet = 0; + TCHAR szName[64] = {0}; + TCHAR szMsg[MAX_PATH] = {0}; + DWORD dwSize = 64; + + GetComputerName(szName,&dwSize); + + // First try a EHLO if we're using ESMTP + wsprintf(szMsg,_T("EHLO %s\r\n"),szName); + if (m_bExtensions) nRet = SendCmd(szMsg); + + // If we got a 250 response, we're using ESMTP, otherwise revert to regular SMTP + if (nRet != 250) + { + m_bUsingExtensions = FALSE; + szMsg[0] = 'H'; + szMsg[1] = 'E'; + nRet = SendCmd(szMsg); + } + else m_bUsingExtensions = TRUE; + + // Raise any unexpected responses + if (nRet != 250) + { + RaiseError(nRet); + return nRet; + } + + // We're connected! + m_bConnected = TRUE; + + // Send authentication if we have any. + // We don't fail just because authentication failed, however. + if (m_bUsingExtensions) SendAuthentication(); + + return 0; +} + +// Requests authentication for the session if the server supports it, +// and attempts to submit the user's credentials. +// Returns 0 if successful, or a positive +// error value if the SMTP server gave an error or failure response. +int CSmtp::SendAuthentication() +{ + USES_CONVERSION; + int nRet = 0; + CBase64 cvt; + LPCSTR pszTemp; + TCHAR szMsg[MAX_PATH] = {0}; + CHAR szAuthType[MAX_PATH] = {0}; + + // This is an authentication loop, we can authenticate multiple times in case of failure. + while(1) + { + // If we don't have a username, skip authentication + if (!m_strUser.length()) return 0; + + // Make the authentication request + nRet = SendCmd(_T("AUTH LOGIN\r\n")); + // If it was rejected, we have to abort. + if (nRet != 334) + { + RaiseWarning(nRet); + return nRet; + } + + // Authentication has 2 stages for username and password. + // It is possible if the authentication fails here that we can + // resubmit proper credentials. + while (1) + { + // Decode the authentication string being requested + _T2A(szAuthType,&(m_strResult.c_str())[4]); + + cvt.Decode(szAuthType); + pszTemp = cvt.DecodedMessage(); + + if (!lstrcmpiA(pszTemp,"Username:")) + cvt.Encode(T2CA(m_strUser.c_str())); + else if (!lstrcmpiA(pszTemp,"Password:")) + cvt.Encode(T2CA(m_strPass.c_str())); + else break; + + wsprintf(szMsg,_T("%s\r\n"),A2CT(cvt.EncodedMessage())); + nRet = SendCmd(szMsg); + + // If we got a failed authentication request, raise a warning. + // this gives the owner a chance to change the username and password. + if (nRet == 535) + { + // Return FALSE to fail, or TRUE to retry + nRet = RaiseWarning(nRet); + if (!nRet) + { + // Reset the error back to 535. It's now an error rather than a warning + nRet = 535; + break; + } + } + // Break on any response other than 334, which indicates a request for more information + if (nRet != 334) break; + } + // Break if we're not retrying a failed authentication + if (nRet != TRUE) break; + } + // Raise an error if we failed to authenticate + if (nRet != 235) RaiseError(nRet); + + return (nRet == 235) ? 0:nRet; +} + +// Send a MAIL FROM command to the server +// Returns 0 if successful, or a positive +// error value if the SMTP server gave an error or failure response. +int CSmtp::SendFrom(LPTSTR pszFrom) +{ + int nRet = 0; + TCHAR szMsg[MAX_PATH] = {0}; + + wsprintf(szMsg,_T("MAIL FROM: <%s>\r\n"),pszFrom); + + while (1) + { + nRet = SendCmd(szMsg); + // Send authentication if required, and retry the command + if (nRet == 530) nRet = SendAuthentication(); + else break; + } + // Raise an error if we failed + if (nRet != 250) RaiseError(nRet); + return (nRet == 250) ? 0:nRet; +} + +// Send a RCPT TO command to the server +// Returns 0 if successful, or a positive +// error value if the SMTP server gave an error or failure response. +int CSmtp::SendTo(LPTSTR pszTo) +{ + int nRet; + TCHAR szMsg[MAX_PATH] = {0}; + + wsprintf(szMsg,_T("RCPT TO: <%s>\r\n"),pszTo); + nRet = SendCmd(szMsg); + if (nRet != 250 && nRet != 251) RaiseWarning(nRet); + return (nRet == 250 || nRet == 251) ? 0:nRet; +} + +// Send the body of an e-mail message to the server +// Returns 0 if successful, or a positive +// error value if the SMTP server gave an error or failure response. +int CSmtp::SendData(CSmtpMessage &msg) +{ + int nRet; + String strMsg; + + // Send the DATA command. We need a 354 to proceed + nRet = SendCmd(_T("DATA\r\n")); + if (nRet != 354) + { + RaiseError(nRet); + return nRet; + } + + // Parse the body of the email message + msg.Parse(strMsg); + strMsg += _T("\r\n.\r\n"); + + // Send the body and expect a 250 OK reply. + nRet = SendCmd((LPTSTR)strMsg.c_str()); + if (nRet != 250) RaiseError(nRet); + + return (nRet == 250) ? 0:nRet; +} diff --git a/Src/Plugins/General/gen_crasher/feedback/smtp/Smtp.h b/Src/Plugins/General/gen_crasher/feedback/smtp/Smtp.h new file mode 100644 index 00000000..50b852b6 --- /dev/null +++ b/Src/Plugins/General/gen_crasher/feedback/smtp/Smtp.h @@ -0,0 +1,245 @@ +// Smtp.h: interface for the CSmtp class. +// +// Written by Robert Simpson (robert@blackcastlesoft.com) +// Created 11/1/2000 +// Version 1.7 -- Last Modified 06/18/2001 +// See smtp.cpp for details of this revision +////////////////////////////////////////////////////////////////////// + +#if !defined(AFX_SMTP_H__F5ACA8FA_AF73_11D4_907D_0080C6F7C752__INCLUDED_) +#define AFX_SMTP_H__F5ACA8FA_AF73_11D4_907D_0080C6F7C752__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#pragma comment(lib,"wsock32.lib") + +#include +#include +#include +#include "Base64.h" + +// Some ATL string conversion enhancements +// ATL's string conversions allocate memory on the stack, which can +// be undesirable if converting huge strings. These enhancements +// provide for a pre-allocated memory block to be used as the +// destination for the string conversion. +#define _W2A(dst,src) AtlW2AHelper(dst,src,lstrlenW(src)+1) +#define _A2W(dst,src) AtlA2WHelper(dst,src,lstrlenA(src)+1) + +typedef std::wstring StringW; +typedef std::string StringA; + +#ifdef _UNICODE +typedef StringW String; +#define _W2T(dst,src) lstrcpyW(dst,src) +#define _T2W(dst,src) lstrcpyW(dst,src) +#define _T2A(dst,src) _W2A(dst,src) +#define _A2T(dst,src) _A2W(dst,src) +#else +typedef StringA String; +#define _W2T(dst,src) _W2A(dst,src) +#define _T2W(dst,src) _A2W(dst,src) +#define _T2A(dst,src) lstrcpyA(dst,src) +#define _A2T(dst,src) lstrcpyA(dst,src) +#endif + +// When the SMTP server responds to a command, this is the +// maximum size of a response I expect back. +#ifndef CMD_RESPONSE_SIZE +#define CMD_RESPONSE_SIZE 1024 +#endif + +// The CSmtp::SendCmd() function will send blocks no larger than this value +// Any outgoing data larger than this value will trigger an SmtpProgress() +// event for all blocks sent. +#ifndef CMD_BLOCK_SIZE +#define CMD_BLOCK_SIZE 1024 +#endif + +// Default mime version is 1.0 of course +#ifndef MIME_VERSION +#define MIME_VERSION _T("1.0") +#endif + +// This is the message that would appear in an e-mail client that doesn't support +// multipart messages +#ifndef MULTIPART_MESSAGE +#define MULTIPART_MESSAGE _T("This is a multipart message in MIME format") +#endif + +// Default message body encoding +#ifndef MESSAGE_ENCODING +#define MESSAGE_ENCODING _T("text/plain") +#endif + +// Default character set +#ifndef MESSAGE_CHARSET +#define MESSAGE_CHARSET _T("iso-8859-1") +#endif + +// Some forward declares +class CSmtp; +class CSmtpAddress; +class CSmtpMessage; +class CSmtpAttachment; +class CSmtpMessageBody; +class CSmtpMimePart; + +// These are the only 4 encoding methods currently supported +typedef enum EncodingEnum +{ + encodeGuess, + encode7Bit, + encode8Bit, + encodeQuotedPrintable, + encodeBase64 +}; + +// This code supports three types of mime-types, and can optionally guess a mime type +// based on message content. +typedef enum MimeTypeEnum +{ + mimeGuess, + mimeMixed, + mimeAlternative, + mimeRelated +}; + +// Attachments and message bodies inherit from this class +// It allows each part of a multipart MIME message to have its own attributes +class CSmtpMimePart +{ +public: + String Encoding; // Content encoding. Leave blank to let the system discover it + String Charset; // Character set for text attachments + String ContentId; // Unique content ID, leave blank to let the system handle it + EncodingEnum TransferEncoding; // How to encode for transferring to the server +}; + +// This class represents a user's text name and corresponding email address +class CSmtpAddress +{ +public: // Constructors + CSmtpAddress(LPCTSTR pszAddress = NULL, LPCTSTR pszName = NULL); + +public: // Operators + const CSmtpAddress& operator=(LPCTSTR pszAddress); + const CSmtpAddress& operator=(const String& strAddress); + +public: // Member Variables + String Name; + String Address; +}; + +// This class represents a file attachment +class CSmtpAttachment : public CSmtpMimePart +{ +public: // Constructors + CSmtpAttachment(LPCTSTR pszFilename = NULL, LPCTSTR pszAltName = NULL, BOOL bIsInline = FALSE, LPCTSTR pszEncoding = NULL, LPCTSTR pszCharset = MESSAGE_CHARSET, EncodingEnum encode = encodeGuess); + +public: // Operators + const CSmtpAttachment& operator=(LPCTSTR pszFilename); + const CSmtpAttachment& operator=(const String& strFilename); + +public: // Member Variables + String FileName; // Fully-qualified path and filename of this attachment + String AltName; // Optional, an alternate name for the file to use when sending + BOOL Inline; // Is this an inline attachment? +}; + +// Multiple message body part support +class CSmtpMessageBody : public CSmtpMimePart +{ +public: // Constructors + CSmtpMessageBody(LPCTSTR pszBody = NULL, LPCTSTR pszEncoding = MESSAGE_ENCODING, LPCTSTR pszCharset = MESSAGE_CHARSET, EncodingEnum encode = encodeGuess); + +public: // Operators + const CSmtpMessageBody& operator=(LPCTSTR pszBody); + const CSmtpMessageBody& operator=(const String& strBody); + +public: // Member Variables + String Data; // Message body; +}; + +// This class represents a single message that can be sent via CSmtp +class CSmtpMessage +{ +public: // Constructors + CSmtpMessage(); + +public: // Member Variables + CSmtpAddress Sender; // Who the message is from + CSmtpAddress Recipient; // The intended recipient + String Subject; // The message subject + CSimpleArray Message; // An array of message bodies + CSimpleArray CC; // Carbon Copy recipients + CSimpleArray BCC; // Blind Carbon Copy recipients + CSimpleArray Attachments; // An array of attachments + CSimpleMap Headers; // Optional headers to include in the message + SYSTEMTIME Timestamp; // Timestamp of the message + MimeTypeEnum MimeType; // Type of MIME message this is + String MessageId; // Optional message ID + +private: // Private Member Variables + int GMTOffset; // GMT timezone offset value + +public: // Public functions + void Parse(String& strDest); + +private: // Private functions to finalize the message headers & parse the message + EncodingEnum GuessEncoding(LPBYTE pByte, DWORD dwLen); + void EncodeMessage(EncodingEnum code, String& strMsg, String& strMethod, LPBYTE pByte = NULL, DWORD dwSize = 0); + void Make7Bit(String& strDest, String& strSrc); + void CommitHeaders(); + void BreakMessage(String& strDest, String& strSrc, int nLength = 76); + void EncodeQuotedPrintable(String& strDest, String& strSrc); +}; + +// The main class for connecting to a SMTP server and sending mail. +class CSmtp +{ +public: // Constructors + CSmtp(); + virtual ~CSmtp(); + +public: // Member Variables. Feel free to modify these to change the system's behavior + BOOL m_bExtensions; // Use ESMTP extensions (TRUE) + DWORD m_dwCmdTimeout; // Timeout for issuing each command (30 seconds) + WORD m_wSmtpPort; // Port to communicate via SMTP (25) + String m_strUser; // Username for authentication + String m_strPass; // Password for authentication + +private: // Private Member Variables + SOCKET m_hSocket; // Socket being used to communicate to the SMTP server + String m_strResult; // String result from a SendCmd() + BOOL m_bConnected; // Connected to SMTP server + BOOL m_bUsingExtensions;// Whether this SMTP server uses ESMTP extensions + +public: // These represent the primary public functionality of this class + BOOL Connect(LPTSTR pszServer); + int SendMessage(CSmtpMessage& msg); + int SendMessage(CSmtpAddress& addrFrom, CSmtpAddress& addrTo, LPCTSTR pszSubject, LPTSTR pszMessage, LPVOID pvAttachments = NULL, DWORD dwAttachmentCount = 0); + int SendMessage(LPTSTR pszAddrFrom, LPTSTR pszAddrTo, LPTSTR pszSubject, LPTSTR pszMessage, LPVOID pvAttachments = NULL, DWORD dwAttachmentCount = 0); + void Close(); + +public: // These represent the overridable methods for receiving events from this class + virtual int SmtpWarning(int nWarning, LPTSTR pszWarning); + virtual int SmtpError(int nCode, LPTSTR pszErr); + virtual void SmtpCommandResponse(LPTSTR pszCmd, int nResponse, LPTSTR pszResponse); + virtual BOOL SmtpProgress(LPSTR pszBuffer, DWORD dwSent, DWORD dwTotal); + +private: // These functions are used privately to conduct a SMTP session + int SendCmd(LPTSTR pszCmd); + int SendAuthentication(); + int SendHello(); + int SendQuitCmd(); + int SendFrom(LPTSTR pszFrom); + int SendTo(LPTSTR pszTo); + int SendData(CSmtpMessage &msg); + int RaiseWarning(int nWarning); + int RaiseError(int nError); +}; + +#endif // !defined(AFX_SMTP_H__F5ACA8FA_AF73_11D4_907D_0080C6F7C752__INCLUDED_) diff --git a/Src/Plugins/General/gen_crasher/feedback/version.rc2 b/Src/Plugins/General/gen_crasher/feedback/version.rc2 new file mode 100644 index 00000000..e896cb91 --- /dev/null +++ b/Src/Plugins/General/gen_crasher/feedback/version.rc2 @@ -0,0 +1,39 @@ + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// +#include "..\..\..\..\Winamp/buildType.h" +VS_VERSION_INFO VERSIONINFO + FILEVERSION WINAMP_PRODUCTVER + PRODUCTVERSION WINAMP_PRODUCTVER + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Winamp SA" + VALUE "FileDescription", "Winamp Error Reporter" + VALUE "FileVersion", STR_WINAMP_PRODUCTVER + VALUE "InternalName", "Winamp Error Reporter" + VALUE "LegalCopyright", "Copyright © 2005-2023 Winamp SA" + VALUE "LegalTrademarks", "Nullsoft and Winamp are trademarks of Winamp SA" + VALUE "OriginalFilename", "feedback.exe" + VALUE "ProductName", "Winamp" + VALUE "ProductVersion", STR_WINAMP_PRODUCTVER + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/Src/Plugins/General/gen_crasher/feedback/xzip/XZip.cpp b/Src/Plugins/General/gen_crasher/feedback/xzip/XZip.cpp new file mode 100644 index 00000000..0e2ac6a3 --- /dev/null +++ b/Src/Plugins/General/gen_crasher/feedback/xzip/XZip.cpp @@ -0,0 +1,2915 @@ +// XZip.cpp Version 1.1 +// +// Authors: Mark Adler et al. (see below) +// +// Modified by: Lucian Wischik +// lu@wischik.com +// +// Version 1.0 - Turned C files into just a single CPP file +// - Made them compile cleanly as C++ files +// - Gave them simpler APIs +// - Added the ability to zip/unzip directly in memory without +// any intermediate files +// +// Modified by: Hans Dietrich +// hdietrich2@hotmail.com +// +// Version 1.1: - Added Unicode support to CreateZip() and ZipAdd() +// - Changed file names to avoid conflicts with Lucian's files +// +/////////////////////////////////////////////////////////////////////////////// +// +// Lucian Wischik's comments: +// -------------------------- +// THIS FILE is almost entirely based upon code by Info-ZIP. +// It has been modified by Lucian Wischik. +// The original code may be found at http://www.info-zip.org +// The original copyright text follows. +// +/////////////////////////////////////////////////////////////////////////////// +// +// Original authors' comments: +// --------------------------- +// This is version 2002-Feb-16 of the Info-ZIP copyright and license. The +// definitive version of this document should be available at +// ftp://ftp.info-zip.org/pub/infozip/license.html indefinitely. +// +// Copyright (c) 1990-2002 Info-ZIP. All rights reserved. +// +// For the purposes of this copyright and license, "Info-ZIP" is defined as +// the following set of individuals: +// +// Mark Adler, John Bush, Karl Davis, Harald Denker, Jean-Michel Dubois, +// Jean-loup Gailly, Hunter Goatley, Ian Gorman, Chris Herborth, Dirk Haase, +// Greg Hartwig, Robert Heath, Jonathan Hudson, Paul Kienitz, +// David Kirschbaum, Johnny Lee, Onno van der Linden, Igor Mandrichenko, +// Steve P. Miller, Sergio Monesi, Keith Owens, George Petrov, Greg Roelofs, +// Kai Uwe Rommel, Steve Salisbury, Dave Smith, Christian Spieler, +// Antoine Verheijen, Paul von Behren, Rich Wales, Mike White +// +// This software is provided "as is", without warranty of any kind, express +// or implied. In no event shall Info-ZIP or its contributors be held liable +// for any direct, indirect, incidental, special or consequential damages +// arising out of the use of or inability to use this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. Redistributions of source code must retain the above copyright notice, +// definition, disclaimer, and this list of conditions. +// +// 2. Redistributions in binary form (compiled executables) must reproduce +// the above copyright notice, definition, disclaimer, and this list of +// conditions in documentation and/or other materials provided with the +// distribution. The sole exception to this condition is redistribution +// of a standard UnZipSFX binary as part of a self-extracting archive; +// that is permitted without inclusion of this license, as long as the +// normal UnZipSFX banner has not been removed from the binary or disabled. +// +// 3. Altered versions--including, but not limited to, ports to new +// operating systems, existing ports with new graphical interfaces, and +// dynamic, shared, or static library versions--must be plainly marked +// as such and must not be misrepresented as being the original source. +// Such altered versions also must not be misrepresented as being +// Info-ZIP releases--including, but not limited to, labeling of the +// altered versions with the names "Info-ZIP" (or any variation thereof, +// including, but not limited to, different capitalizations), +// "Pocket UnZip", "WiZ" or "MacZip" without the explicit permission of +// Info-ZIP. Such altered versions are further prohibited from +// misrepresentative use of the Zip-Bugs or Info-ZIP e-mail addresses or +// of the Info-ZIP URL(s). +// +// 4. Info-ZIP retains the right to use the names "Info-ZIP", "Zip", "UnZip", +// "UnZipSFX", "WiZ", "Pocket UnZip", "Pocket Zip", and "MacZip" for its +// own source and binary releases. +// +/////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include "xzip.h" + + +typedef unsigned char uch; // unsigned 8-bit value +typedef unsigned short ush; // unsigned 16-bit value +typedef unsigned long ulg; // unsigned 32-bit value +typedef size_t extent; // file size +typedef unsigned Pos; // must be at least 32 bits +typedef unsigned IPos; // A Pos is an index in the character window. Pos is used only for parameter passing + +#ifndef EOF +#define EOF (-1) +#endif + + +// Error return values. The values 0..4 and 12..18 follow the conventions +// of PKZIP. The values 4..10 are all assigned to "insufficient memory" +// by PKZIP, so the codes 5..10 are used here for other purposes. +#define ZE_MISS -1 // used by procname(), zipbare() +#define ZE_OK 0 // success +#define ZE_EOF 2 // unexpected end of zip file +#define ZE_FORM 3 // zip file structure error +#define ZE_MEM 4 // out of memory +#define ZE_LOGIC 5 // internal logic error +#define ZE_BIG 6 // entry too large to split +#define ZE_NOTE 7 // invalid comment format +#define ZE_TEST 8 // zip test (-T) failed or out of memory +#define ZE_ABORT 9 // user interrupt or termination +#define ZE_TEMP 10 // error using a temp file +#define ZE_READ 11 // read or seek error +#define ZE_NONE 12 // nothing to do +#define ZE_NAME 13 // missing or empty zip file +#define ZE_WRITE 14 // error writing to a file +#define ZE_CREAT 15 // couldn't open to write +#define ZE_PARMS 16 // bad command line +#define ZE_OPEN 18 // could not open a specified file to read +#define ZE_MAXERR 18 // the highest error number + + +// internal file attribute +#define UNKNOWN (-1) +#define BINARY 0 +#define ASCII 1 + +#define BEST -1 // Use best method (deflation or store) +#define STORE 0 // Store method +#define DEFLATE 8 // Deflation method + +#define CRCVAL_INITIAL 0L + +// MSDOS file or directory attributes +#define MSDOS_HIDDEN_ATTR 0x02 +#define MSDOS_DIR_ATTR 0x10 + +// Lengths of headers after signatures in bytes +#define LOCHEAD 26 +#define CENHEAD 42 +#define ENDHEAD 18 + +// Definitions for extra field handling: +#define EB_HEADSIZE 4 /* length of a extra field block header */ +#define EB_LEN 2 /* offset of data length field in header */ +#define EB_UT_MINLEN 1 /* minimal UT field contains Flags byte */ +#define EB_UT_FLAGS 0 /* byte offset of Flags field */ +#define EB_UT_TIME1 1 /* byte offset of 1st time value */ +#define EB_UT_FL_MTIME (1 << 0) /* mtime present */ +#define EB_UT_FL_ATIME (1 << 1) /* atime present */ +#define EB_UT_FL_CTIME (1 << 2) /* ctime present */ +#define EB_UT_LEN(n) (EB_UT_MINLEN + 4 * (n)) +#define EB_L_UT_SIZE (EB_HEADSIZE + EB_UT_LEN(3)) +#define EB_C_UT_SIZE (EB_HEADSIZE + EB_UT_LEN(1)) + + +// Macros for writing machine integers to little-endian format +#define PUTSH(a,f) {char _putsh_c=(char)((a)&0xff); wfunc(param,&_putsh_c,1); _putsh_c=(char)((a)>>8); wfunc(param,&_putsh_c,1);} +#define PUTLG(a,f) {PUTSH((a) & 0xffff,(f)) PUTSH((a) >> 16,(f))} + + +// -- Structure of a ZIP file -- +// Signatures for zip file information headers +#define LOCSIG 0x04034b50L +#define CENSIG 0x02014b50L +#define ENDSIG 0x06054b50L +#define EXTLOCSIG 0x08074b50L + + +#define MIN_MATCH 3 +#define MAX_MATCH 258 +// The minimum and maximum match lengths + + +#define WSIZE (0x8000) +// Maximum window size = 32K. If you are really short of memory, compile +// with a smaller WSIZE but this reduces the compression ratio for files +// of size > WSIZE. WSIZE must be a power of two in the current implementation. +// + +#define MIN_LOOKAHEAD (MAX_MATCH+MIN_MATCH+1) +// Minimum amount of lookahead, except at the end of the input file. +// See deflate.c for comments about the MIN_MATCH+1. +// + +#define MAX_DIST (WSIZE-MIN_LOOKAHEAD) +// In order to simplify the code, particularly on 16 bit machines, match +// distances are limited to MAX_DIST instead of WSIZE. +// + + + + + +// =========================================================================== +// Constants +// + +#define MAX_BITS 15 +// All codes must not exceed MAX_BITS bits + +#define MAX_BL_BITS 7 +// Bit length codes must not exceed MAX_BL_BITS bits + +#define LENGTH_CODES 29 +// number of length codes, not counting the special END_BLOCK code + +#define LITERALS 256 +// number of literal bytes 0..255 + +#define END_BLOCK 256 +// end of block literal code + +#define L_CODES (LITERALS+1+LENGTH_CODES) +// number of Literal or Length codes, including the END_BLOCK code + +#define D_CODES 30 +// number of distance codes + +#define BL_CODES 19 +// number of codes used to transfer the bit lengths + + +#define STORED_BLOCK 0 +#define STATIC_TREES 1 +#define DYN_TREES 2 +// The three kinds of block type + +#define LIT_BUFSIZE 0x8000 +#define DIST_BUFSIZE LIT_BUFSIZE +// Sizes of match buffers for literals/lengths and distances. There are +// 4 reasons for limiting LIT_BUFSIZE to 64K: +// - frequencies can be kept in 16 bit counters +// - if compression is not successful for the first block, all input data is +// still in the window so we can still emit a stored block even when input +// comes from standard input. (This can also be done for all blocks if +// LIT_BUFSIZE is not greater than 32K.) +// - if compression is not successful for a file smaller than 64K, we can +// even emit a stored file instead of a stored block (saving 5 bytes). +// - creating new Huffman trees less frequently may not provide fast +// adaptation to changes in the input data statistics. (Take for +// example a binary file with poorly compressible code followed by +// a highly compressible string table.) Smaller buffer sizes give +// fast adaptation but have of course the overhead of transmitting trees +// more frequently. +// - I can't count above 4 +// The current code is general and allows DIST_BUFSIZE < LIT_BUFSIZE (to save +// memory at the expense of compression). Some optimizations would be possible +// if we rely on DIST_BUFSIZE == LIT_BUFSIZE. +// + +#define REP_3_6 16 +// repeat previous bit length 3-6 times (2 bits of repeat count) + +#define REPZ_3_10 17 +// repeat a zero length 3-10 times (3 bits of repeat count) + +#define REPZ_11_138 18 +// repeat a zero length 11-138 times (7 bits of repeat count) + +#define HEAP_SIZE (2*L_CODES+1) +// maximum heap size + + +// =========================================================================== +// Local data used by the "bit string" routines. +// + +#define Buf_size (8 * 2*sizeof(char)) +// Number of bits used within bi_buf. (bi_buf may be implemented on +// more than 16 bits on some systems.) + +// Output a 16 bit value to the bit stream, lower (oldest) byte first +#define PUTSHORT(state,w) \ +{ if (state.bs.out_offset >= state.bs.out_size-1) \ + state.flush_outbuf(state.param,state.bs.out_buf, &state.bs.out_offset); \ + state.bs.out_buf[state.bs.out_offset++] = (char) ((w) & 0xff); \ + state.bs.out_buf[state.bs.out_offset++] = (char) ((ush)(w) >> 8); \ +} + +#define PUTBYTE(state,b) \ +{ if (state.bs.out_offset >= state.bs.out_size) \ + state.flush_outbuf(state.param,state.bs.out_buf, &state.bs.out_offset); \ + state.bs.out_buf[state.bs.out_offset++] = (char) (b); \ +} + +// DEFLATE.CPP HEADER + +#define HASH_BITS 15 +// For portability to 16 bit machines, do not use values above 15. + +#define HASH_SIZE (unsigned)(1<= HASH_BITS + +#define max_insert_length max_lazy_match +// Insert new strings in the hash table only if the match length +// is not greater than this length. This saves time but degrades compression. +// max_insert_length is used only for compression levels <= 3. + + + +const int extra_lbits[LENGTH_CODES] // extra bits for each length code + = {0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0}; + +const int extra_dbits[D_CODES] // extra bits for each distance code + = {0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; + +const int extra_blbits[BL_CODES]// extra bits for each bit length code + = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7}; + +const uch bl_order[BL_CODES] = {16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15}; +// The lengths of the bit length codes are sent in order of decreasing +// probability, to avoid transmitting the lengths for unused bit length codes. + + +typedef struct config { + ush good_length; // reduce lazy search above this match length + ush max_lazy; // do not perform lazy search above this match length + ush nice_length; // quit search above this match length + ush max_chain; +} config; + +// Values for max_lazy_match, good_match, nice_match and max_chain_length, +// depending on the desired pack level (0..9). The values given below have +// been tuned to exclude worst case performance for pathological files. +// Better values may be found for specific files. +// + +const config configuration_table[10] = { +// good lazy nice chain + {0, 0, 0, 0}, // 0 store only + {4, 4, 8, 4}, // 1 maximum speed, no lazy matches + {4, 5, 16, 8}, // 2 + {4, 6, 32, 32}, // 3 + {4, 4, 16, 16}, // 4 lazy matches */ + {8, 16, 32, 32}, // 5 + {8, 16, 128, 128}, // 6 + {8, 32, 128, 256}, // 7 + {32, 128, 258, 1024}, // 8 + {32, 258, 258, 4096}};// 9 maximum compression */ + +// Note: the deflate() code requires max_lazy >= MIN_MATCH and max_chain >= 4 +// For deflate_fast() (levels <= 3) good is ignored and lazy has a different meaning. + + + + + +// Data structure describing a single value and its code string. +typedef struct ct_data { + union { + ush freq; // frequency count + ush code; // bit string + } fc; + union { + ush dad; // father node in Huffman tree + ush len; // length of bit string + } dl; +} ct_data; + +typedef struct tree_desc { + ct_data *dyn_tree; // the dynamic tree + ct_data *static_tree; // corresponding static tree or NULL + const int *extra_bits; // extra bits for each code or NULL + int extra_base; // base index for extra_bits + int elems; // max number of elements in the tree + int max_length; // max bit length for the codes + int max_code; // largest code with non zero frequency +} tree_desc; + + + + +class TTreeState +{ public: + TTreeState(); + + ct_data dyn_ltree[HEAP_SIZE]; // literal and length tree + ct_data dyn_dtree[2*D_CODES+1]; // distance tree + ct_data static_ltree[L_CODES+2]; // the static literal tree... + // ... Since the bit lengths are imposed, there is no need for the L_CODES + // extra codes used during heap construction. However the codes 286 and 287 + // are needed to build a canonical tree (see ct_init below). + ct_data static_dtree[D_CODES]; // the static distance tree... + // ... (Actually a trivial tree since all codes use 5 bits.) + ct_data bl_tree[2*BL_CODES+1]; // Huffman tree for the bit lengths + + tree_desc l_desc; + tree_desc d_desc; + tree_desc bl_desc; + + ush bl_count[MAX_BITS+1]; // number of codes at each bit length for an optimal tree + + int heap[2*L_CODES+1]; // heap used to build the Huffman trees + int heap_len; // number of elements in the heap + int heap_max; // element of largest frequency + // The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. + // The same heap array is used to build all trees. + + uch depth[2*L_CODES+1]; + // Depth of each subtree used as tie breaker for trees of equal frequency + + uch length_code[MAX_MATCH-MIN_MATCH+1]; + // length code for each normalized match length (0 == MIN_MATCH) + + uch dist_code[512]; + // distance codes. The first 256 values correspond to the distances + // 3 .. 258, the last 256 values correspond to the top 8 bits of + // the 15 bit distances. + + int base_length[LENGTH_CODES]; + // First normalized length for each code (0 = MIN_MATCH) + + int base_dist[D_CODES]; + // First normalized distance for each code (0 = distance of 1) + + uch far l_buf[LIT_BUFSIZE]; // buffer for literals/lengths + ush far d_buf[DIST_BUFSIZE]; // buffer for distances + + uch flag_buf[(LIT_BUFSIZE/8)]; + // flag_buf is a bit array distinguishing literals from lengths in + // l_buf, and thus indicating the presence or absence of a distance. + + unsigned last_lit; // running index in l_buf + unsigned last_dist; // running index in d_buf + unsigned last_flags; // running index in flag_buf + uch flags; // current flags not yet saved in flag_buf + uch flag_bit; // current bit used in flags + // bits are filled in flags starting at bit 0 (least significant). + // Note: these flags are overkill in the current code since we don't + // take advantage of DIST_BUFSIZE == LIT_BUFSIZE. + + ulg opt_len; // bit length of current block with optimal trees + ulg static_len; // bit length of current block with static trees + + ulg cmpr_bytelen; // total byte length of compressed file + ulg cmpr_len_bits; // number of bits past 'cmpr_bytelen' + + ulg input_len; // total byte length of input file + // input_len is for debugging only since we can get it by other means. + + ush *file_type; // pointer to UNKNOWN, BINARY or ASCII +// int *file_method; // pointer to DEFLATE or STORE +}; + +TTreeState::TTreeState() +{ tree_desc a = {dyn_ltree, static_ltree, extra_lbits, LITERALS+1, L_CODES, MAX_BITS, 0}; l_desc = a; + tree_desc b = {dyn_dtree, static_dtree, extra_dbits, 0, D_CODES, MAX_BITS, 0}; d_desc = b; + tree_desc c = {bl_tree, NULL, extra_blbits, 0, BL_CODES, MAX_BL_BITS, 0}; bl_desc = c; + last_lit=0; + last_dist=0; + last_flags=0; +} + + + +class TBitState +{ public: + + int flush_flg; + // + unsigned bi_buf; + // Output buffer. bits are inserted starting at the bottom (least significant + // bits). The width of bi_buf must be at least 16 bits. + int bi_valid; + // Number of valid bits in bi_buf. All bits above the last valid bit + // are always zero. + char *out_buf; + // Current output buffer. + unsigned out_offset; + // Current offset in output buffer. + // On 16 bit machines, the buffer is limited to 64K. + unsigned out_size; + // Size of current output buffer + ulg bits_sent; // bit length of the compressed data only needed for debugging??? +}; + + + + + + + +class TDeflateState +{ public: + TDeflateState() {window_size=0;} + + uch window[2L*WSIZE]; + // Sliding window. Input bytes are read into the second half of the window, + // and move to the first half later to keep a dictionary of at least WSIZE + // bytes. With this organization, matches are limited to a distance of + // WSIZE-MAX_MATCH bytes, but this ensures that IO is always + // performed with a length multiple of the block size. Also, it limits + // the window size to 64K, which is quite useful on MSDOS. + // To do: limit the window size to WSIZE+CBSZ if SMALL_MEM (the code would + // be less efficient since the data would have to be copied WSIZE/CBSZ times) + Pos prev[WSIZE]; + // Link to older string with same hash index. To limit the size of this + // array to 64K, this link is maintained only for the last 32K strings. + // An index in this array is thus a window index modulo 32K. + Pos head[HASH_SIZE]; + // Heads of the hash chains or NIL. If your compiler thinks that + // HASH_SIZE is a dynamic value, recompile with -DDYN_ALLOC. + + ulg window_size; + // window size, 2*WSIZE except for MMAP or BIG_MEM, where it is the + // input file length plus MIN_LOOKAHEAD. + + long block_start; + // window position at the beginning of the current output block. Gets + // negative when the window is moved backwards. + + int sliding; + // Set to false when the input file is already in memory + + unsigned ins_h; // hash index of string to be inserted + + unsigned int prev_length; + // Length of the best match at previous step. Matches not greater than this + // are discarded. This is used in the lazy match evaluation. + + unsigned strstart; // start of string to insert + unsigned match_start; // start of matching string + int eofile; // flag set at end of input file + unsigned lookahead; // number of valid bytes ahead in window + + unsigned max_chain_length; + // To speed up deflation, hash chains are never searched beyond this length. + // A higher limit improves compression ratio but degrades the speed. + + unsigned int max_lazy_match; + // Attempt to find a better match only when the current match is strictly + // smaller than this value. This mechanism is used only for compression + // levels >= 4. + + unsigned good_match; + // Use a faster search when the previous match is longer than this + + int nice_match; // Stop searching when current match exceeds this +}; + + +typedef struct iztimes { + time_t atime,mtime,ctime; +} iztimes; // access, modify, create times + +typedef struct zlist { + ush vem, ver, flg, how; // See central header in zipfile.c for what vem..off are + ulg tim, crc, siz, len; + extent nam, ext, cext, com; // offset of ext must be >= LOCHEAD + ush dsk, att, lflg; // offset of lflg must be >= LOCHEAD + ulg atx, off; + char name[MAX_PATH]; // File name in zip file + char *extra; // Extra field (set only if ext != 0) + char *cextra; // Extra in central (set only if cext != 0) + char *comment; // Comment (set only if com != 0) + char iname[MAX_PATH]; // Internal file name after cleanup + char zname[MAX_PATH]; // External version of internal name + int mark; // Marker for files to operate on + int trash; // Marker for files to delete + int dosflag; // Set to force MSDOS file attributes + struct zlist far *nxt; // Pointer to next header in list +} TZipFileInfo; + + +class TState; +typedef unsigned (*READFUNC)(TState &state, char *buf,unsigned size); +typedef unsigned (*FLUSHFUNC)(void *param, const char *buf, unsigned *size); +typedef unsigned (*WRITEFUNC)(void *param, const char *buf, unsigned size); +class TState +{ public: TState() {err=0;} + // + void *param; + int level; bool seekable; + READFUNC readfunc; FLUSHFUNC flush_outbuf; + TTreeState ts; TBitState bs; TDeflateState ds; + const char *err; +}; + + + + + + + + + +void Assert(TState &state,bool cond, const char *msg) +{ if (cond) return; + state.err=msg; +} +void __cdecl Trace(const char *x, ...) {va_list paramList; va_start(paramList, x); paramList; va_end(paramList);} +void __cdecl Tracec(bool ,const char *x, ...) {va_list paramList; va_start(paramList, x); paramList; va_end(paramList);} + + + +// =========================================================================== +// Local (static) routines in this file. +// + +void init_block (TState &); +void pqdownheap (TState &,ct_data *tree, int k); +void gen_bitlen (TState &,tree_desc *desc); +void gen_codes (TState &state,ct_data *tree, int max_code); +void build_tree (TState &,tree_desc *desc); +void scan_tree (TState &,ct_data *tree, int max_code); +void send_tree (TState &state,ct_data *tree, int max_code); +int build_bl_tree (TState &); +void send_all_trees (TState &state,int lcodes, int dcodes, int blcodes); +void compress_block (TState &state,ct_data *ltree, ct_data *dtree); +void set_file_type (TState &); +void send_bits (TState &state, int value, int length); +unsigned bi_reverse (unsigned code, int len); +void bi_windup (TState &state); +void copy_block (TState &state,char *buf, unsigned len, int header); + + +#define send_code(state, c, tree) send_bits(state, tree[c].fc.code, tree[c].dl.len) +// Send a code of the given tree. c and tree must not have side effects + +// alternatively... +//#define send_code(state, c, tree) +// { if (state.verbose>1) fprintf(stderr,"\ncd %3d ",(c)); +// send_bits(state, tree[c].fc.code, tree[c].dl.len); } + +#define d_code(dist) ((dist) < 256 ? state.ts.dist_code[dist] : state.ts.dist_code[256+((dist)>>7)]) +// Mapping from a distance to a distance code. dist is the distance - 1 and +// must not have side effects. dist_code[256] and dist_code[257] are never used. + +#define Max(a,b) (a >= b ? a : b) +/* the arguments must not have side effects */ + +/* =========================================================================== + * Allocate the match buffer, initialize the various tables and save the + * location of the internal file attribute (ascii/binary) and method + * (DEFLATE/STORE). + */ +void ct_init(TState &state, ush *attr) +{ + int n; /* iterates over tree elements */ + int bits; /* bit counter */ + int length; /* length value */ + int code; /* code value */ + int dist; /* distance index */ + + state.ts.file_type = attr; + //state.ts.file_method = method; + state.ts.cmpr_bytelen = state.ts.cmpr_len_bits = 0L; + state.ts.input_len = 0L; + + if (state.ts.static_dtree[0].dl.len != 0) return; /* ct_init already called */ + + /* Initialize the mapping length (0..255) -> length code (0..28) */ + length = 0; + for (code = 0; code < LENGTH_CODES-1; code++) { + state.ts.base_length[code] = length; + for (n = 0; n < (1< dist code (0..29) */ + dist = 0; + for (code = 0 ; code < 16; code++) { + state.ts.base_dist[code] = dist; + for (n = 0; n < (1<>= 7; /* from now on, all distances are divided by 128 */ + for ( ; code < D_CODES; code++) { + state.ts.base_dist[code] = dist << 7; + for (n = 0; n < (1<<(extra_dbits[code]-7)); n++) { + state.ts.dist_code[256 + dist++] = (uch)code; + } + } + Assert(state,dist == 256, "ct_init: 256+dist != 512"); + + /* Construct the codes of the static literal tree */ + for (bits = 0; bits <= MAX_BITS; bits++) state.ts.bl_count[bits] = 0; + n = 0; + while (n <= 143) state.ts.static_ltree[n++].dl.len = 8, state.ts.bl_count[8]++; + while (n <= 255) state.ts.static_ltree[n++].dl.len = 9, state.ts.bl_count[9]++; + while (n <= 279) state.ts.static_ltree[n++].dl.len = 7, state.ts.bl_count[7]++; + while (n <= 287) state.ts.static_ltree[n++].dl.len = 8, state.ts.bl_count[8]++; + /* fc.codes 286 and 287 do not exist, but we must include them in the + * tree construction to get a canonical Huffman tree (longest code + * all ones) + */ + gen_codes(state,(ct_data *)state.ts.static_ltree, L_CODES+1); + + /* The static distance tree is trivial: */ + for (n = 0; n < D_CODES; n++) { + state.ts.static_dtree[n].dl.len = 5; + state.ts.static_dtree[n].fc.code = (ush)bi_reverse(n, 5); + } + + /* Initialize the first block of the first file: */ + init_block(state); +} + +/* =========================================================================== + * Initialize a new block. + */ +void init_block(TState &state) +{ + int n; /* iterates over tree elements */ + + /* Initialize the trees. */ + for (n = 0; n < L_CODES; n++) state.ts.dyn_ltree[n].fc.freq = 0; + for (n = 0; n < D_CODES; n++) state.ts.dyn_dtree[n].fc.freq = 0; + for (n = 0; n < BL_CODES; n++) state.ts.bl_tree[n].fc.freq = 0; + + state.ts.dyn_ltree[END_BLOCK].fc.freq = 1; + state.ts.opt_len = state.ts.static_len = 0L; + state.ts.last_lit = state.ts.last_dist = state.ts.last_flags = 0; + state.ts.flags = 0; state.ts.flag_bit = 1; +} + +#define SMALLEST 1 +/* Index within the heap array of least frequent node in the Huffman tree */ + + +/* =========================================================================== + * Remove the smallest element from the heap and recreate the heap with + * one less element. Updates heap and heap_len. + */ +#define pqremove(tree, top) \ +{\ + top = state.ts.heap[SMALLEST]; \ + state.ts.heap[SMALLEST] = state.ts.heap[state.ts.heap_len--]; \ + pqdownheap(state,tree, SMALLEST); \ +} + +/* =========================================================================== + * Compares to subtrees, using the tree depth as tie breaker when + * the subtrees have equal frequency. This minimizes the worst case length. + */ +#define smaller(tree, n, m) \ + (tree[n].fc.freq < tree[m].fc.freq || \ + (tree[n].fc.freq == tree[m].fc.freq && state.ts.depth[n] <= state.ts.depth[m])) + +/* =========================================================================== + * Restore the heap property by moving down the tree starting at node k, + * exchanging a node with the smallest of its two sons if necessary, stopping + * when the heap property is re-established (each father smaller than its + * two sons). + */ +void pqdownheap(TState &state,ct_data *tree, int k) +{ + int v = state.ts.heap[k]; + int j = k << 1; /* left son of k */ + int htemp; /* required because of bug in SASC compiler */ + + while (j <= state.ts.heap_len) { + /* Set j to the smallest of the two sons: */ + if (j < state.ts.heap_len && smaller(tree, state.ts.heap[j+1], state.ts.heap[j])) j++; + + /* Exit if v is smaller than both sons */ + htemp = state.ts.heap[j]; + if (smaller(tree, v, htemp)) break; + + /* Exchange v with the smallest son */ + state.ts.heap[k] = htemp; + k = j; + + /* And continue down the tree, setting j to the left son of k */ + j <<= 1; + } + state.ts.heap[k] = v; +} + +/* =========================================================================== + * Compute the optimal bit lengths for a tree and update the total bit length + * for the current block. + * IN assertion: the fields freq and dad are set, heap[heap_max] and + * above are the tree nodes sorted by increasing frequency. + * OUT assertions: the field len is set to the optimal bit length, the + * array bl_count contains the frequencies for each bit length. + * The length opt_len is updated; static_len is also updated if stree is + * not null. + */ +void gen_bitlen(TState &state,tree_desc *desc) +{ + ct_data *tree = desc->dyn_tree; + const int *extra = desc->extra_bits; + int base = desc->extra_base; + int max_code = desc->max_code; + int max_length = desc->max_length; + ct_data *stree = desc->static_tree; + int h; /* heap index */ + int n, m; /* iterate over the tree elements */ + int bits; /* bit length */ + int xbits; /* extra bits */ + ush f; /* frequency */ + int overflow = 0; /* number of elements with bit length too large */ + + for (bits = 0; bits <= MAX_BITS; bits++) state.ts.bl_count[bits] = 0; + + /* In a first pass, compute the optimal bit lengths (which may + * overflow in the case of the bit length tree). + */ + tree[state.ts.heap[state.ts.heap_max]].dl.len = 0; /* root of the heap */ + + for (h = state.ts.heap_max+1; h < HEAP_SIZE; h++) { + n = state.ts.heap[h]; + bits = tree[tree[n].dl.dad].dl.len + 1; + if (bits > max_length) bits = max_length, overflow++; + tree[n].dl.len = (ush)bits; + /* We overwrite tree[n].dl.dad which is no longer needed */ + + if (n > max_code) continue; /* not a leaf node */ + + state.ts.bl_count[bits]++; + xbits = 0; + if (n >= base) xbits = extra[n-base]; + f = tree[n].fc.freq; + state.ts.opt_len += (ulg)f * (bits + xbits); + if (stree) state.ts.static_len += (ulg)f * (stree[n].dl.len + xbits); + } + if (overflow == 0) return; + + Trace("\nbit length overflow\n"); + /* This happens for example on obj2 and pic of the Calgary corpus */ + + /* Find the first bit length which could increase: */ + do { + bits = max_length-1; + while (state.ts.bl_count[bits] == 0) bits--; + state.ts.bl_count[bits]--; /* move one leaf down the tree */ + state.ts.bl_count[bits+1] += (ush)2; /* move one overflow item as its brother */ + state.ts.bl_count[max_length]--; + /* The brother of the overflow item also moves one step up, + * but this does not affect bl_count[max_length] + */ + overflow -= 2; + } while (overflow > 0); + + /* Now recompute all bit lengths, scanning in increasing frequency. + * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all + * lengths instead of fixing only the wrong ones. This idea is taken + * from 'ar' written by Haruhiko Okumura.) + */ + for (bits = max_length; bits != 0; bits--) { + n = state.ts.bl_count[bits]; + while (n != 0) { + m = state.ts.heap[--h]; + if (m > max_code) continue; + if (tree[m].dl.len != (ush)bits) { + Trace("code %d bits %d->%d\n", m, tree[m].dl.len, bits); + state.ts.opt_len += ((long)bits-(long)tree[m].dl.len)*(long)tree[m].fc.freq; + tree[m].dl.len = (ush)bits; + } + n--; + } + } +} + +/* =========================================================================== + * Generate the codes for a given tree and bit counts (which need not be + * optimal). + * IN assertion: the array bl_count contains the bit length statistics for + * the given tree and the field len is set for all tree elements. + * OUT assertion: the field code is set for all tree elements of non + * zero code length. + */ +void gen_codes (TState &state, ct_data *tree, int max_code) +{ + ush next_code[MAX_BITS+1]; /* next code value for each bit length */ + ush code = 0; /* running code value */ + int bits; /* bit index */ + int n; /* code index */ + + /* The distribution counts are first used to generate the code values + * without bit reversal. + */ + for (bits = 1; bits <= MAX_BITS; bits++) { + next_code[bits] = code = (ush)((code + state.ts.bl_count[bits-1]) << 1); + } + /* Check that the bit counts in bl_count are consistent. The last code + * must be all ones. + */ + Assert(state,code + state.ts.bl_count[MAX_BITS]-1 == (1<< ((ush) MAX_BITS)) - 1, + "inconsistent bit counts"); + Trace("\ngen_codes: max_code %d ", max_code); + + for (n = 0; n <= max_code; n++) { + int len = tree[n].dl.len; + if (len == 0) continue; + /* Now reverse the bits */ + tree[n].fc.code = (ush)bi_reverse(next_code[len]++, len); + + //Tracec(tree != state.ts.static_ltree, "\nn %3d %c l %2d c %4x (%x) ", n, (isgraph(n) ? n : ' '), len, tree[n].fc.code, next_code[len]-1); + } +} + +/* =========================================================================== + * Construct one Huffman tree and assigns the code bit strings and lengths. + * Update the total bit length for the current block. + * IN assertion: the field freq is set for all tree elements. + * OUT assertions: the fields len and code are set to the optimal bit length + * and corresponding code. The length opt_len is updated; static_len is + * also updated if stree is not null. The field max_code is set. + */ +void build_tree(TState &state,tree_desc *desc) +{ + ct_data *tree = desc->dyn_tree; + ct_data *stree = desc->static_tree; + int elems = desc->elems; + int n, m; /* iterate over heap elements */ + int max_code = -1; /* largest code with non zero frequency */ + int node = elems; /* next internal node of the tree */ + + /* Construct the initial heap, with least frequent element in + * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. + * heap[0] is not used. + */ + state.ts.heap_len = 0, state.ts.heap_max = HEAP_SIZE; + + for (n = 0; n < elems; n++) { + if (tree[n].fc.freq != 0) { + state.ts.heap[++state.ts.heap_len] = max_code = n; + state.ts.depth[n] = 0; + } else { + tree[n].dl.len = 0; + } + } + + /* The pkzip format requires that at least one distance code exists, + * and that at least one bit should be sent even if there is only one + * possible code. So to avoid special checks later on we force at least + * two codes of non zero frequency. + */ + while (state.ts.heap_len < 2) { + int newcp = state.ts.heap[++state.ts.heap_len] = (max_code < 2 ? ++max_code : 0); + tree[newcp].fc.freq = 1; + state.ts.depth[newcp] = 0; + state.ts.opt_len--; if (stree) state.ts.static_len -= stree[newcp].dl.len; + /* new is 0 or 1 so it does not have extra bits */ + } + desc->max_code = max_code; + + /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, + * establish sub-heaps of increasing lengths: + */ + for (n = state.ts.heap_len/2; n >= 1; n--) pqdownheap(state,tree, n); + + /* Construct the Huffman tree by repeatedly combining the least two + * frequent nodes. + */ + do { + pqremove(tree, n); /* n = node of least frequency */ + m = state.ts.heap[SMALLEST]; /* m = node of next least frequency */ + + state.ts.heap[--state.ts.heap_max] = n; /* keep the nodes sorted by frequency */ + state.ts.heap[--state.ts.heap_max] = m; + + /* Create a new node father of n and m */ + tree[node].fc.freq = (ush)(tree[n].fc.freq + tree[m].fc.freq); + state.ts.depth[node] = (uch) (Max(state.ts.depth[n], state.ts.depth[m]) + 1); + tree[n].dl.dad = tree[m].dl.dad = (ush)node; + /* and insert the new node in the heap */ + state.ts.heap[SMALLEST] = node++; + pqdownheap(state,tree, SMALLEST); + + } while (state.ts.heap_len >= 2); + + state.ts.heap[--state.ts.heap_max] = state.ts.heap[SMALLEST]; + + /* At this point, the fields freq and dad are set. We can now + * generate the bit lengths. + */ + gen_bitlen(state,(tree_desc *)desc); + + /* The field len is now set, we can generate the bit codes */ + gen_codes (state,(ct_data *)tree, max_code); +} + +/* =========================================================================== + * Scan a literal or distance tree to determine the frequencies of the codes + * in the bit length tree. Updates opt_len to take into account the repeat + * counts. (The contribution of the bit length codes will be added later + * during the construction of bl_tree.) + */ +void scan_tree (TState &state,ct_data *tree, int max_code) +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].dl.len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + if (nextlen == 0) max_count = 138, min_count = 3; + tree[max_code+1].dl.len = (ush)-1; /* guard */ + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].dl.len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + state.ts.bl_tree[curlen].fc.freq = (ush)(state.ts.bl_tree[curlen].fc.freq + count); + } else if (curlen != 0) { + if (curlen != prevlen) state.ts.bl_tree[curlen].fc.freq++; + state.ts.bl_tree[REP_3_6].fc.freq++; + } else if (count <= 10) { + state.ts.bl_tree[REPZ_3_10].fc.freq++; + } else { + state.ts.bl_tree[REPZ_11_138].fc.freq++; + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Send a literal or distance tree in compressed form, using the codes in + * bl_tree. + */ +void send_tree (TState &state, ct_data *tree, int max_code) +{ + int n; /* iterates over all tree elements */ + int prevlen = -1; /* last emitted length */ + int curlen; /* length of current code */ + int nextlen = tree[0].dl.len; /* length of next code */ + int count = 0; /* repeat count of the current code */ + int max_count = 7; /* max repeat count */ + int min_count = 4; /* min repeat count */ + + /* tree[max_code+1].dl.len = -1; */ /* guard already set */ + if (nextlen == 0) max_count = 138, min_count = 3; + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; nextlen = tree[n+1].dl.len; + if (++count < max_count && curlen == nextlen) { + continue; + } else if (count < min_count) { + do { send_code(state, curlen, state.ts.bl_tree); } while (--count != 0); + + } else if (curlen != 0) { + if (curlen != prevlen) { + send_code(state, curlen, state.ts.bl_tree); count--; + } + Assert(state,count >= 3 && count <= 6, " 3_6?"); + send_code(state,REP_3_6, state.ts.bl_tree); send_bits(state,count-3, 2); + + } else if (count <= 10) { + send_code(state,REPZ_3_10, state.ts.bl_tree); send_bits(state,count-3, 3); + + } else { + send_code(state,REPZ_11_138, state.ts.bl_tree); send_bits(state,count-11, 7); + } + count = 0; prevlen = curlen; + if (nextlen == 0) { + max_count = 138, min_count = 3; + } else if (curlen == nextlen) { + max_count = 6, min_count = 3; + } else { + max_count = 7, min_count = 4; + } + } +} + +/* =========================================================================== + * Construct the Huffman tree for the bit lengths and return the index in + * bl_order of the last bit length code to send. + */ +int build_bl_tree(TState &state) +{ + int max_blindex; /* index of last bit length code of non zero freq */ + + /* Determine the bit length frequencies for literal and distance trees */ + scan_tree(state,(ct_data *)state.ts.dyn_ltree, state.ts.l_desc.max_code); + scan_tree(state,(ct_data *)state.ts.dyn_dtree, state.ts.d_desc.max_code); + + /* Build the bit length tree: */ + build_tree(state,(tree_desc *)(&state.ts.bl_desc)); + /* opt_len now includes the length of the tree representations, except + * the lengths of the bit lengths codes and the 5+5+4 bits for the counts. + */ + + /* Determine the number of bit length codes to send. The pkzip format + * requires that at least 4 bit length codes be sent. (appnote.txt says + * 3 but the actual value used is 4.) + */ + for (max_blindex = BL_CODES-1; max_blindex >= 3; max_blindex--) { + if (state.ts.bl_tree[bl_order[max_blindex]].dl.len != 0) break; + } + /* Update opt_len to include the bit length tree and counts */ + state.ts.opt_len += 3*(max_blindex+1) + 5+5+4; + Trace("\ndyn trees: dyn %ld, stat %ld", state.ts.opt_len, state.ts.static_len); + + return max_blindex; +} + +/* =========================================================================== + * Send the header for a block using dynamic Huffman trees: the counts, the + * lengths of the bit length codes, the literal tree and the distance tree. + * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. + */ +void send_all_trees(TState &state,int lcodes, int dcodes, int blcodes) +{ + int rank; /* index in bl_order */ + + Assert(state,lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes"); + Assert(state,lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES, + "too many codes"); + Trace("\nbl counts: "); + send_bits(state,lcodes-257, 5); + /* not +255 as stated in appnote.txt 1.93a or -256 in 2.04c */ + send_bits(state,dcodes-1, 5); + send_bits(state,blcodes-4, 4); /* not -3 as stated in appnote.txt */ + for (rank = 0; rank < blcodes; rank++) { + Trace("\nbl code %2d ", bl_order[rank]); + send_bits(state,state.ts.bl_tree[bl_order[rank]].dl.len, 3); + } + Trace("\nbl tree: sent %ld", state.bs.bits_sent); + + send_tree(state,(ct_data *)state.ts.dyn_ltree, lcodes-1); /* send the literal tree */ + Trace("\nlit tree: sent %ld", state.bs.bits_sent); + + send_tree(state,(ct_data *)state.ts.dyn_dtree, dcodes-1); /* send the distance tree */ + Trace("\ndist tree: sent %ld", state.bs.bits_sent); +} + +/* =========================================================================== + * Determine the best encoding for the current block: dynamic trees, static + * trees or store, and output the encoded block to the zip file. This function + * returns the total compressed length (in bytes) for the file so far. + */ +ulg flush_block(TState &state,char *buf, ulg stored_len, int eof) +{ + ulg opt_lenb, static_lenb; /* opt_len and static_len in bytes */ + int max_blindex; /* index of last bit length code of non zero freq */ + + state.ts.flag_buf[state.ts.last_flags] = state.ts.flags; /* Save the flags for the last 8 items */ + + /* Check if the file is ascii or binary */ + if (*state.ts.file_type == (ush)UNKNOWN) set_file_type(state); + + /* Construct the literal and distance trees */ + build_tree(state,(tree_desc *)(&state.ts.l_desc)); + Trace("\nlit data: dyn %ld, stat %ld", state.ts.opt_len, state.ts.static_len); + + build_tree(state,(tree_desc *)(&state.ts.d_desc)); + Trace("\ndist data: dyn %ld, stat %ld", state.ts.opt_len, state.ts.static_len); + /* At this point, opt_len and static_len are the total bit lengths of + * the compressed block data, excluding the tree representations. + */ + + /* Build the bit length tree for the above two trees, and get the index + * in bl_order of the last bit length code to send. + */ + max_blindex = build_bl_tree(state); + + /* Determine the best encoding. Compute first the block length in bytes */ + opt_lenb = (state.ts.opt_len+3+7)>>3; + static_lenb = (state.ts.static_len+3+7)>>3; + state.ts.input_len += stored_len; /* for debugging only */ + + Trace("\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u dist %u ", + opt_lenb, state.ts.opt_len, static_lenb, state.ts.static_len, stored_len, + state.ts.last_lit, state.ts.last_dist); + + if (static_lenb <= opt_lenb) opt_lenb = static_lenb; + + // Originally, zip allowed the file to be transformed from a compressed + // into a stored file in the case where compression failed, there + // was only one block, and it was allowed to change. I've removed this + // possibility since the code's cleaner if no changes are allowed. + //if (stored_len <= opt_lenb && eof && state.ts.cmpr_bytelen == 0L + // && state.ts.cmpr_len_bits == 0L && state.seekable) + //{ // && state.ts.file_method != NULL + // // Since LIT_BUFSIZE <= 2*WSIZE, the input data must be there: + // Assert(state,buf!=NULL,"block vanished"); + // copy_block(state,buf, (unsigned)stored_len, 0); // without header + // state.ts.cmpr_bytelen = stored_len; + // Assert(state,false,"unimplemented *state.ts.file_method = STORE;"); + // //*state.ts.file_method = STORE; + //} + //else + if (stored_len+4 <= opt_lenb && buf != (char*)NULL) { + /* 4: two words for the lengths */ + /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. + * Otherwise we can't have processed more than WSIZE input bytes since + * the last block flush, because compression would have been + * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to + * transform a block into a stored block. + */ + send_bits(state,(STORED_BLOCK<<1)+eof, 3); /* send block type */ + state.ts.cmpr_bytelen += ((state.ts.cmpr_len_bits + 3 + 7) >> 3) + stored_len + 4; + state.ts.cmpr_len_bits = 0L; + + copy_block(state,buf, (unsigned)stored_len, 1); /* with header */ + } + else if (static_lenb == opt_lenb) { + send_bits(state,(STATIC_TREES<<1)+eof, 3); + compress_block(state,(ct_data *)state.ts.static_ltree, (ct_data *)state.ts.static_dtree); + state.ts.cmpr_len_bits += 3 + state.ts.static_len; + state.ts.cmpr_bytelen += state.ts.cmpr_len_bits >> 3; + state.ts.cmpr_len_bits &= 7L; + } + else { + send_bits(state,(DYN_TREES<<1)+eof, 3); + send_all_trees(state,state.ts.l_desc.max_code+1, state.ts.d_desc.max_code+1, max_blindex+1); + compress_block(state,(ct_data *)state.ts.dyn_ltree, (ct_data *)state.ts.dyn_dtree); + state.ts.cmpr_len_bits += 3 + state.ts.opt_len; + state.ts.cmpr_bytelen += state.ts.cmpr_len_bits >> 3; + state.ts.cmpr_len_bits &= 7L; + } + Assert(state,((state.ts.cmpr_bytelen << 3) + state.ts.cmpr_len_bits) == state.bs.bits_sent, "bad compressed size"); + init_block(state); + + if (eof) { + // Assert(state,input_len == isize, "bad input size"); + bi_windup(state); + state.ts.cmpr_len_bits += 7; /* align on byte boundary */ + } + Trace("\n"); + + return state.ts.cmpr_bytelen + (state.ts.cmpr_len_bits >> 3); +} + +/* =========================================================================== + * Save the match info and tally the frequency counts. Return true if + * the current block must be flushed. + */ +int ct_tally (TState &state,int dist, int lc) +{ + state.ts.l_buf[state.ts.last_lit++] = (uch)lc; + if (dist == 0) { + /* lc is the unmatched char */ + state.ts.dyn_ltree[lc].fc.freq++; + } else { + /* Here, lc is the match length - MIN_MATCH */ + dist--; /* dist = match distance - 1 */ + Assert(state,(ush)dist < (ush)MAX_DIST && + (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) && + (ush)d_code(dist) < (ush)D_CODES, "ct_tally: bad match"); + + state.ts.dyn_ltree[state.ts.length_code[lc]+LITERALS+1].fc.freq++; + state.ts.dyn_dtree[d_code(dist)].fc.freq++; + + state.ts.d_buf[state.ts.last_dist++] = (ush)dist; + state.ts.flags |= state.ts.flag_bit; + } + state.ts.flag_bit <<= 1; + + /* Output the flags if they fill a byte: */ + if ((state.ts.last_lit & 7) == 0) { + state.ts.flag_buf[state.ts.last_flags++] = state.ts.flags; + state.ts.flags = 0, state.ts.flag_bit = 1; + } + /* Try to guess if it is profitable to stop the current block here */ + if (state.level > 2 && (state.ts.last_lit & 0xfff) == 0) { + /* Compute an upper bound for the compressed length */ + ulg out_length = (ulg)state.ts.last_lit*8L; + ulg in_length = (ulg)state.ds.strstart-state.ds.block_start; + int dcode; + for (dcode = 0; dcode < D_CODES; dcode++) { + out_length += (ulg)state.ts.dyn_dtree[dcode].fc.freq*(5L+extra_dbits[dcode]); + } + out_length >>= 3; + Trace("\nlast_lit %u, last_dist %u, in %ld, out ~%ld(%ld%%) ", + state.ts.last_lit, state.ts.last_dist, in_length, out_length, + 100L - out_length*100L/in_length); + if (state.ts.last_dist < state.ts.last_lit/2 && out_length < in_length/2) return 1; + } + return (state.ts.last_lit == LIT_BUFSIZE-1 || state.ts.last_dist == DIST_BUFSIZE); + /* We avoid equality with LIT_BUFSIZE because of wraparound at 64K + * on 16 bit machines and because stored blocks are restricted to + * 64K-1 bytes. + */ +} + +/* =========================================================================== + * Send the block data compressed using the given Huffman trees + */ +void compress_block(TState &state,ct_data *ltree, ct_data *dtree) +{ + unsigned dist; /* distance of matched string */ + int lc; /* match length or unmatched char (if dist == 0) */ + unsigned lx = 0; /* running index in l_buf */ + unsigned dx = 0; /* running index in d_buf */ + unsigned fx = 0; /* running index in flag_buf */ + uch flag = 0; /* current flags */ + unsigned code; /* the code to send */ + int extra; /* number of extra bits to send */ + + if (state.ts.last_lit != 0) do { + if ((lx & 7) == 0) flag = state.ts.flag_buf[fx++]; + lc = state.ts.l_buf[lx++]; + if ((flag & 1) == 0) { + send_code(state,lc, ltree); /* send a literal byte */ + } else { + /* Here, lc is the match length - MIN_MATCH */ + code = state.ts.length_code[lc]; + send_code(state,code+LITERALS+1, ltree); /* send the length code */ + extra = extra_lbits[code]; + if (extra != 0) { + lc -= state.ts.base_length[code]; + send_bits(state,lc, extra); /* send the extra length bits */ + } + dist = state.ts.d_buf[dx++]; + /* Here, dist is the match distance - 1 */ + code = d_code(dist); + Assert(state,code < D_CODES, "bad d_code"); + + send_code(state,code, dtree); /* send the distance code */ + extra = extra_dbits[code]; + if (extra != 0) { + dist -= state.ts.base_dist[code]; + send_bits(state,dist, extra); /* send the extra distance bits */ + } + } /* literal or match pair ? */ + flag >>= 1; + } while (lx < state.ts.last_lit); + + send_code(state,END_BLOCK, ltree); +} + +/* =========================================================================== + * Set the file type to ASCII or BINARY, using a crude approximation: + * binary if more than 20% of the bytes are <= 6 or >= 128, ascii otherwise. + * IN assertion: the fields freq of dyn_ltree are set and the total of all + * frequencies does not exceed 64K (to fit in an int on 16 bit machines). + */ +void set_file_type(TState &state) +{ + int n = 0; + unsigned ascii_freq = 0; + unsigned bin_freq = 0; + while (n < 7) bin_freq += state.ts.dyn_ltree[n++].fc.freq; + while (n < 128) ascii_freq += state.ts.dyn_ltree[n++].fc.freq; + while (n < LITERALS) bin_freq += state.ts.dyn_ltree[n++].fc.freq; + *state.ts.file_type = (ush)(bin_freq > (ascii_freq >> 2) ? BINARY : ASCII); +} + + +/* =========================================================================== + * Initialize the bit string routines. + */ +void bi_init (TState &state,char *tgt_buf, unsigned tgt_size, int flsh_allowed) +{ + state.bs.out_buf = tgt_buf; + state.bs.out_size = tgt_size; + state.bs.out_offset = 0; + state.bs.flush_flg = flsh_allowed; + + state.bs.bi_buf = 0; + state.bs.bi_valid = 0; + state.bs.bits_sent = 0L; +} + +/* =========================================================================== + * Send a value on a given number of bits. + * IN assertion: length <= 16 and value fits in length bits. + */ +void send_bits(TState &state,int value, int length) +{ + Assert(state,length > 0 && length <= 15, "invalid length"); + state.bs.bits_sent += (ulg)length; + /* If not enough room in bi_buf, use (bi_valid) bits from bi_buf and + * (Buf_size - bi_valid) bits from value to flush the filled bi_buf, + * then fill in the rest of (value), leaving (length - (Buf_size-bi_valid)) + * unused bits in bi_buf. + */ + state.bs.bi_buf |= (value << state.bs.bi_valid); + state.bs.bi_valid += length; + if (state.bs.bi_valid > (int)Buf_size) { + PUTSHORT(state,state.bs.bi_buf); + state.bs.bi_valid -= Buf_size; + state.bs.bi_buf = (unsigned)value >> (length - state.bs.bi_valid); + } +} + +/* =========================================================================== + * Reverse the first len bits of a code, using straightforward code (a faster + * method would use a table) + * IN assertion: 1 <= len <= 15 + */ +unsigned bi_reverse(unsigned code, int len) +{ + register unsigned res = 0; + do { + res |= code & 1; + code >>= 1, res <<= 1; + } while (--len > 0); + return res >> 1; +} + +/* =========================================================================== + * Write out any remaining bits in an incomplete byte. + */ +void bi_windup(TState &state) +{ + if (state.bs.bi_valid > 8) { + PUTSHORT(state,state.bs.bi_buf); + } else if (state.bs.bi_valid > 0) { + PUTBYTE(state,state.bs.bi_buf); + } + if (state.bs.flush_flg) { + state.flush_outbuf(state.param,state.bs.out_buf, &state.bs.out_offset); + } + state.bs.bi_buf = 0; + state.bs.bi_valid = 0; + state.bs.bits_sent = (state.bs.bits_sent+7) & ~7; +} + +/* =========================================================================== + * Copy a stored block to the zip file, storing first the length and its + * one's complement if requested. + */ +void copy_block(TState &state, char *block, unsigned len, int header) +{ + bi_windup(state); /* align on byte boundary */ + + if (header) { + PUTSHORT(state,(ush)len); + PUTSHORT(state,(ush)~len); + state.bs.bits_sent += 2*16; + } + if (state.bs.flush_flg) { + state.flush_outbuf(state.param,state.bs.out_buf, &state.bs.out_offset); + state.bs.out_offset = len; + state.flush_outbuf(state.param,block, &state.bs.out_offset); + } else if (state.bs.out_offset + len > state.bs.out_size) { + Assert(state,false,"output buffer too small for in-memory compression"); + } else { + memcpy(state.bs.out_buf + state.bs.out_offset, block, len); + state.bs.out_offset += len; + } + state.bs.bits_sent += (ulg)len<<3; +} + + + + + + + + +/* =========================================================================== + * Prototypes for functions. + */ + +void fill_window (TState &state); +ulg deflate_fast (TState &state); + +int longest_match (TState &state,IPos cur_match); + + +/* =========================================================================== + * Update a hash value with the given input byte + * IN assertion: all calls to to UPDATE_HASH are made with consecutive + * input characters, so that a running hash key can be computed from the + * previous key instead of complete recalculation each time. + */ +#define UPDATE_HASH(h,c) (h = (((h)< 0 if the input file is already read or + * mmap'ed in the window[] array, 0 otherwise. In the first case, + * window_size is sufficient to contain the whole input file plus + * MIN_LOOKAHEAD bytes (to avoid referencing memory beyond the end + * of window[] when looking for matches towards the end). + */ +void lm_init (TState &state, int pack_level, ush *flags) +{ + register unsigned j; + + Assert(state,pack_level>=1 && pack_level<=8,"bad pack level"); + + /* Do not slide the window if the whole input is already in memory + * (window_size > 0) + */ + state.ds.sliding = 0; + if (state.ds.window_size == 0L) { + state.ds.sliding = 1; + state.ds.window_size = (ulg)2L*WSIZE; + } + + /* Initialize the hash table (avoiding 64K overflow for 16 bit systems). + * prev[] will be initialized on the fly. + */ + state.ds.head[HASH_SIZE-1] = NIL; + memset((char*)state.ds.head, NIL, (unsigned)(HASH_SIZE-1)*sizeof(*state.ds.head)); + + /* Set the default configuration parameters: + */ + state.ds.max_lazy_match = configuration_table[pack_level].max_lazy; + state.ds.good_match = configuration_table[pack_level].good_length; + state.ds.nice_match = configuration_table[pack_level].nice_length; + state.ds.max_chain_length = configuration_table[pack_level].max_chain; + if (pack_level <= 2) { + *flags |= FAST; + } else if (pack_level >= 8) { + *flags |= SLOW; + } + /* ??? reduce max_chain_length for binary files */ + + state.ds.strstart = 0; + state.ds.block_start = 0L; + + j = WSIZE; + j <<= 1; // Can read 64K in one step + state.ds.lookahead = state.readfunc(state, (char*)state.ds.window, j); + + if (state.ds.lookahead == 0 || state.ds.lookahead == (unsigned)EOF) { + state.ds.eofile = 1, state.ds.lookahead = 0; + return; + } + state.ds.eofile = 0; + /* Make sure that we always have enough lookahead. This is important + * if input comes from a device such as a tty. + */ + if (state.ds.lookahead < MIN_LOOKAHEAD) fill_window(state); + + state.ds.ins_h = 0; + for (j=0; j= 1 + */ +// For 80x86 and 680x0 and ARM, an optimized version is in match.asm or +// match.S. The code is functionally equivalent, so you can use the C version +// if desired. Which I do so desire! +int longest_match(TState &state,IPos cur_match) +{ + unsigned chain_length = state.ds.max_chain_length; /* max hash chain length */ + register uch far *scan = state.ds.window + state.ds.strstart; /* current string */ + register uch far *match; /* matched string */ + register int len; /* length of current match */ + int best_len = state.ds.prev_length; /* best match length so far */ + IPos limit = state.ds.strstart > (IPos)MAX_DIST ? state.ds.strstart - (IPos)MAX_DIST : NIL; + /* Stop when cur_match becomes <= limit. To simplify the code, + * we prevent matches with the string of window index 0. + */ + + // The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + // It is easy to get rid of this optimization if necessary. + Assert(state,HASH_BITS>=8 && MAX_MATCH==258,"Code too clever"); + + + + register uch far *strend = state.ds.window + state.ds.strstart + MAX_MATCH; + register uch scan_end1 = scan[best_len-1]; + register uch scan_end = scan[best_len]; + + /* Do not waste too much time if we already have a good match: */ + if (state.ds.prev_length >= state.ds.good_match) { + chain_length >>= 2; + } + + Assert(state,state.ds.strstart <= state.ds.window_size-MIN_LOOKAHEAD, "insufficient lookahead"); + + do { + Assert(state,cur_match < state.ds.strstart, "no future"); + match = state.ds.window + cur_match; + + /* Skip to next match if the match length cannot increase + * or if the match length is less than 2: + */ + if (match[best_len] != scan_end || + match[best_len-1] != scan_end1 || + *match != *scan || + *++match != scan[1]) continue; + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2, match++; + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + } while (*++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + *++scan == *++match && *++scan == *++match && + scan < strend); + + Assert(state,scan <= state.ds.window+(unsigned)(state.ds.window_size-1), "wild scan"); + + len = MAX_MATCH - (int)(strend - scan); + scan = strend - MAX_MATCH; + + + if (len > best_len) { + state.ds.match_start = cur_match; + best_len = len; + if (len >= state.ds.nice_match) break; + scan_end1 = scan[best_len-1]; + scan_end = scan[best_len]; + } + } while ((cur_match = state.ds.prev[cur_match & WMASK]) > limit + && --chain_length != 0); + + return best_len; +} + + + +#define check_match(state,start, match, length) +// or alternatively... +//void check_match(TState &state,IPos start, IPos match, int length) +//{ // check that the match is indeed a match +// if (memcmp((char*)state.ds.window + match, +// (char*)state.ds.window + start, length) != EQUAL) { +// fprintf(stderr, +// " start %d, match %d, length %d\n", +// start, match, length); +// error("invalid match"); +// } +// if (state.verbose > 1) { +// fprintf(stderr,"\\[%d,%d]", start-match, length); +// do { fprintf(stdout,"%c",state.ds.window[start++]); } while (--length != 0); +// } +//} + +/* =========================================================================== + * Fill the window when the lookahead becomes insufficient. + * Updates strstart and lookahead, and sets eofile if end of input file. + * + * IN assertion: lookahead < MIN_LOOKAHEAD && strstart + lookahead > 0 + * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD + * At least one byte has been read, or eofile is set; file reads are + * performed for at least two bytes (required for the translate_eol option). + */ +void fill_window(TState &state) +{ + register unsigned n, m; + unsigned more; /* Amount of free space at the end of the window. */ + + do { + more = (unsigned)(state.ds.window_size - (ulg)state.ds.lookahead - (ulg)state.ds.strstart); + + /* If the window is almost full and there is insufficient lookahead, + * move the upper half to the lower one to make room in the upper half. + */ + if (more == (unsigned)EOF) { + /* Very unlikely, but possible on 16 bit machine if strstart == 0 + * and lookahead == 1 (input done one byte at time) + */ + more--; + + /* For MMAP or BIG_MEM, the whole input file is already in memory so + * we must not perform sliding. We must however call (*read_buf)() in + * order to compute the crc, update lookahead and possibly set eofile. + */ + } else if (state.ds.strstart >= WSIZE+MAX_DIST && state.ds.sliding) { + + /* By the IN assertion, the window is not empty so we can't confuse + * more == 0 with more == 64K on a 16 bit machine. + */ + memcpy((char*)state.ds.window, (char*)state.ds.window+WSIZE, (unsigned)WSIZE); + state.ds.match_start -= WSIZE; + state.ds.strstart -= WSIZE; /* we now have strstart >= MAX_DIST: */ + + state.ds.block_start -= (long) WSIZE; + + for (n = 0; n < HASH_SIZE; n++) { + m = state.ds.head[n]; + state.ds.head[n] = (Pos)(m >= WSIZE ? m-WSIZE : NIL); + } + for (n = 0; n < WSIZE; n++) { + m = state.ds.prev[n]; + state.ds.prev[n] = (Pos)(m >= WSIZE ? m-WSIZE : NIL); + /* If n is not on any hash chain, prev[n] is garbage but + * its value will never be used. + */ + } + more += WSIZE; + } + if (state.ds.eofile) return; + + /* If there was no sliding: + * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && + * more == window_size - lookahead - strstart + * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) + * => more >= window_size - 2*WSIZE + 2 + * In the MMAP or BIG_MEM case (not yet supported in gzip), + * window_size == input_size + MIN_LOOKAHEAD && + * strstart + lookahead <= input_size => more >= MIN_LOOKAHEAD. + * Otherwise, window_size == 2*WSIZE so more >= 2. + * If there was sliding, more >= WSIZE. So in all cases, more >= 2. + */ + Assert(state,more >= 2, "more < 2"); + + n = state.readfunc(state, (char*)state.ds.window+state.ds.strstart+state.ds.lookahead, more); + + if (n == 0 || n == (unsigned)EOF) { + state.ds.eofile = 1; + } else { + state.ds.lookahead += n; + } + } while (state.ds.lookahead < MIN_LOOKAHEAD && !state.ds.eofile); +} + +/* =========================================================================== + * Flush the current block, with given end-of-file flag. + * IN assertion: strstart is set to the end of the current match. + */ +#define FLUSH_BLOCK(state,eof) \ + flush_block(state,state.ds.block_start >= 0L ? (char*)&state.ds.window[(unsigned)state.ds.block_start] : \ + (char*)NULL, (long)state.ds.strstart - state.ds.block_start, (eof)) + +/* =========================================================================== + * Processes a new input file and return its compressed length. This + * function does not perform lazy evaluation of matches and inserts + * new strings in the dictionary only for unmatched strings or for short + * matches. It is used only for the fast compression options. + */ +ulg deflate_fast(TState &state) +{ + IPos hash_head = NIL; /* _head of the hash chain */ + int flush; /* set if current block must be flushed */ + unsigned match_length = 0; /* length of best match */ + + state.ds.prev_length = MIN_MATCH-1; + while (state.ds.lookahead != 0) { + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the _head of the hash chain: + */ + if (state.ds.lookahead >= MIN_MATCH) + INSERT_STRING(state.ds.strstart, hash_head); + + /* Find the longest match, discarding those <= prev_length. + * At this point we have always match_length < MIN_MATCH + */ + if (hash_head != NIL && state.ds.strstart - hash_head <= MAX_DIST) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + /* Do not look for matches beyond the end of the input. + * This is necessary to make deflate deterministic. + */ + if ((unsigned)state.ds.nice_match > state.ds.lookahead) state.ds.nice_match = (int)state.ds.lookahead; + match_length = longest_match (state,hash_head); + /* longest_match() sets match_start */ + if (match_length > state.ds.lookahead) match_length = state.ds.lookahead; + } + if (match_length >= MIN_MATCH) { + check_match(state,state.ds.strstart, state.ds.match_start, match_length); + + flush = ct_tally(state,state.ds.strstart-state.ds.match_start, match_length - MIN_MATCH); + + state.ds.lookahead -= match_length; + + /* Insert new strings in the hash table only if the match length + * is not too large. This saves time but degrades compression. + */ + if (match_length <= state.ds.max_insert_length + && state.ds.lookahead >= MIN_MATCH) { + match_length--; /* string at strstart already in hash table */ + do { + state.ds.strstart++; + INSERT_STRING(state.ds.strstart, hash_head); + /* strstart never exceeds WSIZE-MAX_MATCH, so there are + * always MIN_MATCH bytes ahead. + */ + } while (--match_length != 0); + state.ds.strstart++; + } else { + state.ds.strstart += match_length; + match_length = 0; + state.ds.ins_h = state.ds.window[state.ds.strstart]; + UPDATE_HASH(state.ds.ins_h, state.ds.window[state.ds.strstart+1]); + Assert(state,MIN_MATCH==3,"Call UPDATE_HASH() MIN_MATCH-3 more times"); + } + } else { + /* No match, output a literal byte */ + flush = ct_tally (state,0, state.ds.window[state.ds.strstart]); + state.ds.lookahead--; + state.ds.strstart++; + } + if (flush) FLUSH_BLOCK(state,0), state.ds.block_start = state.ds.strstart; + + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (state.ds.lookahead < MIN_LOOKAHEAD) fill_window(state); + } + return FLUSH_BLOCK(state,1); /* eof */ +} + +/* =========================================================================== + * Same as above, but achieves better compression. We use a lazy + * evaluation for matches: a match is finally adopted only if there is + * no better match at the next window position. + */ +ulg deflate(TState &state) +{ + IPos hash_head = NIL; /* _head of hash chain */ + IPos prev_match; /* previous match */ + int flush; /* set if current block must be flushed */ + int match_available = 0; /* set if previous match exists */ + register unsigned match_length = MIN_MATCH-1; /* length of best match */ + + if (state.level <= 3) return deflate_fast(state); /* optimized for speed */ + + /* Process the input block. */ + while (state.ds.lookahead != 0) { + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the _head of the hash chain: + */ + if (state.ds.lookahead >= MIN_MATCH) + INSERT_STRING(state.ds.strstart, hash_head); + + /* Find the longest match, discarding those <= prev_length. + */ + state.ds.prev_length = match_length, prev_match = state.ds.match_start; + match_length = MIN_MATCH-1; + + if (hash_head != NIL && state.ds.prev_length < state.ds.max_lazy_match && + state.ds.strstart - hash_head <= MAX_DIST) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + /* Do not look for matches beyond the end of the input. + * This is necessary to make deflate deterministic. + */ + if ((unsigned)state.ds.nice_match > state.ds.lookahead) state.ds.nice_match = (int)state.ds.lookahead; + match_length = longest_match (state,hash_head); + /* longest_match() sets match_start */ + if (match_length > state.ds.lookahead) match_length = state.ds.lookahead; + + /* Ignore a length 3 match if it is too distant: */ + if (match_length == MIN_MATCH && state.ds.strstart-state.ds.match_start > TOO_FAR){ + /* If prev_match is also MIN_MATCH, match_start is garbage + * but we will ignore the current match anyway. + */ + match_length = MIN_MATCH-1; + } + } + /* If there was a match at the previous step and the current + * match is not better, output the previous match: + */ + if (state.ds.prev_length >= MIN_MATCH && match_length <= state.ds.prev_length) { + unsigned max_insert = state.ds.strstart + state.ds.lookahead - MIN_MATCH; + check_match(state,state.ds.strstart-1, prev_match, state.ds.prev_length); + flush = ct_tally(state,state.ds.strstart-1-prev_match, state.ds.prev_length - MIN_MATCH); + + /* Insert in hash table all strings up to the end of the match. + * strstart-1 and strstart are already inserted. + */ + state.ds.lookahead -= state.ds.prev_length-1; + state.ds.prev_length -= 2; + do { + if (++state.ds.strstart <= max_insert) { + INSERT_STRING(state.ds.strstart, hash_head); + /* strstart never exceeds WSIZE-MAX_MATCH, so there are + * always MIN_MATCH bytes ahead. + */ + } + } while (--state.ds.prev_length != 0); + state.ds.strstart++; + match_available = 0; + match_length = MIN_MATCH-1; + + if (flush) FLUSH_BLOCK(state,0), state.ds.block_start = state.ds.strstart; + + } else if (match_available) { + /* If there was no match at the previous position, output a + * single literal. If there was a match but the current match + * is longer, truncate the previous match to a single literal. + */ + if (ct_tally (state,0, state.ds.window[state.ds.strstart-1])) { + FLUSH_BLOCK(state,0), state.ds.block_start = state.ds.strstart; + } + state.ds.strstart++; + state.ds.lookahead--; + } else { + /* There is no previous match to compare with, wait for + * the next step to decide. + */ + match_available = 1; + state.ds.strstart++; + state.ds.lookahead--; + } +// Assert(state,strstart <= isize && lookahead <= isize, "a bit too far"); + + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (state.ds.lookahead < MIN_LOOKAHEAD) fill_window(state); + } + if (match_available) ct_tally (state,0, state.ds.window[state.ds.strstart-1]); + + return FLUSH_BLOCK(state,1); /* eof */ +} + + + + + + + + + + + + +int putlocal(struct zlist far *z, WRITEFUNC wfunc,void *param) +{ // Write a local header described by *z to file *f. Return a ZE_ error code. + PUTLG(LOCSIG, f); + PUTSH(z->ver, f); + PUTSH(z->lflg, f); + PUTSH(z->how, f); + PUTLG(z->tim, f); + PUTLG(z->crc, f); + PUTLG(z->siz, f); + PUTLG(z->len, f); + PUTSH(z->nam, f); + PUTSH(z->ext, f); + size_t res = (size_t)wfunc(param, z->iname, (unsigned int)z->nam); + if (res!=z->nam) return ZE_TEMP; + if (z->ext) + { res = (size_t)wfunc(param, z->extra, (unsigned int)z->ext); + if (res!=z->ext) return ZE_TEMP; + } + return ZE_OK; +} + +int putextended(struct zlist far *z, WRITEFUNC wfunc, void *param) +{ // Write an extended local header described by *z to file *f. Returns a ZE_ code + PUTLG(EXTLOCSIG, f); + PUTLG(z->crc, f); + PUTLG(z->siz, f); + PUTLG(z->len, f); + return ZE_OK; +} + +int putcentral(struct zlist far *z, WRITEFUNC wfunc, void *param) +{ // Write a central header entry of *z to file *f. Returns a ZE_ code. + PUTLG(CENSIG, f); + PUTSH(z->vem, f); + PUTSH(z->ver, f); + PUTSH(z->flg, f); + PUTSH(z->how, f); + PUTLG(z->tim, f); + PUTLG(z->crc, f); + PUTLG(z->siz, f); + PUTLG(z->len, f); + PUTSH(z->nam, f); + PUTSH(z->cext, f); + PUTSH(z->com, f); + PUTSH(z->dsk, f); + PUTSH(z->att, f); + PUTLG(z->atx, f); + PUTLG(z->off, f); + if ((size_t)wfunc(param, z->iname, (unsigned int)z->nam) != z->nam || + (z->cext && (size_t)wfunc(param, z->cextra, (unsigned int)z->cext) != z->cext) || + (z->com && (size_t)wfunc(param, z->comment, (unsigned int)z->com) != z->com)) + return ZE_TEMP; + return ZE_OK; +} + + +int putend(int n, ulg s, ulg c, extent m, char *z, WRITEFUNC wfunc, void *param) +{ // write the end of the central-directory-data to file *f. + PUTLG(ENDSIG, f); + PUTSH(0, f); + PUTSH(0, f); + PUTSH(n, f); + PUTSH(n, f); + PUTLG(s, f); + PUTLG(c, f); + PUTSH(m, f); + // Write the comment, if any + if (m && wfunc(param, z, (unsigned int)m) != m) return ZE_TEMP; + return ZE_OK; +} + + + + + + +const ulg crc_table[256] = { + 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, + 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, + 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, + 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, + 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, + 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, + 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, + 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, + 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, + 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, + 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, + 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, + 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, + 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, + 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, + 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, + 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, + 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, + 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, + 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, + 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, + 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, + 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, + 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, + 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, + 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, + 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, + 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, + 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, + 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, + 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, + 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, + 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, + 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, + 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, + 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, + 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, + 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, + 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, + 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, + 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, + 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, + 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, + 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, + 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, + 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, + 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, + 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, + 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, + 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, + 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, + 0x2d02ef8dL +}; + +#define CRC32(c, b) (crc_table[((int)(c) ^ (b)) & 0xff] ^ ((c) >> 8)) +#define DO1(buf) crc = CRC32(crc, *buf++) +#define DO2(buf) DO1(buf); DO1(buf) +#define DO4(buf) DO2(buf); DO2(buf) +#define DO8(buf) DO4(buf); DO4(buf) + +ulg crc32(ulg crc, const uch *buf, extent len) +{ if (buf==NULL) return 0L; + crc = crc ^ 0xffffffffL; + while (len >= 8) {DO8(buf); len -= 8;} + if (len) do {DO1(buf);} while (--len); + return crc ^ 0xffffffffL; // (instead of ~c for 64-bit machines) +} + + + + + + + + +bool HasZipSuffix(const char *fn) +{ const char *ext = fn+strlen(fn); + while (ext>fn && *ext!='.') ext--; + if (ext==fn && *ext!='.') return false; + if (_stricmp(ext,".Z")==0) return true; + if (_stricmp(ext,".zip")==0) return true; + if (_stricmp(ext,".zoo")==0) return true; + if (_stricmp(ext,".arc")==0) return true; + if (_stricmp(ext,".lzh")==0) return true; + if (_stricmp(ext,".arj")==0) return true; + if (_stricmp(ext,".gz")==0) return true; + if (_stricmp(ext,".tgz")==0) return true; + return false; +} + + +time_t filetime2timet(const FILETIME ft) +{ SYSTEMTIME st; FileTimeToSystemTime(&ft,&st); + if (st.wYear<1970) {st.wYear=1970; st.wMonth=1; st.wDay=1;} + if (st.wYear>=2038) {st.wYear=2037; st.wMonth=12; st.wDay=31;} + struct tm tm; + tm.tm_sec = st.wSecond; + tm.tm_min = st.wMinute; + tm.tm_hour = st.wHour; + tm.tm_mday = st.wDay; + tm.tm_mon = st.wMonth-1; + tm.tm_year = st.wYear-1900; + tm.tm_isdst = 0; + time_t t = mktime(&tm); + return t; +} + + +ZRESULT GetFileInfo(HANDLE hf, ulg *attr, long *size, iztimes *times, ulg *timestamp) +{ + DWORD type=GetFileType(hf); + if (type!=FILE_TYPE_DISK) + return ZR_NOTINITED; + // The handle must be a handle to a file + // The date and time is returned in a long with the date most significant to allow + // unsigned integer comparison of absolute times. The attributes have two + // high bytes unix attr, and two low bytes a mapping of that to DOS attr. + //struct stat s; int res=stat(fn,&s); if (res!=0) return false; + // translate windows file attributes into zip ones. + BY_HANDLE_FILE_INFORMATION bhi; + BOOL res=GetFileInformationByHandle(hf,&bhi); + if (!res) + return ZR_NOFILE; + DWORD fa=bhi.dwFileAttributes; + ulg a=0; + // Zip uses the lower word for its interpretation of windows stuff + if (fa&FILE_ATTRIBUTE_READONLY) a|=0x01; + if (fa&FILE_ATTRIBUTE_HIDDEN) a|=0x02; + if (fa&FILE_ATTRIBUTE_SYSTEM) a|=0x04; + if (fa&FILE_ATTRIBUTE_DIRECTORY)a|=0x10; + if (fa&FILE_ATTRIBUTE_ARCHIVE) a|=0x20; + // It uses the upper word for standard unix attr, which we must manually construct + if (fa&FILE_ATTRIBUTE_DIRECTORY)a|=0x40000000; // directory + else a|=0x80000000; // normal file + a|=0x01000000; // readable + if (fa&FILE_ATTRIBUTE_READONLY) {} + else a|=0x00800000; // writeable + // now just a small heuristic to check if it's an executable: + DWORD red = 0, hsize=GetFileSize(hf,NULL); if (hsize>40) + { SetFilePointer(hf,0,NULL,FILE_BEGIN); unsigned short magic; ReadFile(hf,&magic,sizeof(magic),&red,NULL); red = 0; + SetFilePointer(hf,36,NULL,FILE_BEGIN); unsigned long hpos; ReadFile(hf,&hpos,sizeof(hpos),&red,NULL); red = 0; + if (magic==0x54AD && hsize>hpos+4+20+28) + { SetFilePointer(hf,hpos,NULL,FILE_BEGIN); unsigned long signature; ReadFile(hf,&signature,sizeof(signature),&red,NULL); + if (signature==IMAGE_DOS_SIGNATURE || signature==IMAGE_OS2_SIGNATURE + || signature==IMAGE_OS2_SIGNATURE_LE || signature==IMAGE_NT_SIGNATURE) + { a |= 0x00400000; // executable + } + } + } + // + if (attr!=NULL) *attr = a; + if (size!=NULL) *size = hsize; + if (times!=NULL) + { // time_t is 32bit number of seconds elapsed since 0:0:0GMT, Jan1, 1970. + // but FILETIME is 64bit number of 100-nanosecs since Jan1, 1601 + times->atime = filetime2timet(bhi.ftLastAccessTime); + times->mtime = filetime2timet(bhi.ftLastWriteTime); + times->ctime = filetime2timet(bhi.ftCreationTime); + } + if (timestamp!=NULL) + { WORD dosdate,dostime; + FileTimeToDosDateTime(&bhi.ftLastWriteTime,&dosdate,&dostime); + *timestamp = (WORD)dostime | (((DWORD)dosdate)<<16); + } + return ZR_OK; +} + + + + + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + +class TZip +{ public: + TZip() : hfout(0),hmapout(0),zfis(0),obuf(0),hfin(0),writ(0),oerr(false),hasputcen(false),ooffset(0) {} + ~TZip() {} + + // These variables say about the file we're writing into + // We can write to pipe, file-by-handle, file-by-name, memory-to-memmapfile + HANDLE hfout; // if valid, we'll write here (for files or pipes) + HANDLE hmapout; // otherwise, we'll write here (for memmap) + unsigned ooffset; // for hfout, this is where the pointer was initially + ZRESULT oerr; // did a write operation give rise to an error? + unsigned writ; // how far have we written. This is maintained by Add, not write(), to avoid confusion over seeks + bool ocanseek; // can we seek? + char *obuf; // this is where we've locked mmap to view. + unsigned int opos; // current pos in the mmap + unsigned int mapsize; // the size of the map we created + bool hasputcen; // have we yet placed the central directory? + // + TZipFileInfo *zfis; // each file gets added onto this list, for writing the table at the end + + ZRESULT Create(void *z,unsigned int len,DWORD flags); + static unsigned sflush(void *param,const char *buf, unsigned *size); + static unsigned swrite(void *param,const char *buf, unsigned size); + unsigned int write(const char *buf,unsigned int size); + bool oseek(unsigned int pos); + ZRESULT GetMemory(void **pbuf, unsigned long *plen); + ZRESULT Close(); + + // some variables to do with the file currently being read: + // I haven't done it object-orientedly here, just put them all + // together, since OO didn't seem to make the design any clearer. + ulg attr; iztimes times; ulg timestamp; // all open_* methods set these + bool iseekable; long isize,ired; // size is not set until close() on pips + ulg crc; // crc is not set until close(). iwrit is cumulative + HANDLE hfin; bool selfclosehf; // for input files and pipes + const char *bufin; unsigned int lenin,posin; // for memory + // and a variable for what we've done with the input: (i.e. compressed it!) + ulg csize; // compressed size, set by the compression routines + // and this is used by some of the compression routines + char buf[16384]; + + + ZRESULT open_file(const TCHAR *fn); + ZRESULT open_handle(HANDLE hf,unsigned int len); + ZRESULT open_mem(void *src,unsigned int len); + ZRESULT open_dir(); + static unsigned sread(TState &s,char *buf,unsigned size); + unsigned read(char *buf, unsigned size); + ZRESULT iclose(); + + ZRESULT ideflate(TZipFileInfo *zfi); + ZRESULT istore(); + + ZRESULT Add(const char *odstzn, void *src,unsigned int len, DWORD flags); + ZRESULT AddCentral(); + +}; + +ZRESULT TZip::Create(void *z,unsigned int len,DWORD flags) +{ + if (hfout!=0 || hmapout!=0 || obuf!=0 || writ!=0 || oerr!=ZR_OK || hasputcen) + return ZR_NOTINITED; + // + if (flags==ZIP_HANDLE) + { + HANDLE hf = (HANDLE)z; + BOOL res = DuplicateHandle(GetCurrentProcess(),hf,GetCurrentProcess(),&hfout,0,FALSE,DUPLICATE_SAME_ACCESS); + if (!res) + return ZR_NODUPH; + // now we have our own hfout, which we must close. And the caller will close hf + DWORD type = GetFileType(hfout); + ocanseek = (type==FILE_TYPE_DISK); + if (type==FILE_TYPE_DISK) + ooffset=SetFilePointer(hfout,0,NULL,FILE_CURRENT); + else + ooffset=0; + return ZR_OK; + } + else if (flags==ZIP_FILENAME) + { +#ifdef _UNICODE + const TCHAR *fn = (const TCHAR*)z; + hfout = CreateFileW(fn,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL); +#else + const char *fn = (const char*)z; + hfout = CreateFileA(fn,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL); +#endif + + if (hfout==INVALID_HANDLE_VALUE) + { + hfout=0; + return ZR_NOFILE; + } + ocanseek=true; + ooffset=0; + return ZR_OK; + } + else if (flags==ZIP_MEMORY) + { + unsigned int size = len; + if (size==0) + return ZR_MEMSIZE; + if (z!=0) + obuf=(char*)z; + else + { + hmapout = CreateFileMapping(INVALID_HANDLE_VALUE,NULL,PAGE_READWRITE,0,size,NULL); + if (hmapout==NULL) + return ZR_NOALLOC; + obuf = (char*)MapViewOfFile(hmapout,FILE_MAP_ALL_ACCESS,0,0,size); + if (obuf==0) + { + CloseHandle(hmapout); + hmapout=0; + return ZR_NOALLOC; + } + } + ocanseek=true; + opos=0; + mapsize=size; + return ZR_OK; + } + else + return ZR_ARGS; +} + + +unsigned TZip::sflush(void *param,const char *buf, unsigned *size) +{ // static + if (*size==0) return 0; + TZip *zip = (TZip*)param; + unsigned int writ = zip->write(buf,*size); + if (writ!=0) *size=0; + return writ; +} +unsigned TZip::swrite(void *param,const char *buf, unsigned size) +{ // static + if (size==0) return 0; + TZip *zip=(TZip*)param; return zip->write(buf,size); +} +unsigned int TZip::write(const char *buf,unsigned int size) +{ if (obuf!=0) + { if (opos+size>=mapsize) {oerr=ZR_MEMSIZE; return 0;} + memcpy(obuf+opos, buf, size); + opos+=size; + return size; + } + else if (hfout!=0) + { DWORD writ=0; WriteFile(hfout,buf,size,&writ,NULL); + return writ; + } + oerr=ZR_NOTINITED; return 0; +} + +bool TZip::oseek(unsigned int pos) +{ if (!ocanseek) {oerr=ZR_SEEK; return false;} + if (obuf!=0) + { if (pos>=mapsize) {oerr=ZR_MEMSIZE; return false;} + opos=pos; + return true; + } + else if (hfout!=0) + { SetFilePointer(hfout,pos+ooffset,NULL,FILE_BEGIN); + return true; + } + oerr=ZR_NOTINITED; return 0; +} + +ZRESULT TZip::GetMemory(void **pbuf, unsigned long *plen) +{ // When the user calls GetMemory, they're presumably at the end + // of all their adding. In any case, we have to add the central + // directory now, otherwise the memory we tell them won't be complete. + if (!hasputcen) AddCentral(); hasputcen=true; + if (pbuf!=NULL) *pbuf=(void*)obuf; + if (plen!=NULL) *plen=writ; + if (obuf==NULL) return ZR_NOTMMAP; + return ZR_OK; +} + +ZRESULT TZip::Close() +{ // if the directory hadn't already been added through a call to GetMemory, + // then we do it now + ZRESULT res=ZR_OK; if (!hasputcen) res=AddCentral(); hasputcen=true; + if (obuf!=0 && hmapout!=0) UnmapViewOfFile(obuf); obuf=0; + if (hmapout!=0) CloseHandle(hmapout); hmapout=0; + if (hfout!=0) CloseHandle(hfout); hfout=0; + return res; +} + + + + +ZRESULT TZip::open_file(const TCHAR *fn) +{ hfin=0; bufin=0; selfclosehf=false; crc=CRCVAL_INITIAL; isize=0; csize=0; ired=0; + if (fn==0) return ZR_ARGS; + HANDLE hf = CreateFile(fn,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,0,NULL); + if (hf==INVALID_HANDLE_VALUE) return ZR_NOFILE; + ZRESULT res = open_handle(hf,0); + if (res!=ZR_OK) {CloseHandle(hf); return res;} + selfclosehf=true; + return ZR_OK; +} +ZRESULT TZip::open_handle(HANDLE hf,unsigned int len) +{ hfin=0; bufin=0; selfclosehf=false; crc=CRCVAL_INITIAL; isize=0; csize=0; ired=0; + if (hf==0 || hf==INVALID_HANDLE_VALUE) return ZR_ARGS; + DWORD type = GetFileType(hf); + if (type==FILE_TYPE_DISK) + { ZRESULT res = GetFileInfo(hf,&attr,&isize,×,×tamp); + if (res!=ZR_OK) return res; + SetFilePointer(hf,0,NULL,FILE_BEGIN); // because GetFileInfo will have screwed it up + iseekable=true; hfin=hf; + return ZR_OK; + } + else + { attr= 0x80000000; // just a normal file + isize = -1; // can't know size until at the end + if (len!=0) isize=len; // unless we were told explicitly! + iseekable=false; + SYSTEMTIME st; GetLocalTime(&st); + FILETIME ft; SystemTimeToFileTime(&st,&ft); + WORD dosdate,dostime; FileTimeToDosDateTime(&ft,&dosdate,&dostime); + times.atime = filetime2timet(ft); + times.mtime = times.atime; + times.ctime = times.atime; + timestamp = (WORD)dostime | (((DWORD)dosdate)<<16); + hfin=hf; + return ZR_OK; + } +} +ZRESULT TZip::open_mem(void *src,unsigned int len) +{ hfin=0; bufin=(const char*)src; selfclosehf=false; crc=CRCVAL_INITIAL; ired=0; csize=0; ired=0; + lenin=len; posin=0; + if (src==0 || len==0) return ZR_ARGS; + attr= 0x80000000; // just a normal file + isize = len; + iseekable=true; + SYSTEMTIME st; GetLocalTime(&st); + FILETIME ft; SystemTimeToFileTime(&st,&ft); + WORD dosdate,dostime; FileTimeToDosDateTime(&ft,&dosdate,&dostime); + times.atime = filetime2timet(ft); + times.mtime = times.atime; + times.ctime = times.atime; + timestamp = (WORD)dostime | (((DWORD)dosdate)<<16); + return ZR_OK; +} +ZRESULT TZip::open_dir() +{ hfin=0; bufin=0; selfclosehf=false; crc=CRCVAL_INITIAL; isize=0; csize=0; ired=0; + attr= 0x41C00010; // a readable writable directory, and again directory + isize = 0; + iseekable=false; + SYSTEMTIME st; GetLocalTime(&st); + FILETIME ft; SystemTimeToFileTime(&st,&ft); + WORD dosdate,dostime; FileTimeToDosDateTime(&ft,&dosdate,&dostime); + times.atime = filetime2timet(ft); + times.mtime = times.atime; + times.ctime = times.atime; + timestamp = (WORD)dostime | (((DWORD)dosdate)<<16); + return ZR_OK; +} + +unsigned TZip::sread(TState &s,char *buf,unsigned size) +{ // static + TZip *zip = (TZip*)s.param; + return zip->read(buf,size); +} + +unsigned TZip::read(char *buf, unsigned size) +{ if (bufin!=0) + { if (posin>=lenin) return 0; // end of input + ulg red = lenin-posin; + if (red>size) red=size; + memcpy(buf, bufin+posin, red); + posin += red; + ired += red; + crc = crc32(crc, (uch*)buf, red); + return red; + } + else if (hfin!=0) + { DWORD red = 0; + BOOL ok = ReadFile(hfin,buf,size,&red,NULL); + if (!ok) return 0; + ired += red; + crc = crc32(crc, (uch*)buf, red); + return red; + } + else {oerr=ZR_NOTINITED; return 0;} +} + +ZRESULT TZip::iclose() +{ if (selfclosehf && hfin!=0) CloseHandle(hfin); hfin=0; + bool mismatch = (isize!=-1 && isize!=ired); + isize=ired; // and crc has been being updated anyway + if (mismatch) return ZR_MISSIZE; + else return ZR_OK; +} + + + +ZRESULT TZip::ideflate(TZipFileInfo *zfi) +{ TState state; + state.readfunc=sread; state.flush_outbuf=sflush; + state.param=this; state.level=8; state.seekable=iseekable; state.err=NULL; + // the following line will make ct_init realise it has to perform the init + state.ts.static_dtree[0].dl.len = 0; + // It would be nicer if I could figure out precisely which data had to + // be initted each time, and which didn't, but that's kind of difficult. + // Maybe for the next version... + // + bi_init(state,buf, sizeof(buf), TRUE); // it used to be just 1024-size, not 16384 as here + ct_init(state,&zfi->att); + lm_init(state,state.level, &zfi->flg); + ulg sz = deflate(state); + csize=sz; + if (state.err!=NULL) return ZR_FLATE; + else return ZR_OK; +} + +ZRESULT TZip::istore() +{ ulg size=0; + for (;;) + { unsigned int cin=read(buf,16384); if (cin<=0 || cin==(unsigned int)EOF) break; + unsigned int cout = write(buf,cin); if (cout!=cin) return ZR_MISSIZE; + size += cin; + } + csize=size; + return ZR_OK; +} + + + + +ZRESULT TZip::Add(const char *odstzn, void *src,unsigned int len, DWORD flags) +{ + if (oerr) + return ZR_FAILED; + if (hasputcen) + return ZR_ENDED; + + // zip has its own notion of what its names should look like: i.e. dir/file.stuff + char dstzn[MAX_PATH] = {0}; + strcpy(dstzn, odstzn); + if (*dstzn == 0) + return ZR_ARGS; + char *d=dstzn; + while (d && *d != 0) + { + if (*d == '\\') + *d = '/'; d++; + } + bool isdir = (flags==ZIP_FOLDER); + bool needs_trailing_slash = (isdir && dstzn[strlen(dstzn)-1]!='/'); + int method=DEFLATE; + if (isdir || HasZipSuffix(dstzn)) + method=STORE; + + // now open whatever was our input source: + ZRESULT openres; + if (flags==ZIP_FILENAME) + openres=open_file((const TCHAR*)src); + else if (flags==ZIP_HANDLE) + openres=open_handle((HANDLE)src,len); + else if (flags==ZIP_MEMORY) + openres=open_mem(src,len); + else if (flags==ZIP_FOLDER) + openres=open_dir(); + else return ZR_ARGS; + if (openres!=ZR_OK) + return openres; + + // A zip "entry" consists of a local header (which includes the file name), + // then the compressed data, and possibly an extended local header. + + // Initialize the local header + TZipFileInfo zfi; zfi.nxt=NULL; + strcpy(zfi.name,""); + strcpy(zfi.iname,dstzn); + zfi.nam=strlen(zfi.iname); + if (needs_trailing_slash) + { + strcat(zfi.iname,"/"); + zfi.nam++; + } + strcpy(zfi.zname,""); + zfi.extra=NULL; zfi.ext=0; // extra header to go after this compressed data, and its length + zfi.cextra=NULL; zfi.cext=0; // extra header to go in the central end-of-zip directory, and its length + zfi.comment=NULL; zfi.com=0; // comment, and its length + zfi.mark = 1; + zfi.dosflag = 0; + zfi.att = (ush)BINARY; + zfi.vem = (ush)0xB17; // 0xB00 is win32 os-code. 0x17 is 23 in decimal: zip 2.3 + zfi.ver = (ush)20; // Needs PKUNZIP 2.0 to unzip it + zfi.tim = timestamp; + // Even though we write the header now, it will have to be rewritten, since we don't know compressed size or crc. + zfi.crc = 0; // to be updated later + zfi.flg = 8; // 8 means 'there is an extra header'. Assume for the moment that we need it. + zfi.lflg = zfi.flg; // to be updated later + zfi.how = (ush)method; // to be updated later + zfi.siz = (ulg)(method==STORE && isize>=0 ? isize : 0); // to be updated later + zfi.len = (ulg)(isize); // to be updated later + zfi.dsk = 0; + zfi.atx = attr; + zfi.off = writ+ooffset; // offset within file of the start of this local record + // stuff the 'times' structure into zfi.extra + char xloc[EB_L_UT_SIZE] = {0}; + zfi.extra=xloc; + zfi.ext=EB_L_UT_SIZE; + char xcen[EB_C_UT_SIZE] = {0}; + zfi.cextra=xcen; + zfi.cext=EB_C_UT_SIZE; + xloc[0] = 'U'; + xloc[1] = 'T'; + xloc[2] = EB_UT_LEN(3); // length of data part of e.f. + xloc[3] = 0; + xloc[4] = EB_UT_FL_MTIME | EB_UT_FL_ATIME | EB_UT_FL_CTIME; + xloc[5] = (char)(times.mtime); + xloc[6] = (char)(times.mtime >> 8); + xloc[7] = (char)(times.mtime >> 16); + xloc[8] = (char)(times.mtime >> 24); + xloc[9] = (char)(times.atime); + xloc[10] = (char)(times.atime >> 8); + xloc[11] = (char)(times.atime >> 16); + xloc[12] = (char)(times.atime >> 24); + xloc[13] = (char)(times.ctime); + xloc[14] = (char)(times.ctime >> 8); + xloc[15] = (char)(times.ctime >> 16); + xloc[16] = (char)(times.ctime >> 24); + memcpy(zfi.cextra,zfi.extra,EB_C_UT_SIZE); + zfi.cextra[EB_LEN] = EB_UT_LEN(1); + + + // (1) Start by writing the local header: + int r = putlocal(&zfi,swrite,this); + if (r!=ZE_OK) + { + iclose(); + return ZR_WRITE; + } + writ += 4 + LOCHEAD + (unsigned int)zfi.nam + (unsigned int)zfi.ext; + if (oerr!=ZR_OK) + { + iclose(); + return oerr; + } + + //(2) Write deflated/stored file to zip file + ZRESULT writeres=ZR_OK; + if (!isdir && method==DEFLATE) + writeres=ideflate(&zfi); + else if (!isdir && method==STORE) + writeres=istore(); + else if (isdir) + csize=0; + iclose(); + writ += csize; + if (oerr!=ZR_OK) + return oerr; + if (writeres!=ZR_OK) + return ZR_WRITE; + + // (3) Either rewrite the local header with correct information... + bool first_header_has_size_right = (zfi.siz==csize); + zfi.crc = crc; + zfi.siz = csize; + zfi.len = isize; + if (ocanseek) + { + zfi.how = (ush)method; + if ((zfi.flg & 1) == 0) + zfi.flg &= ~8; // clear the extended local header flag + zfi.lflg = zfi.flg; + // rewrite the local header: + if (!oseek(zfi.off-ooffset)) + return ZR_SEEK; + if ((r = putlocal(&zfi, swrite,this)) != ZE_OK) + return ZR_WRITE; + if (!oseek(writ)) + return ZR_SEEK; + } + else + { + // (4) ... or put an updated header at the end + if (zfi.how != (ush) method) + return ZR_NOCHANGE; + if (method==STORE && !first_header_has_size_right) + return ZR_NOCHANGE; + if ((r = putextended(&zfi, swrite,this)) != ZE_OK) + return ZR_WRITE; + writ += 16L; + zfi.flg = zfi.lflg; // if flg modified by inflate, for the central index + } + if (oerr!=ZR_OK) + return oerr; + + // Keep a copy of the zipfileinfo, for our end-of-zip directory + char *cextra = new char[zfi.cext]; + memcpy(cextra,zfi.cextra,zfi.cext); zfi.cextra=cextra; + TZipFileInfo *pzfi = new TZipFileInfo; + memcpy(pzfi,&zfi,sizeof(zfi)); + if (zfis==NULL) + zfis=pzfi; + else + { + TZipFileInfo *z=zfis; + while (z->nxt!=NULL) + z=z->nxt; + z->nxt=pzfi; + } + return ZR_OK; +} + +ZRESULT TZip::AddCentral() +{ // write central directory + int numentries = 0; + ulg pos_at_start_of_central = writ; + //ulg tot_unc_size=0, tot_compressed_size=0; + bool okay=true; + for (TZipFileInfo *zfi=zfis; zfi!=NULL; ) + { if (okay) + { int res = putcentral(zfi, swrite,this); + if (res!=ZE_OK) okay=false; + } + writ += 4 + CENHEAD + (unsigned int)zfi->nam + (unsigned int)zfi->cext + (unsigned int)zfi->com; + //tot_unc_size += zfi->len; + //tot_compressed_size += zfi->siz; + numentries++; + // + TZipFileInfo *zfinext = zfi->nxt; + if (zfi->cextra!=0) delete[] zfi->cextra; + delete zfi; + zfi = zfinext; + } + ulg center_size = writ - pos_at_start_of_central; + if (okay) + { int res = putend(numentries, center_size, pos_at_start_of_central+ooffset, 0, NULL, swrite,this); + if (res!=ZE_OK) okay=false; + writ += 4 + ENDHEAD + 0; + } + if (!okay) return ZR_WRITE; + return ZR_OK; +} + + + + + +ZRESULT lasterrorZ=ZR_OK; + +unsigned int FormatZipMessageZ(ZRESULT code, char *buf,unsigned int len) +{ if (code==ZR_RECENT) code=lasterrorZ; + const char *msg="unknown zip result code"; + switch (code) + { case ZR_OK: msg="Success"; break; + case ZR_NODUPH: msg="Culdn't duplicate handle"; break; + case ZR_NOFILE: msg="Couldn't create/open file"; break; + case ZR_NOALLOC: msg="Failed to allocate memory"; break; + case ZR_WRITE: msg="Error writing to file"; break; + case ZR_NOTFOUND: msg="File not found in the zipfile"; break; + case ZR_MORE: msg="Still more data to unzip"; break; + case ZR_CORRUPT: msg="Zipfile is corrupt or not a zipfile"; break; + case ZR_READ: msg="Error reading file"; break; + case ZR_ARGS: msg="Caller: faulty arguments"; break; + case ZR_PARTIALUNZ: msg="Caller: the file had already been partially unzipped"; break; + case ZR_NOTMMAP: msg="Caller: can only get memory of a memory zipfile"; break; + case ZR_MEMSIZE: msg="Caller: not enough space allocated for memory zipfile"; break; + case ZR_FAILED: msg="Caller: there was a previous error"; break; + case ZR_ENDED: msg="Caller: additions to the zip have already been ended"; break; + case ZR_ZMODE: msg="Caller: mixing creation and opening of zip"; break; + case ZR_NOTINITED: msg="Zip-bug: internal initialisation not completed"; break; + case ZR_SEEK: msg="Zip-bug: trying to seek the unseekable"; break; + case ZR_MISSIZE: msg="Zip-bug: the anticipated size turned out wrong"; break; + case ZR_NOCHANGE: msg="Zip-bug: tried to change mind, but not allowed"; break; + case ZR_FLATE: msg="Zip-bug: an internal error during flation"; break; + } + unsigned int mlen=(unsigned int)strlen(msg); + if (buf==0 || len==0) return mlen; + unsigned int n=mlen; if (n+1>len) n=len-1; + strncpy(buf,msg,n); buf[n]=0; + return mlen; +} + + + +typedef struct +{ DWORD flag; + TZip *zip; +} TZipHandleData; + + +HZIP CreateZipZ(void *z,unsigned int len,DWORD flags) +{ + _tzset(); + TZip *zip = new TZip(); + lasterrorZ = zip->Create(z,len,flags); + if (lasterrorZ != ZR_OK) + { + delete zip; + return 0; + } + TZipHandleData *han = new TZipHandleData; + han->flag = 2; + han->zip = zip; + return (HZIP)han; +} + +ZRESULT ZipAdd(HZIP hz, const TCHAR *dstzn, void *src, unsigned int len, DWORD flags) +{ + if (hz == 0) + { + lasterrorZ = ZR_ARGS; + return ZR_ARGS; + } + TZipHandleData *han = (TZipHandleData*)hz; + if (han->flag != 2) + { + lasterrorZ = ZR_ZMODE; + return ZR_ZMODE; + } + TZip *zip = han->zip; + + + if (flags == ZIP_FILENAME) + { + char szDest[MAX_PATH*2]; + memset(szDest, 0, sizeof(szDest)); + +#ifdef _UNICODE + // need to convert Unicode dest to ANSI + int nActualChars = WideCharToMultiByte(CP_ACP, // code page + 0, // performance and mapping flags + (LPCWSTR) dstzn, // wide-character string + -1, // number of chars in string + szDest, // buffer for new string + MAX_PATH*2-2, // size of buffer + NULL, // default for unmappable chars + NULL); // set when default char used + if (nActualChars == 0) + return ZR_ARGS; +#else + strcpy(szDest, dstzn); +#endif + + lasterrorZ = zip->Add(szDest, src, len, flags); + } + else + { + lasterrorZ = zip->Add((char *)dstzn, src, len, flags); + } + + return lasterrorZ; +} + +ZRESULT ZipGetMemory(HZIP hz, void **buf, unsigned long *len) +{ if (hz==0) {if (buf!=0) *buf=0; if (len!=0) *len=0; lasterrorZ=ZR_ARGS;return ZR_ARGS;} + TZipHandleData *han = (TZipHandleData*)hz; + if (han->flag!=2) {lasterrorZ=ZR_ZMODE;return ZR_ZMODE;} + TZip *zip = han->zip; + lasterrorZ = zip->GetMemory(buf,len); + return lasterrorZ; +} + +ZRESULT CloseZipZ(HZIP hz) +{ if (hz==0) {lasterrorZ=ZR_ARGS;return ZR_ARGS;} + TZipHandleData *han = (TZipHandleData*)hz; + if (han->flag!=2) {lasterrorZ=ZR_ZMODE;return ZR_ZMODE;} + TZip *zip = han->zip; + lasterrorZ = zip->Close(); + delete zip; + delete han; + return lasterrorZ; +} + +bool IsZipHandleZ(HZIP hz) +{ if (hz==0) return true; + TZipHandleData *han = (TZipHandleData*)hz; + return (han->flag==2); +} + diff --git a/Src/Plugins/General/gen_crasher/feedback/xzip/XZip.h b/Src/Plugins/General/gen_crasher/feedback/xzip/XZip.h new file mode 100644 index 00000000..9cc015b9 --- /dev/null +++ b/Src/Plugins/General/gen_crasher/feedback/xzip/XZip.h @@ -0,0 +1,324 @@ +// XZip.h Version 1.1 +// +// Authors: Mark Adler et al. (see below) +// +// Modified by: Lucian Wischik +// lu@wischik.com +// +// Version 1.0 - Turned C files into just a single CPP file +// - Made them compile cleanly as C++ files +// - Gave them simpler APIs +// - Added the ability to zip/unzip directly in memory without +// any intermediate files +// +// Modified by: Hans Dietrich +// hdietrich2@hotmail.com +// +// Version 1.1: - Added Unicode support to CreateZip() and ZipAdd() +// - Changed file names to avoid conflicts with Lucian's files +// +/////////////////////////////////////////////////////////////////////////////// +// +// Lucian Wischik's comments: +// -------------------------- +// THIS FILE is almost entirely based upon code by info-zip. +// It has been modified by Lucian Wischik. +// The original code may be found at http://www.info-zip.org +// The original copyright text follows. +// +/////////////////////////////////////////////////////////////////////////////// +// +// Original authors' comments: +// --------------------------- +// This is version 2002-Feb-16 of the Info-ZIP copyright and license. The +// definitive version of this document should be available at +// ftp://ftp.info-zip.org/pub/infozip/license.html indefinitely. +// +// Copyright (c) 1990-2002 Info-ZIP. All rights reserved. +// +// For the purposes of this copyright and license, "Info-ZIP" is defined as +// the following set of individuals: +// +// Mark Adler, John Bush, Karl Davis, Harald Denker, Jean-Michel Dubois, +// Jean-loup Gailly, Hunter Goatley, Ian Gorman, Chris Herborth, Dirk Haase, +// Greg Hartwig, Robert Heath, Jonathan Hudson, Paul Kienitz, +// David Kirschbaum, Johnny Lee, Onno van der Linden, Igor Mandrichenko, +// Steve P. Miller, Sergio Monesi, Keith Owens, George Petrov, Greg Roelofs, +// Kai Uwe Rommel, Steve Salisbury, Dave Smith, Christian Spieler, +// Antoine Verheijen, Paul von Behren, Rich Wales, Mike White +// +// This software is provided "as is", without warranty of any kind, express +// or implied. In no event shall Info-ZIP or its contributors be held liable +// for any direct, indirect, incidental, special or consequential damages +// arising out of the use of or inability to use this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. Redistributions of source code must retain the above copyright notice, +// definition, disclaimer, and this list of conditions. +// +// 2. Redistributions in binary form (compiled executables) must reproduce +// the above copyright notice, definition, disclaimer, and this list of +// conditions in documentation and/or other materials provided with the +// distribution. The sole exception to this condition is redistribution +// of a standard UnZipSFX binary as part of a self-extracting archive; +// that is permitted without inclusion of this license, as long as the +// normal UnZipSFX banner has not been removed from the binary or disabled. +// +// 3. Altered versions--including, but not limited to, ports to new +// operating systems, existing ports with new graphical interfaces, and +// dynamic, shared, or static library versions--must be plainly marked +// as such and must not be misrepresented as being the original source. +// Such altered versions also must not be misrepresented as being +// Info-ZIP releases--including, but not limited to, labeling of the +// altered versions with the names "Info-ZIP" (or any variation thereof, +// including, but not limited to, different capitalizations), +// "Pocket UnZip", "WiZ" or "MacZip" without the explicit permission of +// Info-ZIP. Such altered versions are further prohibited from +// misrepresentative use of the Zip-Bugs or Info-ZIP e-mail addresses or +// of the Info-ZIP URL(s). +// +// 4. Info-ZIP retains the right to use the names "Info-ZIP", "Zip", "UnZip", +// "UnZipSFX", "WiZ", "Pocket UnZip", "Pocket Zip", and "MacZip" for its +// own source and binary releases. +// +/////////////////////////////////////////////////////////////////////////////// + +#ifndef XZIP_H +#define XZIP_H + +// ZIP functions -- for creating zip files +// This file is a repackaged form of the Info-Zip source code available +// at www.info-zip.org. The original copyright notice may be found in +// zip.cpp. The repackaging was done by Lucian Wischik to simplify its +// use in Windows/C++. + +#ifndef XUNZIP_H +DECLARE_HANDLE(HZIP); // An HZIP identifies a zip file that is being created +#endif + +typedef DWORD ZRESULT; // result codes from any of the zip functions. Listed later. + +// flag values passed to some functions +#define ZIP_HANDLE 1 +#define ZIP_FILENAME 2 +#define ZIP_MEMORY 3 +#define ZIP_FOLDER 4 + + +/////////////////////////////////////////////////////////////////////////////// +// +// CreateZip() +// +// Purpose: Create a zip archive file +// +// Parameters: z - archive file name if flags is ZIP_FILENAME; for other +// uses see below +// len - for memory (ZIP_MEMORY) should be the buffer size; +// for other uses, should be 0 +// flags - indicates usage, see below; for files, this will be +// ZIP_FILENAME +// +// Returns: HZIP - non-zero if zip archive created ok, otherwise 0 +// +HZIP CreateZip(void *z, unsigned int len, DWORD flags); +// CreateZip - call this to start the creation of a zip file. +// As the zip is being created, it will be stored somewhere: +// to a pipe: CreateZip(hpipe_write, 0,ZIP_HANDLE); +// in a file (by handle): CreateZip(hfile, 0,ZIP_HANDLE); +// in a file (by name): CreateZip("c:\\test.zip", 0,ZIP_FILENAME); +// in memory: CreateZip(buf, len,ZIP_MEMORY); +// or in pagefile memory: CreateZip(0, len,ZIP_MEMORY); +// The final case stores it in memory backed by the system paging file, +// where the zip may not exceed len bytes. This is a bit friendlier than +// allocating memory with new[]: it won't lead to fragmentation, and the +// memory won't be touched unless needed. +// Note: because pipes don't allow random access, the structure of a zipfile +// created into a pipe is slightly different from that created into a file +// or memory. In particular, the compressed-size of the item cannot be +// stored in the zipfile until after the item itself. (Also, for an item added +// itself via a pipe, the uncompressed-size might not either be known until +// after.) This is not normally a problem. But if you try to unzip via a pipe +// as well, then the unzipper will not know these things about the item until +// after it has been unzipped. Therefore: for unzippers which don't just write +// each item to disk or to a pipe, but instead pre-allocate memory space into +// which to unzip them, then either you have to create the zip not to a pipe, +// or you have to add items not from a pipe, or at least when adding items +// from a pipe you have to specify the length. + + +/////////////////////////////////////////////////////////////////////////////// +// +// ZipAdd() +// +// Purpose: Add a file to a zip archive +// +// Parameters: hz - handle to an open zip archive +// dstzn - name used inside the zip archive to identify the file +// src - for a file (ZIP_FILENAME) this specifies the filename +// to be added to the archive; for other uses, see below +// len - for memory (ZIP_MEMORY) this specifies the buffer +// length; for other uses, this should be 0 +// flags - indicates usage, see below; for files, this will be +// ZIP_FILENAME +// +// Returns: ZRESULT - ZR_OK if success, otherwise some other value +// +ZRESULT ZipAdd(HZIP hz, const TCHAR *dstzn, void *src, unsigned int len, DWORD flags); +// ZipAdd - call this for each file to be added to the zip. +// dstzn is the name that the file will be stored as in the zip file. +// The file to be added to the zip can come +// from a pipe: ZipAdd(hz,"file.dat", hpipe_read,0,ZIP_HANDLE); +// from a file: ZipAdd(hz,"file.dat", hfile,0,ZIP_HANDLE); +// from a fname: ZipAdd(hz,"file.dat", "c:\\docs\\origfile.dat",0,ZIP_FILENAME); +// from memory: ZipAdd(hz,"subdir\\file.dat", buf,len,ZIP_MEMORY); +// (folder): ZipAdd(hz,"subdir", 0,0,ZIP_FOLDER); +// Note: if adding an item from a pipe, and if also creating the zip file itself +// to a pipe, then you might wish to pass a non-zero length to the ZipAdd +// function. This will let the zipfile store the items size ahead of the +// compressed item itself, which in turn makes it easier when unzipping the +// zipfile into a pipe. + + +/////////////////////////////////////////////////////////////////////////////// +// +// CloseZip() +// +// Purpose: Close an open zip archive +// +// Parameters: hz - handle to an open zip archive +// +// Returns: ZRESULT - ZR_OK if success, otherwise some other value +// +ZRESULT CloseZip(HZIP hz); +// CloseZip - the zip handle must be closed with this function. + + +ZRESULT ZipGetMemory(HZIP hz, void **buf, unsigned long *len); +// ZipGetMemory - If the zip was created in memory, via ZipCreate(0,ZIP_MEMORY), +// then this function will return information about that memory block. +// buf will receive a pointer to its start, and len its length. +// Note: you can't add any more after calling this. + + +unsigned int FormatZipMessage(ZRESULT code, char *buf,unsigned int len); +// FormatZipMessage - given an error code, formats it as a string. +// It returns the length of the error message. If buf/len points +// to a real buffer, then it also writes as much as possible into there. + + + +// These are the result codes: +#define ZR_OK 0x00000000 // nb. the pseudo-code zr-recent is never returned, +#define ZR_RECENT 0x00000001 // but can be passed to FormatZipMessage. +// The following come from general system stuff (e.g. files not openable) +#define ZR_GENMASK 0x0000FF00 +#define ZR_NODUPH 0x00000100 // couldn't duplicate the handle +#define ZR_NOFILE 0x00000200 // couldn't create/open the file +#define ZR_NOALLOC 0x00000300 // failed to allocate some resource +#define ZR_WRITE 0x00000400 // a general error writing to the file +#define ZR_NOTFOUND 0x00000500 // couldn't find that file in the zip +#define ZR_MORE 0x00000600 // there's still more data to be unzipped +#define ZR_CORRUPT 0x00000700 // the zipfile is corrupt or not a zipfile +#define ZR_READ 0x00000800 // a general error reading the file +// The following come from mistakes on the part of the caller +#define ZR_CALLERMASK 0x00FF0000 +#define ZR_ARGS 0x00010000 // general mistake with the arguments +#define ZR_NOTMMAP 0x00020000 // tried to ZipGetMemory, but that only works on mmap zipfiles, which yours wasn't +#define ZR_MEMSIZE 0x00030000 // the memory size is too small +#define ZR_FAILED 0x00040000 // the thing was already failed when you called this function +#define ZR_ENDED 0x00050000 // the zip creation has already been closed +#define ZR_MISSIZE 0x00060000 // the indicated input file size turned out mistaken +#define ZR_PARTIALUNZ 0x00070000 // the file had already been partially unzipped +#define ZR_ZMODE 0x00080000 // tried to mix creating/opening a zip +// The following come from bugs within the zip library itself +#define ZR_BUGMASK 0xFF000000 +#define ZR_NOTINITED 0x01000000 // initialisation didn't work +#define ZR_SEEK 0x02000000 // trying to seek in an unseekable file +#define ZR_NOCHANGE 0x04000000 // changed its mind on storage, but not allowed +#define ZR_FLATE 0x05000000 // an internal error in the de/inflation code + + + +// e.g. +// +// (1) Traditional use, creating a zipfile from existing files +// HZIP hz = CreateZip("c:\\temp.zip",0,ZIP_FILENAME); +// ZipAdd(hz,"src1.txt", "c:\\src1.txt",0,ZIP_FILENAME); +// ZipAdd(hz,"src2.bmp", "c:\\src2_origfn.bmp",0,ZIP_FILENAME); +// CloseZip(hz); +// +// (2) Memory use, creating an auto-allocated mem-based zip file from various sources +// HZIP hz = CreateZip(0,100000,ZIP_MEMORY); +// // adding a conventional file... +// ZipAdd(hz,"src1.txt", "c:\\src1.txt",0,ZIP_FILENAME); +// // adding something from memory... +// char buf[1000]; for (int i=0; i<1000; i++) buf[i]=(char)(i&0x7F); +// ZipAdd(hz,"file.dat", buf,1000,ZIP_MEMORY); +// // adding something from a pipe... +// HANDLE hread,hwrite; CreatePipe(&hread,&write,NULL,0); +// HANDLE hthread = CreateThread(ThreadFunc,(void*)hwrite); +// ZipAdd(hz,"unz3.dat", hread,0,ZIP_HANDLE); +// WaitForSingleObject(hthread,INFINITE); +// CloseHandle(hthread); CloseHandle(hread); +// ... meanwhile DWORD CALLBACK ThreadFunc(void *dat) +// { HANDLE hwrite = (HANDLE)dat; +// char buf[1000]={17}; +// DWORD writ; WriteFile(hwrite,buf,1000,&writ,NULL); +// CloseHandle(hwrite); +// return 0; +// } +// // and now that the zip is created, let's do something with it: +// void *zbuf; unsigned long zlen; ZipGetMemory(hz,&zbuf,&zlen); +// HANDLE hfz = CreateFile("test2.zip",GENERIC_WRITE,CREATE_ALWAYS); +// DWORD writ; WriteFile(hfz,zbuf,zlen,&writ,NULL); +// CloseHandle(hfz); +// CloseZip(hz); +// +// (3) Handle use, for file handles and pipes +// HANDLE hzread,hzwrite; CreatePipe(&hzread,&hzwrite); +// HANDLE hthread = CreateThread(ZipReceiverThread,(void*)hread); +// HZIP hz = ZipCreate(hzwrite,ZIP_HANDLE); +// // ... add to it +// CloseZip(hz); +// CloseHandle(hzwrite); +// WaitForSingleObject(hthread,INFINITE); +// CloseHandle(hthread); +// ... meanwhile DWORD CALLBACK ThreadFunc(void *dat) +// { HANDLE hread = (HANDLE)dat; +// char buf[1000] = {0}; +// while (true) +// { DWORD red = 0; ReadFile(hread,buf,1000,&red,NULL); +// // ... and do something with this zip data we're receiving +// if (red==0) break; +// } +// CloseHandle(hread); +// return 0; +// } +// + + +// Now we indulge in a little skullduggery so that the code works whether +// the user has included just zip or both zip and unzip. +// Idea: if header files for both zip and unzip are present, then presumably +// the cpp files for zip and unzip are both present, so we will call +// one or the other of them based on a dynamic choice. If the header file +// for only one is present, then we will bind to that particular one. +HZIP CreateZipZ(void *z,unsigned int len,DWORD flags); +ZRESULT CloseZipZ(HZIP hz); +unsigned int FormatZipMessageZ(ZRESULT code, char *buf,unsigned int len); +bool IsZipHandleZ(HZIP hz); +#define CreateZip CreateZipZ + +#ifdef XUNZIP_H +#undef CloseZip +#define CloseZip(hz) (IsZipHandleZ(hz)?CloseZipZ(hz):CloseZipU(hz)) +#else +#define CloseZip CloseZipZ +#define FormatZipMessage FormatZipMessageZ +#endif + + +#endif //XZIP_H diff --git a/Src/Plugins/General/gen_crasher/gen_crasher.rc b/Src/Plugins/General/gen_crasher/gen_crasher.rc new file mode 100644 index 00000000..f989782e --- /dev/null +++ b/Src/Plugins/General/gen_crasher/gen_crasher.rc @@ -0,0 +1,193 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "#include ""version.rc2""\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_CRASHDLG DIALOGEX 0, 0, 187, 42 +STYLE DS_SYSMODAL | DS_SETFONT | DS_SETFOREGROUND | DS_FIXEDSYS | DS_NOFAILCREATE | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +EXSTYLE WS_EX_NOPARENTNOTIFY +CAPTION "Winamp Error Reporter" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + ICON 102,IDC_BMP_LOGO,8,6,21,20,SS_REALSIZEIMAGE + LTEXT "",IDC_LBL_STEP,48,6,127,10 + CONTROL "",IDC_PRG_COLLECT,"msctls_progress32",WS_BORDER,48,19,127,9 +END + +IDD_CONFIG DIALOGEX 0, 0, 273, 246 +STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + GROUPBOX "General",IDC_GRP_GENERAL,0,0,273,64 + CONTROL "Auto Restart",IDC_CHK_RESTART,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,6,12,90,10 + CONTROL "Compress results",IDC_CHK_COMPRESS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,6,24,90,10 + CONTROL "Create Dump File",IDC_CHK_CREATEDMP,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,6,36,90,10 + CONTROL "Create Log File",IDC_CHK_CREATELOG,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,6,49,90,10 + GROUPBOX "",IDC_GRP_EMAIL,101,13,165,45 + CONTROL "Send Data",IDC_CHK_SEND,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,106,12,46,10 + CONTROL "Using default email program",IDC_RB_USECLIENT,"Button",BS_AUTORADIOBUTTON,106,27,105,10 + CONTROL "Using SMTP server",IDC_RB_USESMTP,"Button",BS_AUTORADIOBUTTON,106,41,75,10 + PUSHBUTTON "SMTP Settings...",IDC_BTN_SMTP,185,39,75,13 + GROUPBOX "Dump File",IDC_GRP_DUMP,0,67,273,66 + LTEXT "OS version:",IDC_LBL_OSVERSION_CAPTION,6,78,38,8 + LTEXT "",IDC_LBL_OSVERSION,48,78,218,8 + LTEXT "Dll path:",IDC_LBL_DLLPATH_CAPTION,6,90,38,8 + LTEXT "",IDC_LBL_DLLPATH,48,90,218,8,SS_PATHELLIPSIS + LTEXT "Dll version:",IDC_LBL_DLLVERSION_CAPTION,6,102,38,8 + LTEXT "unknown [unable to load]",IDC_LBL_DLLVERSION,48,102,219,8 + LTEXT "Type:",IDC_LBL_DMPTYPE,6,116,20,8 + COMBOBOX IDC_CMB_DMPTYPE,30,114,237,84,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + GROUPBOX "Log File",IDC_GRP_LOG,0,136,273,28 + CONTROL "System info",IDC_CHK_LOGSYSTEM,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,6,149,53,10 + CONTROL "Stack data",IDC_CHK_LOGSTACK,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,63,149,50,10 + CONTROL "Registry state",IDC_CHK_LOGREGISTRY,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,117,149,62,10 + CONTROL "Loaded modules",IDC_CHK_LOGMODULE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,183,149,67,10 + LTEXT "Save report in:",IDC_LBL_PATH,6,180,50,8 + EDITTEXT IDC_EDT_PATH,60,178,188,12,ES_AUTOHSCROLL + PUSHBUTTON "...",IDC_BTN_PATH,249,178,19,12 + GROUPBOX "File Paths",IDC_GRP_ZIP,1,168,272,76 + LTEXT "Zip filename:",IDC_LBL_ZIPNAME,6,196,50,8 + EDITTEXT IDC_EDT_ZIPNAME,60,194,208,12,ES_AUTOHSCROLL + LTEXT "Dump filename:",IDC_LBL_DMPNAME,6,212,50,8 + EDITTEXT IDC_EDT_DMPNAME,60,210,208,12,ES_AUTOHSCROLL + LTEXT "Log filename:",IDC_LBL_LOGNAME,6,229,50,8 + EDITTEXT IDC_EDT_LOGNAME,60,227,208,12,ES_AUTOHSCROLL + CONTROL "Do not ask questions",IDC_CHK_SILENT,"Button",BS_AUTOCHECKBOX | NOT WS_VISIBLE | WS_DISABLED | WS_TABSTOP,281,0,90,10 +END + +IDD_DLG_SMTP DIALOGEX 0, 0, 180, 155 +STYLE DS_SETFONT | DS_SETFOREGROUND | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +EXSTYLE WS_EX_TOOLWINDOW +CAPTION "SMTP Settings" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + GROUPBOX "Server Details",IDC_GRP_AUTH2,5,5,170,63,BS_LEFT + LTEXT "Server:",IDC_LBL_SERVER,12,18,54,8 + EDITTEXT IDC_EDT_SERVER,70,16,100,12,ES_AUTOHSCROLL + LTEXT "Port:",IDC_LBL_PORT,12,33,54,8 + EDITTEXT IDC_EDT_PORT,70,32,21,12,ES_AUTOHSCROLL | ES_NUMBER,WS_EX_RIGHT + LTEXT "Sender Address:",IDC_LBL_ADDRESS,12,50,54,8 + EDITTEXT IDC_EDT_ADDRESS,70,48,100,14,ES_AUTOHSCROLL + GROUPBOX "Authentication",IDC_GRP_AUTH,5,71,170,62,BS_LEFT + CONTROL "Server requires authentication",IDC_CHK_AUTH,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,11,84,116,9 + LTEXT "User:",IDC_LBL_USER,11,100,34,8 + EDITTEXT IDC_EDT_USER,49,97,120,12,ES_AUTOHSCROLL + LTEXT "Password:",IDC_LBL_PWD,11,116,34,8 + EDITTEXT IDC_EDT_PWD,49,113,120,12,ES_PASSWORD | ES_AUTOHSCROLL + DEFPUSHBUTTON "Close",IDCANCEL,125,137,50,13 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_CRASHDLG, DIALOG + BEGIN + BOTTOMMARGIN, 39 + END + + IDD_DLG_SMTP, DIALOG + BEGIN + LEFTMARGIN, 5 + RIGHTMARGIN, 175 + TOPMARGIN, 5 + BOTTOMMARGIN, 150 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + IDS_ERROR_FEEDBACK "Error Feedback" + IDS_UNKNOWN "unknown" + IDS_LOADED_OK "loaded" + IDS_UNABLE_TO_LOAD "unable to load" + IDS_NOT_FOUND "not found" + IDS_UNABLE_TO_SAVE_SETTINGS "Unable to save error feedback settings" + IDS_SAVE_ERROR "Save Error" + IDS_SELECT_FOLDER_FOR_ERROR_INFO + "Select folder where the error information will be saved to" +END + +STRINGTABLE +BEGIN + IDS_NULLSOFT_ERROR_FEEDBACK "Nullsoft Error Feedback v%s" + 65535 "{092A97EF-7DC0-41a7-80D1-90DEEB18F12D}" +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// +#include "version.rc2" + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/Src/Plugins/General/gen_crasher/gen_crasher.sln b/Src/Plugins/General/gen_crasher/gen_crasher.sln new file mode 100644 index 00000000..e849e1e7 --- /dev/null +++ b/Src/Plugins/General/gen_crasher/gen_crasher.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29424.173 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gen_crasher", "gen_crasher.vcxproj", "{A029F791-2838-4D16-BC92-C8E04D677948}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A029F791-2838-4D16-BC92-C8E04D677948}.Debug|Win32.ActiveCfg = Debug|Win32 + {A029F791-2838-4D16-BC92-C8E04D677948}.Debug|Win32.Build.0 = Debug|Win32 + {A029F791-2838-4D16-BC92-C8E04D677948}.Debug|x64.ActiveCfg = Debug|x64 + {A029F791-2838-4D16-BC92-C8E04D677948}.Debug|x64.Build.0 = Debug|x64 + {A029F791-2838-4D16-BC92-C8E04D677948}.Release|Win32.ActiveCfg = Release|Win32 + {A029F791-2838-4D16-BC92-C8E04D677948}.Release|Win32.Build.0 = Release|Win32 + {A029F791-2838-4D16-BC92-C8E04D677948}.Release|x64.ActiveCfg = Release|x64 + {A029F791-2838-4D16-BC92-C8E04D677948}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {A8CBA02D-A7C5-4784-BC88-C554B8121167} + EndGlobalSection +EndGlobal diff --git a/Src/Plugins/General/gen_crasher/gen_crasher.vcxproj b/Src/Plugins/General/gen_crasher/gen_crasher.vcxproj new file mode 100644 index 00000000..5412bea0 --- /dev/null +++ b/Src/Plugins/General/gen_crasher/gen_crasher.vcxproj @@ -0,0 +1,290 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {A029F791-2838-4D16-BC92-C8E04D677948} + gen_crasher + 10.0.19041.0 + + + + DynamicLibrary + v142 + Unicode + + + DynamicLibrary + v142 + Unicode + + + DynamicLibrary + v142 + Unicode + + + DynamicLibrary + v142 + Unicode + + + + + + + + + + + + + + + + + + + false + $(PlatformShortName)_$(Configuration)\ + $(PlatformShortName)_$(Configuration)\ + $(IncludePath) + $(LibraryPath) + + + false + $(PlatformShortName)_$(Configuration)\ + $(PlatformShortName)_$(Configuration)\ + + + false + $(PlatformShortName)_$(Configuration)\ + $(PlatformShortName)_$(Configuration)\ + $(IncludePath) + $(LibraryPath) + true + + + false + $(PlatformShortName)_$(Configuration)\ + $(PlatformShortName)_$(Configuration)\ + + + false + + + Debug + + + + Disabled + ..\..\..\Wasabi;%(AdditionalIncludeDirectories) + _WIN32_WINNT=0x0601;WINVER=0x0601;WIN32;_DEBUG;_WINDOWS;_USRDLL;GEN_CRASHER_EXPORTS;_WIN32_IE=0x0A00;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + false + true + EnableFastChecks + MultiThreadedDebugDLL + false + Level3 + ProgramDatabase + 4996;%(DisableSpecificWarnings) + false + $(IntDir)$(TargetName).pdb + + + shlwapi.lib;Version.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)$(TargetExt) + true + $(IntDir)$(TargetName).pdb + Windows + false + $(IntDir)$(TargetName).lib + MachineX86 + false + %(AdditionalLibraryDirectories) + + + xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ +xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ + Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\' + + + _WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions) + + + + + Disabled + ..\..\..\Wasabi;%(AdditionalIncludeDirectories) + _WIN32_WINNT=0x0601;WINVER=0x0601;_DEBUG;WIN64;_WINDOWS;_USRDLL;GEN_CRASHER_EXPORTS;_WIN32_IE=0x0A00;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + false + true + EnableFastChecks + MultiThreadedDebugDLL + true + Level3 + ProgramDatabase + Default + 4996;%(DisableSpecificWarnings) + false + $(IntDir)$(TargetName).pdb + + + shlwapi.lib;Version.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)$(TargetExt) + true + $(IntDir)$(TargetName).pdb + false + $(IntDir)$(TargetName).lib + false + %(AdditionalLibraryDirectories) + Windows + + + xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ +xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ + Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\' + + + _WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions) + + + + + MinSpace + OnlyExplicitInline + Size + ..\..\..\Wasabi;%(AdditionalIncludeDirectories) + _WIN32_WINNT=0x0601;WINVER=0x0601;WIN32;NDEBUG;_WINDOWS;_USRDLL;GEN_CRASHER_EXPORTS;_WIN32_IE=0x0A00;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + true + true + MultiThreadedDLL + false + $(IntDir) + Level3 + None + 4996;%(DisableSpecificWarnings) + $(IntDir)$(TargetName).pdb + + + shlwapi.lib;Version.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)$(TargetExt) + false + $(IntDir)$(TargetName).pdb + Windows + true + true + false + true + $(IntDir)$(TargetName).lib + MachineX86 + false + %(AdditionalLibraryDirectories) + + + xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ + Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\' + + + _WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions) + + + + + MinSpace + OnlyExplicitInline + Size + ..\..\..\Wasabi;%(AdditionalIncludeDirectories) + _WIN32_WINNT=0x0601;WINVER=0x0601;NDEBUG;WIN64;_WINDOWS;_USRDLL;GEN_CRASHER_EXPORTS;_WIN32_IE=0x0A00;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + true + true + MultiThreadedDLL + false + $(IntDir) + Level3 + None + 4996;%(DisableSpecificWarnings) + $(IntDir)$(TargetName).pdb + + + shlwapi.lib;Version.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)$(TargetExt) + false + $(IntDir)$(TargetName).pdb + true + true + false + true + $(IntDir)$(TargetName).lib + false + %(AdditionalLibraryDirectories) + Windows + + + xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ + Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\' + + + _WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions) + + + + + {3e0bfa8a-b86a-42e9-a33f-ec294f823f7f} + + + {a845d04c-a95e-424c-bfa8-d7706dba78bf} + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Src/Plugins/General/gen_crasher/gen_crasher.vcxproj.filters b/Src/Plugins/General/gen_crasher/gen_crasher.vcxproj.filters new file mode 100644 index 00000000..a1cf4506 --- /dev/null +++ b/Src/Plugins/General/gen_crasher/gen_crasher.vcxproj.filters @@ -0,0 +1,92 @@ + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + {387133cc-523c-4c1f-8215-3de0d29784a3} + + + {1515be17-59cd-45ca-abc3-a3b3e66c36b9} + + + {89f5adbb-9d33-4588-b2cb-942e0cc75987} + + + + + Ressource Files + + + \ No newline at end of file diff --git a/Src/Plugins/General/gen_crasher/main.cpp b/Src/Plugins/General/gen_crasher/main.cpp new file mode 100644 index 00000000..7385db07 --- /dev/null +++ b/Src/Plugins/General/gen_crasher/main.cpp @@ -0,0 +1,140 @@ +// Winamp error feedback plugin +// Copyright (C) 2005 Nullsoft + +//#define PLUGIN_DESC "Nullsoft Error Feedback" +#define PLUGIN_VER L"1.16" + +#include ".\main.h" +#include "configDlg.h" +#include "crashDlg.h" +#include "api__gen_crasher.h" +#include "../nu/ServiceWatcher.h" + +ServiceWatcher watcher; +Settings settings; +prefsDlgRecW prefItem = {0}; +char *winampVersion; +static wchar_t prefsTitle[64]; + +// wasabi based services for localisation support +api_service *WASABI_API_SVC = 0; +api_language *WASABI_API_LNG = 0; +api_syscb *WASABI_API_SYSCB = 0; +api_application *WASABI_API_APP = 0; +HINSTANCE WASABI_API_LNG_HINST = 0, WASABI_API_ORIG_HINST = 0; + +int init(void); +void config(void); +void quit(void); + +extern "C" winampGeneralPurposePlugin plugin = +{ + GPPHDR_VER_U, + "nullsoft(gen_crasher.dll)", + init, + config, + quit, +}; + +extern "C" __declspec(dllexport) winampGeneralPurposePlugin * winampGetGeneralPurposePlugin() { return &plugin; } + +int init(void) +{ + if (!settings.IsOk()) return GEN_INIT_FAILURE; + + // loader so that we can get the localisation service api for use + WASABI_API_SVC = (api_service*)SendMessage(plugin.hwndParent, WM_WA_IPC, 0, IPC_GET_API_SERVICE); + if (!WASABI_API_SVC || WASABI_API_SVC == (api_service *)1) + return GEN_INIT_FAILURE; + + waServiceFactory *sf = WASABI_API_SVC->service_getServiceByGuid(applicationApiServiceGuid); + if (sf) WASABI_API_SYSCB = reinterpret_cast(sf->getInterface()); + + watcher.WatchWith(WASABI_API_SVC); + watcher.WatchFor(&WASABI_API_LNG, languageApiGUID); + WASABI_API_SYSCB->syscb_registerCallback(&watcher); + + sf = WASABI_API_SVC->service_getServiceByGuid(applicationApiServiceGuid); + if (sf) WASABI_API_APP = reinterpret_cast(sf->getInterface()); + + // need to have this initialised before we try to do anything with localisation features + WASABI_API_START_LANG(plugin.hDllInstance,GenCrasherLangGUID); + + static wchar_t szDescription[256]; + swprintf(szDescription, ARRAYSIZE(szDescription), + WASABI_API_LNGSTRINGW(IDS_NULLSOFT_ERROR_FEEDBACK), PLUGIN_VER); + plugin.description = (char*)szDescription; + + //register prefs screen + prefItem.dlgID = IDD_CONFIG; + prefItem.name = WASABI_API_LNGSTRINGW_BUF(IDS_ERROR_FEEDBACK,prefsTitle,64); + prefItem.proc = (void*) ConfigDlgProc; + prefItem.hInst = WASABI_API_LNG_HINST; + prefItem.where = -1; + SendMessageA(plugin.hwndParent, WM_WA_IPC, (WPARAM) &prefItem, IPC_ADD_PREFS_DLGW); + winampVersion = (char *)SendMessageA(plugin.hwndParent,WM_WA_IPC,0,IPC_GETVERSIONSTRING); + return GEN_INIT_SUCCESS; +} + +void config(void) +{ + SendMessage(plugin.hwndParent,WM_WA_IPC,(WPARAM)&prefItem,IPC_OPENPREFSTOPAGE); +} + +void quit(void) +{ + watcher.StopWatching(); + watcher.Clear(); + + waServiceFactory *sf = WASABI_API_SVC->service_getServiceByGuid(languageApiGUID); + if (sf) sf->releaseInterface(WASABI_API_LNG); + WASABI_API_LNG=0; +} + +int StartHandler(wchar_t* iniPath) +{ + settings.SetPath(iniPath); + if (!settings.Load()) + { + if (!(settings.CreateDefault(iniPath) && settings.Save())) + { + //OutputDebugString(L"Feedback plugin - unable to read settings. Error feedback disabled.\r\n"); + } + } + SetUnhandledExceptionFilter(FeedBackFilter); + //OutputDebugString(L"Error FeedBack started.\r\n"); + return 0; +} + +PEXCEPTION_POINTERS gExceptionInfo; + +LONG WINAPI FeedBackFilter( struct _EXCEPTION_POINTERS *pExceptionInfo ) +{ + if (alreadyProccessing) return EXCEPTION_CONTINUE_EXECUTION; + + alreadyProccessing = TRUE; + gExceptionInfo = pExceptionInfo; + + // show user dialog + HWND hwnd; + if (WASABI_API_LNG) + hwnd = WASABI_API_CREATEDIALOGPARAMW(IDD_CRASHDLG, NULL, CrashDlgProc, (LPARAM)WASABI_API_ORIG_HINST); + else + #ifdef _M_IX86 + hwnd = CreateDialogParam(plugin.hDllInstance, MAKEINTRESOURCE(IDD_CRASHDLG), NULL, CrashDlgProc, (LPARAM)plugin.hDllInstance); + #endif + #ifdef _M_X64 + hwnd = CreateDialogParam(plugin.hDllInstance, MAKEINTRESOURCE(IDD_CRASHDLG), NULL, (DLGPROC)CrashDlgProc, (LPARAM)plugin.hDllInstance); + #endif + + SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE); + while (IsWindow(hwnd)) + { + MSG msg; + if (!GetMessage(&msg, NULL, 0, 0)) break; + if (IsDialogMessage(hwnd, &msg)) continue; + TranslateMessage(&msg); + DispatchMessage(&msg); + } + return EXCEPTION_EXECUTE_HANDLER; +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_crasher/main.h b/Src/Plugins/General/gen_crasher/main.h new file mode 100644 index 00000000..ee3262f0 --- /dev/null +++ b/Src/Plugins/General/gen_crasher/main.h @@ -0,0 +1,24 @@ +#ifndef NULLSOFT_CRASHER_MAIN_H +#define NULLSOFT_CRASHER_MAIN_H + +#include +#include +#include "resource.h" +#define NO_IVIDEO_DECLARE +#include "..\winamp\wa_ipc.h" +#include "settings.h" +#include "../winamp/gen.h" + +extern Settings settings; +extern prefsDlgRecW prefItem; +extern char *winampVersion; +extern "C" winampGeneralPurposePlugin plugin; + +extern "C" __declspec(dllexport) int StartHandler(wchar_t* iniPath); +extern "C" LONG WINAPI FeedBackFilter( struct _EXCEPTION_POINTERS *pExceptionInfo ); + +//typedef struct _EXCEPTION_POINTERS EXCEPTION_POINTERS, *PEXCEPTION_POINTERS; + +static BOOL alreadyProccessing; + +#endif // NULLSOFT_CRASHER_MAIN_H \ No newline at end of file diff --git a/Src/Plugins/General/gen_crasher/minidump.h b/Src/Plugins/General/gen_crasher/minidump.h new file mode 100644 index 00000000..139acc60 --- /dev/null +++ b/Src/Plugins/General/gen_crasher/minidump.h @@ -0,0 +1,29 @@ +#pragma once +#include +/* +typedef struct _MINIDUMP_EXCEPTION_INFORMATION +{ + DWORD ThreadId; + PEXCEPTION_POINTERS ExceptionPointers; + BOOL ClientPointers; +} MINIDUMP_EXCEPTION_INFORMATION, *PMINIDUMP_EXCEPTION_INFORMATION; + +typedef enum _MINIDUMP_TYPE +{ + MiniDumpNormal = 0x00000000, + MiniDumpWithDataSegs = 0x00000001, + MiniDumpWithFullMemory = 0x00000002, + MiniDumpWithHandleData = 0x00000004, + MiniDumpFilterMemory = 0x00000008, + MiniDumpScanMemory = 0x00000010, + MiniDumpWithUnloaded = 0x00000020, + MiniDumpWithIndirectlyReferencedMemory = 0x00000040, + MiniDumpFilterModulePaths = 0x00000080, + MiniDumpWithProcessThreadData = 0x00000100, + MiniDumpWithPrivateReadWriteMemory = 0x00000200, + MiniDumpWithoutOptionalData = 0x00000400, + MiniDumpWithFullMemoryInfo = 0x00000800, + MiniDumpWithThreadInfo = 0x00001000, + MiniDumpWithCodeSegs = 0x00002000 +} MINIDUMP_TYPE; +*/ \ No newline at end of file diff --git a/Src/Plugins/General/gen_crasher/resource.h b/Src/Plugins/General/gen_crasher/resource.h new file mode 100644 index 00000000..2c7e076d --- /dev/null +++ b/Src/Plugins/General/gen_crasher/resource.h @@ -0,0 +1,96 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by gen_crasher.rc +// +#define IDS_ERROR_FEEDBACK 1 +#define IDS_UNKNOWN 2 +#define IDS_LOADED_OK 3 +#define IDS_UNABLE_TO_LOAD 4 +#define IDS_NOT_FOUND 5 +#define IDS_UNABLE_TO_SAVE_SETTINGS 6 +#define IDS_SAVE_ERROR 7 +#define IDS_SELECT_FOLDER_FOR_ERROR_INFO 8 +#define IDD_CRASHDLG 101 +#define IDD_CONFIG 102 +#define IDD_DLG_SMTP 103 +#define IDC_CHK_CREATEDMP 1003 +#define IDC_EDT_DMPPATH 1004 +#define IDC_EDT_DMPNAME 1004 +#define IDC_BTN_DMPPATH 1005 +#define IDC_BTN_PATH 1005 +#define IDC_CMB_DMPTYPE 1006 +#define IDC_CHK_CREATELOG 1007 +#define IDC_EDT_LOGPATH 1008 +#define IDC_EDT_LOGNAME 1008 +#define IDC_BUTTON2 1009 +#define IDC_BTN_LOGPATH 1009 +#define IDC_EDT_PATH 1009 +#define IDC_RB_USECLIENT 1010 +#define IDC_CHK_LOGSYSTEM 1011 +#define IDC_CHK_LOGFILE 1012 +#define IDC_CHK_LOGREGISTRY 1012 +#define IDC_CHK_LOGMACHINE 1013 +#define IDC_CHK_LOGSTACK 1013 +#define IDC_CHK_RESTART 1014 +#define IDC_CHK_SILENT 1015 +#define IDC_CHK_SEND 1016 +#define IDC_USESMTP 1017 +#define IDC_RB_USESMTP 1017 +#define IDC_EDT_SERVER 1018 +#define IDC_EDT_ZIPNAME 1018 +#define IDC_EDT_USER 1019 +#define IDC_EDT_PWD 1020 +#define IDC_CHK_LOGMODULE 1021 +#define IDC_EDT_PORT 1021 +#define IDC_GRP_DUMP 1022 +#define IDC_GRP_GENERAL 1023 +#define IDC_GRP_LOG 1024 +#define IDC_GRP_EMAIL 1025 +#define IDC_LBL_SERVER 1026 +#define IDC_GRP_ZIP 1026 +#define IDC_LBL_USER 1027 +#define IDC_LBL_PWD 1028 +#define IDC_LBL_DMPTYPE 1029 +#define IDC_LBL_PORT 1029 +#define IDC_LBL_DMPPATH 1030 +#define IDC_LBL_DMPNAME 1030 +#define IDC_LBL_LOGPATH 1031 +#define IDC_LBL_LOGNAME 1031 +#define IDC_LBL_OSVERSION_CAPTION 1032 +#define IDC_LBL_DLLPATH_CAPTION 1033 +#define IDC_LBL_DLLVERSION_CAPTION 1034 +#define IDC_LBL_OSVERSION 1035 +#define IDC_LBL_DLLPATH 1036 +#define IDC_LBL_DLLVERSION 1037 +#define IDC_CHK_COMPRESS 1040 +#define IDC_BTN_SMTP 1041 +#define IDC_LBL_ZIPNAME 1042 +#define IDC_LBL_PATH 1043 +#define IDC_LBL_INFO 1045 +#define IDC_LBL_STEP 1046 +#define IDC_BMP_LOGO 1047 +#define IDC_EDIT1 1048 +#define IDC_EDT_ADDRESS 1048 +#define IDC_CHK_AUTH 1049 +#define IDC_GRP_AUTH 1050 +#define IDC_LBL_ADDRESS 1051 +#define IDC_GRP_AUTH2 1052 +#define IDC_TITLELBL 1101 +#define IDC_WORKLBL 1102 +#define IDC_PROGRESS1 1103 +#define IDC_PRG_COLLECT 1103 +#define IDC_PROGRESS2 1104 +#define IDC_PROGRESS3 1105 +#define IDS_NULLSOFT_ERROR_FEEDBACK 65534 +#define IDS_STRING107 65535 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 108 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1052 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/Src/Plugins/General/gen_crasher/settings.cpp b/Src/Plugins/General/gen_crasher/settings.cpp new file mode 100644 index 00000000..d37f0ce5 --- /dev/null +++ b/Src/Plugins/General/gen_crasher/settings.cpp @@ -0,0 +1,251 @@ +#include ".\settings.h" +#include +#include + +Settings::Settings(void) +{ + dumpPath = NULL; + logPath = NULL; + smtpServer = NULL; + smtpUser = NULL; + smtpPwd = NULL; + path = NULL; + smtpAddress = NULL; + updatePath = TRUE; + createDMP = TRUE; + createLOG = TRUE; + autoRestart = FALSE; + silentMode = TRUE; + sendData = TRUE; + zipData = TRUE; + zipPath = NULL; + sendByClient = TRUE; + sendBySMTP = FALSE; + smtpPort = 25; + smtpAuth = TRUE; + dumpType = 0; + logSystem = TRUE; + logRegistry = TRUE; + logStack = TRUE; + logModule = TRUE; +} + +Settings::~Settings(void) +{ + if (dumpPath) free(dumpPath); + if (logPath) free(logPath); + if (smtpServer) free(smtpServer); + if (smtpUser) free(smtpUser); + if (smtpPwd) free(smtpPwd); + if (path) free(path); + if (smtpAddress) free(smtpAddress); +} + +void Settings::SetPath(wchar_t *iniPath) +{ + size_t size = lstrlen(iniPath); + if (path) free(path); + path = NULL; + path = (wchar_t*)malloc((size + 1) * sizeof(wchar_t)); + StringCchCopy(path, size+1, iniPath); + wchar_t iniFile[MAX_PATH*2] = {0}; + size += 14 * sizeof(wchar_t); + CreateDirectory(iniPath, NULL); + StringCchPrintf(iniFile, size, L"%s\\feedback.ini", iniPath); + cfg.SetIniFile(iniFile); +} + +const wchar_t* Settings::GetPath(void) +{ + return path; +} + +BOOL Settings::Load(void) +{ + if (!cfg.IsFileExist()) return FALSE; + cfg.SetSection(L"General"); + updatePath = cfg.ReadInt(L"UpdatePath", TRUE); + if (updatePath) return FALSE; + createDMP = cfg.ReadInt(L"CreateDmp", TRUE); + createLOG = cfg.ReadInt(L"CreateLog", TRUE); + autoRestart = cfg.ReadInt(L"AutoRestart", FALSE); + silentMode = cfg.ReadInt(L"SilentMode", TRUE); + sendData = cfg.ReadInt(L"SendData", TRUE); + + cfg.SetSection(L"Send"); + sendByClient = cfg.ReadInt(L"UseClient", TRUE); + sendBySMTP = cfg.ReadInt(L"UseSMTP", FALSE); + smtpPort = cfg.ReadInt(L"Port", 25); + smtpAuth = cfg.ReadInt(L"ReqAuth", TRUE); + CreateStrCopy(&smtpAddress, cfg.ReadStringW(L"Address", L"bug@winamp.com")); + CreateStrCopy(&smtpServer, cfg.ReadStringW(L"Server", NULL)); + CreateStrCopy(&smtpUser, cfg.ReadStringW(L"User", NULL)); + CreateStrCopy(&smtpPwd, cfg.ReadStringW(L"Pwd", NULL)); + + cfg.SetSection(L"Zip"); + zipData = cfg.ReadInt(L"ZipData", TRUE); + CreateStrCopy(&zipPath, cfg.ReadStringW(L"Path", NULL)); + + cfg.SetSection(L"Dump"); + dumpType = cfg.ReadInt(L"Type", 0); + CreateStrCopy(&dumpPath, cfg.ReadStringW(L"Path", NULL)); + + cfg.SetSection(L"Log"); + logSystem = cfg.ReadInt(L"System", TRUE); + logRegistry = cfg.ReadInt(L"Registry", TRUE); + logStack = cfg.ReadInt(L"Stack", TRUE); + logModule = cfg.ReadInt(L"Module", TRUE); + CreateStrCopy(&logPath, cfg.ReadStringW(L"Path", NULL)); + return TRUE; +} + +void Settings::CreateStrCopy(wchar_t **dest, const wchar_t* source) +{ + if (*dest) free(*dest); + *dest = NULL; + if (source) + { + size_t len = lstrlen(source) + 1; + *dest = (wchar_t*) malloc(len*sizeof(wchar_t)); + StringCchCopy(*dest, len, source); + } +} + +BOOL Settings::Save(void) +{ + BOOL error = FALSE; + if (FALSE == cfg.SetSection(L"General")) error = TRUE; + if (FALSE == cfg.Write(L"UpdatePath", FALSE)) error = TRUE; + if (FALSE == cfg.Write(L"CreateDmp", createDMP)) error = TRUE; + if (FALSE == cfg.Write(L"CreateLog", createLOG)) error = TRUE; + if (FALSE == cfg.Write(L"AutoRestart", autoRestart)) error = TRUE; + if (FALSE == cfg.Write(L"SilentMode", silentMode)) error = TRUE; + if (FALSE == cfg.Write(L"SendData", sendData)) error = TRUE; + if (FALSE == cfg.SetSection(L"Send")) error = TRUE; + if (FALSE == cfg.Write(L"UseClient", sendByClient)) error = TRUE; + if (FALSE == cfg.Write(L"UseSMTP", sendBySMTP)) error = TRUE; + if (FALSE == cfg.Write(L"Port", smtpPort)) error = TRUE; + if (FALSE == cfg.Write(L"Server", smtpServer)) error = TRUE; + if (FALSE == cfg.Write(L"Address", smtpAddress)) error = TRUE; + if (FALSE == cfg.Write(L"ReqAuth", smtpAuth)) error = TRUE; + if (FALSE == cfg.Write(L"User", smtpUser)) error = TRUE; + if (FALSE == cfg.Write(L"Pwd", smtpPwd)) error = TRUE; + if (FALSE == cfg.SetSection(L"Zip")) error = TRUE; + if (FALSE == cfg.Write(L"ZipData", zipData)) error = TRUE; + if (FALSE == cfg.Write(L"Path", zipPath)) error = TRUE; + if (FALSE == cfg.SetSection(L"Dump")) error = TRUE; + if (FALSE == cfg.Write(L"Type", dumpType)) error = TRUE; + if (FALSE == cfg.Write(L"Path", dumpPath)) error = TRUE; + if (FALSE == cfg.SetSection(L"Log")) error = TRUE; + if (FALSE == cfg.Write(L"System", logSystem)) error = TRUE; + if (FALSE == cfg.Write(L"Registry", logRegistry)) error = TRUE; + if (FALSE == cfg.Write(L"Stack", logStack)) error = TRUE; + if (FALSE == cfg.Write(L"Module", logModule)) error = TRUE; + if (FALSE == cfg.Write(L"Path", logPath)) error = TRUE; + return !error; +} + +BOOL Settings::CreateDefault(wchar_t* iniPath) +{ + wchar_t temp[MAX_PATH] = {0}; + int len; + + createDMP = TRUE; + createLOG = TRUE; + autoRestart = FALSE; + silentMode = TRUE; + sendData = TRUE; +// zip + PathCombine(temp, iniPath, L"report.zip"); + len = (int)wcslen(temp) + 1; + zipData = TRUE; + zipPath = (wchar_t*) malloc(len*2); + StringCchCopy(zipPath, len, temp); +// send + sendByClient = TRUE; + sendBySMTP = FALSE; + smtpPort = 25; + smtpAddress = (wchar_t*) malloc(32*2); + StringCchCopy(smtpAddress, 32, L"bug@winamp.com"); + smtpAuth = TRUE; + smtpServer = NULL; + smtpUser = NULL; + smtpPwd = NULL; +// dump + PathCombine(temp, iniPath, L"_crash.dmp"); + len = (int)wcslen(temp) + 1; + dumpType = NULL; + dumpPath = (wchar_t*) malloc(len*2); + StringCchCopy(dumpPath, len, temp); +// log + logSystem = TRUE; + logRegistry = TRUE; + logStack = TRUE; + logModule = TRUE; + PathCombine(temp, iniPath, L"_crash.log"); + len = (int)wcslen(temp) + 1; + logPath = (wchar_t*) malloc(len*2); + StringCchCopy(logPath, len, temp); + return TRUE; +} + +BOOL Settings::IsOk(void) +{ + return (logPath != NULL && dumpPath != NULL); +} + +void Settings::ClearTempData(void) +{ + cfg.Write(L"Temp", L"TS", L""); + cfg.Write(L"Temp", L"LOG", L"0"); + cfg.Write(L"Temp", L"DMP", L"0"); +} + +void Settings::WriteErrorTS(const wchar_t *time) +{ + cfg.Write(L"Temp", L"TS", time); +} + +void Settings::WriteLogCollectResult(BOOL result) +{ + cfg.Write(L"Temp", L"LOG", result); +} + +void Settings::WriteDmpCollectResult(BOOL result) +{ + cfg.Write(L"Temp", L"DMP", result); +} + +void Settings::WriteWinamp(const wchar_t *winamp) +{ + cfg.Write(L"Temp", L"WA", winamp); +} + +const wchar_t* Settings::ReadErrorTS(void) +{ + return cfg.ReadStringW(L"Temp", L"TS", L""); +} + +BOOL Settings::ReadLogCollectResult(void) +{ + return cfg.ReadInt(L"Temp", L"LOG", 0); +} +BOOL Settings::ReadDmpCollectResult(void) +{ + return cfg.ReadInt(L"Temp", L"DMP", 0); +} + +const wchar_t* Settings::ReadWinamp(void) +{ + return cfg.ReadStringW(L"Temp", L"WA", L""); +} + +void Settings::WriteBody(const wchar_t *body) +{ + cfg.Write(L"Temp", L"Body", body); +} + +const wchar_t* Settings::ReadBody(void) +{ + return cfg.ReadStringW(L"Temp", L"Body", L""); +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_crasher/settings.h b/Src/Plugins/General/gen_crasher/settings.h new file mode 100644 index 00000000..e90932fb --- /dev/null +++ b/Src/Plugins/General/gen_crasher/settings.h @@ -0,0 +1,67 @@ +#pragma once + +#include "config.h" + +class Settings +{ +public: + Settings(void); + ~Settings(void); + +public: + void SetPath(wchar_t *iniPath); + BOOL Load(void); + BOOL Save(void); + BOOL CreateDefault(wchar_t* iniPath); + BOOL IsOk(void); + const wchar_t* GetPath(void); + +protected: + void CreateStrCopy(wchar_t **dest, const wchar_t* source); +private: + ConfigW cfg; + wchar_t* path; + +public: +// general + BOOL updatePath; + BOOL createDMP; + BOOL createLOG; + BOOL autoRestart; + BOOL silentMode; + BOOL sendData; +//zip + BOOL zipData; + wchar_t* zipPath; +// send + BOOL sendByClient; + BOOL sendBySMTP; + int smtpPort; + wchar_t *smtpServer; + wchar_t *smtpAddress; + BOOL smtpAuth; + wchar_t *smtpUser; + wchar_t *smtpPwd; +// dump + int dumpType; + wchar_t *dumpPath; +// log + BOOL logSystem; + BOOL logRegistry; + BOOL logStack; + BOOL logModule; + wchar_t *logPath; +// tmp + void ClearTempData(void); + void WriteErrorTS(const wchar_t *time); + void WriteLogCollectResult(BOOL result); + void WriteDmpCollectResult(BOOL result); + void WriteWinamp(const wchar_t *winamp); + void WriteBody(const wchar_t *body); + + const wchar_t* ReadErrorTS(void); + BOOL ReadLogCollectResult(void); + BOOL ReadDmpCollectResult(void); + const wchar_t* ReadWinamp(void); + const wchar_t* ReadBody(void); +}; \ No newline at end of file diff --git a/Src/Plugins/General/gen_crasher/smtpDlg.cpp b/Src/Plugins/General/gen_crasher/smtpDlg.cpp new file mode 100644 index 00000000..ed7a34dd --- /dev/null +++ b/Src/Plugins/General/gen_crasher/smtpDlg.cpp @@ -0,0 +1,127 @@ +#include ".\smtpdlg.h" +#include ".\resource.h" +#include ".\settings.h" + +#include + +extern Settings settings; + +void UpdateAuth(HWND hwndDlg, BOOL enabled) +{ + EnableWindow(GetDlgItem(hwndDlg, IDC_LBL_USER), enabled); + EnableWindow(GetDlgItem(hwndDlg, IDC_EDT_USER), enabled); + EnableWindow(GetDlgItem(hwndDlg, IDC_LBL_PWD), enabled); + EnableWindow(GetDlgItem(hwndDlg, IDC_EDT_PWD), enabled); +} + +BOOL CALLBACK smtpDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch(uMsg) + { + case WM_INITDIALOG: + { + wchar_t num[16] = {0}; + CenterDialog(hwndDlg); + SetWindowText(GetDlgItem(hwndDlg, IDC_EDT_SERVER), settings.smtpServer); + SetWindowText(GetDlgItem(hwndDlg, IDC_EDT_USER), settings.smtpUser); + SetWindowText(GetDlgItem(hwndDlg, IDC_EDT_PWD), settings.smtpPwd); + SetWindowText(GetDlgItem(hwndDlg, IDC_EDT_PORT), _itow(settings.smtpPort, num, 10)); + SetWindowText(GetDlgItem(hwndDlg, IDC_EDT_ADDRESS), settings.smtpAddress); + CheckDlgButton(hwndDlg, IDC_CHK_AUTH, settings.smtpAuth); + UpdateAuth(hwndDlg, settings.smtpAuth); + break; + } + case WM_DESTROY: + { + wchar_t buf[1024] = {0}; + int len; + if (settings.smtpServer) free(settings.smtpServer); + settings.smtpServer = NULL; + len = GetWindowText(GetDlgItem(hwndDlg, IDC_EDT_SERVER), buf, 1024); + if (len) + { + settings.smtpServer = (wchar_t*)malloc((len + 1)*2); + StringCchCopy(settings.smtpServer, len+1, buf); + } + + len = GetWindowText(GetDlgItem(hwndDlg, IDC_EDT_PORT), buf, 1024); + if (len) settings.smtpPort = _wtoi(buf); + + if (settings.smtpUser) free(settings.smtpUser); + settings.smtpUser = NULL; + len = GetWindowText(GetDlgItem(hwndDlg, IDC_EDT_USER), buf, 1024); + if (len) + { + settings.smtpUser = (wchar_t*)malloc((len + 1)*2); + StringCchCopy(settings.smtpUser, len+1, buf); + } + + if (settings.smtpPwd) free(settings.smtpPwd); + settings.smtpPwd = NULL; + len = GetWindowText(GetDlgItem(hwndDlg, IDC_EDT_PWD), buf, 1024); + if (len) + { + settings.smtpPwd = (wchar_t*)malloc((len + 1)*2); + StringCchCopy(settings.smtpPwd, len+1, buf); + } + + if (settings.smtpAddress) free(settings.smtpAddress); + settings.smtpAddress = NULL; + len = GetWindowText(GetDlgItem(hwndDlg, IDC_EDT_ADDRESS), buf, 1024); + if (len) + { + settings.smtpAddress = (wchar_t*)malloc((len + 1)*2); + StringCchCopy(settings.smtpAddress, len+1, buf); + } + settings.smtpAuth = (SendMessage(GetDlgItem(hwndDlg, IDC_CHK_AUTH), BM_GETCHECK, 0,0) == BST_CHECKED); + settings.Save(); + break; + } + case WM_COMMAND: + switch(LOWORD(wParam)) + { + case IDC_CHK_AUTH: + UpdateAuth(hwndDlg, (SendMessage((HWND) lParam, BM_GETCHECK, 0,0) == BST_CHECKED)); + break; + case IDCANCEL: + EndDialog(hwndDlg, 0); + break; + } + break; + + } + return FALSE; +} + +void CenterDialog(HWND hwndDlg) +{ + HWND hwndOwner; + RECT rc, rcDlg, rcOwner; + if ((hwndOwner = GetParent(hwndDlg)) == NULL) + { + hwndOwner = GetDesktopWindow(); + } + + GetWindowRect(hwndOwner, &rcOwner); + GetWindowRect(hwndDlg, &rcDlg); + CopyRect(&rc, &rcOwner); + + // Offset the owner and dialog box rectangles so that + // right and bottom values represent the width and + // height, and then offset the owner again to discard + // space taken up by the dialog box. + + OffsetRect(&rcDlg, -rcDlg.left, -rcDlg.top); + OffsetRect(&rc, -rc.left, -rc.top); + OffsetRect(&rc, -rcDlg.right, -rcDlg.bottom); + + // The new position is the sum of half the remaining + // space and the owner's original position. + + SetWindowPos(hwndDlg, + HWND_TOP, + rcOwner.left + (rc.right / 2), + rcOwner.top + (rc.bottom / 2), + 0, 0, // ignores size arguments + SWP_NOSIZE); +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_crasher/smtpDlg.h b/Src/Plugins/General/gen_crasher/smtpDlg.h new file mode 100644 index 00000000..3ddac466 --- /dev/null +++ b/Src/Plugins/General/gen_crasher/smtpDlg.h @@ -0,0 +1,5 @@ +#pragma once +#include + +void CenterDialog(HWND hwndDlg); +BOOL CALLBACK smtpDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); \ No newline at end of file diff --git a/Src/Plugins/General/gen_crasher/version.rc2 b/Src/Plugins/General/gen_crasher/version.rc2 new file mode 100644 index 00000000..e06748f6 --- /dev/null +++ b/Src/Plugins/General/gen_crasher/version.rc2 @@ -0,0 +1,39 @@ + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// +#include "..\..\..\Winamp/buildType.h" +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,16,0,0 + PRODUCTVERSION WINAMP_PRODUCTVER + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Winamp SA" + VALUE "FileDescription", "Winamp General Purpose Plug-in" + VALUE "FileVersion", "1,16,0,0" + VALUE "InternalName", "Nullsoft Winamp Error Feedback Plug-in" + VALUE "LegalCopyright", "Copyright © 2005-2023 Winamp SA" + VALUE "LegalTrademarks", "Nullsoft and Winamp are trademarks of Winamp SA" + VALUE "OriginalFilename", "gen_crasher.dll" + VALUE "ProductName", "Winamp" + VALUE "ProductVersion", STR_WINAMP_PRODUCTVER + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/Src/Plugins/General/gen_ff/AlbumArt.cpp b/Src/Plugins/General/gen_ff/AlbumArt.cpp new file mode 100644 index 00000000..90fba9da --- /dev/null +++ b/Src/Plugins/General/gen_ff/AlbumArt.cpp @@ -0,0 +1,561 @@ +#include "precomp__gen_ff.h" + +#include +#include "main.h" +#include "AlbumArt.h" +#include "wa2frontend.h" +#include +#include +#include +#include +#include + +#define ALBUMART_MAX_THREADS 4 + +const wchar_t albumArtXuiObjectStr[] = L"AlbumArt"; // This is the xml tag +char albumArtXuiSvcName[] = "Album Art XUI object"; // this is the name of the xuiservice + +AlbumArtScriptController _albumartController; +AlbumArtScriptController *albumartController = &_albumartController; + +BEGIN_SERVICES( wa2AlbumArt_Svcs ); +DECLARE_SERVICE( XuiObjectCreator ); +END_SERVICES( wa2AlbumArt_Svcs, _wa2AlbumArt_Svcs ); + +// -------------------------------------------------------- +// Maki Script Object +// -------------------------------------------------------- + +// -- Functions table ------------------------------------- +function_descriptor_struct AlbumArtScriptController::exportedFunction[] = { + {L"refresh", 0, (void *)AlbumArt::script_vcpu_refresh }, + {L"onAlbumArtLoaded", 1, (void *)AlbumArt::script_vcpu_onAlbumArtLoaded }, + {L"isLoading", 0, (void *)AlbumArt::script_vcpu_isLoading }, +}; + +const wchar_t *AlbumArtScriptController::getClassName() +{ + return L"AlbumArtLayer"; +} + +const wchar_t *AlbumArtScriptController::getAncestorClassName() +{ + return L"Layer"; +} + +ScriptObject *AlbumArtScriptController::instantiate() +{ + AlbumArt *a = new AlbumArt; + + ASSERT( a != NULL ); + + return a->getScriptObject(); +} + +void AlbumArtScriptController::destroy( ScriptObject *o ) +{ + AlbumArt *a = static_cast( o->vcpu_getInterface( albumArtGuid ) ); + + ASSERT( a != NULL ); + + delete a; +} + +void *AlbumArtScriptController::encapsulate( ScriptObject *o ) +{ + return NULL; // no encapsulation yet +} + +void AlbumArtScriptController::deencapsulate( void *o ) +{} + +int AlbumArtScriptController::getNumFunctions() +{ + return sizeof( exportedFunction ) / sizeof( function_descriptor_struct ); +} + +const function_descriptor_struct *AlbumArtScriptController::getExportedFunctions() +{ + return exportedFunction; +} + +GUID AlbumArtScriptController::getClassGuid() +{ + return albumArtGuid; +} + + +XMLParamPair AlbumArt::params[] = +{ + {ALBUMART_NOTFOUNDIMAGE, L"NOTFOUNDIMAGE"}, + {ALBUMART_SOURCE, L"SOURCE"}, + {ALBUMART_VALIGN, L"VALIGN"}, + {ALBUMART_ALIGN, L"ALIGN"}, + {ALBUMART_STRETCHED, L"STRETCHED"}, + {ALBUMART_NOREFRESH, L"NOAUTOREFRESH"}, +}; + +class AlbumArtThreadContext +{ + public: + AlbumArtThreadContext( const wchar_t *_filename, AlbumArt *_wnd ) + { + /* lazy load these two handles */ + if ( !_wnd->hMainThread ) + _wnd->hMainThread = WASABI_API_APP->main_getMainThreadHandle(); + + if ( !_wnd->thread_semaphore ) + _wnd->thread_semaphore = CreateSemaphore( 0, ALBUMART_MAX_THREADS, ALBUMART_MAX_THREADS, 0 ); + + wnd = _wnd; + iterator = wnd->iterator; + h = w = 0; + bits = 0; + filename = _wcsdup( _filename ); + } + + void FreeBits() + { + if ( bits ) + WASABI_API_MEMMGR->sysFree( bits ); + + bits = 0; + } + + ~AlbumArtThreadContext() + { + if ( wnd ) + { + if ( wnd->thread_semaphore ) + ReleaseSemaphore( wnd->thread_semaphore, 1, 0 ); + + wnd->isLoading--; + } + + free( filename ); + } + + static void CALLBACK AlbumArtNotifyAPC( ULONG_PTR p ); + bool LoadArt(); + + int h; + int w; + ARGB32 *bits; + LONG iterator; + wchar_t *filename; + AlbumArt *wnd; +}; + + +AlbumArt::AlbumArt() +{ + getScriptObject()->vcpu_setInterface( albumArtGuid, ( void * )static_cast( this ) ); + getScriptObject()->vcpu_setClassName( L"AlbumArtLayer" ); + getScriptObject()->vcpu_setController( albumartController ); + + WASABI_API_MEDIACORE->core_addCallback( 0, this ); + + w = 0; + h = 0; + iterator = 0; + bits = 0; + hMainThread = 0; + thread_semaphore = 0; + artBitmap = 0; + valign = 0; + align = 0; + stretched = false; + missing_art_image = L"winamp.cover.notfound"; // default to this. + src_file = L""; + forceRefresh = false; + noAutoRefresh = false; + noMakiCallback = false; + isLoading = 0; + + /* register XML parameters */ + xuihandle = newXuiHandle(); + + CreateXMLParameters( xuihandle ); +} + +void AlbumArt::CreateXMLParameters( int master_handle ) +{ + //ALBUMART_PARENT::CreateXMLParameters(master_handle); + int numParams = sizeof( params ) / sizeof( params[ 0 ] ); + hintNumberOfParams( xuihandle, numParams ); + + for ( int i = 0; i < numParams; i++ ) + addParam( xuihandle, params[ i ], XUI_ATTRIBUTE_IMPLIED ); +} + + +AlbumArt::~AlbumArt() +{ + WASABI_API_SYSCB->syscb_deregisterCallback( static_cast( this ) ); + WASABI_API_MEDIACORE->core_delCallback( 0, this ); + + // wait for all of our threads to finish + InterlockedIncrement( &iterator ); // our kill switch (will invalidate iterator on all outstanding threads) + if ( thread_semaphore ) + { + for ( int i = 0; i < ALBUMART_MAX_THREADS; i++ ) + { + if ( WaitForMultipleObjectsEx( 1, &thread_semaphore, FALSE, INFINITE, TRUE ) != WAIT_OBJECT_0 ) + i--; + } + } + + delete artBitmap; + + if ( bits ) + WASABI_API_MEMMGR->sysFree( bits ); + + if ( thread_semaphore ) + CloseHandle( thread_semaphore ); + + if ( hMainThread ) + CloseHandle( hMainThread ); +} + +bool AlbumArt::layer_isInvalid() +{ + return !bits; +} + +void CALLBACK AlbumArtThreadContext::AlbumArtNotifyAPC( ULONG_PTR p ) +{ + AlbumArtThreadContext *context = (AlbumArtThreadContext *)p; + if ( context->wnd->iterator == context->iterator ) + context->wnd->ArtLoaded( context->w, context->h, context->bits ); + else + context->FreeBits(); + + delete context; +} + +bool AlbumArtThreadContext::LoadArt() +{ + if ( wnd->iterator != iterator ) + return false; + + if ( AGAVE_API_ALBUMART->GetAlbumArt( filename, L"cover", &w, &h, &bits ) != ALBUMART_SUCCESS ) + { + bits = 0; + w = 0; + h = 0; + } + + if ( wnd->iterator == iterator ) // make sure we're still valid + { + QueueUserAPC( AlbumArtNotifyAPC, wnd->hMainThread, (ULONG_PTR)this ); + + return true; + } + else + { + FreeBits(); + + return false; + } +} + +static int AlbumArtThreadPoolFunc( HANDLE handle, void *user_data, intptr_t id ) +{ + AlbumArtThreadContext *context = (AlbumArtThreadContext *)user_data; + if ( context->LoadArt() == false ) + { + delete context; + } + + return 0; +} + +void AlbumArt::ArtLoaded( int _w, int _h, ARGB32 *_bits ) +{ + if ( bits ) + WASABI_API_MEMMGR->sysFree( bits ); + + if ( artBitmap ) + { + delete artBitmap; + artBitmap = 0; + } + + bits = _bits; + w = _w; + h = _h; + + if ( !bits ) + { + SkinBitmap *albumart = missing_art_image.getBitmap(); + if ( albumart ) + { + w = albumart->getWidth(); + h = albumart->getHeight(); + } + } + + deleteRegion(); + makeRegion(); + notifyParent( ChildNotify::AUTOWHCHANGED ); + invalidate(); + + // notify maki scripts that albumart has been found or not + if ( !noMakiCallback && _bits ) + { + AlbumArt::script_vcpu_onAlbumArtLoaded( SCRIPT_CALL, this->getScriptObject(), MAKE_SCRIPT_BOOLEAN( (int)bits ) ); + } +} + +void AlbumArt::onSetVisible( int show ) +{ + if ( show ) + { + corecb_onUrlChange( wa2.GetCurrentFile() ); + } + else + { + if ( bits ) + { + WASABI_API_MEMMGR->sysFree( bits ); + + delete artBitmap; + artBitmap = 0; + } + + bits = 0; + } +} + +int AlbumArt::corecb_onUrlChange( const wchar_t *filename ) +{ + // Martin> if we call this from maki we want to do a refresh regardless of the albumartlayer being visible or not + if ( forceRefresh || ( !noAutoRefresh && isVisible() ) ) + { + isLoading++; + + // Martin > do a check for a specific file, defined via source param + if ( WCSICMP( src_file, L"" ) ) + filename = src_file; + + InterlockedIncrement( &iterator ); + AlbumArtThreadContext *context = new AlbumArtThreadContext( filename, this ); + + // make sure we have an available thread free (wait for one if we don't) + while ( WaitForMultipleObjectsEx( 1, &thread_semaphore, FALSE, INFINITE, TRUE ) != WAIT_OBJECT_0 ) + {} + + // int vis__ = isVisible(); + WASABI_API_THREADPOOL->RunFunction( 0, AlbumArtThreadPoolFunc, context, 0, api_threadpool::FLAG_LONG_EXECUTION ); + } + + return 1; +} + +int AlbumArt::onInit() +{ + int r = ALBUMART_PARENT::onInit(); + WASABI_API_SYSCB->syscb_registerCallback( static_cast( this ) ); + + AlbumArt::corecb_onUrlChange( wa2.GetCurrentFile() ); + + return r; +} + +int AlbumArt::skincb_onColorThemeChanged( const wchar_t *newcolortheme ) +{ + ALBUMART_PARENT::skincb_onColorThemeChanged( newcolortheme ); + invalidate(); + + return 0; +} + +SkinBitmap *AlbumArt::getBitmap() +{ + if ( artBitmap ) + return artBitmap; + + if ( bits ) + { + artBitmap = new HQSkinBitmap( bits, w, h ); //TH WDP2-212 + + return artBitmap; + } + + return missing_art_image.getBitmap(); +} + +void AlbumArt::layer_adjustDest( RECT *r ) +{ + if ( !w || !h ) + return; + + if ( stretched ) + return; + + //getClientRect(r); + // maintain 'square' stretching + int dstW = r->right - r->left; + int dstH = r->bottom - r->top; + double aspX = (double)( dstW ) / (double)w; + double aspY = (double)( dstH ) / (double)h; + double asp = min( aspX, aspY ); + int newW = (int)( w * asp ); + int newH = (int)( h * asp ); + + // Align + int offsetX = ( dstW - newW ) / 2; + if ( align == 1 ) + offsetX *= 2; + else if ( align == -1 ) + offsetX = 0; + + // Valign + int offsetY = ( dstH - newH ) / 2; + if ( valign == 1 ) + offsetY *= 2; + else if ( valign == -1 ) + offsetY = 0; + + r->left += offsetX; + r->right = r->left + newW; + r->top += offsetY; + r->bottom = r->top + newH; + + // This prevents parts of the image being cut off (if the img has the same dimensions as the rect) on moving/clicking winamp + // (they will just flicker, but at least they won't stay now) + // benski> CUT!!! no no no no no this is very bad because this gets called inside layer::onPaint + //invalidate(); +} +/* +int AlbumArt::getWidth() +{ + RECT r; + getClientRect(&r); + getDest(&r); + return r.right-r.left; +} + +int AlbumArt::getHeight() +{ + RECT r; + getClientRect(&r); + getDest(&r); + return r.bottom-r.top; +} +*/ +/* +int AlbumArt::onPaint(Canvas *canvas) +{ + ALBUMART_PARENT::onPaint(canvas); + if (bits) + { + SkinBitmap albumart(bits, w, h); + RECT dst; + getBufferPaintDest(&dst); + albumart.stretchToRectAlpha(canvas, &dst, getPaintingAlpha()); + } + return 1; +} +*/ +int AlbumArt::setXuiParam( int _xuihandle, int attrid, const wchar_t *name, const wchar_t *strval ) +{ + if ( xuihandle != _xuihandle ) + return ALBUMART_PARENT::setXuiParam( _xuihandle, attrid, name, strval ); + + switch ( attrid ) + { + case ALBUMART_NOTFOUNDIMAGE: + missing_art_image = strval; + if ( !bits ) + { + noMakiCallback = true; + ArtLoaded( 0, 0, 0 ); + noMakiCallback = false; + } + break; + case ALBUMART_SOURCE: + src_file = strval; + AlbumArt::corecb_onUrlChange( wa2.GetCurrentFile() ); // This Param should _always_ hold our current file + break; + case ALBUMART_VALIGN: + if ( !WCSICMP( strval, L"top" ) ) + valign = -1; + else if ( !WCSICMP( strval, L"bottom" ) ) + valign = 1; + else + valign = 0; + + deferedInvalidate(); + break; + case ALBUMART_ALIGN: + if ( !WCSICMP( strval, L"left" ) ) + align = -1; + else if ( !WCSICMP( strval, L"right" ) ) + align = 1; + else + align = 0; + + deferedInvalidate(); + break; + case ALBUMART_STRETCHED: + if ( !WCSICMP( strval, L"0" ) || !WCSICMP( strval, L"" ) ) + stretched = false; + else + stretched = true; + + deferedInvalidate(); + break; + case ALBUMART_NOREFRESH: + if ( !WCSICMP( strval, L"0" ) || !WCSICMP( strval, L"" ) ) + noAutoRefresh = false; + else + noAutoRefresh = true; + break; + default: + return 0; + } + return 1; +} + +void AlbumArt::metacb_ArtUpdated( const wchar_t *filename ) +{ + // it'd be nice to do this, but we can't guarantee that our file didn't get updated in the process + //const wchar_t *curFn = wa2.GetCurrentFile(); +// if (curFn && filename && *filename && *curFn && !_wcsicmp(filename, curFn)) + AlbumArt::corecb_onUrlChange( wa2.GetCurrentFile() ); +} + + +scriptVar AlbumArt::script_vcpu_refresh( SCRIPT_FUNCTION_PARAMS, ScriptObject *o ) +{ + SCRIPT_FUNCTION_INIT; + AlbumArt *a = static_cast( o->vcpu_getInterface( albumArtGuid ) ); + if ( a ) + { + a->forceRefresh = true; + a->corecb_onUrlChange( wa2.GetCurrentFile() ); + a->forceRefresh = false; + } + RETURN_SCRIPT_VOID; +} + +scriptVar AlbumArt::script_vcpu_isLoading( SCRIPT_FUNCTION_PARAMS, ScriptObject *o ) +{ + SCRIPT_FUNCTION_INIT; + AlbumArt *a = static_cast( o->vcpu_getInterface( albumArtGuid ) ); + if ( a ) + { + return MAKE_SCRIPT_BOOLEAN( !!a->isLoading ); + } + + return MAKE_SCRIPT_BOOLEAN( false ); +} + +scriptVar AlbumArt::script_vcpu_onAlbumArtLoaded( SCRIPT_FUNCTION_PARAMS, ScriptObject *o, scriptVar success ) +{ + SCRIPT_FUNCTION_INIT + PROCESS_HOOKS1( o, albumartController, success ); + SCRIPT_FUNCTION_CHECKABORTEVENT; + SCRIPT_EXEC_EVENT1( o, success ); +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_ff/AlbumArt.h b/Src/Plugins/General/gen_ff/AlbumArt.h new file mode 100644 index 00000000..b0fa2c3f --- /dev/null +++ b/Src/Plugins/General/gen_ff/AlbumArt.h @@ -0,0 +1,115 @@ +#ifndef NULLSOFT_GEN_FF_ALBUMART_H +#define NULLSOFT_GEN_FF_ALBUMART_H +#include +#include +#include +//#include + +//#define ALBUMART_PARENT GuiObjectWnd +//#define ALBUMART_PARENT BufferPaintWnd +#define ALBUMART_PARENT Layer + +// {6DCB05E4-8AC4-48c2-B193-49F0910EF54A} +static const GUID albumArtGuid = +{ 0x6dcb05e4, 0x8ac4, 0x48c2, { 0xb1, 0x93, 0x49, 0xf0, 0x91, 0xe, 0xf5, 0x4a } }; + + +class AlbumArtScriptController : public LayerScriptController +{ + public: + + virtual const wchar_t *getClassName(); + virtual const wchar_t *getAncestorClassName(); + virtual ScriptObjectController *getAncestorController() { return layerController; } + virtual int getNumFunctions(); + virtual const function_descriptor_struct *getExportedFunctions(); + virtual GUID getClassGuid(); + virtual ScriptObject *instantiate(); + virtual void destroy(ScriptObject *o); + virtual void *encapsulate(ScriptObject *o); + virtual void deencapsulate(void *o); + + private: + + static function_descriptor_struct exportedFunction[]; + +}; + +extern AlbumArtScriptController *albumartController; + +class AlbumArtThreadContext; + +class AlbumArt : public ALBUMART_PARENT, + public CoreCallbackI, /* to get song change updates */ + public MetadataCallbackI /* to find out when album art changes */ +// public SkinCallbackI /* to get color theme changes */ +{ +public: + AlbumArt(); + ~AlbumArt(); + +protected: + void metacb_ArtUpdated(const wchar_t *filename); + virtual int corecb_onUrlChange(const wchar_t *filename); + virtual int onInit(); + //virtual int onBufferPaint(BltCanvas *canvas, int w, int h); + int setXuiParam(int _xuihandle, int attrid, const wchar_t *name, const wchar_t *strval); + int skincb_onColorThemeChanged(const wchar_t *newcolortheme); + + bool LoadArt(AlbumArtThreadContext *context, HANDLE thread_to_notify); + /* Layer */ + SkinBitmap *getBitmap(); + bool layer_isInvalid(); + void onSetVisible(int show); +/* + virtual int getWidth(); + virtual int getHeight(); +*/ + /*static */void CreateXMLParameters(int master_handle); +private: + void layer_adjustDest(RECT *r); + + int w,h; + int valign; // 0 = center, 1 = bottom, -1 = top + int align; // 0 = center, 1 = right, -1 = left + bool stretched; + bool forceRefresh; + bool noAutoRefresh; + bool noMakiCallback; + volatile int isLoading; + + void ArtLoaded(int _w, int _h, ARGB32 *_bits); + StringW src_file; + ARGB32 *bits; + SkinBitmap *artBitmap; + AutoSkinBitmap missing_art_image; + volatile LONG iterator; + HANDLE thread_semaphore, hMainThread; + + friend class AlbumArtThreadContext; +private: + /* XML Parameters */ + enum + { + ALBUMART_NOTFOUNDIMAGE, + ALBUMART_SOURCE, + ALBUMART_VALIGN, + ALBUMART_ALIGN, + ALBUMART_STRETCHED, + ALBUMART_NOREFRESH + }; + static XMLParamPair params[]; + int xuihandle; + +public: + static scriptVar script_vcpu_refresh(SCRIPT_FUNCTION_PARAMS, ScriptObject *o); + static scriptVar script_vcpu_onAlbumArtLoaded(SCRIPT_FUNCTION_PARAMS, ScriptObject *o, scriptVar success); + static scriptVar script_vcpu_isLoading(SCRIPT_FUNCTION_PARAMS, ScriptObject *o); +}; + +extern const wchar_t albumArtXuiObjectStr[]; +extern char albumArtXuiSvcName[]; +class AlbumArtXuiSvc : public XuiObjectSvc {}; + + +#endif diff --git a/Src/Plugins/General/gen_ff/MediaDownloader.cpp b/Src/Plugins/General/gen_ff/MediaDownloader.cpp new file mode 100644 index 00000000..70a0f476 --- /dev/null +++ b/Src/Plugins/General/gen_ff/MediaDownloader.cpp @@ -0,0 +1,307 @@ +#include "precomp__gen_ff.h" +#include "main.h" +#include "../Agave/Language/api_language.h" +#include "../../..\Components\wac_network\wac_network_http_receiver_api.h" +#include "../nu/AutoWide.h" +#include "../nu/AutoChar.h" +#include "../nu/AutoCharFn.h" +#include "wa2frontend.h" +#include "resource.h" +#include "../nu/ns_wc.h" +#include "../nu/refcount.h" +#include "api/skin/widgets/xuidownloadslist.h" + +#include +#include +#include + +extern wchar_t *INI_DIR; + +static void createDirForFileW(wchar_t *str) +{ + wchar_t *p = str; + if ((p[0] ==L'\\' || p[0] ==L'/') && (p[1] ==L'\\' || p[1] ==L'/')) + { + p += 2; + while (p && *p && *p !=L'\\' && *p !=L'/') p++; + if (!p || !*p) return ; + p++; + while (p && *p && *p !=L'\\' && *p !=L'/') p++; + } + else + { + while (p && *p && *p !=L'\\' && *p !=L'/') p++; + } + + while (p && *p) + { + while (p && *p !=L'\\' && *p !=L'/' && *p) p = CharNextW(p); + if (p && *p) + { + wchar_t lp = *p; + *p = 0; + CreateDirectoryW(str, NULL); + *p++ = lp; + } + } +} + +static wchar_t* defaultPathToStore() +{ + static wchar_t pathToStore[MAX_PATH] = {0}; + if(FAILED(SHGetFolderPathW(NULL, CSIDL_MYMUSIC, NULL, SHGFP_TYPE_CURRENT, pathToStore))) + { + if(FAILED(SHGetFolderPathW(NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, pathToStore))) + { + lstrcpynW(pathToStore, L"C:\\My Music", MAX_PATH); + } + } + return pathToStore; +} + +static void GetPathToStore(wchar_t path_to_store[MAX_PATH]) +{ + GetPrivateProfileStringW( L"gen_ml_config", L"extractpath", defaultPathToStore(), path_to_store, MAX_PATH, (const wchar_t *)SendMessageW( plugin.hwndParent, WM_WA_IPC, 0, IPC_GETMLINIFILEW ) ); +} + +void Winamp2FrontEnd::getDownloadPath(wchar_t path2store[MAX_PATH]) +{ + GetPathToStore(path2store); +} + +void Winamp2FrontEnd::setDownloadPath(const wchar_t * path2store) +{ + WritePrivateProfileStringA( "gen_ml_config", "extractpath", AutoChar( path2store, CP_UTF8 ), (const char *)SendMessageW( plugin.hwndParent, WM_WA_IPC, 0, IPC_GETMLINIFILE ) ); +} + +class DownloadCallback : public Countable +{ +public: + DownloadCallback( const wchar_t *destination_filepath, bool storeInMl = true, bool notifyDownloadsList = true ) + { + WCSCPYN( this->destination_filepath, destination_filepath, MAX_PATH ); + + this->storeInMl = storeInMl; + this->notifyDownloadsList = notifyDownloadsList; + } + + + void OnFinish( DownloadToken token ); + + void OnError( DownloadToken token, int error ) + { + if ( notifyDownloadsList ) + DownloadsList::onDownloadError( token, error ); + + Release(); + } + + void OnCancel( DownloadToken token ) + { + if ( notifyDownloadsList ) + DownloadsList::onDownloadCancel( token ); + + Release(); + } + void OnTick( DownloadToken token ) + { + if ( notifyDownloadsList ) + DownloadsList::onDownloadTick( token ); + } + + const wchar_t *getPreferredFilePath() + { + return destination_filepath; + } + + bool getStoreInML() + { + return storeInMl; + } + REFERENCE_COUNT_IMPLEMENTATION; + +protected: + RECVS_DISPATCH; + +private: + bool storeInMl; + bool notifyDownloadsList; + wchar_t destination_filepath[ MAX_PATH ]; +}; + +// TODO: benski> this is a big hack. we should have a MIME manager in Winamp +struct +{ + const char *mime; const char *ext; +} +hack_mimes[] = { {"audio/mpeg", ".mp3"} }; + +void DownloadCallback::OnFinish(DownloadToken token) +{ + api_httpreceiver *http = WAC_API_DOWNLOADMANAGER->GetReceiver(token); + if (http) + { + const char *extension = 0; + const char *headers = http->getallheaders(); + // we're trying to figure out wtf kind of file this is + + if (!extension) // just adding this if to make this easier to re-arrange + { + // 1) check if there's a content-disposition, it might have an extension + const char *content_disposition = http->getheader("content-disposition"); + if (content_disposition) + { + const char *content_filename = strstr(content_disposition, "filename="); + if (content_filename && *content_filename) + { + content_filename+=9; + extension = PathFindExtensionA(content_filename); + } + } + } + + if (!extension) + { + // 2) check MIME type for something good + const char *content_type = http->getheader("content-type"); + if (content_type) + { + for (int i=0;i!=sizeof(hack_mimes)/sizeof(hack_mimes[0]);i++) + { + if (!strcmp(hack_mimes[i].mime, content_type)) + { + extension=hack_mimes[i].ext; + } + } + } + } + + const char *url = http->get_url(); + if (!extension) + { + // 3) check if URL has an extension + + if (url) // umm, we better have a URL :) but worth a check + { + extension = PathFindExtensionA(url); + } + } + + if (!extension) + { + // 4) ask for winamp's default extension and use that (most likely mp3) + extension=".mp3"; // TODO: actually use the setting and not hardcode this :) + } + + // then we rename the file + wchar_t temppath[MAX_PATH-14] = {0}; + wchar_t filename[MAX_PATH] = {0}; + + GetTempPathW(MAX_PATH-14, temppath); + GetTempFileNameW(temppath, L"atf", 0, filename); + PathRemoveExtensionW(filename); + PathAddExtensionW(filename, AutoWide(extension)); + + const wchar_t *downloadDest = WAC_API_DOWNLOADMANAGER->GetLocation(token); + MoveFileW(downloadDest, filename); + + // then we build a filename with ATF + wchar_t formatted_filename[MAX_PATH] = {0}, path_to_store[MAX_PATH] = {0}; + if (!WCSICMP(this->getPreferredFilePath(), L"")) + GetPathToStore(path_to_store); + else + WCSCPYN(path_to_store, this->getPreferredFilePath(), MAX_PATH); + + // TODO: benski> this is very temporary + char temp_filename[MAX_PATH] = {0}, *t = temp_filename, + *tfn = PathFindFileNameA(url); + + while(tfn && *tfn) + { + if(*tfn == '%') + { + if(_strnicmp(tfn,"%20",3)) + { + *t = *tfn; + } + else{ + *t = ' '; + tfn = CharNextA(tfn); + tfn = CharNextA(tfn); + } + } + else + { + *t = *tfn; + } + tfn = CharNextA(tfn); + t = CharNextA(t); + } + + *t = 0; + + PathCombineW(formatted_filename, path_to_store, AutoWide(temp_filename)); + createDirForFileW(formatted_filename); + + // then move the file there + if (!MoveFileW(filename, formatted_filename)) + { + CopyFileW(filename, formatted_filename, FALSE); + DeleteFileW(filename); + } + + //Std::messageBox(formatted_filename, filename, 0); + if (this->getStoreInML() && PathFileExistsW(formatted_filename)) + { + // then add to the media library :) + // TOOD: benski> use api_mldb because it's more thread-friendly than SendMessageW + HWND library = wa2.getMediaLibrary(); + LMDB_FILE_ADD_INFOW fi = {const_cast(formatted_filename), -1, -1}; + SendMessageW(library, WM_ML_IPC, (WPARAM)&fi, ML_IPC_DB_ADDORUPDATEFILEW); + PostMessage(library, WM_ML_IPC, 0, ML_IPC_DB_SYNCDB); + } + + if (notifyDownloadsList) + DownloadsList::onDownloadEnd(token, const_cast(formatted_filename)); + + SystemObject::onDownloadFinished(AutoWide(url), true, formatted_filename); + + Release(); + + return; + } + + if (notifyDownloadsList) + DownloadsList::onDownloadEnd(token, NULL); + + Release(); +} + +int Winamp2FrontEnd::DownloadFile( const char *url, const wchar_t *destfilepath, bool addToMl, bool notifyDownloadsList ) +{ + DownloadCallback *callback = new DownloadCallback( destfilepath, addToMl, notifyDownloadsList ); + DownloadToken dt = WAC_API_DOWNLOADMANAGER->Download( url, callback ); + + // Notify + if ( notifyDownloadsList ) + DownloadsList::onDownloadStart( url, dt ); + + return 0; + /* + HTTPRETRIEVEFILEW func = (HTTPRETRIEVEFILEW) SendMessageW(hwnd_winamp, WM_WA_IPC, 0, IPC_GETHTTPGETTERW); + if (func || func == (HTTPRETRIEVEFILEW)1) + return func(NULL, url, destfilename, title); + else + return 0; + */ +} + +#define CBCLASS DownloadCallback +START_DISPATCH; +REFERENCE_COUNTED; +VCB( IFC_DOWNLOADMANAGERCALLBACK_ONFINISH, OnFinish ) +VCB( IFC_DOWNLOADMANAGERCALLBACK_ONTICK, OnTick ) +VCB( IFC_DOWNLOADMANAGERCALLBACK_ONERROR, OnError ) +VCB( IFC_DOWNLOADMANAGERCALLBACK_ONCANCEL, OnCancel ) +END_DISPATCH; +#undef CBCLASS diff --git a/Src/Plugins/General/gen_ff/README.txt b/Src/Plugins/General/gen_ff/README.txt new file mode 100644 index 00000000..80adc9f8 --- /dev/null +++ b/Src/Plugins/General/gen_ff/README.txt @@ -0,0 +1,6 @@ +- unzip skindirectory.zip into your Winamp2 Skins directory. for now, gen_ff will manually load an + unzipped mmd3. +- unzip plugindirectory in the ... wa2 plugin directory :) these are the xml and png files for default + objects look, skins rely on that for fallback, it's also where we put the new art if we need + any (ie static bucketitems will go there eventually instead of being resources) + diff --git a/Src/Plugins/General/gen_ff/WinampConfigScriptObject.cpp b/Src/Plugins/General/gen_ff/WinampConfigScriptObject.cpp new file mode 100644 index 00000000..ae1a7ef0 --- /dev/null +++ b/Src/Plugins/General/gen_ff/WinampConfigScriptObject.cpp @@ -0,0 +1,276 @@ +#include +#include "WinampConfigScriptObject.h" + +// {B2AD3F2B-31ED-4e31-BC6D-E9951CD555BB} +static const GUID winampConfigScriptGuid = +{ 0xb2ad3f2b, 0x31ed, 0x4e31, { 0xbc, 0x6d, 0xe9, 0x95, 0x1c, 0xd5, 0x55, 0xbb } }; + +// {FC17844E-C72B-4518-A068-A8F930A5BA80} +static const GUID winampConfigGroupScriptGuid = +{ 0xfc17844e, 0xc72b, 0x4518, { 0xa0, 0x68, 0xa8, 0xf9, 0x30, 0xa5, 0xba, 0x80 } }; + +static WinampConfigScriptController _winampConfigController; +ScriptObjectController *winampConfigController = &_winampConfigController; + +static WinampConfigGroupScriptController _winampConfigGroupController; +ScriptObjectController *winampConfigGroupController = &_winampConfigGroupController; + +BEGIN_SERVICES(WinampConfig_svcs); +DECLARE_SERVICETSINGLE(svc_scriptObject, WinampConfigScriptObjectSvc); +END_SERVICES(WinampConfig_svcs, _WinampConfig_svcs); + +#ifdef _X86_ +extern "C" +{ + int _link_WinampConfig_svcs; +} +#else +extern "C" +{ + int __link_WinampConfig_svcs; +} +#endif +// ----------------------------------------------------------------------------------------------------- +// Service + +ScriptObjectController *WinampConfigScriptObjectSvc::getController(int n) +{ + switch (n) + { + case 0: + return winampConfigController; + case 1: + return winampConfigGroupController; + } + return NULL; +} + +// -- Functions table ------------------------------------- +function_descriptor_struct WinampConfigScriptController::exportedFunction[] = +{ + {L"getGroup", 1, (void*)WinampConfig::script_vcpu_getGroup }, +}; + +// -------------------------------------------------------- + +const wchar_t *WinampConfigScriptController::getClassName() +{ + return L"WinampConfig"; +} + +const wchar_t *WinampConfigScriptController::getAncestorClassName() +{ + return L"Object"; +} + +ScriptObject *WinampConfigScriptController::instantiate() +{ + WinampConfig *wc = new WinampConfig; + ASSERT(wc != NULL); + return wc->getScriptObject(); +} + +void WinampConfigScriptController::destroy(ScriptObject *o) +{ + WinampConfig *wc = static_cast(o->vcpu_getInterface(winampConfigScriptGuid)); + if (wc) + delete wc; +} + +void *WinampConfigScriptController::encapsulate(ScriptObject *o) +{ + return NULL; // no encapsulation yet +} + +void WinampConfigScriptController::deencapsulate(void *o) +{ +} + +int WinampConfigScriptController::getNumFunctions() +{ + return sizeof(exportedFunction) / sizeof(function_descriptor_struct); +} + +const function_descriptor_struct *WinampConfigScriptController::getExportedFunctions() +{ + return exportedFunction; +} + +GUID WinampConfigScriptController::getClassGuid() +{ + return winampConfigScriptGuid; +} + +/* ------------- */ + +WinampConfig::WinampConfig() +{ + getScriptObject()->vcpu_setInterface(winampConfigScriptGuid, (void *)static_cast(this)); + getScriptObject()->vcpu_setClassName(L"WinampConfig"); + getScriptObject()->vcpu_setController(winampConfigController); + + waServiceFactory *sf = WASABI_API_SVC->service_getServiceByGuid(Agave::AgaveConfigGUID); + if (sf) + config = (Agave::api_config *)sf->getInterface(); +} + +WinampConfig::~WinampConfig() +{ + waServiceFactory *sf = WASABI_API_SVC->service_getServiceByGuid(Agave::AgaveConfigGUID); + if (sf) + sf->releaseInterface(config); +} + +scriptVar WinampConfig::script_vcpu_getGroup(SCRIPT_FUNCTION_PARAMS, ScriptObject *o, scriptVar groupguid) +{ + SCRIPT_FUNCTION_INIT + + const wchar_t *g = GET_SCRIPT_STRING(groupguid); + GUID _g = nsGUID::fromCharW(g); + + WinampConfig *wc = static_cast(o->vcpu_getInterface(winampConfigScriptGuid)); + if (wc) + { + Agave::ifc_configgroup *group = wc->config->GetGroup(_g); + if (group) + { + WinampConfigGroup *winampConfigGroup = new WinampConfigGroup(group); + return MAKE_SCRIPT_OBJECT(winampConfigGroup->getScriptObject()); + } + } + RETURN_SCRIPT_NULL; +} + +/* ------------- */ + +// -- Functions table ------------------------------------- +function_descriptor_struct WinampConfigGroupScriptController::exportedFunction[] = +{ + {L"getBool", 1, (void*)WinampConfigGroup::script_vcpu_getBool }, + {L"getString", 1, (void*)WinampConfigGroup::script_vcpu_getString }, + {L"getInt", 1, (void*)WinampConfigGroup::script_vcpu_getInt }, + {L"setBool", 2, (void*)WinampConfigGroup::script_vcpu_setBool }, +}; +// -------------------------------------------------------- + +const wchar_t *WinampConfigGroupScriptController::getClassName() +{ + return L"WinampConfigGroup"; +} + +const wchar_t *WinampConfigGroupScriptController::getAncestorClassName() +{ + return L"Object"; +} + +ScriptObject *WinampConfigGroupScriptController::instantiate() +{ + WinampConfigGroup *wc = new WinampConfigGroup; + ASSERT(wc != NULL); + return wc->getScriptObject(); +} + +void WinampConfigGroupScriptController::destroy(ScriptObject *o) +{ + WinampConfigGroup *wc = static_cast(o->vcpu_getInterface(winampConfigGroupScriptGuid)); + if (wc) + delete wc; +} + +void *WinampConfigGroupScriptController::encapsulate(ScriptObject *o) +{ + return NULL; // no encapsulation yet +} + +void WinampConfigGroupScriptController::deencapsulate(void *o) +{ +} + +int WinampConfigGroupScriptController::getNumFunctions() +{ + return sizeof(exportedFunction) / sizeof(function_descriptor_struct); +} + +const function_descriptor_struct *WinampConfigGroupScriptController::getExportedFunctions() +{ + return exportedFunction; +} + +GUID WinampConfigGroupScriptController::getClassGuid() +{ + return winampConfigGroupScriptGuid; +} + +/* ------------- */ + +WinampConfigGroup::WinampConfigGroup() +{ +getScriptObject()->vcpu_setInterface(winampConfigGroupScriptGuid, (void *)static_cast(this)); + getScriptObject()->vcpu_setClassName(L"WinampConfigGroup"); + getScriptObject()->vcpu_setController(winampConfigGroupController); + configGroup = 0; +} + +WinampConfigGroup::WinampConfigGroup(Agave::ifc_configgroup *_configGroup) +{ + getScriptObject()->vcpu_setInterface(winampConfigGroupScriptGuid, (void *)static_cast(this)); + getScriptObject()->vcpu_setClassName(L"WinampConfigGroup"); + getScriptObject()->vcpu_setController(winampConfigGroupController); + configGroup = _configGroup; +} + +Agave::ifc_configitem *WinampConfigGroup::GetItem(ScriptObject *o, scriptVar itemname) +{ + const wchar_t *item = GET_SCRIPT_STRING(itemname); + WinampConfigGroup *group = static_cast(o->vcpu_getInterface(winampConfigGroupScriptGuid)); + if (group) + { + Agave::ifc_configitem *configitem = group->configGroup->GetItem(item); + return configitem; + } + return 0; +} + +scriptVar WinampConfigGroup::script_vcpu_getBool(SCRIPT_FUNCTION_PARAMS, ScriptObject *o, scriptVar itemname) +{ + SCRIPT_FUNCTION_INIT + Agave::ifc_configitem *configitem = GetItem(o, itemname); + if (configitem) + return MAKE_SCRIPT_BOOLEAN(configitem->GetBool()); + + RETURN_SCRIPT_ZERO; +} + +scriptVar WinampConfigGroup::script_vcpu_getString(SCRIPT_FUNCTION_PARAMS, ScriptObject *o, scriptVar itemname) +{ + SCRIPT_FUNCTION_INIT + Agave::ifc_configitem *configitem = GetItem(o, itemname); + if (configitem) + return MAKE_SCRIPT_STRING(configitem->GetString()); + + RETURN_SCRIPT_ZERO; +} + +scriptVar WinampConfigGroup::script_vcpu_getInt(SCRIPT_FUNCTION_PARAMS, ScriptObject *o, scriptVar itemname) +{ + SCRIPT_FUNCTION_INIT + Agave::ifc_configitem *configitem = GetItem(o, itemname); + if (configitem) + return MAKE_SCRIPT_INT(configitem->GetInt()); + + RETURN_SCRIPT_ZERO; +} + +scriptVar WinampConfigGroup::script_vcpu_setBool(SCRIPT_FUNCTION_PARAMS, ScriptObject *o, scriptVar itemname, scriptVar value) +{ + SCRIPT_FUNCTION_INIT + const wchar_t *item = GET_SCRIPT_STRING(itemname); + WinampConfigGroup *group = static_cast(o->vcpu_getInterface(winampConfigGroupScriptGuid)); + if (group) + { + Agave::ifc_configitem *configitem = group->configGroup->GetItem(item); + if (configitem) + configitem->SetBool(!!GET_SCRIPT_BOOLEAN(value)); + } + RETURN_SCRIPT_VOID; +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_ff/WinampConfigScriptObject.h b/Src/Plugins/General/gen_ff/WinampConfigScriptObject.h new file mode 100644 index 00000000..12cff269 --- /dev/null +++ b/Src/Plugins/General/gen_ff/WinampConfigScriptObject.h @@ -0,0 +1,99 @@ +#ifndef NULLSOFT_GEN_FF_WINAMPCONFIGSCRIPTOBJECT_H +#define NULLSOFT_GEN_FF_WINAMPCONFIGSCRIPTOBJECT_H + +#include +#include +#include +#include +namespace Agave +{ + #include "../Agave/Config/api_config.h" +} + +// ----------------------------------------------------------------------------------------------------- +// ScriptObject Service +class WinampConfigScriptObjectSvc : public svc_scriptObjectI +{ +public: + WinampConfigScriptObjectSvc() {}; + virtual ~WinampConfigScriptObjectSvc() {}; + + static const char *getServiceName() { return "Winamp Config script object"; } + virtual ScriptObjectController *getController(int n); +}; + +class WinampConfigScriptController : public ScriptObjectControllerI +{ + public: + virtual const wchar_t *getClassName(); + virtual const wchar_t *getAncestorClassName(); + virtual ScriptObjectController *getAncestorController() { return rootScriptObjectController; } + virtual int getNumFunctions(); + virtual const function_descriptor_struct *getExportedFunctions(); + virtual GUID getClassGuid(); + virtual ScriptObject *instantiate(); + virtual void destroy(ScriptObject *o); + virtual void *encapsulate(ScriptObject *o); + virtual void deencapsulate(void *o); + + private: + static function_descriptor_struct exportedFunction[]; +}; + +extern ScriptObjectController *winampConfigController; +extern ScriptObjectController *winampConfigGroupController; + +#define WINAMPCONFIG_PARENT RootObjectInstance + +class WinampConfig : public WINAMPCONFIG_PARENT +{ +public: + WinampConfig(); + ~WinampConfig(); +public: + virtual const wchar_t *vcpu_getClassName() { return L"WinampConfig"; } + virtual ScriptObjectController *vcpu_getController() { return winampConfigController; } + + static scriptVar script_vcpu_getGroup(SCRIPT_FUNCTION_PARAMS, ScriptObject *o, scriptVar n); + + Agave::api_config *config; +}; + +class WinampConfigGroupScriptController : public ScriptObjectControllerI { + public: + virtual const wchar_t *getClassName(); + virtual const wchar_t *getAncestorClassName(); + virtual ScriptObjectController *getAncestorController() { return rootScriptObjectController; } + virtual int getNumFunctions(); + virtual const function_descriptor_struct *getExportedFunctions(); + virtual GUID getClassGuid(); + virtual ScriptObject *instantiate(); + virtual void destroy(ScriptObject *o); + virtual void *encapsulate(ScriptObject *o); + virtual void deencapsulate(void *o); + + private: + static function_descriptor_struct exportedFunction[]; +}; + +#define WINAMPCONFIGGROUP_PARENT RootObjectInstance +class WinampConfigGroup : public WINAMPCONFIGGROUP_PARENT +{ +public: + WinampConfigGroup(); + WinampConfigGroup(Agave::ifc_configgroup *_configGroup); + +public: + virtual const wchar_t *vcpu_getClassName() { return L"WinampConfigGroup"; } + virtual ScriptObjectController *vcpu_getController() { return winampConfigGroupController; } + + static Agave::ifc_configitem *GetItem(ScriptObject *o, scriptVar n); // helper function + static scriptVar script_vcpu_getBool(SCRIPT_FUNCTION_PARAMS, ScriptObject *o, scriptVar n); + static scriptVar script_vcpu_getString(SCRIPT_FUNCTION_PARAMS, ScriptObject *o, scriptVar n); + static scriptVar script_vcpu_getInt(SCRIPT_FUNCTION_PARAMS, ScriptObject *o, scriptVar n); + static scriptVar script_vcpu_setBool(SCRIPT_FUNCTION_PARAMS, ScriptObject *o, scriptVar itemname, scriptVar value); + + Agave::ifc_configgroup *configGroup; +}; + +#endif diff --git a/Src/Plugins/General/gen_ff/api__gen_ff.h b/Src/Plugins/General/gen_ff/api__gen_ff.h new file mode 100644 index 00000000..f011e918 --- /dev/null +++ b/Src/Plugins/General/gen_ff/api__gen_ff.h @@ -0,0 +1,20 @@ +#ifndef NULLSOFT_GEN_FF_API_H +#define NULLSOFT_GEN_FF_API_H + +#include +extern api_memmgr *memmgrApi; +#define WASABI_API_MEMMGR memmgrApi + +#include +#define WASABI_API_COLORTHEMES colorThemesApi + +#include +extern api_palette *paletteManagerApi; +#define WASABI_API_PALETTE paletteManagerApi + +#include "../nu/threadpool/api_threadpool.h" +extern api_threadpool *threadPoolApi; +#define WASABI_API_THREADPOOL threadPoolApi + + +#endif // !NULLSOFT_GEN_FF_API_H diff --git a/Src/Plugins/General/gen_ff/bitmaps/library-hilited.png b/Src/Plugins/General/gen_ff/bitmaps/library-hilited.png new file mode 100644 index 00000000..7abf190f Binary files /dev/null and b/Src/Plugins/General/gen_ff/bitmaps/library-hilited.png differ diff --git a/Src/Plugins/General/gen_ff/bitmaps/library-selected.png b/Src/Plugins/General/gen_ff/bitmaps/library-selected.png new file mode 100644 index 00000000..904639f1 Binary files /dev/null and b/Src/Plugins/General/gen_ff/bitmaps/library-selected.png differ diff --git a/Src/Plugins/General/gen_ff/bitmaps/library-unselected.png b/Src/Plugins/General/gen_ff/bitmaps/library-unselected.png new file mode 100644 index 00000000..a2a3d7a9 Binary files /dev/null and b/Src/Plugins/General/gen_ff/bitmaps/library-unselected.png differ diff --git a/Src/Plugins/General/gen_ff/bitmaps/mb-hilited.png b/Src/Plugins/General/gen_ff/bitmaps/mb-hilited.png new file mode 100644 index 00000000..2128096c Binary files /dev/null and b/Src/Plugins/General/gen_ff/bitmaps/mb-hilited.png differ diff --git a/Src/Plugins/General/gen_ff/bitmaps/mb-selected.png b/Src/Plugins/General/gen_ff/bitmaps/mb-selected.png new file mode 100644 index 00000000..f05591d5 Binary files /dev/null and b/Src/Plugins/General/gen_ff/bitmaps/mb-selected.png differ diff --git a/Src/Plugins/General/gen_ff/bitmaps/mb-unselected.png b/Src/Plugins/General/gen_ff/bitmaps/mb-unselected.png new file mode 100644 index 00000000..63338214 Binary files /dev/null and b/Src/Plugins/General/gen_ff/bitmaps/mb-unselected.png differ diff --git a/Src/Plugins/General/gen_ff/bitmaps/pledit-hover.png b/Src/Plugins/General/gen_ff/bitmaps/pledit-hover.png new file mode 100644 index 00000000..c4c7c5e9 Binary files /dev/null and b/Src/Plugins/General/gen_ff/bitmaps/pledit-hover.png differ diff --git a/Src/Plugins/General/gen_ff/bitmaps/pledit-selected.png b/Src/Plugins/General/gen_ff/bitmaps/pledit-selected.png new file mode 100644 index 00000000..3438a11e Binary files /dev/null and b/Src/Plugins/General/gen_ff/bitmaps/pledit-selected.png differ diff --git a/Src/Plugins/General/gen_ff/bitmaps/pledit-unselected.png b/Src/Plugins/General/gen_ff/bitmaps/pledit-unselected.png new file mode 100644 index 00000000..b6293781 Binary files /dev/null and b/Src/Plugins/General/gen_ff/bitmaps/pledit-unselected.png differ diff --git a/Src/Plugins/General/gen_ff/bitmaps/video-hilited.png b/Src/Plugins/General/gen_ff/bitmaps/video-hilited.png new file mode 100644 index 00000000..4176634a Binary files /dev/null and b/Src/Plugins/General/gen_ff/bitmaps/video-hilited.png differ diff --git a/Src/Plugins/General/gen_ff/bitmaps/video-selected.png b/Src/Plugins/General/gen_ff/bitmaps/video-selected.png new file mode 100644 index 00000000..15a6027d Binary files /dev/null and b/Src/Plugins/General/gen_ff/bitmaps/video-selected.png differ diff --git a/Src/Plugins/General/gen_ff/bitmaps/video-unselected.png b/Src/Plugins/General/gen_ff/bitmaps/video-unselected.png new file mode 100644 index 00000000..fa58bedf Binary files /dev/null and b/Src/Plugins/General/gen_ff/bitmaps/video-unselected.png differ diff --git a/Src/Plugins/General/gen_ff/bitmaps/vis-hilited.png b/Src/Plugins/General/gen_ff/bitmaps/vis-hilited.png new file mode 100644 index 00000000..f9ac424c Binary files /dev/null and b/Src/Plugins/General/gen_ff/bitmaps/vis-hilited.png differ diff --git a/Src/Plugins/General/gen_ff/bitmaps/vis-selected.png b/Src/Plugins/General/gen_ff/bitmaps/vis-selected.png new file mode 100644 index 00000000..5d478429 Binary files /dev/null and b/Src/Plugins/General/gen_ff/bitmaps/vis-selected.png differ diff --git a/Src/Plugins/General/gen_ff/bitmaps/vis-unselected.png b/Src/Plugins/General/gen_ff/bitmaps/vis-unselected.png new file mode 100644 index 00000000..b0074200 Binary files /dev/null and b/Src/Plugins/General/gen_ff/bitmaps/vis-unselected.png differ diff --git a/Src/Plugins/General/gen_ff/embedwndguid.cpp b/Src/Plugins/General/gen_ff/embedwndguid.cpp new file mode 100644 index 00000000..db12ad13 --- /dev/null +++ b/Src/Plugins/General/gen_ff/embedwndguid.cpp @@ -0,0 +1,200 @@ +#include "precomp__gen_ff.h" +#include "embedwndguid.h" +#include "wa2wndembed.h" +#include "wa2frontend.h" +#include "wa2cfgitems.h" +#include + +extern void initFFApi(); + +EmbedWndGuidMgr embedWndGuidMgr; + +EmbedWndGuid::EmbedWndGuid(embedWindowState *_ws) +{ + hwnd = NULL; + guid = INVALID_GUID; + if (_ws == NULL) return ; + + ws = _ws; + hwnd = ws->me; + embedWndGuidMgr.getGuid(this); +} + +EmbedWndGuid::EmbedWndGuid(EmbedWndGuid *wg) +{ + hwnd = NULL; + guid = INVALID_GUID; + if (wg == NULL) return ; + + ws = wg->getEmbedWindowState(); + hwnd = ws->me; + guid = wg->getGuid(); +} + +GUID EmbedWndGuidMgr::getGuid(embedWindowState *ws) +{ + foreach(table) + EmbedWndGuid *ewg = table.getfor(); + if (ewg->getEmbedWindowState() == ws) + { + return ewg->getGuid(); + } + endfor; + return INVALID_GUID; +} + +GUID EmbedWndGuidMgr::getGuid(EmbedWndGuid *wg) +{ + if (wg == NULL) return INVALID_GUID; + // if we aren't loaded yet, init wasabi, otherwise ignore + initFFApi(); + + int gotit = 0; + int nowrite = 0; + wchar_t windowTitle[256] = L""; + HWND child; + + GUID newGuid = INVALID_GUID; + + if (wg->getEmbedWindowState()->flags & EMBED_FLAGS_GUID) + { + newGuid = GET_EMBED_GUID(wg->getEmbedWindowState()); + nowrite=1; + goto bypass; + } + + if (wa2.isVis(wg->getEmbedWindowState()->me)) + { + newGuid = avs_guid; + nowrite = 1; + extern int disable_send_visrandom; + extern _bool visrandom; + wa2.pollVisRandom(); + goto bypass; + } + + child = GetWindow(wg->getEmbedWindowState()->me, GW_CHILD); + if (child == wa2.getMediaLibrary()) + { newGuid = library_guid; nowrite = 1; goto bypass; } + + if (!gotit) + { + foreach(table) + EmbedWndGuid *ewg = table.getfor(); + if (ewg->getEmbedWindowState() == wg->getEmbedWindowState()) + { + wg->setGuid(ewg->getGuid()); + gotit = 1; + break; + } + endfor; + } + + if (!gotit) + { + // not found, look for window title in saved table + GetWindowTextW(wg->getEmbedWindowState()->me, windowTitle, 256); + if (*windowTitle) + { + wchar_t str[256] = L""; + StringW configString = windowTitle; + configString += L"_guid"; + WASABI_API_CONFIG->getStringPrivate(configString, str, 256, L""); + if (*str) + { + wg->setGuid(nsGUID::fromCharW(str)); + table.addItem(new EmbedWndGuid(wg)); + gotit = 1; + } + } + } + + if (!gotit) + { + // not found in saved table, or no title for the window, assign a new guid + if (!_wcsicmp(windowTitle, L"AVS")) + { newGuid = avs_guid; nowrite = 1; } + else + CoCreateGuid(&newGuid); + + bypass: + + wg->setGuid(newGuid); + + // save a copy of the element + table.addItem(new EmbedWndGuid(wg)); + + // write the guid in the saved table + if (*windowTitle && !nowrite) + { + wchar_t str[256] = {0}; + nsGUID::toCharW(newGuid, str); + StringW configString = windowTitle; + configString += L"_guid"; + WASABI_API_CONFIG->setStringPrivate(configString, str); + } + } + return wg->getGuid(); +} + +embedWindowState *EmbedWndGuidMgr::getEmbedWindowState(GUID g) +{ + foreach(table) + EmbedWndGuid *ewg = table.getfor(); + if (ewg->getGuid() == g) + { + embedWindowState *ews = ewg->getEmbedWindowState(); + if (wa2.isValidEmbedWndState(ews)) + return ews; + } + endfor; + return NULL; +} + +void EmbedWndGuidMgr::retireEmbedWindowState(embedWindowState *ws) +{ + foreach(table) + EmbedWndGuid *ewg = table.getfor(); + if (ewg->getEmbedWindowState() == ws) + { + delete table.getfor(); + table.removeByPos(foreach_index); + break; + } + endfor; +} + +int EmbedWndGuidMgr::testGuid(GUID g) +{ + // if (g == library_guid) return 1; + // if (g == avs_guid) return 1; + foreach(table) + EmbedWndGuid *ewg = table.getfor(); + if (ewg->getGuid() == g) + { + if (!wa2.isValidEmbedWndState(ewg->getEmbedWindowState())) retireEmbedWindowState(ewg->getEmbedWindowState()); + else return 1; + } + endfor; + return 0; +} + +int EmbedWndGuidMgr::getNumWindowStates() +{ + return table.getNumItems(); +} + +GUID EmbedWndGuidMgr::enumWindowState(int n, embedWindowState **ws) +{ + EmbedWndGuid *ewg = table.enumItem(n); + if (ewg) + { + embedWindowState *ews = ewg->getEmbedWindowState(); + if (wa2.isValidEmbedWndState(ews)) + { + if (ws) *ws = ews; + return ewg->getGuid(); + } + } + return INVALID_GUID; +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_ff/embedwndguid.h b/Src/Plugins/General/gen_ff/embedwndguid.h new file mode 100644 index 00000000..8e9e39e7 --- /dev/null +++ b/Src/Plugins/General/gen_ff/embedwndguid.h @@ -0,0 +1,42 @@ +#ifndef _EMBEDWNDGUID_H +#define _EMBEDWNDGUID_H + +#include "../winamp/wa_ipc.h" + +class EmbedWndGuid +{ + public: + EmbedWndGuid(EmbedWndGuid *wg); + EmbedWndGuid(embedWindowState *ws); + GUID getGuid() { return guid; } + embedWindowState *getEmbedWindowState() { return ws; } + void setGuid(GUID g) { guid = g; } + HWND getHWND() { return hwnd; } + void setHWND(HWND w) { hwnd = w; } + + + private: + GUID guid; + embedWindowState *ws; + HWND hwnd; +}; + +class EmbedWndGuidMgr +{ + public: + GUID getGuid(EmbedWndGuid *wg); + GUID getGuid(embedWindowState *ws); + embedWindowState *getEmbedWindowState(GUID g); + int testGuid(GUID g); + void retireEmbedWindowState(embedWindowState *ws); + int getNumWindowStates(); + GUID enumWindowState(int n, embedWindowState **ws=NULL); + + private: + PtrList table; + +}; + +extern EmbedWndGuidMgr embedWndGuidMgr; + +#endif // _EMBEDWNDGUID_H \ No newline at end of file diff --git a/Src/Plugins/General/gen_ff/ff_ipc.cpp b/Src/Plugins/General/gen_ff/ff_ipc.cpp new file mode 100644 index 00000000..12d0325f --- /dev/null +++ b/Src/Plugins/General/gen_ff/ff_ipc.cpp @@ -0,0 +1,535 @@ +#include "precomp__gen_ff.h" +#include "gen_ff_ipc.h" +#include "ff_ipc.h" +#include "../winamp/wa_ipc.h" +#include "wa2frontend.h" +#include +#include +#include +#include +#include +#include +#include "wa2wndembed.h" +#include "embedwndguid.h" +#include +#include "../nu/AutoChar.h" +#include "../nu/AutoWide.h" +ColorThemeMonitor *colorThemeMonitor = NULL; + +HBITMAP CreateBitmapDIB(int w, int h, int planes, int bpp, void *data) +{ +#if 0 + return CreateBitmap(w, h, planes, bpp, data); +#else + void *bits=0; + BITMAPINFO bmi={0,}; + bmi.bmiHeader.biSize=sizeof(BITMAPINFOHEADER); + bmi.bmiHeader.biWidth = w; + bmi.bmiHeader.biHeight = -h; + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biBitCount = bpp; + bmi.bmiHeader.biSizeImage = w*h*(bpp/8); + bmi.bmiHeader.biCompression=BI_RGB; + + HBITMAP bm=CreateDIBSection(0,&bmi,0,&bits,NULL,0); + + if (bm && bits) memcpy(bits,data,w*h*(bpp/8)); + return bm; +#endif +} + +HWND ff_ipc_getContentWnd(HWND w) { + HWND ret = w; + + ifc_window *wnd = windowTracker->rootWndFromHwnd(w); // TODO: API_WNDMGR-> + if (wnd) { + ifc_window *dp = wnd->getDesktopParent(); + if (dp) { + Layout *l = static_cast(dp->getInterface(layoutGuid)); + if (l) { + Container *c = l->getParentContainer(); + if (c) { + GUID g = c->getDefaultContent(); + if (g != INVALID_GUID) { + if (g == playerWndGuid) + ret = wa2.getMainWindow(); + else if (g == pleditWndGuid) + ret = wa2.getWnd(IPC_GETWND_PE); + else if (g == videoWndGuid) + ret = wa2.getWnd(IPC_GETWND_VIDEO); + else { + embedWindowState *ews = embedWndGuidMgr.getEmbedWindowState(g); + if (ews) + ret = ews->me; + } + } + } + } + } + } + return ret; +} + + +void ff_ipc_getSkinColor(ff_skincolor *cs) +{ + if (cs == NULL) return; + cs->color = RGBTOBGR(SkinColor(AutoWide(cs->colorname))); +} +#define USE_WIN32_ALPHABLEND +static void ButtonSetup(ButtonWnd &_w) +{ + _w.setVirtual(0); + _w.setStartHidden(1); + _w.setParent(WASABI_API_WND->main_getRootWnd()); + _w.init(WASABI_API_WND->main_getRootWnd()); + _w.setCloaked(1); + _w.setVisible(1); +} + +static void DoButtonBlit(ButtonWnd &_w, int w, int h, int state, const wchar_t *overlayelement, int xpos, int ypos, BltCanvas *c) +{ + if (state == BUTTONSTATE_PUSHED) _w.setPushed(1); + else _w.setPushed(0); + _w.resize(0, 0, w, h); + _w.deferedInvalidate(); + _w.paint(NULL, NULL); + Canvas *cv = NULL; + cv = _w.getFrameBuffer(); + if (cv != NULL) { + BltCanvas *bltcanvas = static_cast(cv); // hackish +#ifdef USE_WIN32_ALPHABLEND + bltcanvas->/*getSkinBitmap()->*/blitAlpha(c, xpos, ypos); +#else + bltcanvas->getSkinBitmap()->blitAlpha(c, xpos, ypos); +#endif + } + if (overlayelement && *overlayelement) { + AutoSkinBitmap b(overlayelement); + SkinBitmap *sb = b.getBitmap(); + int shift = (state == BUTTONSTATE_PUSHED) ? 1 : 0; + sb->blitAlpha(c, xpos+(w-sb->getWidth())/2+shift, ypos+(h-sb->getHeight())/2+shift); + } +} + +void blitButtonToCanvas(int w, int h, int state, const wchar_t *overlayelement, int xpos, int ypos, BltCanvas *c) +{ + ButtonWnd _w; + ButtonSetup(_w); + DoButtonBlit(_w, w, h, state, overlayelement, xpos, ypos, c); + +} + +static HBITMAP generateButtonBitmap(int w, int h, int state, const wchar_t *overlayelement=NULL) { + BltCanvas c(w, h); + blitButtonToCanvas(w,h,state,overlayelement,0,0,&c); + SysCanvas cc; + c.blit(0, 0, &cc, 0, 0, w, h); + return CreateBitmapDIB(w, h, 1, 32, c.getBits()); +} + +COLORREF getWindowBackground(COLORREF *wb) +{ + static String last_skin, last_theme; + static COLORREF last_windowbackground = 0x00000000; + + COLORREF windowbackground = 0x00000000; + + // window background (used to set the bg color for the dialog) + if (!WASABI_API_SKIN->skin_getColorElementRef(L"wasabi.window.background")) + { + String curskin = AutoChar(WASABI_API_SKIN->getSkinName()); + String curtheme = AutoChar(WASABI_API_SKIN->colortheme_getColorSet()); + if (!last_skin.iscaseequal(curskin) || !last_theme.iscaseequal(curtheme)) { + last_skin = curskin; + last_theme = curtheme; + // extract the window color from an active piece of skin + SkinWnd w(L"$$$.some.purposedly.undefined.group.$$$", NULL, 0, NULL, 1, 1); + ifc_window *wnd = w.getWindow(); + if (wnd != NULL) { + ifc_window *wh = wnd->findWindowByInterface(windowHolderGuid); + if (wh != NULL) { + wnd = wnd->getDesktopParent(); + if (w.isNewContainer()) { + wnd->setCloaked(1); + wnd->setVisible(1); + wnd->resize(0, 0, 320, 200); + wnd->paint(); + } + BltCanvas *canvas = static_cast(wnd->getFrameBuffer()); + if (!canvas) goto basetexture; // eek :-D + int x=0, y=0; + RECT r; + wh->getClientRect(&r); + x = r.left + (r.right-r.left)/2; y = r.top + (r.bottom-r.top)/2; + COLORREF *bits = (COLORREF *)canvas->getBits(); + int w, h; + canvas->getDim(&w, &h, NULL); + if (w == 0 || h == 0) + windowbackground = 0; // black by default + else + windowbackground = bits[y*w+x]; + } + } + w.destroy(); + if (windowbackground == 0x00000000) { + basetexture: + // try for wasabi.basetexture ... + int _w, _h, _x, _y; + ARGB32 *b = WASABI_API_SKIN->imgldr_requestSkinBitmap(L"wasabi.basetexture", NULL, &_x, &_y, NULL, NULL, &_w, &_h, 1); + if (b != NULL) { + windowbackground = b[_w*_y+_x]; + WASABI_API_SKIN->imgldr_releaseSkinBitmap(b); + } else { + // no idea... we'll just set the default windows color + windowbackground = GetSysColor(COLOR_WINDOWFRAME); + } + } + last_windowbackground = windowbackground; + } else { + windowbackground = last_windowbackground; + } + if (wb) *wb=windowbackground; + return windowbackground; + } else { + COLORREF c = RGBTOBGR(SkinColor(L"wasabi.window.background")); + if (wb) *wb = c; + return c; + } +} + +inline int lumidiff(int a, int b) { + int r1 = (a & 0xFF0000) >> 16; + int r2 = (b & 0xFF0000) >> 16; + int g1 = (a & 0xFF00) >> 8; + int g2 = (b & 0xFF00) >> 8; + int b1 = a & 0xFF; + int b2 = b & 0xFF; + return MIN((ABS(r1-r2), ABS(g1-g2)), ABS(b1-b2)); +} + +HBITMAP ff_genwa2skinbitmap() +{ + int interpolate = 0; + if (SkinParser::getSkinVersion()*10 < 10) interpolate = 1; + + BltCanvas c(132, 75); + COLORREF windowbackground = 0; + COLORREF wbg=getWindowBackground(&windowbackground); + + COLORREF *ptr=(COLORREF *)c.getBits(); + c.fillBits(wbg); + + // set up bg color for the picky pixel parser heh + int x; + for (x = 47; x < 132; x ++) ptr[x]=ptr[x+132]=RGB(0,198,255); + + ButtonWnd _w; + ButtonSetup(_w); + + DoButtonBlit(_w, 47,15,0,NULL,0,0,&c); + DoButtonBlit(_w, 47,15,1,NULL,0,15,&c); + + DoButtonBlit(_w, 14,14,0,L"wasabi.button.label.arrow.up",0,31,&c); + DoButtonBlit(_w, 14,14,0,L"wasabi.button.label.arrow.down",14,31,&c); + DoButtonBlit(_w, 14,14,1,L"wasabi.button.label.arrow.up",28,31,&c); + DoButtonBlit(_w, 14,14,1,L"wasabi.button.label.arrow.down",42,31,&c); + + DoButtonBlit(_w, 14,14,0,L"wasabi.button.label.arrow.left",0,45,&c); + DoButtonBlit(_w, 14,14,0,L"wasabi.button.label.arrow.right",14,45,&c); + DoButtonBlit(_w, 14,14,1,L"wasabi.button.label.arrow.left",28,45,&c); + DoButtonBlit(_w, 14,14,1,L"wasabi.button.label.arrow.right",42,45,&c); + + DoButtonBlit(_w, 14,28,0,L"wasabi.scrollbar.vertical.grip",56,31,&c); + DoButtonBlit(_w, 14,28,1,L"wasabi.scrollbar.vertical.grip",70,31,&c); + + DoButtonBlit(_w, 28,14,0,L"wasabi.scrollbar.horizontal.grip",84,31,&c); + DoButtonBlit(_w, 28,14,1,L"wasabi.scrollbar.horizontal.grip",84,45,&c); + + // item background (background to edits, listviews etc) + if (WASABI_API_SKIN->skin_getColorElementRef(L"wasabi.list.background")) + ptr[48] = RGBTOBGR(SkinColor(L"wasabi.list.background")); + else + ptr[48] = WASABI_API_SKIN->skin_getBitmapColor(L"wasabi.list.background"); // RGBTOBGR(SkinColor(L"wasabi.edit.background")); + + COLORREF listbkg = ptr[48]; + + // item foreground (text color of edit/listview, etc) + if (WASABI_API_SKIN->skin_getColorElementRef(L"wasabi.list.text")) + ptr[50] = RGBTOBGR(SkinColor(L"wasabi.list.text")); + else { + int c = RGBTOBGR(SkinColor(L"wasabi.edit.text")); + ptr[50] = c; + } + + if (interpolate) { + int c = ptr[50]; + c = lumidiff(c, listbkg) < 0x1F ? Blenders::BLEND_AVG(ptr[50], 0xFF7F7F7F) : c; + c = lumidiff(c, listbkg) < 0x1F ? Blenders::BLEND_AVG(ptr[50], 0xFF101010) : c; + ptr[50] = c | 0xFF000000; + } + + ptr[52] = wbg; + + ptr[54] = RGBTOBGR(SkinColor(SKINCOLOR_BUTTON_TEXT)); + + // window text color + if (!interpolate && WASABI_API_SKIN->skin_getColorElementRef(L"wasabi.window.text")) + ptr[56] = RGBTOBGR(SkinColor(L"wasabi.window.text")); + else { + ptr[56] = RGBTOBGR(SkinColor(SKINCOLOR_LIST_ITEMTEXT)); //"wasabi.textbar.text");// + } + + + // color of dividers and sunken borders + if (!interpolate && WASABI_API_SKIN->skin_getColorElementRef(L"wasabi.border.sunken")) + ptr[58] = RGBTOBGR(SkinColor(L"wasabi.border.sunken")); + else { + int a = MAX((windowbackground & 0xFF0000) >> 16, MAX((windowbackground & 0xFF00) >> 8, windowbackground & 0xFF)); + ptr[58] = Blenders::BLEND_AVG(windowbackground, a > 0xE0 ? 0xFF000000: 0xFFFFFFFF); + } + + // listview header background color + COLORREF col = RGBTOBGR(SkinColor(SKINCOLOR_LIST_COLUMNBKG)); + if (interpolate) { + int a = MAX((col & 0xFF0000) >> 16, MAX((col & 0xFF00) >> 8, col & 0xFF)); + col = a < 0x1F ? Blenders::BLEND_AVG(windowbackground, 0xFF000000) : col; + } + ptr[62] = col; + + // listview header text color + ptr[64] = RGBTOBGR(SkinColor(SKINCOLOR_LIST_COLUMNTEXT)); + + // listview header frame top color + if (!interpolate && WASABI_API_SKIN->skin_getColorElementRef(L"wasabi.list.column.frame.top")) + ptr[66] = RGBTOBGR(SkinColor(L"wasabi.list.column.frame.top")); + else + ptr[66] = Blenders::BLEND_AVG(col, 0xFFFFFFFF);//listview header frame top color + + // listview header frame middle color + if (!interpolate && WASABI_API_SKIN->skin_getColorElementRef(L"wasabi.list.column.frame.middle")) + ptr[68] = RGBTOBGR(SkinColor(L"wasabi.list.column.frame.middle")); + else + ptr[68] = Blenders::BLEND_AVG(col, 0xFF2F2F2F); + + // listview header frame bottom color + if (!interpolate && WASABI_API_SKIN->skin_getColorElementRef(L"wasabi.list.column.frame.bottom")) + ptr[70] = RGBTOBGR(SkinColor(L"wasabi.list.column.frame.bottom")); + else + ptr[70] = Blenders::BLEND_AVG(col, 0xFF000000); + + // listview header empty color + COLORREF empty; + if (!interpolate && WASABI_API_SKIN->skin_getColorElementRef(L"wasabi.list.column.empty")) + empty = RGBTOBGR(SkinColor(L"wasabi.list.column.empty")); + else + empty = Blenders::BLEND_AVG(col, 0xFF000000); + ptr[72] = empty; + + // scrollbar foreground color + if (!interpolate && WASABI_API_SKIN->skin_getColorElementRef(L"wasabi.scrollbar.foreground")) + ptr[74] = RGBTOBGR(SkinColor(L"wasabi.scrollbar.foreground")); + else + ptr[74] = empty; + + // scrollbar background color + if (!interpolate && WASABI_API_SKIN->skin_getColorElementRef(L"wasabi.scrollbar.background")) + ptr[76] = RGBTOBGR(SkinColor(L"wasabi.scrollbar.background")); + else + ptr[76] = empty; + + // inverse scrollbar foreground color + if (!interpolate && WASABI_API_SKIN->skin_getColorElementRef(L"wasabi.scrollbar.foreground.inverted")) + ptr[78] = RGBTOBGR(SkinColor(L"wasabi.scrollbar.foreground.inverted")); + else + ptr[78] = col; + + // inverse scrollbar background color + if (!interpolate && WASABI_API_SKIN->skin_getColorElementRef(L"wasabi.scrollbar.background.inverted")) + ptr[80] = RGBTOBGR(SkinColor(L"wasabi.scrollbar.background.inverted")); + else + ptr[80] = empty; + + // scrollbar dead area color + ptr[82] = windowbackground; + + // listview/treeview selection bar text color + COLORREF selfg; + + if (WASABI_API_SKIN->skin_getColorElementRef(L"wasabi.list.text.selected")) + selfg = RGBTOBGR(SkinColor(L"wasabi.list.text.selected")); + else + if (WASABI_API_SKIN->skin_getColorElementRef(L"wasabi.list.text")) + selfg = RGBTOBGR(SkinColor(L"wasabi.list.text")); + else + selfg = SkinColor(L"wasabi.edit.text"); + + ptr[84] = selfg; + + + // listview/treeview selection bar back color + COLORREF selbg; + if (WASABI_API_SKIN->skin_getColorElementRef(L"wasabi.list.text.selected.background")) + selbg = RGBTOBGR(SkinColor(L"wasabi.list.text.selected.background")); + else { + if (WASABI_API_SKIN->skin_getColorElementRef(L"wasabi.list.item.selected")) + selbg = RGBTOBGR(SkinColor(L"wasabi.list.item.selected")); + else + selbg = RGBTOBGR(SkinColor(SKINCOLOR_TREE_SELITEMBKG)); + selbg = lumidiff(selbg, listbkg) < 0x2F ? Blenders::BLEND_AVG(windowbackground, 0xFF7F7F7F) : selbg; + selbg = lumidiff(selbg, listbkg) < 0x2F ? Blenders::BLEND_AVG(listbkg, 0xFF7F7F7F) : selbg; + selbg = lumidiff(selbg, listbkg) < 0x2F ? Blenders::BLEND_AVG(windowbackground, 0xFFF0F0F0) : selbg; + selbg = lumidiff(selbg, listbkg) < 0x2F ? Blenders::BLEND_AVG(listbkg, 0xFFF0F0F0) : selbg; + selbg = lumidiff(selbg, listbkg) < 0x2F ? Blenders::BLEND_AVG(col, 0xFFF0F0F0) : selbg; + selbg = lumidiff(selbg, listbkg) < 0x2F ? Blenders::BLEND_AVG(col, 0xFF101010) : selbg; + selbg = lumidiff(selbg, selfg) < 0x1F ? Blenders::BLEND_AVG(selbg, 0xFF101010) : selbg; + selbg = lumidiff(selbg, selfg) < 0x1F ? Blenders::BLEND_AVG(selbg, 0xFFF0F0F0) : selbg; + } + ptr[86] = ptr[60] = (selbg | 0xFF000000); + + // listview/treeview selection bar text color (inactive) + if (!interpolate && WASABI_API_SKIN->skin_getColorElementRef(L"wasabi.list.text.selected.inactive")) + ptr[88] = RGBTOBGR(SkinColor(L"wasabi.list.text.selected.inactive")); + else + ptr[88] = selfg; + + // listview/treeview selection bar back color (inactive) + if (!interpolate && WASABI_API_SKIN->skin_getColorElementRef(L"wasabi.list.text.selected.background.inactive")) + ptr[90] = RGBTOBGR(SkinColor(L"wasabi.list.text.selected.background.inactive")); + else + ptr[90] = Blenders::BLEND_ADJ1(selbg, 0xFF000000, 210); + + // alternate item background + if (WASABI_API_SKIN->skin_getColorElementRef(L"wasabi.list.background.alternate")) + ptr[92] = RGBTOBGR(SkinColor(L"wasabi.list.background.alternate")); + else + ptr[92] = ptr[48]; + + // alternate item foreground + if (WASABI_API_SKIN->skin_getColorElementRef(L"wasabi.list.text.alternate")) + { + ptr[50] = RGBTOBGR(SkinColor(L"wasabi.list.text.alternate")); + if (interpolate) { + int c = ptr[94]; + c = lumidiff(c, listbkg) < 0x1F ? Blenders::BLEND_AVG(ptr[94], 0xFF7F7F7F) : c; + c = lumidiff(c, listbkg) < 0x1F ? Blenders::BLEND_AVG(ptr[94], 0xFF101010) : c; + ptr[94] = c | 0xFF000000; + } + } + else { + ptr[94] = ptr[50]; + } + + return CreateBitmapDIB(132, 75, 1, 32, c.getBits()); +} + +void ff_ipc_genSkinBitmap(ff_skinbitmap *sb) { + if (sb == NULL) return; + switch (sb->id) { + case SKINBITMAP_BUTTON: { + HBITMAP bmp = generateButtonBitmap(sb->w, sb->h, sb->state); + sb->bitmap = bmp; + break; + } + case SKINBITMAP_SCROLLBARVBUTTON: { + HBITMAP bmp = generateButtonBitmap(sb->w, sb->h, sb->state, L"wasabi.scrollbar.vertical.grip"); + sb->bitmap = bmp; + break; + } + case SKINBITMAP_SCROLLBARHBUTTON: { + HBITMAP bmp = generateButtonBitmap(sb->w, sb->h, sb->state, L"wasabi.scrollbar.horizontal.grip"); + sb->bitmap = bmp; + break; + } + case SKINBITMAP_SCROLLBARUPBUTTON: { + HBITMAP bmp = generateButtonBitmap(sb->w, sb->h, sb->state, L"wasabi.button.label.arrow.up"); + sb->bitmap = bmp; + break; + } + case SKINBITMAP_SCROLLBARDOWNBUTTON: { + HBITMAP bmp = generateButtonBitmap(sb->w, sb->h, sb->state, L"wasabi.button.label.arrow.down"); + sb->bitmap = bmp; + break; + } + case SKINBITMAP_SCROLLBARLEFTBUTTON: { + HBITMAP bmp = generateButtonBitmap(sb->w, sb->h, sb->state, L"wasabi.button.label.arrow.left"); + sb->bitmap = bmp; + break; + } + case SKINBITMAP_SCROLLBAR_FF_UPBUTTON: { + HBITMAP bmp = generateButtonBitmap(sb->w, sb->h, SCROLLBARSTATE_NORMAL, + ((sb->state==SCROLLBARSTATE_PRESSED)?L"wasabi.scrollbar.vertical.left.pressed": + ((sb->state==SCROLLBARSTATE_HOVER)?L"wasabi.scrollbar.vertical.left.hover": + L"wasabi.scrollbar.vertical.left"))); + sb->bitmap = bmp; + break; + } + case SKINBITMAP_SCROLLBAR_FF_DOWNBUTTON: { + HBITMAP bmp = generateButtonBitmap(sb->w, sb->h, SCROLLBARSTATE_NORMAL, + ((sb->state==SCROLLBARSTATE_PRESSED)?L"wasabi.scrollbar.vertical.right.pressed": + ((sb->state==SCROLLBARSTATE_HOVER)?L"wasabi.scrollbar.vertical.right.hover": + L"wasabi.scrollbar.vertical.right"))); + sb->bitmap = bmp; + break; + } + case SKINBITMAP_SCROLLBAR_FF_LEFTBUTTON: { + HBITMAP bmp = generateButtonBitmap(sb->w, sb->h, SCROLLBARSTATE_NORMAL, + ((sb->state==SCROLLBARSTATE_PRESSED)?L"wasabi.scrollbar.horizontal.left.pressed": + ((sb->state==SCROLLBARSTATE_HOVER)?L"wasabi.scrollbar.horizontal.left.hover": + L"wasabi.scrollbar.horizontal.left"))); + sb->bitmap = bmp; + break; + } + case SKINBITMAP_SCROLLBAR_FF_RIGHTBUTTON: { + HBITMAP bmp = generateButtonBitmap(sb->w, sb->h, SCROLLBARSTATE_NORMAL, + ((sb->state==SCROLLBARSTATE_PRESSED)?L"wasabi.scrollbar.horizontal.right.pressed": + ((sb->state==SCROLLBARSTATE_HOVER)?L"wasabi.scrollbar.horizontal.right.hover": + L"wasabi.scrollbar.horizontal.right"))); + sb->bitmap = bmp; + break; + } + case SKINBITMAP_SCROLLBAR_FF_BARHBUTTON: { + HBITMAP bmp = generateButtonBitmap(sb->w, sb->h, SCROLLBARSTATE_NORMAL, + ((sb->state==SCROLLBARSTATE_PRESSED)?L"wasabi.scrollbar.horizontal.button.pressed": + ((sb->state==SCROLLBARSTATE_HOVER)?L"wasabi.scrollbar.horizontal.button.hover": + L"wasabi.scrollbar.horizontal.button"))); + sb->bitmap = bmp; + break; + } + case SKINBITMAP_SCROLLBAR_FF_BARVBUTTON: { + HBITMAP bmp = generateButtonBitmap(sb->w, sb->h, SCROLLBARSTATE_NORMAL, + ((sb->state==SCROLLBARSTATE_PRESSED)?L"wasabi.scrollbar.vertical.button.pressed": + ((sb->state==SCROLLBARSTATE_HOVER)?L"wasabi.scrollbar.vertical.button.hover": + L"wasabi.scrollbar.vertical.button.pressed"))); + sb->bitmap = bmp; + break; + } + } + + #if 0 // debug + + HDC ddc = GetDC(NULL); + HDC sdc = CreateCompatibleDC(ddc); + SelectObject(sdc, sb->bitmap); + + BitBlt(ddc, 100, 100, sb->w, sb->h, sdc, 0, 0, SRCCOPY); + + DeleteObject(sdc); + ReleaseDC(NULL, ddc); + + #endif +} + +ColorThemeMonitor::ColorThemeMonitor() { + WASABI_API_SYSCB->syscb_registerCallback(this); +} + +ColorThemeMonitor::~ColorThemeMonitor() { + WASABI_API_SYSCB->syscb_deregisterCallback(this); +} + +int ColorThemeMonitor::skincb_onColorThemeChanged( const wchar_t *colortheme ) +{ + SendMessageW( wa2.getMainWindow(), WM_WA_IPC, (WPARAM) colortheme, IPC_FF_ONCOLORTHEMECHANGED ); + return 1; +} diff --git a/Src/Plugins/General/gen_ff/ff_ipc.h b/Src/Plugins/General/gen_ff/ff_ipc.h new file mode 100644 index 00000000..6a422e2a --- /dev/null +++ b/Src/Plugins/General/gen_ff/ff_ipc.h @@ -0,0 +1,86 @@ +#ifndef _FF_IPC_H +#define _FF_IPC_H + +// ----------------------------------------------------------------------------------------------------- +// ----- IPC_FF_GETSKINCOLOR : Ask for a skin color -- the color is filtered for the current theme ----- +// ----------------------------------------------------------------------------------------------------- + +#define IPC_FF_GETSKINCOLOR IPC_FF_FIRST + 1 // data = ff_skincolor struct with .colorname, fills in .color + +typedef struct { + char colorname[256]; + COLORREF color; +} ff_skincolor; + +// List of default colors as of june 30, 2003. see freeform/xml/wasabi/xml/system-colors.xml for latest/complete list + +// Trees + #define SKINCOLOR_TREE_ITEMTEXT "wasabi.tree.text" + #define SKINCOLOR_TREE_SELITEMBKG L"wasabi.tree.selected" + #define SKINCOLOR_TREE_HILITEDROP "wasabi.tree.hiliteddrop" + +// Lists + #define SKINCOLOR_LIST_ITEMTEXT L"wasabi.list.text" + #define SKINCOLOR_LIST_SELITEMBKG "wasabi.list.item.selected" + #define SKINCOLOR_LIST_FOCUSITEMBKG "wasabi.list.item.focused" + #define SKINCOLOR_LIST_COLUMNBKG L"wasabi.list.column.background" + #define SKINCOLOR_LIST_COLUMNTEXT L"wasabi.list.column.text" + #define SKINCOLOR_LIST_SELITEMTEXT "wasabi.list.item.selected.fg" + #define SKINCOLOR_LIST_COLUMNSEPARATOR "wasabi.list.column.separator" + +// Buttons + #define SKINCOLOR_BUTTON_TEXT L"wasabi.button.text" + #define SKINCOLOR_BUTTON_HILITETEXT "wasabi.button.hiliteText" + #define SKINCOLOR_BUTTON_DIMMEDTEXT "wasabi.button.dimmedText" + + +// ---------------------------------------------------------------------------------------- +// ----- IPC_FF_GENSKINBITMAP: Ask gen_ff to create a bitmap of various skin elements ----- +// ---------------------------------------------------------------------------------------- + +// NOTE: You should free the hbitmap eventually using DeleteObject + +#define IPC_FF_GENSKINBITMAP IPC_FF_FIRST + 2 // data = ff_skinbitmap with bitmap .id .w .h and .state, fills in .bitmap + +typedef struct { + int id; // see below + int w, h; + int state; // id specific, see below + HBITMAP bitmap; +} ff_skinbitmap; + +// Bitmap IDs : + +#define SKINBITMAP_BUTTON 0 // Generate a button bitmap. states are as follows : + + #define BUTTONSTATE_NORMAL 0 + #define BUTTONSTATE_PUSHED 1 + +#define SKINBITMAP_SCROLLBARUPBUTTON 1 // Generate a scrollbar up button bitmap. states are button states +#define SKINBITMAP_SCROLLBARDOWNBUTTON 2 // Generate a scrollbar down button bitmap. states are button states +#define SKINBITMAP_SCROLLBARLEFTBUTTON 3 // Generate a scrollbar left button bitmap. states are button states +#define SKINBITMAP_SCROLLBARRIGHTBUTTON 4 // Generate a scrollbar right button bitmap. states are button states +#define SKINBITMAP_SCROLLBARVBUTTON 5 // Generate a scrollbar vertical button bitmap. states are button states +#define SKINBITMAP_SCROLLBARHBUTTON 6 // Generate a scrollbar horizontal button bitmap. states are button states + +#define SKINBITMAP_SCROLLBAR_FF_UPBUTTON 7 // Generate a freeform scrollbar up button bitmap. states are scrollbar states +#define SKINBITMAP_SCROLLBAR_FF_DOWNBUTTON 8 // Generate a freeform scrollbar down button bitmap. states are scrollbar states +#define SKINBITMAP_SCROLLBAR_FF_LEFTBUTTON 9 // Generate a freeform scrollbar left button bitmap. states are scrollbar states +#define SKINBITMAP_SCROLLBAR_FF_RIGHTBUTTON 10 // Generate a freeform scrollbar right button bitmap. states are scrollbar states +#define SKINBITMAP_SCROLLBAR_FF_BARHBUTTON 11 // Generate a freeform scrollbar vertical button bitmap. states are scrollbar states +#define SKINBITMAP_SCROLLBAR_FF_BARVBUTTON 12 // Generate a freeform scrollbar horizontal button bitmap. states are scrollbar states + + #define SCROLLBARSTATE_NORMAL 0 + #define SCROLLBARSTATE_PRESSED 1 + #define SCROLLBARSTATE_HOVER 2 + +// ----------------------------------------------------------------------------------------------- +// ----- IPC_FF_ONCOLORTHEMECHANGED: CALLBACK - sent when the skin's color theme has changed ----- +// ----------------------------------------------------------------------------------------------- + +#define IPC_FF_ONCOLORTHEMECHANGED IPC_FF_FIRST + 3 // data = name of the new color theme (const char *) +#define IPC_FF_ISMAINWND IPC_FF_FIRST + 4 // data = hwnd, returns 1 if hwnd is main window or any of its windowshade +#define IPC_FF_GETCONTENTWND IPC_FF_FIRST + 5 // data = HWND, returns the wa2 window that is embedded in the window's container (ie if hwnd is the pledit windowshade hwnd, it returns the wa2 pledit hwnd). if no content is found (ie, the window has nothing embedded) it returns the parameter you gave it +#define IPC_FF_NOTIFYHOTKEY IPC_FF_FIRST + 6 // data = const char * to hotkey description + +#endif diff --git a/Src/Plugins/General/gen_ff/fsmonitor.cpp b/Src/Plugins/General/gen_ff/fsmonitor.cpp new file mode 100644 index 00000000..31a3decd --- /dev/null +++ b/Src/Plugins/General/gen_ff/fsmonitor.cpp @@ -0,0 +1,174 @@ +#include "precomp__gen_ff.h" +#include "fsmonitor.h" + +#define tag L"wa5_fsmonitorclass" + +LRESULT CALLBACK fsMonitorWndProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ); +extern HINSTANCE hInstance; +//------------------------------------------------------------------------ +FullScreenMonitor::FullScreenMonitor() +{ + m_go_fs_timer_set = 0; + m_cancel_fs_timer_set = 0; + m_fs = 0; + WNDCLASSW wc; + if (!GetClassInfoW( hInstance, tag, &wc )) + { + MEMSET( &wc, 0, sizeof( wc ) ); + wc.lpfnWndProc = fsMonitorWndProc; + wc.hInstance = hInstance; // hInstance of DLL + wc.lpszClassName = tag; // our window class name + wc.style = 0; + + int _r = RegisterClassW( &wc ); + ASSERTPR( _r, "cannot create fsmonitor wndclass" ); + } + + hWnd = CreateWindowExW( 0, tag, L"", 0, 0, 0, 1, 1, NULL, NULL, hInstance, NULL ); + + ASSERT( hWnd ); + + SetWindowLongPtrW( hWnd, GWLP_USERDATA, (LONG_PTR) this ); + + APPBARDATA abd; + + abd.cbSize = sizeof( APPBARDATA ); + abd.hWnd = hWnd; + abd.uCallbackMessage = APPBAR_CALLBACK; + + SHAppBarMessage( ABM_NEW, &abd ); +} + +//------------------------------------------------------------------------ +FullScreenMonitor::~FullScreenMonitor() +{ + APPBARDATA abd; + + abd.cbSize = sizeof( APPBARDATA ); + abd.hWnd = hWnd; + + SHAppBarMessage( ABM_REMOVE, &abd ); + + if (IsWindow( hWnd )) + DestroyWindow( hWnd ); +} + +//------------------------------------------------------------------------ +void FullScreenMonitor::registerCallback( FSCallback *cb ) +{ + if (m_callbacks.haveItem( cb )) return; + m_callbacks.addItem( cb ); +} + + +//------------------------------------------------------------------------ +void FullScreenMonitor::unregisterCallback( FSCallback *cb ) +{ + m_callbacks.removeItem( cb ); +} + +//------------------------------------------------------------------------ +void FullScreenMonitor::onGoFullscreen() +{ + if (m_cancel_fs_timer_set) + { + timerclient_killTimer( 2 ); + m_cancel_fs_timer_set = 0; + } + else + { + timerclient_setTimer( 1, 250 ); + m_go_fs_timer_set = 1; + } +} + +//------------------------------------------------------------------------ +void FullScreenMonitor::onCancelFullscreen() +{ + if (m_go_fs_timer_set) + { + timerclient_killTimer( 1 ); + m_go_fs_timer_set = 0; + } + else + { + timerclient_setTimer( 2, 250 ); + m_cancel_fs_timer_set = 1; + } +} + +//------------------------------------------------------------------------ +void FullScreenMonitor::sendGoFSCallbacks() +{ + foreach( m_callbacks ); + m_callbacks.getfor()->onGoFullscreen(); + endfor; +} + +//------------------------------------------------------------------------ +void FullScreenMonitor::sendCancelFSCallbacks() +{ + foreach( m_callbacks ); + m_callbacks.getfor()->onCancelFullscreen(); + endfor; +} + +//------------------------------------------------------------------------ +void FullScreenMonitor::timerclient_timerCallback( int id ) +{ + if (id == 1) + { + timerclient_killTimer( 1 ); + m_go_fs_timer_set = 0; + sendGoFSCallbacks(); + } + else if (id == 2) + { + timerclient_killTimer( 2 ); + m_cancel_fs_timer_set = 0; + sendCancelFSCallbacks(); + } +} + +//------------------------------------------------------------------------ +int FullScreenMonitor::wndProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) +{ + switch (uMsg) + { + case APPBAR_CALLBACK: + { + switch (wParam) + { + case ABN_FULLSCREENAPP: + //DebugString("ABN_FULLSCREENAPP\n"); + if (lParam && !m_fs) + { + m_fs = 1; + onGoFullscreen(); + } + else if (!lParam && m_fs) + { + m_fs = 0; + onCancelFullscreen(); + } + break; + } + } + } + return DefWindowProc( hwnd, uMsg, wParam, lParam ); +} + +//------------------------------------------------------------------------ +LRESULT CALLBACK fsMonitorWndProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) +{ +#ifdef WIN32 + FullScreenMonitor *gThis = (FullScreenMonitor *) GetWindowLongPtrW( hwnd, GWLP_USERDATA ); + if (!gThis) + return DefWindowProc( hwnd, uMsg, wParam, lParam ); + else + return gThis->wndProc( hwnd, uMsg, wParam, lParam ); +#else + return 0; +#endif +} + diff --git a/Src/Plugins/General/gen_ff/fsmonitor.h b/Src/Plugins/General/gen_ff/fsmonitor.h new file mode 100644 index 00000000..4cae9ce0 --- /dev/null +++ b/Src/Plugins/General/gen_ff/fsmonitor.h @@ -0,0 +1,39 @@ +#ifndef _FS_MONITOR_H +#define _FS_MONITOR_H + +#include +#include + +class FSCallback { +public: + virtual void onGoFullscreen()=0; + virtual void onCancelFullscreen()=0; +}; + +class FullScreenMonitor : public TimerClientDI { +public: + FullScreenMonitor(); + virtual ~FullScreenMonitor(); + + void registerCallback(FSCallback *cb); + void unregisterCallback(FSCallback *cb); + + int isFullScreen() { return m_fs; } + + int wndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + + void timerclient_timerCallback(int id); + +private: + void onGoFullscreen(); + void onCancelFullscreen(); + void sendGoFSCallbacks(); + void sendCancelFSCallbacks(); + PtrList m_callbacks; + HWND hWnd; + int m_fs; + int m_go_fs_timer_set; + int m_cancel_fs_timer_set; +}; + +#endif diff --git a/Src/Plugins/General/gen_ff/gen.h b/Src/Plugins/General/gen_ff/gen.h new file mode 100644 index 00000000..53b7431a --- /dev/null +++ b/Src/Plugins/General/gen_ff/gen.h @@ -0,0 +1 @@ +#include "../Winamp/gen.h" diff --git a/Src/Plugins/General/gen_ff/gen_ff.rc b/Src/Plugins/General/gen_ff/gen_ff.rc new file mode 100644 index 00000000..033885fb --- /dev/null +++ b/Src/Plugins/General/gen_ff/gen_ff.rc @@ -0,0 +1,543 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "#include ""gen_ff.rc2""\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_PREFS_GENERAL DIALOGEX 0, 0, 261, 228 +STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD +FONT 8, "MS Shell Dlg", 0, 0, 0x0 +BEGIN + GROUPBOX "Desktop Alpha Blending",IDC_STATIC_DA1,4,3,256,48 + LTEXT "Desktop Alpha Blending allows some skins to display subtle shadows, smoothed edges, and other visual enhancements.",IDC_STATIC,9,15,244,16 + CONTROL "Enable Desktop Alpha Blending (when requested by the skin)",IDC_CHECK_DESKTOPALPHA, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,9,35,210,10 + GROUPBOX "Timers Resolution : 33ms",IDC_STATIC_TIMERRES,4,54,256,37 + CONTROL "Slider1",IDC_SLIDER_TIMERRESOLUTION,"msctls_trackbar32",TBS_AUTOTICKS | TBS_BOTH | TBS_NOTICKS | WS_TABSTOP,9,66,190,10 + PUSHBUTTON "Optimize...",IDC_BUTTON_AUTOTIMERRES,201,63,52,13 + LTEXT "Smoother",IDC_STATIC,12,76,32,8 + RTEXT "Choppier",IDC_STATIC,165,76,30,8 + GROUPBOX "Text Scroll Speed : Average",IDC_STATIC_TICKER,4,94,256,34 + CONTROL "Slider1",IDC_SLIDER_TICKERSPEED,"msctls_trackbar32",TBS_BOTH | TBS_NOTICKS | WS_TABSTOP,9,105,111,10 + LTEXT "Faster",IDC_STATIC,11,114,22,8 + LTEXT "Slower",IDC_STATIC,100,114,23,8,0,WS_EX_RIGHT + LTEXT "Move text by",IDC_STATIC,145,106,44,8 + EDITTEXT IDC_EDIT_INCREMENT,190,104,30,12,ES_AUTOHSCROLL | ES_NUMBER + CONTROL "",IDC_SPIN_INCREMENT,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS | UDS_NOTHOUSANDS,209,103,10,14 + LTEXT "pixels",IDC_STATIC,225,106,19,8 + GROUPBOX "Miscellaneous",IDC_STATIC,4,131,256,86 + CONTROL "Enable tooltips",IDC_CHECK_TOOLTIPS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,11,144,65,10 + CONTROL "Dock windows at ",IDC_CHECK_DOCKING,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,11,156,67,11 + EDITTEXT IDC_EDIT_DOCKDISTANCE,79,155,30,12,ES_AUTOHSCROLL | ES_NUMBER + CONTROL "",IDC_SPIN_DOCKDISTANCE,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS | UDS_NOTHOUSANDS,97,154,10,14 + LTEXT "pixels",IDC_STATIC,111,157,18,8 + CONTROL "Dock toolbars at ",IDC_CHECK_DOCKING2,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,11,170,67,11 + EDITTEXT IDC_EDIT_DOCKDISTANCE2,79,169,30,12,ES_AUTOHSCROLL | ES_NUMBER + CONTROL "",IDC_SPIN_DOCKDISTANCE2,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS | UDS_NOTHOUSANDS,97,168,11,14 + LTEXT "pixels",IDC_STATIC,111,171,18,8 + LTEXT "Wait",IDC_STATIC,11,186,16,8 + EDITTEXT IDC_EDIT_SHOWTIME,30,184,35,12,ES_AUTOHSCROLL | ES_NUMBER + CONTROL "",IDC_SPIN_SHOWTIME,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS | UDS_NOTHOUSANDS,50,183,10,14 + LTEXT "ms before showing docked toolbars",IDC_STATIC,70,186,114,8 + LTEXT "Wait",IDC_STATIC,11,201,16,8 + EDITTEXT IDC_EDIT_HIDETIME,30,199,35,12,ES_AUTOHSCROLL | ES_NUMBER + CONTROL "",IDC_SPIN_HIDETIME,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS | UDS_NOTHOUSANDS,49,198,10,14 + LTEXT "ms before hiding docked toolbars",IDC_STATIC,70,201,106,8 +END + +IDD_PREFS DIALOGEX 0, 0, 271, 246 +STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD +FONT 8, "MS Shell Dlg", 0, 0, 0x0 +BEGIN + CONTROL "Tab1",IDC_TAB1,"SysTabControl32",WS_TABSTOP,0,0,271,246 +END + +IDD_PREFS_THEMES DIALOGEX 0, 0, 261, 228 +STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD +FONT 8, "MS Shell Dlg", 0, 0, 0x0 +BEGIN + LISTBOX IDC_LIST1,4,6,256,203,LBS_SORT | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "&Set Theme",IDC_BUTTON_SETTHEME,210,212,50,13 +END + +IDD_ABOUT DIALOGEX 0, 0, 278, 175 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION +CAPTION "About" +FONT 8, "MS Shell Dlg", 0, 0, 0x0 +BEGIN + DEFPUSHBUTTON "OK",IDOK,220,157,50,13 + CONTROL "",IDC_STATIC_GROUP,"Static",SS_BLACKFRAME | SS_SUNKEN,7,7,263,146 +END + +IDD_AUTOTIMERRES DIALOGEX 0, 0, 195, 90 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Auto Detection of Optimum Timers Resolution" +FONT 8, "MS Shell Dlg", 0, 0, 0x0 +BEGIN + DEFPUSHBUTTON "Go",IDOK,85,70,50,13 + PUSHBUTTON "Cancel",IDCANCEL,138,70,50,13 + CTEXT "Auto detection works best if other applications are not saturating the CPU.\n\nBefore you continue, please start playing a song.\n\nPress Go when ready.\n(animations will temporarily slow down)",IDC_TXT,7,7,181,57 +END + +IDD_CUSTOMSCALE DIALOGEX 0, 0, 165, 70 +STYLE DS_SYSMODAL | DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +EXSTYLE WS_EX_TOOLWINDOW +CAPTION "Custom Scale" +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + GROUPBOX "Scale : 100%",IDC_STATIC_SCALE,7,7,151,37 + CONTROL "Slider1",IDC_SLIDER_CUSTOMSCALE,"msctls_trackbar32",TBS_BOTH | TBS_NOTICKS | WS_TABSTOP,13,17,139,11 + LTEXT "10%",IDC_STATIC,17,30,18,8 + LTEXT "300%",IDC_STATIC,130,30,21,8 + DEFPUSHBUTTON "OK",IDOK,54,50,50,13 + PUSHBUTTON "Cancel",IDCANCEL,108,50,50,13 +END + +IDD_PREFS_FONTS DIALOGEX 0, 0, 261, 228 +STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD +FONT 8, "MS Shell Dlg", 0, 0, 0x0 +BEGIN + CONTROL "Use FreeType TrueType font rendering for older skins",IDC_CHECK_FREETYPETTF, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,5,4,191,10 + LTEXT "Charmap Encoding :",IDC_STATIC_CHARMAP,15,18,65,8 + COMBOBOX IDC_COMBO_CHARMAP,85,16,170,235,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + GROUPBOX "Alternate Fonts",IDC_STATIC_ALTFONTS,4,33,256,40 + CONTROL "Use the alternate fonts if the skin defines them",IDC_CHECK_ALTFONTS, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,45,169,10 + CONTROL "Keep primary font in strings that contain only 7-bit characters",IDC_CHECK_NO_ALT_7BIT_OVERRIDE, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,58,215,10 + GROUPBOX "Font Mapper",IDC_STATIC,4,76,256,105 + CONTROL "Use skin font mapper",IDC_CHECK_USEFONTMAPPER,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,88,85,10 + PUSHBUTTON "Configure skin font mapping...",IDC_BUTTON_FONTMAPPER,133,86,120,13 + CONTROL "Allow use of bitmap fonts that have not been mapped",IDC_CHECK_ALLOWBITMAPFONTS, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,101,188,10 + LTEXT "Replace with :",IDC_STATIC,17,128,46,8 + GROUPBOX "TrueType Replacement for Winamp Charset bitmap fonts",IDC_STATIC_TTFOVERRIDE,10,114,244,62 + COMBOBOX IDC_COMBO_TTFOVERRIDE,67,126,179,151,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + CONTROL "Slider1",IDC_SLIDER_SCALETTFOVERRIDE,"msctls_trackbar32",TBS_BOTH | TBS_NOTICKS | WS_TABSTOP,16,142,231,10 + LTEXT "Decrease size",IDC_STATIC_DECREASESIZE,20,151,46,8 + CTEXT "100%",IDC_STATIC_TTFSCALE,118,151,31,8 + LTEXT "Increase size",IDC_STATIC_INCREASESIZE,201,151,42,8 + CONTROL "Keep bitmap font in strings that contain only 7-bit characters",IDC_NO_7BIT_OVERRIDE, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,18,162,210,10 + GROUPBOX "Default / Fallback Font",IDC_STATIC,4,185,256,43 + LTEXT "If a font was not specified or cannot be found, use this font at this scale :",IDC_STATIC,10,196,243,8 + COMBOBOX IDC_COMBO_DEFAULTFONT,9,209,100,151,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + CONTROL "Slider1",IDC_SLIDER_SCALEDEFAULTFONT,"msctls_trackbar32",TBS_BOTH | TBS_NOTICKS | WS_TABSTOP,116,206,139,10 + LTEXT "Decrease size",IDC_STATIC_DECREASESIZE2,119,214,46,8 + CTEXT "100%",IDC_STATIC_SCALEDEFAULTFONT,172,214,31,8 + LTEXT "Increase size",IDC_STATIC_INCREASESIZE2,210,214,42,8 + CTEXT "100%",IDC_STATIC_SCALEDEFAULTFONT2,174,214,31,8 +END + +IDD_PREFS_WINDOWS DIALOGEX 0, 0, 260, 228 +STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD +FONT 8, "MS Shell Dlg", 0, 0, 0x0 +BEGIN + GROUPBOX "Scaling",IDC_STATIC,4,3,256,76 + CONTROL "Link all windows in all skins:",IDC_CHECK_LINKALLRATIO, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,15,103,10 + CTEXT "100%",IDC_STATIC_SCALE,159,10,51,8 + LTEXT "10%",IDC_STATIC_SCALE11,124,17,16,8 + CONTROL "",IDC_SLIDER_CUSTOMSCALE,"msctls_trackbar32",TBS_BOTH | TBS_NOTICKS | WS_TABSTOP,142,18,84,10 + LTEXT "300%",IDC_STATIC_SCALE301,227,17,20,8 + CONTROL "WindowShade scale follows Normal Window Scale",IDC_CHECK_LINKRATIO, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,28,173,10 + GROUPBOX "On Window Close/Reopen",IDC_STATIC,10,41,244,32 + COMBOBOX IDC_COMBO_SCALE,16,54,232,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + GROUPBOX "Opacity",IDC_STATIC_OPACITY,4,81,256,138 + CONTROL "Link all windows in all skins:",IDC_CHECK_LINKALLALPHA, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,92,102,10 + COMBOBOX IDC_COMBO_OPACITY,11,106,98,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + CONTROL "Slider1",IDC_SLIDER_CUSTOMALPHA,"msctls_trackbar32",TBS_BOTH | TBS_NOTICKS | WS_TABSTOP,114,106,140,10 + LTEXT "Transparent",IDC_STATIC_TRANSP,117,116,40,8 + CTEXT "100%",IDC_STATIC_ALPHA,176,116,28,8 + LTEXT "Opaque",IDC_STATIC_OPAQUE,223,116,26,8 + CONTROL "WindowShade opacity follows Normal Window opacity",IDC_CHECK_LINKALPHA, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,128,186,10 + GROUPBOX "Auto Opaque",IDC_STATIC_AUTOON,10,142,244,71 + LTEXT "To use Auto Opaque on a window without linking all of them, use the context menu on that window and select Window Settings, Opacity.",IDC_STATIC_AUTOONTXT,15,153,224,18 + LTEXT "Fade in :",IDC_STATIC_FADEIN2,15,174,28,8 + CONTROL "Slider1",IDC_SLIDER_FADEIN,"msctls_trackbar32",TBS_BOTH | TBS_NOTICKS | WS_TABSTOP,51,175,102,10 + LTEXT "250ms",IDC_STATIC_FADEIN,154,174,31,8 + LTEXT "Hold :",IDC_STATIC_HOLD2,15,186,22,8 + CONTROL "Slider1",IDC_SLIDER_HOLD,"msctls_trackbar32",TBS_BOTH | TBS_NOTICKS | WS_TABSTOP,51,186,102,10 + LTEXT "2000ms",IDC_STATIC_HOLD,154,186,32,8 + LTEXT "Fade out :",IDC_STATIC_FADEOUT2,15,198,34,8 + CONTROL "Slider1",IDC_SLIDER_FADEOUT,"msctls_trackbar32",TBS_BOTH | TBS_NOTICKS | WS_TABSTOP,51,198,102,10 + LTEXT "2000ms",IDC_STATIC_FADEOUT,154,198,32,8 + GROUPBOX "Extend Hover",IDC_STATIC_EXTENDBOX,183,177,64,30 + EDITTEXT IDC_EDIT_EXTEND,189,189,30,12,ES_AUTOHSCROLL | ES_NUMBER + CONTROL "",IDC_SPIN_EXTEND,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS | UDS_NOTHOUSANDS,211,188,10,14 + LTEXT "pixels",IDC_STATIC_EXTEND,224,191,19,8 +END + +IDD_CUSTOMALPHA DIALOGEX 0, 0, 165, 70 +STYLE DS_SYSMODAL | DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +EXSTYLE WS_EX_TOOLWINDOW +CAPTION "Custom Opacity" +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + GROUPBOX "Opacity : 100%",IDC_STATIC_ALPHA,7,7,151,37 + CONTROL "Slider1",IDC_SLIDER_CUSTOMALPHA,"msctls_trackbar32",TBS_BOTH | TBS_NOTICKS | WS_TABSTOP,13,17,139,11 + LTEXT "10%",IDC_STATIC,17,30,18,8 + LTEXT "100%",IDC_STATIC,130,30,21,8 + DEFPUSHBUTTON "OK",IDOK,54,50,50,13 + PUSHBUTTON "Cancel",IDCANCEL,108,50,50,13 +END + +IDD_FONTMAPPER DIALOGEX 0, 0, 277, 169 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Font Mapper" +FONT 8, "MS Shell Dlg", 0, 0, 0x0 +BEGIN + CONTROL "List2",IDC_LIST_MAPPINGS,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_SORTASCENDING | WS_BORDER | WS_TABSTOP,7,7,263,80 + LTEXT "Map when using:",IDC_STATIC,9,91,35,18 + CONTROL "This skin",IDC_RADIO_THISSKIN,"Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,47,92,44,8 + CONTROL "All skins",IDC_RADIO_ALLSKINS,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,47,101,41,8 + LTEXT "Map font :",IDC_STATIC,9,114,33,8 + COMBOBOX IDC_COMBO_SKINFONTS,46,112,132,122,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + LTEXT "Into :",IDC_STATIC,9,128,33,8 + COMBOBOX IDC_COMBO_FONTS,46,126,132,122,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + LTEXT "Scale :",IDC_STATIC,9,144,23,8 + CONTROL "Slider1",IDC_SLIDER_SCALE,"msctls_trackbar32",TBS_BOTH | TBS_NOTICKS | WS_TABSTOP,42,144,140,10 + LTEXT "Decrease size",IDC_STATIC,43,154,46,8 + LTEXT "100%",IDC_STATIC_SCALE,104,155,20,8 + LTEXT "Increase size",IDC_STATIC,138,154,42,8 + PUSHBUTTON "Delete",IDC_BUTTON_DEL,212,90,58,13 + PUSHBUTTON "Set",IDC_BUTTON_SET,212,111,58,13 + PUSHBUTTON "New",IDC_BUTTON_NEW,212,127,58,13 + DEFPUSHBUTTON "Close",IDOK,220,149,50,13 +END + +IDD_PREFS_SKIN DIALOGEX 0, 0, 261, 228 +STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + LTEXT "",IDC_STATIC_GROUP,4,6,256,200,0,WS_EX_CLIENTEDGE + CTEXT "-",IDC_STATIC_EMPTY,34,23,192,162 + PUSHBUTTON "Skin Options Menu",IDC_BUTTON_SKINSPECIFIC,74,212,114,13 +END + +IDD_MEDIA_DOWNLOADER DIALOGEX 0, 0, 307, 59 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +EXSTYLE WS_EX_APPWINDOW +CAPTION "Download" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "",IDC_URL,7,7,293,10 + LTEXT "",IDC_PROGRESS,7,28,293,8 + LTEXT "",IDC_DOWNLOADTO,7,44,293,8 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_PREFS_GENERAL, DIALOG + BEGIN + LEFTMARGIN, 4 + RIGHTMARGIN, 260 + END + + IDD_PREFS_THEMES, DIALOG + BEGIN + RIGHTMARGIN, 260 + END + + IDD_ABOUT, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 270 + TOPMARGIN, 7 + BOTTOMMARGIN, 170 + END + + IDD_AUTOTIMERRES, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 188 + TOPMARGIN, 7 + BOTTOMMARGIN, 83 + END + + IDD_CUSTOMSCALE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 158 + TOPMARGIN, 7 + BOTTOMMARGIN, 63 + END + + IDD_PREFS_FONTS, DIALOG + BEGIN + RIGHTMARGIN, 260 + END + + IDD_CUSTOMALPHA, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 158 + TOPMARGIN, 7 + BOTTOMMARGIN, 63 + END + + IDD_FONTMAPPER, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 270 + TOPMARGIN, 7 + BOTTOMMARGIN, 162 + END + + IDD_MEDIA_DOWNLOADER, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 300 + TOPMARGIN, 7 + BOTTOMMARGIN, 52 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDR_CONTROLMENU MENU +BEGIN + POPUP "ControlMenu" + BEGIN + POPUP "&Opacity" + BEGIN + MENUITEM "1&00%", ID_CONTROLMENU_OPACITY_100 + MENUITEM "&90%", ID_CONTROLMENU_OPACITY_90 + MENUITEM "&80%", ID_CONTROLMENU_OPACITY_80 + MENUITEM "&70%", ID_CONTROLMENU_OPACITY_70 + MENUITEM "&60%", ID_CONTROLMENU_OPACITY_60 + MENUITEM "&50%", ID_CONTROLMENU_OPACITY_50 + MENUITEM "&40%", ID_CONTROLMENU_OPACITY_40 + MENUITEM "&30%", ID_CONTROLMENU_OPACITY_30 + MENUITEM "&20%", ID_CONTROLMENU_OPACITY_20 + MENUITEM "&10%", ID_CONTROLMENU_OPACITY_10 + MENUITEM "&Custom", ID_CONTROLMENU_OPACITY_CUSTOM + MENUITEM SEPARATOR + MENUITEM "Opaque on &Focus", ID_CONTROLMENU_OPACITY_AUTO100_FOCUS + MENUITEM "Opaque on &Hover", ID_CONTROLMENU_OPACITY_AUTO100_HOVER + END + POPUP "&Scaling" + BEGIN + MENUITEM "&50%", ID_CONTROLMENU_SCALING_50 + MENUITEM "&75%", ID_CONTROLMENU_SCALING_75 + MENUITEM "&100%", ID_CONTROLMENU_SCALING_100 + MENUITEM "125%", ID_CONTROLMENU_SCALING_125 + MENUITEM "150%", ID_CONTROLMENU_SCALING_150 + MENUITEM "&200%", ID_CONTROLMENU_SCALING_200 + MENUITEM "250%", ID_CONTROLMENU_SCALING_250 + MENUITEM "&300%", ID_CONTROLMENU_SCALING_300 + MENUITEM "&Custom", ID_CONTROLMENU_SCALING_CUSTOM + MENUITEM SEPARATOR + MENUITEM "&Locked", ID_CONTROLMENU_SCALING_LOCKED + MENUITEM "&Temporary", ID_CONTROLMENU_SCALING_FOLLOWDOUBLESIZE + END + POPUP "Docked Toolbar" + BEGIN + MENUITEM "Auto-&Hide", ID_CONTROLMENU_TOOLBAR_AUTOHIDE + MENUITEM "&Always On Top", ID_CONTROLMENU_TOOLBAR_ALWAYSONTOP + MENUITEM SEPARATOR + MENUITEM "Top", ID_CONTROLMENU_TOOLBAR_TOP + MENUITEM "Left", ID_CONTROLMENU_TOOLBAR_LEFT + MENUITEM "Right", ID_CONTROLMENU_TOOLBAR_RIGHT + MENUITEM "Bottom", ID_CONTROLMENU_TOOLBAR_BOTTOM + MENUITEM "Not docked", ID_CONTROLMENU_TOOLBAR_DISABLED + MENUITEM SEPARATOR + MENUITEM "Dock/Undock Windows by Dragging", ID_CONTROLMENU_TOOLBAR_AUTODOCKONDRAG + END + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + IDS_NULLSOFT_MODERN_SKINS "Nullsoft Modern Skins Support v%s" + 65535 "{ACD05A75-030B-4943-A100-540DAD98FB00}" +END + +STRINGTABLE +BEGIN + IDS_SEND_TO "Send to:" + IDS_NO_THEME_AVAILABLE "No theme available" + IDS_COLOR_THEMES "Color Themes" + IDS_CURRENT_SKIN "Current Skin" + IDS_NO_SKIN_LOADED "No modern skin loaded" + IDS_MODERN_SKIN_SUPPORT_CLASSIC + "Modern Skin support (switch to a modern skin for a much more interesting about box =)" + IDS_MODERN_SKIN_SUPPORT "Modern Skin support" + IDS_CUSTOM_X_PERCENT "&Custom (%d%%)..." + IDS_CUSTOM "&Custom..." + IDS_WINDOW_SETTINGS "Window Settings" + IDS_SCALE_X_PERCENT "Scale : %d%%" + IDS_OPACITY_X_PERCENT "Opacity : %d%%" + IDS_GHK_SHOW_NOTIFICATION "Playback: Show notification" + IDS_MODERN_SKINS "Modern Skins" + IDS_GENERAL "General" +END + +STRINGTABLE +BEGIN + IDS_ALPHA_BLENDING "Alpha Blending" + IDS_FONT_RENDERING "Font Rendering" + IDS_ERROR_WHILE_LOADING_SKIN_WINDOW "Error while loading skin window" + IDS_NO_OPTIONS_AVAILABLE_FOR_THIS_SKIN + "No Options Available For This Skin" + IDS_AUTO_UNICODE_LATIN1_ASCII "Auto (Unicode -> Latin-1 -> ASCII)" + IDS_FONT "Font" + IDS_MAPPING "Mapping" + IDS_SCALE "Scale" + IDS_TYPE "Type" + IDS_THIS_SKIN "This skin" + IDS_ALL_SKINS "All skins" + IDS_FASTER "Faster" + IDS_FAST "Fast" + IDS_AVERAGE "Average" + IDS_SLOW "Slow" + IDS_SLOWER "Slower" +END + +STRINGTABLE +BEGIN + IDS_FAILED_TO_DETECT_OPTIMAL_RESOLUTION + "\n\nFailed to detect optimal resolution.\n\nThis machine may be very slow, or the CPU may be otherwise too busy for auto-detection to work." + IDS_AUTO_DETECTING "\n\nAuto detecting...\n\nNow trying %dms." + IDS_PREPARING_AUTO_DETECTION "\n\n\nPreparing auto detection..." + IDS_AUTO_DETECTION_SUCCESSFUL + "\n\nAuto detection successful.\n\nOptimum Resolution : %dms" + IDS_ACCEPT "Accept" + IDS_N_A "n/a" + IDS_TIMERS_RESOLUTION "Timers Resolution : %dms" + IDS_TEXT_SCROLL_SPEED "Text Scroll Speed : %s" + IDS_CROSSFADER_ONLY_UNDER_OUT_DS + "Crossfader is only supported by DirectSound Output.\nWould you like to change your Output plug-in now?" + IDS_NOT_SUPPORTED "Not Supported" + IDS_PLAYLIST_EDITOR "Playlist Editor" + IDS_VIDEO "Video" + IDS_MONO " mono" + IDS_STEREO " stereo" + IDS_X_CHANNELS " %d Channels" + IDS_BY_SPACE "by " +END + +STRINGTABLE +BEGIN + IDS_COULD_NOT_FIND_WINAMP "Could not find Winamp!" + IDS_ERROR "Error" + IDS_WINAMP_NOT_FOUND "Winamp not found" + IDS_VISUALIZATIONS "Visualizations" + IDS_MEDIA_LIBRARY "Media Library" + IDS_NO_SET "no set" + IDS_SLOT_X_X "Slot %d (%s)" + IDS_COLOR_EDITOR "Color Editor" + IDS_SKIN_SETTINGS "Skin Settings\tAlt+C" + IDS_WEB_BROWSER "Web Browser\tAlt+X" + IDS_ALBUM_ART "Album Art\tAlt+A" + IDS_NO_VISUALISATION "No visualization" + IDS_SPECTRUM_ANALYZER "Spectrum analyzer" + IDS_OSCILLOSCOPE "Oscilloscope" + IDS_SKIN_LOAD_FORMAT_OLD + "The skin you are trying to load is using an old format.\nAre you sure you want to load this skin? (It might crash!)" + IDS_SKIN_LOAD_FORMAT_TOO_RECENT + "The skin you are trying to load is using a format that is too recent.\nAre you sure you want to load this skin? (It might crash!)" +END + +STRINGTABLE +BEGIN + IDS_SKIN_LOAD_WARNING "Warning!" + IDS_SKIN_LOAD_NOT_SUPPORTED + "The skin you are trying to load doesn't seem to be supported." + IDS_NO_SKIN_LOADED_ "No skin loaded" + IDS_XUITHEME_LOAD "Load" + IDS_XUITHEME_SAVE "Save" + IDS_MS "ms" + IDS_KHZ "kHz" + IDS_KBPS "kbps" + IDS_USELOCK "Reset window scale to global 'double size' except if it is locked" + IDS_ALLLOCKED "All windows remember their scale, disregard global 'double size'" + IDS_OPAQUE_FOCUS "Auto opaque on focus" + IDS_OPAQUE_HOVER "Auto opaque on hover" + IDS_NO_OPACITY "No auto opacity" + IDS_LOADING "Loading..." +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// +#include "gen_ff.rc2" +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/Src/Plugins/General/gen_ff/gen_ff.rc2 b/Src/Plugins/General/gen_ff/gen_ff.rc2 new file mode 100644 index 00000000..4c28a37a --- /dev/null +++ b/Src/Plugins/General/gen_ff/gen_ff.rc2 @@ -0,0 +1,58 @@ +// Microsoft Visual C++ generated resource script. +// +#ifdef APSTUDIO_INVOKED + #error this file is not editable by Microsoft Visual C++ +#endif //APSTUDIO_INVOKED + +#include "resource.h" + +///////////////////////////////////////////////////////////////////////////// +// +// Data +// + +//IDB_MB_TAB_NORMAL RCDATA DISCARDABLE "bitmaps\\mb-unselected.png" +//IDB_MB_TAB_HILITED RCDATA DISCARDABLE "bitmaps\\mb-hilited.png" +//IDB_MB_TAB_SELECTED RCDATA DISCARDABLE "bitmaps\\mb-selected.png" + +///////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// +#include "..\..\..\Winamp/buildType.h" +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,55,0,0 + PRODUCTVERSION WINAMP_PRODUCTVER + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Winamp SA" + VALUE "FileDescription", "Winamp General Purpose Plug-in" + VALUE "FileVersion", "1,55,0,0" + VALUE "InternalName", "Nullsoft Modern Skins Support" + VALUE "LegalCopyright", "Copyright © 2003-2023 Winamp SA" + VALUE "LegalTrademarks", "Nullsoft and Winamp are trademarks of Winamp SA" + VALUE "OriginalFilename", "gen_ff.dll" + VALUE "ProductName", "Winamp" + VALUE "ProductVersion", STR_WINAMP_PRODUCTVER + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/Src/Plugins/General/gen_ff/gen_ff.sln b/Src/Plugins/General/gen_ff/gen_ff.sln new file mode 100644 index 00000000..a122851c --- /dev/null +++ b/Src/Plugins/General/gen_ff/gen_ff.sln @@ -0,0 +1,204 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29424.173 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gen_ff", "gen_ff.vcxproj", "{0A75D6C7-6F14-4032-BBCC-7A234E626A56}" + ProjectSection(ProjectDependencies) = postProject + {DABE6307-F8DD-416D-9DAC-673E2DECB73F} = {DABE6307-F8DD-416D-9DAC-673E2DECB73F} + {4F4B5925-2D0B-48DB-BBB0-D321B14EF621} = {4F4B5925-2D0B-48DB-BBB0-D321B14EF621} + {FA669B69-C4E6-48F1-B7BF-BB40B6DC0BC0} = {FA669B69-C4E6-48F1-B7BF-BB40B6DC0BC0} + {3E0BFA8A-B86A-42E9-A33F-EC294F823F7F} = {3E0BFA8A-B86A-42E9-A33F-EC294F823F7F} + {78B079BD-9FC7-4B9E-B4A6-96DA0F00248B} = {78B079BD-9FC7-4B9E-B4A6-96DA0F00248B} + {0F9730E4-45DA-4BD2-A50A-403A4BC9751A} = {0F9730E4-45DA-4BD2-A50A-403A4BC9751A} + {053B7CED-1A95-4473-8A58-409E93531692} = {053B7CED-1A95-4473-8A58-409E93531692} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bfc", "..\Wasabi\bfc\bfc.vcxproj", "{D0EC862E-DDDD-4F4F-934F-B75DC9062DC1}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tataki", "..\tataki\tataki.vcxproj", "{255B68B5-7EF8-45EF-A675-2D6B88147909}" + ProjectSection(ProjectDependencies) = postProject + {DABE6307-F8DD-416D-9DAC-673E2DECB73F} = {DABE6307-F8DD-416D-9DAC-673E2DECB73F} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "freetypewac", "..\freetypewac\freetypewac.vcxproj", "{2E31944B-64BE-4160-A1CF-7A31A923E204}" + ProjectSection(ProjectDependencies) = postProject + {DABE6307-F8DD-416D-9DAC-673E2DECB73F} = {DABE6307-F8DD-416D-9DAC-673E2DECB73F} + {78B079BD-9FC7-4B9E-B4A6-96DA0F00248B} = {78B079BD-9FC7-4B9E-B4A6-96DA0F00248B} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Wasabi", "..\Wasabi\Wasabi.vcxproj", "{3E0BFA8A-B86A-42E9-A33F-EC294F823F7F}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nsutil", "..\nsutil\nsutil.vcxproj", "{DABE6307-F8DD-416D-9DAC-673E2DECB73F}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "freetype", "..\freetype\freetype.vcxproj", "{78B079BD-9FC7-4B9E-B4A6-96DA0F00248B}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bmp", "..\bmp\bmp.vcxproj", "{3E4C3F3B-5D94-4691-AF6D-13C1E6F54501}" + ProjectSection(ProjectDependencies) = postProject + {0F9730E4-45DA-4BD2-A50A-403A4BC9751A} = {0F9730E4-45DA-4BD2-A50A-403A4BC9751A} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "zlib", "..\replicant\zlib\zlib.vcxproj", "{0F9730E4-45DA-4BD2-A50A-403A4BC9751A}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "jpeg", "..\jpeg\jpeg.vcxproj", "{74EADF6F-F023-4D8C-B03A-5258B192A5E2}" + ProjectSection(ProjectDependencies) = postProject + {4F4B5925-2D0B-48DB-BBB0-D321B14EF621} = {4F4B5925-2D0B-48DB-BBB0-D321B14EF621} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ijg", "..\ijg\ijg.vcxproj", "{4F4B5925-2D0B-48DB-BBB0-D321B14EF621}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "png", "..\png\png.vcxproj", "{EBA98B5E-F516-47AD-9D97-F5923283C6D8}" + ProjectSection(ProjectDependencies) = postProject + {0F9730E4-45DA-4BD2-A50A-403A4BC9751A} = {0F9730E4-45DA-4BD2-A50A-403A4BC9751A} + {053B7CED-1A95-4473-8A58-409E93531692} = {053B7CED-1A95-4473-8A58-409E93531692} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libpng", "..\libpng\libpng.vcxproj", "{053B7CED-1A95-4473-8A58-409E93531692}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "xml", "..\xml\xml.vcxproj", "{4FA2D01A-8932-45BA-9C54-E8247DD2CCAB}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "timer", "..\timer\timer.vcxproj", "{C7C45E25-5C76-4F59-AEBD-992CEC2A5C2E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {0A75D6C7-6F14-4032-BBCC-7A234E626A56}.Debug|Win32.ActiveCfg = Debug|Win32 + {0A75D6C7-6F14-4032-BBCC-7A234E626A56}.Debug|Win32.Build.0 = Debug|Win32 + {0A75D6C7-6F14-4032-BBCC-7A234E626A56}.Debug|x64.ActiveCfg = Debug|x64 + {0A75D6C7-6F14-4032-BBCC-7A234E626A56}.Debug|x64.Build.0 = Debug|x64 + {0A75D6C7-6F14-4032-BBCC-7A234E626A56}.Release|Win32.ActiveCfg = Release|Win32 + {0A75D6C7-6F14-4032-BBCC-7A234E626A56}.Release|Win32.Build.0 = Release|Win32 + {0A75D6C7-6F14-4032-BBCC-7A234E626A56}.Release|x64.ActiveCfg = Release|x64 + {0A75D6C7-6F14-4032-BBCC-7A234E626A56}.Release|x64.Build.0 = Release|x64 + {D0EC862E-DDDD-4F4F-934F-B75DC9062DC1}.Debug|Win32.ActiveCfg = Debug|Win32 + {D0EC862E-DDDD-4F4F-934F-B75DC9062DC1}.Debug|Win32.Build.0 = Debug|Win32 + {D0EC862E-DDDD-4F4F-934F-B75DC9062DC1}.Debug|x64.ActiveCfg = Debug|x64 + {D0EC862E-DDDD-4F4F-934F-B75DC9062DC1}.Debug|x64.Build.0 = Debug|x64 + {D0EC862E-DDDD-4F4F-934F-B75DC9062DC1}.Release|Win32.ActiveCfg = Release|Win32 + {D0EC862E-DDDD-4F4F-934F-B75DC9062DC1}.Release|Win32.Build.0 = Release|Win32 + {D0EC862E-DDDD-4F4F-934F-B75DC9062DC1}.Release|x64.ActiveCfg = Release|x64 + {D0EC862E-DDDD-4F4F-934F-B75DC9062DC1}.Release|x64.Build.0 = Release|x64 + {255B68B5-7EF8-45EF-A675-2D6B88147909}.Debug|Win32.ActiveCfg = Debug|Win32 + {255B68B5-7EF8-45EF-A675-2D6B88147909}.Debug|Win32.Build.0 = Debug|Win32 + {255B68B5-7EF8-45EF-A675-2D6B88147909}.Debug|x64.ActiveCfg = Debug|x64 + {255B68B5-7EF8-45EF-A675-2D6B88147909}.Debug|x64.Build.0 = Debug|x64 + {255B68B5-7EF8-45EF-A675-2D6B88147909}.Release|Win32.ActiveCfg = Release|Win32 + {255B68B5-7EF8-45EF-A675-2D6B88147909}.Release|Win32.Build.0 = Release|Win32 + {255B68B5-7EF8-45EF-A675-2D6B88147909}.Release|x64.ActiveCfg = Release|x64 + {255B68B5-7EF8-45EF-A675-2D6B88147909}.Release|x64.Build.0 = Release|x64 + {2E31944B-64BE-4160-A1CF-7A31A923E204}.Debug|Win32.ActiveCfg = Debug|Win32 + {2E31944B-64BE-4160-A1CF-7A31A923E204}.Debug|Win32.Build.0 = Debug|Win32 + {2E31944B-64BE-4160-A1CF-7A31A923E204}.Debug|x64.ActiveCfg = Debug|x64 + {2E31944B-64BE-4160-A1CF-7A31A923E204}.Debug|x64.Build.0 = Debug|x64 + {2E31944B-64BE-4160-A1CF-7A31A923E204}.Release|Win32.ActiveCfg = Release|Win32 + {2E31944B-64BE-4160-A1CF-7A31A923E204}.Release|Win32.Build.0 = Release|Win32 + {2E31944B-64BE-4160-A1CF-7A31A923E204}.Release|x64.ActiveCfg = Release|x64 + {2E31944B-64BE-4160-A1CF-7A31A923E204}.Release|x64.Build.0 = Release|x64 + {3E0BFA8A-B86A-42E9-A33F-EC294F823F7F}.Debug|Win32.ActiveCfg = Debug|Win32 + {3E0BFA8A-B86A-42E9-A33F-EC294F823F7F}.Debug|Win32.Build.0 = Debug|Win32 + {3E0BFA8A-B86A-42E9-A33F-EC294F823F7F}.Debug|x64.ActiveCfg = Debug|x64 + {3E0BFA8A-B86A-42E9-A33F-EC294F823F7F}.Debug|x64.Build.0 = Debug|x64 + {3E0BFA8A-B86A-42E9-A33F-EC294F823F7F}.Release|Win32.ActiveCfg = Release|Win32 + {3E0BFA8A-B86A-42E9-A33F-EC294F823F7F}.Release|Win32.Build.0 = Release|Win32 + {3E0BFA8A-B86A-42E9-A33F-EC294F823F7F}.Release|x64.ActiveCfg = Release|x64 + {3E0BFA8A-B86A-42E9-A33F-EC294F823F7F}.Release|x64.Build.0 = Release|x64 + {DABE6307-F8DD-416D-9DAC-673E2DECB73F}.Debug|Win32.ActiveCfg = Debug|Win32 + {DABE6307-F8DD-416D-9DAC-673E2DECB73F}.Debug|Win32.Build.0 = Debug|Win32 + {DABE6307-F8DD-416D-9DAC-673E2DECB73F}.Debug|x64.ActiveCfg = Debug|x64 + {DABE6307-F8DD-416D-9DAC-673E2DECB73F}.Debug|x64.Build.0 = Debug|x64 + {DABE6307-F8DD-416D-9DAC-673E2DECB73F}.Release|Win32.ActiveCfg = Release|Win32 + {DABE6307-F8DD-416D-9DAC-673E2DECB73F}.Release|Win32.Build.0 = Release|Win32 + {DABE6307-F8DD-416D-9DAC-673E2DECB73F}.Release|x64.ActiveCfg = Release|x64 + {DABE6307-F8DD-416D-9DAC-673E2DECB73F}.Release|x64.Build.0 = Release|x64 + {78B079BD-9FC7-4B9E-B4A6-96DA0F00248B}.Debug|Win32.ActiveCfg = Debug|Win32 + {78B079BD-9FC7-4B9E-B4A6-96DA0F00248B}.Debug|Win32.Build.0 = Debug|Win32 + {78B079BD-9FC7-4B9E-B4A6-96DA0F00248B}.Debug|x64.ActiveCfg = Debug|x64 + {78B079BD-9FC7-4B9E-B4A6-96DA0F00248B}.Debug|x64.Build.0 = Debug|x64 + {78B079BD-9FC7-4B9E-B4A6-96DA0F00248B}.Release|Win32.ActiveCfg = Release|Win32 + {78B079BD-9FC7-4B9E-B4A6-96DA0F00248B}.Release|Win32.Build.0 = Release|Win32 + {78B079BD-9FC7-4B9E-B4A6-96DA0F00248B}.Release|x64.ActiveCfg = Release|x64 + {78B079BD-9FC7-4B9E-B4A6-96DA0F00248B}.Release|x64.Build.0 = Release|x64 + {3E4C3F3B-5D94-4691-AF6D-13C1E6F54501}.Debug|Win32.ActiveCfg = Debug|Win32 + {3E4C3F3B-5D94-4691-AF6D-13C1E6F54501}.Debug|Win32.Build.0 = Debug|Win32 + {3E4C3F3B-5D94-4691-AF6D-13C1E6F54501}.Debug|x64.ActiveCfg = Debug|x64 + {3E4C3F3B-5D94-4691-AF6D-13C1E6F54501}.Debug|x64.Build.0 = Debug|x64 + {3E4C3F3B-5D94-4691-AF6D-13C1E6F54501}.Release|Win32.ActiveCfg = Release|Win32 + {3E4C3F3B-5D94-4691-AF6D-13C1E6F54501}.Release|Win32.Build.0 = Release|Win32 + {3E4C3F3B-5D94-4691-AF6D-13C1E6F54501}.Release|x64.ActiveCfg = Release|x64 + {3E4C3F3B-5D94-4691-AF6D-13C1E6F54501}.Release|x64.Build.0 = Release|x64 + {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Debug|Win32.ActiveCfg = Debug|Win32 + {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Debug|Win32.Build.0 = Debug|Win32 + {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Debug|x64.ActiveCfg = Debug|x64 + {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Debug|x64.Build.0 = Debug|x64 + {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Release|Win32.ActiveCfg = Release|Win32 + {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Release|Win32.Build.0 = Release|Win32 + {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Release|x64.ActiveCfg = Release|x64 + {0F9730E4-45DA-4BD2-A50A-403A4BC9751A}.Release|x64.Build.0 = Release|x64 + {74EADF6F-F023-4D8C-B03A-5258B192A5E2}.Debug|Win32.ActiveCfg = Debug|Win32 + {74EADF6F-F023-4D8C-B03A-5258B192A5E2}.Debug|Win32.Build.0 = Debug|Win32 + {74EADF6F-F023-4D8C-B03A-5258B192A5E2}.Debug|x64.ActiveCfg = Debug|x64 + {74EADF6F-F023-4D8C-B03A-5258B192A5E2}.Debug|x64.Build.0 = Debug|x64 + {74EADF6F-F023-4D8C-B03A-5258B192A5E2}.Release|Win32.ActiveCfg = Release|Win32 + {74EADF6F-F023-4D8C-B03A-5258B192A5E2}.Release|Win32.Build.0 = Release|Win32 + {74EADF6F-F023-4D8C-B03A-5258B192A5E2}.Release|x64.ActiveCfg = Release|x64 + {74EADF6F-F023-4D8C-B03A-5258B192A5E2}.Release|x64.Build.0 = Release|x64 + {4F4B5925-2D0B-48DB-BBB0-D321B14EF621}.Debug|Win32.ActiveCfg = Debug|Win32 + {4F4B5925-2D0B-48DB-BBB0-D321B14EF621}.Debug|Win32.Build.0 = Debug|Win32 + {4F4B5925-2D0B-48DB-BBB0-D321B14EF621}.Debug|x64.ActiveCfg = Debug|x64 + {4F4B5925-2D0B-48DB-BBB0-D321B14EF621}.Debug|x64.Build.0 = Debug|x64 + {4F4B5925-2D0B-48DB-BBB0-D321B14EF621}.Release|Win32.ActiveCfg = Release|Win32 + {4F4B5925-2D0B-48DB-BBB0-D321B14EF621}.Release|Win32.Build.0 = Release|Win32 + {4F4B5925-2D0B-48DB-BBB0-D321B14EF621}.Release|x64.ActiveCfg = Release|x64 + {4F4B5925-2D0B-48DB-BBB0-D321B14EF621}.Release|x64.Build.0 = Release|x64 + {EBA98B5E-F516-47AD-9D97-F5923283C6D8}.Debug|Win32.ActiveCfg = Debug|Win32 + {EBA98B5E-F516-47AD-9D97-F5923283C6D8}.Debug|Win32.Build.0 = Debug|Win32 + {EBA98B5E-F516-47AD-9D97-F5923283C6D8}.Debug|x64.ActiveCfg = Debug|x64 + {EBA98B5E-F516-47AD-9D97-F5923283C6D8}.Debug|x64.Build.0 = Debug|x64 + {EBA98B5E-F516-47AD-9D97-F5923283C6D8}.Release|Win32.ActiveCfg = Release|Win32 + {EBA98B5E-F516-47AD-9D97-F5923283C6D8}.Release|Win32.Build.0 = Release|Win32 + {EBA98B5E-F516-47AD-9D97-F5923283C6D8}.Release|x64.ActiveCfg = Release|x64 + {EBA98B5E-F516-47AD-9D97-F5923283C6D8}.Release|x64.Build.0 = Release|x64 + {053B7CED-1A95-4473-8A58-409E93531692}.Debug|Win32.ActiveCfg = Debug|Win32 + {053B7CED-1A95-4473-8A58-409E93531692}.Debug|Win32.Build.0 = Debug|Win32 + {053B7CED-1A95-4473-8A58-409E93531692}.Debug|x64.ActiveCfg = Debug|x64 + {053B7CED-1A95-4473-8A58-409E93531692}.Debug|x64.Build.0 = Debug|x64 + {053B7CED-1A95-4473-8A58-409E93531692}.Release|Win32.ActiveCfg = Release|Win32 + {053B7CED-1A95-4473-8A58-409E93531692}.Release|Win32.Build.0 = Release|Win32 + {053B7CED-1A95-4473-8A58-409E93531692}.Release|x64.ActiveCfg = Release|x64 + {053B7CED-1A95-4473-8A58-409E93531692}.Release|x64.Build.0 = Release|x64 + {4FA2D01A-8932-45BA-9C54-E8247DD2CCAB}.Debug|Win32.ActiveCfg = Debug|Win32 + {4FA2D01A-8932-45BA-9C54-E8247DD2CCAB}.Debug|Win32.Build.0 = Debug|Win32 + {4FA2D01A-8932-45BA-9C54-E8247DD2CCAB}.Debug|x64.ActiveCfg = Debug|x64 + {4FA2D01A-8932-45BA-9C54-E8247DD2CCAB}.Debug|x64.Build.0 = Debug|x64 + {4FA2D01A-8932-45BA-9C54-E8247DD2CCAB}.Release|Win32.ActiveCfg = Release|Win32 + {4FA2D01A-8932-45BA-9C54-E8247DD2CCAB}.Release|Win32.Build.0 = Release|Win32 + {4FA2D01A-8932-45BA-9C54-E8247DD2CCAB}.Release|x64.ActiveCfg = Release|x64 + {4FA2D01A-8932-45BA-9C54-E8247DD2CCAB}.Release|x64.Build.0 = Release|x64 + {FA669B69-C4E6-48F1-B7BF-BB40B6DC0BC0}.Debug|Win32.ActiveCfg = Debug|Win32 + {FA669B69-C4E6-48F1-B7BF-BB40B6DC0BC0}.Debug|Win32.Build.0 = Debug|Win32 + {FA669B69-C4E6-48F1-B7BF-BB40B6DC0BC0}.Debug|x64.ActiveCfg = Debug|x64 + {FA669B69-C4E6-48F1-B7BF-BB40B6DC0BC0}.Debug|x64.Build.0 = Debug|x64 + {FA669B69-C4E6-48F1-B7BF-BB40B6DC0BC0}.Release|Win32.ActiveCfg = Release|Win32 + {FA669B69-C4E6-48F1-B7BF-BB40B6DC0BC0}.Release|Win32.Build.0 = Release|Win32 + {FA669B69-C4E6-48F1-B7BF-BB40B6DC0BC0}.Release|x64.ActiveCfg = Release|x64 + {FA669B69-C4E6-48F1-B7BF-BB40B6DC0BC0}.Release|x64.Build.0 = Release|x64 + {C7C45E25-5C76-4F59-AEBD-992CEC2A5C2E}.Debug|Win32.ActiveCfg = Debug|Win32 + {C7C45E25-5C76-4F59-AEBD-992CEC2A5C2E}.Debug|Win32.Build.0 = Debug|Win32 + {C7C45E25-5C76-4F59-AEBD-992CEC2A5C2E}.Debug|x64.ActiveCfg = Debug|x64 + {C7C45E25-5C76-4F59-AEBD-992CEC2A5C2E}.Debug|x64.Build.0 = Debug|x64 + {C7C45E25-5C76-4F59-AEBD-992CEC2A5C2E}.Release|Win32.ActiveCfg = Release|Win32 + {C7C45E25-5C76-4F59-AEBD-992CEC2A5C2E}.Release|Win32.Build.0 = Release|Win32 + {C7C45E25-5C76-4F59-AEBD-992CEC2A5C2E}.Release|x64.ActiveCfg = Release|x64 + {C7C45E25-5C76-4F59-AEBD-992CEC2A5C2E}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {5A4ED350-80C8-4501-87C2-26BBC67EAD28} + EndGlobalSection +EndGlobal diff --git a/Src/Plugins/General/gen_ff/gen_ff.vcxproj b/Src/Plugins/General/gen_ff/gen_ff.vcxproj new file mode 100644 index 00000000..84fd0c5e --- /dev/null +++ b/Src/Plugins/General/gen_ff/gen_ff.vcxproj @@ -0,0 +1,988 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {0A75D6C7-6F14-4032-BBCC-7A234E626A56} + gen_ff + 10.0.19041.0 + + + + DynamicLibrary + v142 + Unicode + + + DynamicLibrary + v142 + Unicode + + + DynamicLibrary + v142 + Unicode + + + DynamicLibrary + v142 + Unicode + + + + + + + + + + + + + + + + + + + false + $(PlatformShortName)_$(Configuration)\ + $(PlatformShortName)_$(Configuration)\ + $(IncludePath) + $(LibraryPath) + + + false + $(PlatformShortName)_$(Configuration)\ + $(PlatformShortName)_$(Configuration)\ + + + false + $(PlatformShortName)_$(Configuration)\ + $(PlatformShortName)_$(Configuration)\ + $(IncludePath) + $(LibraryPath) + true + + + false + $(PlatformShortName)_$(Configuration)\ + $(PlatformShortName)_$(Configuration)\ + + + false + + + + + false + Debug + x86-windows-static-md + + + + + false + x86-windows-static-md + + + + + false + x86-windows-static-md + Debug + + + ..\..\..\external_dependencies\vcpkg + true + + + + Disabled + .;..;..\..\..;..\..\..\Wasabi;%(AdditionalIncludeDirectories) + _WIN32_WINNT=0x0601;WINVER=0x0601;WIN32;_WINDOWS;_USRDLL;GEN_FF_EXPORTS;NOSVCMGR;_WIN32_IE=0x0A00;_CRT_SECURE_NO_WARNINGS;_DEBUG;%(PreprocessorDefinitions) + false + true + EnableFastChecks + MultiThreadedDebugDLL + true + true + $(IntDir) + $(IntDir) + $(IntDir)$(TargetName).pdb + true + Level3 + ProgramDatabase + Default + 4100;4127;4238;4244;4267;4302;4311;4312;4651;4838;4090;4996;%(DisableSpecificWarnings) + false + + + 0x0409 + _WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions) + + + shlwapi.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)$(TargetExt) + tataki.dll;%(DelayLoadDLLs) + true + $(IntDir)$(TargetName).pdb + false + $(IntDir)$(TargetName).lib + MachineX86 + false + %(AdditionalLibraryDirectories) + Windows + + + xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ +xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ + Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\' + + + + + Disabled + .;..;..\..\..;..\..\..\Wasabi;%(AdditionalIncludeDirectories) + _WIN32_WINNT=0x0601;WINVER=0x0601;WIN64;_DEBUG;_WINDOWS;_USRDLL;GEN_FF_EXPORTS;NOSVCMGR;_WIN32_IE=0x0A00;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + false + true + EnableFastChecks + MultiThreadedDebugDLL + true + true + $(IntDir) + $(IntDir) + $(IntDir)$(TargetName).pdb + true + Level3 + ProgramDatabase + 4100;4127;4238;4244;4267;4302;4311;4312;4651;4838;4090;4996;%(DisableSpecificWarnings) + false + + + 0x0409 + _WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions) + + + shlwapi.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)$(TargetExt) + tataki.dll;%(DelayLoadDLLs) + true + $(IntDir)$(TargetName).pdb + false + $(IntDir)$(TargetName).lib + false + %(AdditionalLibraryDirectories) + Windows + + + xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ +xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ + Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\' + + + + + MinSpace + Size + .;..;..\..\..;..\..\..\Wasabi;%(AdditionalIncludeDirectories) + _WIN32_WINNT=0x0601;WINVER=0x0601;WIN32;_WINDOWS;_USRDLL;GEN_FF_EXPORTS;NOSVCMGR;_WIN32_IE=0x0A00;_CRT_SECURE_NO_WARNINGS;NDEBUG;%(PreprocessorDefinitions) + true + true + MultiThreadedDLL + true + $(IntDir) + $(IntDir) + $(IntDir)$(TargetName).pdb + Level3 + None + 4100;4127;4238;4244;4267;4302;4311;4312;4651;4838;4090;4996;%(DisableSpecificWarnings) + true + + + 0x0409 + _WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions) + + + shlwapi.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)$(TargetExt) + tataki.dll;%(DelayLoadDLLs) + false + $(IntDir)$(TargetName).pdb + true + true + true + $(IntDir)$(TargetName).lib + MachineX86 + false + %(AdditionalLibraryDirectories) + Windows + + + xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ + Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\' + + + + + MinSpace + Size + .;..;..\..\..;..\..\..\Wasabi;%(AdditionalIncludeDirectories) + _WIN32_WINNT=0x0601;WINVER=0x0601;WIN64;NDEBUG;_WINDOWS;_USRDLL;GEN_ff_EXPORTS;NOSVCMGR;_WIN32_IE=0x0A00;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + true + true + MultiThreadedDLL + true + $(IntDir) + $(IntDir) + $(IntDir)$(TargetName).pdb + Level3 + None + 4100;4127;4238;4244;4267;4302;4311;4312;4651;4838;4090;4996;%(DisableSpecificWarnings) + true + + + 0x0409 + _WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions) + + + shlwapi.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)$(TargetExt) + tataki.dll;%(DelayLoadDLLs) + false + $(IntDir)$(TargetName).pdb + true + true + true + $(IntDir)$(TargetName).lib + false + %(AdditionalLibraryDirectories) + Windows + + + xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ + Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\' + + + + + {efb9b882-6a8b-463d-a8e3-a2807afc5d9f} + + + {255b68b5-7ef8-45ef-a675-2d6b88147909} + true + true + + + {d0ec862e-dddd-4f4f-934f-b75dc9062dc1} + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + $(IntDir)$(TargetName)%(Filename)1.obj + $(IntDir)$(TargetName)%(Filename)1.obj + $(IntDir)$(TargetName)%(Filename)1.obj + $(IntDir)$(TargetName)%(Filename)1.obj + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS + WIN64;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS + + + WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS + WIN64;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS + + + WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS + WIN64;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS + + + WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS + WIN64;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS + WIN32;_NDEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS + WIN64;_NDEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS + + + + WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS + WIN64;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS + + + + WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS + WIN64;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS + + + + + + + + WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS + WIN64;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS + + + WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS + WIN64;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS + + + WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS + WIN64;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS + + + WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS + WIN64;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS + + + WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS + WIN64;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS + + + WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS + WIN64;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS + + + WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS + WIN64;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS + + + WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS + WIN64;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS + + + WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS + WIN64;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS + + + WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS + WIN64;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS + + + WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS + WIN64;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS + + + + WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS + WIN64;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS + + + + + + + true + true + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + true + true + true + + + true + true + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Src/Plugins/General/gen_ff/gen_ff.vcxproj.filters b/Src/Plugins/General/gen_ff/gen_ff.vcxproj.filters new file mode 100644 index 00000000..149de5c8 --- /dev/null +++ b/Src/Plugins/General/gen_ff/gen_ff.vcxproj.filters @@ -0,0 +1,2074 @@ + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files\Wasabi\api\wnd\wndclass + + + Source Files\Wasabi\api\wndmgr + + + Source Files\Wasabi\api\wndmgr + + + Source Files\Wasabi\api\skin\widgets + + + Source Files\Wasabi\api\config + + + Source Files\Wasabi\api\config + + + Source Files\Wasabi\api\config + + + Source Files\Wasabi\api\core + + + Source Files\Wasabi\api\dependency + + + Source Files\Wasabi\api\dependency + + + Source Files\Wasabi\api\filereader + + + Source Files\Wasabi\api\imgldr + + + Source Files\Wasabi\api\locales + + + Source Files\Wasabi\api\locales + + + Source Files\Wasabi\api\locales + + + Source Files\Wasabi\api\script + + + Source Files\Wasabi\api\script\debugger + + + Source Files\Wasabi\api\script + + + Source Files\Wasabi\api\script + + + Source Files\Wasabi\api\skin + + + Source Files\Wasabi\api\service + + + Source Files\Wasabi\api\skin + + + Source Files\Wasabi\api\syscb + + + Source Files\Wasabi\api\skin\feeds + + + Source Files\Wasabi\api\wnd + + + Source Files\Wasabi\api\wnd + + + Source Files\Wasabi\api\wndmgr + + + Source Files\Wasabi\api\wnd\wndclass + + + Source Files\Wasabi\api\wndmgr + + + Source Files\Wasabi\api\config\items + + + Source Files\Wasabi\api\config\items + + + Source Files\Wasabi\api\wndmgr + + + Source Files\Wasabi\api\wnd + + + Source Files\Wasabi\api\font + + + Source Files\Wasabi\api\wnd\wndclass + + + Source Files\Wasabi\api\syscb\callbacks + + + Source Files\Wasabi\api\wnd\wndclass + + + Source Files\Wasabi\api\wnd\wndclass + + + Source Files\Wasabi\api\skin\widgets + + + Source Files\Wasabi\api\wnd\wndclass + + + Source Files\Wasabi\api\script\objects\c_script + + + Source Files\Wasabi\api\script\objects\c_script + + + Source Files\Wasabi\api\script\objects\c_script + + + Source Files\Wasabi\api\script\objects\c_script + + + Source Files\Wasabi\api\script\objects\c_script + + + Source Files\Wasabi\api\script\objects\c_script + + + Source Files\Wasabi\api\script\objects\c_script + + + Source Files\Wasabi\api\script\objects\c_script + + + Source Files\Wasabi\api\script\objects\c_script + + + Source Files\Wasabi\api\script\objects\c_script + + + Source Files\Wasabi\api\script\objects\c_script + + + Source Files\Wasabi\api\script\objects\c_script + + + Source Files\Wasabi\api\script\objects\c_script + + + Source Files\Wasabi\api\script\objects\c_script + + + Source Files\Wasabi\api\script\objects\c_script + + + Source Files\Wasabi\api\script\objects\c_script + + + Source Files\Wasabi\api\script\objects\c_script + + + Source Files\Wasabi\api\script\objects\c_script + + + Source Files\Wasabi\api\config\items + + + Source Files\Wasabi\api\config\items + + + Source Files\Wasabi\api + + + Source Files\Wasabi\api\skin\widgets + + + Source Files\Wasabi\api\script\objects + + + Source Files\Wasabi\api\script\objects + + + Source Files\Wasabi\api\script\objects + + + Source Files\Wasabi\api\skin\widgets + + + Source Files\Wasabi\api\skin\widgets + + + Source Files\Wasabi\api\skin\widgets\stats + + + Source Files\Wasabi\api\wnd\wndclass + + + Source Files\Wasabi\bfc + + + Source Files\Wasabi\api\service\svcs + + + Source Files\Wasabi\api\service + + + Source Files\Wasabi\api\service\svcs + + + Source Files\Wasabi\api\service\svcs + + + Source Files\Wasabi\api\service\svcs + + + Source Files\Wasabi\api\service\svcs + + + Source Files\Wasabi\api\service\svcs + + + Source Files\Wasabi\api\script\objects\core + + + Source Files\Wasabi\api\service\svcs + + + Source Files\Wasabi\api\service\svcs + + + Source Files\Wasabi\api\service\svcs + + + Source Files\Wasabi\api\service\svcs + + + Source Files\Wasabi\api\service\svcs + + + Source Files\Wasabi\api\service\svcs + + + Source Files\Wasabi\api\service + + + Source Files\Wasabi\api\service + + + Source Files\Wasabi\api\service + + + Source Files\Wasabi\api\service + + + Source Files\Wasabi\api\wnd\wndclass + + + Source Files\Wasabi\api\skin\widgets + + + Source Files\Wasabi\api\script\objects + + + Source Files\Wasabi\api\syscb\callbacks + + + Source Files\Wasabi\api\syscb\callbacks + + + Source Files\Wasabi\api\syscb\callbacks + + + Source Files\Wasabi\api\script\objects + + + Source Files\Wasabi\api\util + + + Source Files\Wasabi\api\wnd\wndclass + + + Source Files\Wasabi\api\wnd\wndclass + + + Source Files\Wasabi\api\skin\widgets + + + Source Files\Wasabi\api\skin\widgets + + + Source Files\Wasabi\api\skin\feeds + + + Source Files\Wasabi\api\skin\widgets + + + Source Files\Wasabi\bfc\util + + + Source Files\Wasabi\api\timer + + + Source Files\Wasabi\api\skin\widgets + + + Source Files\Wasabi\api\skin\widgets + + + Source Files\Wasabi\api\wnd\wndclass + + + Source Files\Wasabi\api\wnd\wndclass + + + Source Files\Wasabi\api\font\win32 + + + Source Files\Wasabi\api\wnd\wndclass + + + Source Files\Wasabi\api\config + + + Source Files\Wasabi\api\util + + + Source Files\Wasabi\api\script + + + Source Files\Wasabi\api\script\debugger + + + Source Files\Wasabi\api\wnd + + + Source Files\Wasabi\api\script\objects + + + Source Files\Wasabi\api\service + + + Source Files\Wasabi\api\service + + + Source Files\Wasabi\api\service + + + Source Files\Wasabi\api\service + + + Source Files\Wasabi\api\service + + + Source Files\Wasabi\api\service + + + Source Files\Wasabi\api\skin + + + Source Files\Wasabi\api\wnd + + + Source Files\Wasabi\api\wnd\wndclass + + + Source Files\Wasabi\api\wndmgr + + + Source Files\Wasabi\api\wnd + + + Source Files\Wasabi\api\xml + + + Source Files\Wasabi\api\xml + + + Source Files\Wasabi\api\xml + + + Source Files\Wasabi\api\xml + + + Source Files\Wasabi\api\xml + + + Source Files\Wasabi\api\xml + + + Source Files\Wasabi\api\skin\widgets + + + Source Files\Wasabi\api\skin\widgets + + + Source Files\Wasabi\api\skin\widgets\mb + + + Source Files\Wasabi\api\skin\widgets + + + Source Files\Wasabi\api\skin\widgets + + + Source Files\Wasabi\api\skin\widgets + + + Source Files\Wasabi\api\skin\widgets + + + Source Files\Wasabi\api\skin\widgets + + + Source Files\Wasabi\api\skin\widgets + + + Source Files\Wasabi\api\skin\widgets + + + Source Files\Wasabi\api\skin\widgets + + + Source Files\Wasabi\api\skin\widgets + + + Source Files\Wasabi\api\skin\widgets + + + Source Files\Wasabi\api\skin\widgets + + + Source Files\Wasabi\api\skin\widgets + + + Source Files\Wasabi\api\skin\widgets + + + Source Files\Wasabi\api\skin\widgets + + + Source Files\Wasabi\api\skin\widgets + + + Source Files\Wasabi\api\skin\widgets + + + Source Files\Wasabi\api\skin\widgets + + + Source Files\Wasabi\api\skin\widgets + + + Source Files\Wasabi\api\skin\widgets + + + Source Files\Wasabi\api\skin\widgets + + + Source Files\Wasabi\api\skin\widgets + + + Source Files\Wasabi\api\skin\widgets + + + Source Files\Wasabi\api\skin\widgets\stats + + + Source Files\Wasabi\api\skin\widgets + + + Source Files\Wasabi\api\skin\widgets + + + Source Files\Wasabi\api\skin\widgets + + + Source Files\Wasabi\api\skin\widgets + + + Source Files\Wasabi\api\skin\widgets + + + Source Files\Wasabi\api\skin\widgets\wa2 + + + Source Files\Wasabi\api\skin\widgets + + + Source Files\Wasabi\api\config + + + Source Files\Wasabi\api\config + + + Source Files\Wasabi\api\skin\widgets + + + Source Files\Wasabi\api\wnd\wndclass + + + Source Files\Wasabi\api\skin\widgets + + + Source Files\Wasabi\api\skin\widgets + + + Source Files\Wasabi\api\wac + + + Source Files\Wasabi\api\config + + + Source Files\Wasabi\api\wndmgr + + + Source Files\Wasabi\api\wnd + + + Source Files\Wasabi\api\script\objects\core + + + Source Files\Wasabi\api\syscb\callbacks + + + Source Files\Wasabi\api\script\objects\core + + + Source Files\Wasabi\api\wnd + + + Source Files\Wasabi\api\skin + + + Source Files\Wasabi\api\skin\widgets + + + Source Files\Wasabi\api\wnd + + + Source Files\Wasabi\api\script\debugger + + + Source Files\Wasabi\api\script\debugger + + + Source Files\Wasabi\api\script\debugger + + + Source Files\Wasabi\bfc + + + Source Files\Wasabi\bfc + + + Source Files\Wasabi\api\wnd + + + Source Files\Wasabi\api\script\debugger + + + Source Files\Wasabi\api\wnd + + + Source Files\Wasabi\bfc\draw + + + Source Files\Wasabi\api\skin\widgets + + + Source Files\Wasabi\api\skin\widgets + + + Source Files\Wasabi\api\wnd\wndclass + + + Source Files\Wasabi\api\wnd\wndclass + + + Source Files\Wasabi\api\wnd\wndclass + + + Source Files\Wasabi\api\skin\feeds + + + Source Files\Wasabi\api\skin\feeds + + + Source Files\Wasabi\api\filereader\local + + + Source Files\Wasabi\api\filereader + + + Source Files\Wasabi\api\wnd + + + Source Files\Wasabi\bfc\util + + + Source Files\Wasabi\api\font + + + Source Files\Wasabi\api\font + + + Source Files\Wasabi\api\wnd\wndclass + + + Source Files\Wasabi\api\wnd\wndclass + + + Source Files\Wasabi\api\skin\widgets + + + Source Files\Wasabi\api\skin + + + Source Files\Wasabi\api\wndmgr + + + Source Files\Wasabi\api\imgldr\imggen + + + Source Files\Wasabi\bfc\draw + + + Source Files\Wasabi\api\wnd\wndclass + + + Source Files\Wasabi\api\skin\widgets + + + Source Files\Wasabi\api\skin\widgets + + + Source Files\Wasabi\api\skin\widgets + + + Source Files\Wasabi\api\skin + + + Source Files\Wasabi\api\skin\widgets + + + Source Files\Wasabi\api\skin\widgets + + + Source Files\Wasabi\api\skin + + + Source Files\Wasabi\api\script\objects + + + Source Files\Wasabi\api\script\objects + + + Source Files\Wasabi\api\wnd\wndclass + + + Source Files\Wasabi\api\skin\widgets + + + Source Files\Wasabi\api\wndmgr + + + Source Files\Wasabi\api\skin + + + Source Files\Wasabi\api\script + + + Source Files\Wasabi\api\script\objects\c_script + + + Source Files\Wasabi\api\script\objects\c_script + + + Source Files\Wasabi\api\script\objects\c_script + + + Source Files\Wasabi\api\script\objects\c_script + + + Source Files\Wasabi\api\script\objects\c_script + + + Source Files\Wasabi\api\script\objects\c_script + + + Source Files\Wasabi\api\script\objects\c_script + + + Source Files\Wasabi\api\script\objects\c_script + + + Source Files\Wasabi\api\script\objects\c_script + + + Source Files\Wasabi\api\script\objects\c_script + + + Source Files\Wasabi\api\script\objects\c_script + + + Source Files\Wasabi\api\script\objects\c_script + + + Source Files\Wasabi\api\script\objects\c_script + + + Source Files\Wasabi\api\script\objects\c_script + + + Source Files\Wasabi\api\script\objects\c_script + + + Source Files\Wasabi\api\script\objects\c_script + + + Source Files\Wasabi\api\script\objects\c_script + + + Source Files\Wasabi\api\script\objects\c_script + + + Source Files\Wasabi\api\skin\widgets + + + Source Files\nu + + + Source Files\Wasabi\api\skin\widgets\mb + + + Source Files\Wasabi\api\application + + + Source Files\Wasabi\api\imgldr + + + Source Files\Wasabi\api\imgldr + + + Source Files\Wasabi\bfc\util + + + Source Files\Wasabi\api\config\items + + + Source Files\Wasabi\api\script\debugger + + + Source Files\Wasabi\api\script\debugger + + + Source Files\Wasabi\api\wnd + + + Source Files\Wasabi\api\wnd\wndclass + + + Source Files\Wasabi\api\skin\widgets + + + Source Files\Wasabi\api\wndmgr + + + Source Files\nu + + + Source Files\Wasabi\api\wnd\wndclass + + + Source Files\Wasabi\api\xml + + + Source Files\Wasabi\api\skin\widgets\mb + + + Source Files\Wasabi\api\skin\widgets\mb + + + Source Files\Wasabi\api\skin\widgets\mb + + + Source Files\Wasabi\api\skin\widgets\mb + + + Source Files\Wasabi\api\skin\widgets + + + Source Files\Wasabi\api\wndmgr + + + Source Files\Wasabi\api\skin + + + Source Files\Wasabi\api\script + + + Source Files\Wasabi\api\script + + + Source Files\Wasabi\api\skin\widgets + + + Source Files\Wasabi\api\skin + + + Source Files\Wasabi\api\script + + + Source Files\Wasabi\api\config + + + Source Files\Wasabi\api\imgldr\imggen + + + Source Files\Wasabi\api\wnd\wndclass + + + Source Files\Wasabi\api\wnd\wndclass + + + Source Files\Wasabi\api\wnd + + + Source Files\Wasabi\api\skin\widgets + + + Source Files\Wasabi\api\syscb\callbacks + + + Source Files\Wasabi\api\script\objects + + + Source Files\Wasabi\api\imgldr\imggen + + + Source Files\Wasabi\api\wnd + + + Source Files\Wasabi\api\wnd + + + Source Files\Wasabi\api\wnd + + + Source Files\Wasabi\api\skin\widgets + + + Source Files\Wasabi\api\wnd\wndclass + + + Source Files\nu + + + Source Files\Wasabi\api\skin + + + Source Files\Wasabi\api\wndmgr + + + Source Files\Wasabi\api\script\objects + + + Source Files\Wasabi\api\script\objects + + + Source Files\Wasabi\api\script\objects + + + Source Files\Wasabi\api\script\objects + + + Source Files\Wasabi\api\script\objects + + + Source Files\Wasabi\api\script\objects + + + Source Files\Wasabi\api\script\objects + + + Source Files\Wasabi\api\script\objects + + + Source Files\Wasabi\api\wnd + + + Source Files\Wasabi\api\wnd\wndclass + + + Source Files\Wasabi\api\skin\widgets + + + Source Files\Wasabi\api\script\objects + + + Source Files\Wasabi\api\script\objects + + + Source Files\Wasabi\api\wnd\wndclass + + + Source Files\Wasabi\api\script\objects + + + Source Files\Wasabi\api\script\objects + + + Source Files\Wasabi\api\locales + + + Source Files\Wasabi\api\script + + + Source Files\Wasabi\api\script\objects\c_script + + + Source Files\Wasabi\api\script + + + Source Files\Wasabi\api\script + + + Source Files\Wasabi\api\script + + + Source Files\Wasabi\api\script + + + Source Files\Wasabi\api\wnd\wndclass + + + Source Files\Wasabi\api\script\debugger + + + Source Files\Wasabi\api\util + + + Source Files\Wasabi\api\wnd\wndclass + + + Source Files\Wasabi\api\wnd\wndclass + + + Source Files\Wasabi\api\skin\widgets + + + Source Files\Wasabi\api\skin\widgets + + + Source Files\Wasabi\api\skin\widgets + + + Source Files\Wasabi\api\service + + + Source Files\Wasabi\api\script\objects + + + Source Files\Wasabi\api\script\objects + + + Source Files\Wasabi\api\script\objects + + + Source Files\Wasabi\api\skin + + + Source Files\Wasabi\api\skin + + + Source Files\Wasabi\api\syscb\callbacks + + + Source Files\Wasabi\api\skin + + + Source Files\Wasabi\api\wndmgr + + + Source Files\Wasabi\api\skin + + + Source Files\Wasabi\api\skin + + + Source Files\Wasabi\api\skin + + + Source Files\Wasabi\api\skin + + + Source Files\Wasabi\api\skin + + + Source Files\Wasabi\api\wndmgr + + + Source Files\Wasabi\api\wnd\wndclass + + + Source Files\Wasabi\api\script\objects + + + Source Files\Wasabi\api\script\objects + + + Source Files\Wasabi\api\wndmgr + + + Source Files\Wasabi\api\imgldr\imggen + + + Source Files\gen_ml + + + Source Files\Wasabi\api\script\debugger + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files\Wasabi\api\wnd\wndclass + + + Header Files\Wasabi\api\wndmgr + + + Header Files\Wasabi\api\wndmgr + + + Header Files\Wasabi\api\config + + + Header Files\Wasabi\api\config + + + Header Files\Wasabi\api\config + + + Header Files\Wasabi\api\font + + + Header Files\Wasabi\api\filereader + + + Header Files\Wasabi\api\skin\feeds + + + Header Files\Wasabi\api\timer + + + Header Files\Wasabi\api\wndmgr + + + Header Files\Wasabi\api\wnd\wndclass + + + Header Files\Wasabi\api\wndmgr + + + Header Files\Wasabi\api\wndmgr + + + Header Files\Wasabi\api\font + + + Header Files\Wasabi\api\wnd\wndclass + + + Header Files\Wasabi\api\syscb\callbacks + + + Header Files\Wasabi\api\syscb\callbacks + + + Header Files\Wasabi\api\wnd\wndclass + + + Header Files\Wasabi\api\skin\widgets + + + Header Files\Wasabi\api\wnd\wndclass + + + Header Files\Wasabi\api\config + + + Header Files\Wasabi\api\skin\widgets + + + Header Files\Wasabi\api\skin\widgets + + + Header Files\Wasabi\api\config + + + Header Files\Wasabi\api\wndmgr + + + Header Files\Wasabi\api\skin\widgets + + + Header Files\Wasabi\api\skin\widgets + + + Header Files\Wasabi\api\wnd\wndclass + + + Header Files\Wasabi\api\skin\feeds + + + Header Files\Wasabi\api\filereader + + + Header Files\Wasabi\api\font + + + Header Files\Wasabi\api\font + + + Header Files\Wasabi\api\wnd\wndclass + + + Header Files\Wasabi\api\wndmgr + + + Header Files\Wasabi\api\wnd\wndclass + + + Header Files\Wasabi\api\skin\widgets + + + Header Files\Wasabi\api\skin\widgets + + + Header Files\Wasabi\api\skin\widgets + + + Header Files\Wasabi\api\wnd\wndclass + + + Header Files\Wasabi\api\skin\widgets + + + Header Files + + + Header Files\Wasabi\api\skin\widgets + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files\Wasabi\api\wnd\wndclass + + + Header Files\Wasabi\api\skin\widgets + + + Header Files\Wasabi\api\wndmgr + + + Header Files\Wasabi\api\wnd\wndclass + + + Header Files + + + Header Files\Wasabi\api\syscb\callbacks + + + Header Files + + + Header Files + + + Header Files\Wasabi\api\skin\widgets + + + Header Files\Wasabi\api\wndmgr + + + Header Files\Wasabi\api\skin\widgets + + + Header Files\Wasabi\api\syscb\callbacks + + + Header Files + + + Header Files\Wasabi\api\skin\widgets + + + Header Files\Wasabi\api\wndmgr + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files\Wasabi\api\wnd\wndclass + + + Header Files + + + Header Files\Wasabi\api\wnd\wndclass + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files\Wasabi\api\wnd\wndclass + + + Header Files\Wasabi\api\skin\widgets + + + Header Files\Wasabi\api\skin\widgets + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files\Wasabi\api\wndmgr + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files\Wasabi\api\wndmgr + + + Header Files\Wasabi\api\wndmgr + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files\Wasabi\api\filereader + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files\Wasabi\api\syscb\callbacks + + + Header Files + + + Header Files\Wasabi\api\wnd\wndclass + + + Header Files + + + Header Files\Wasabi\api\syscb\callbacks + + + Header Files + + + Header Files\Wasabi\api\wnd\wndclass + + + Header Files\Wasabi\api\wnd\wndclass + + + Header Files\Wasabi\api\skin\widgets + + + Header Files\Wasabi\api\skin\widgets + + + Header Files\Wasabi\api\skin\feeds + + + Header Files + + + Header Files\Wasabi\api\timer + + + Header Files\Wasabi\api\timer + + + Header Files\Wasabi\api\timer + + + Header Files\Wasabi\api\wnd\wndclass + + + Header Files\Wasabi\api\font\win32 + + + Header Files\Wasabi\api\config + + + Header Files + + + Header Files + + + Header Files\Wasabi\api\wnd\wndclass + + + Header Files + + + Header Files + + + Header Files\Wasabi\api\syscb\callbacks + + + Header Files\Wasabi\api\wnd\wndclass + + + Header Files\Wasabi\api\wndmgr + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files\Wasabi\api\skin\widgets + + + Header Files\Wasabi\api\skin\widgets + + + Header Files\Wasabi\api\skin\widgets + + + Header Files\Wasabi\api\skin\widgets + + + Header Files\Wasabi\api\skin\widgets + + + Header Files\Wasabi\api\skin\widgets + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files\Wasabi\api\skin + + + Header Files\Wasabi\api\core + + + Header Files\Wasabi\api\dependency + + + Header Files\Wasabi\api\dependency + + + Header Files\Wasabi\api\filereader + + + Header Files\Wasabi\api\imgldr + + + Header Files\Wasabi\api\locales + + + Header Files\Wasabi\api\locales + + + Header Files\Wasabi\api\locales + + + Header Files\Wasabi\api\script + + + Header Files\Wasabi\api\script + + + Header Files\Wasabi\api\script + + + Header Files\Wasabi\api\skin + + + Header Files\Wasabi\api\skin + + + Header Files\Wasabi\api\wnd + + + Header Files\Wasabi\api\wnd + + + Header Files\Wasabi\api + + + Header Files\Wasabi\api\config\items + + + Header Files\Wasabi\api\config\items + + + Header Files\Wasabi\api\wnd + + + Header Files\Wasabi\api\wnd\platform\win32 + + + Header Files\Wasabi\api\config\items + + + Header Files\Wasabi\api\config\items + + + Header Files\Wasabi\api\config\items + + + Header Files\Wasabi\api\wac + + + Header Files\Wasabi\api\wnd + + + Header Files\Wasabi\api\core + + + Header Files\Wasabi\api\script\objects\core + + + Header Files\Wasabi\api\script\objects\core + + + Header Files\Wasabi\api\wnd + + + Header Files\Wasabi\api\skin + + + Header Files\Wasabi\api\wnd + + + Header Files\Wasabi\api\script\debugger + + + Header Files\Wasabi\bfc + + + Header Files\Wasabi\bfc + + + Header Files\Wasabi\api\wnd + + + Header Files\Wasabi\api\wnd + + + Header Files\Wasabi\api\wnd + + + Header Files\Wasabi\api\filereader\local + + + Header Files\Wasabi\api\wnd + + + Header Files\Wasabi\bfc\util + + + Header Files\Wasabi\api\skin + + + Header Files\Wasabi\api\imgldr\imggen + + + Header Files\Wasabi\api\skin + + + Header Files\Wasabi\api\skin + + + Header Files\Wasabi\api\skin + + + Header Files\Wasabi\api\script\objects + + + Header Files\Wasabi\api\script\objects + + + Header Files\Wasabi\api\script\objects + + + Header Files\Wasabi\api\skin + + + Header Files\Wasabi\api\skin\widgets\mb + + + + + Header Files + + + Header Files + + + Header Files\Winamp + + + + + {bf65d618-5e45-4f2a-8125-af87835711e2} + + + {bbaa79ab-c1cc-4e73-b671-e9532c2313cf} + + + {076c0d6d-9764-48af-83ce-e1e298abadcc} + + + {3ddc66e2-c10a-44eb-846f-dde2fd579af9} + + + {ed9da2cf-84b0-4883-8183-5756443f1d45} + + + {428efb3d-499b-4538-b225-59995d0a4ad8} + + + {711418c8-1bf4-487d-8e5e-55bc545352a3} + + + {a935b145-a8e6-4fb1-b6d3-2a7d252c9a51} + + + {71c5c868-2d6d-405c-8cb7-76423e1d7c52} + + + {430dcbf9-48d9-4b4a-ab86-d7d4d6a6d60b} + + + {6d228362-30a9-4c00-a03e-2a09d669b992} + + + {83e1c33a-cf4b-4207-ac1c-87b8755d4ad7} + + + {8520b576-ce2c-469c-86fa-14d0f9763eca} + + + {f2411e13-0f08-4b12-a167-2f4aafb392a3} + + + {cf9bb71c-7c6e-4425-b0a4-0f5a534db77f} + + + {d148307d-f103-4179-b419-39c6d223d3b1} + + + {a47af283-0ae4-4855-9719-db7403be60f8} + + + {c2af4389-c666-45af-9218-86f2adccdcdb} + + + {117e60d7-5011-40d3-b3ec-abdceaa4c72e} + + + {ebddb0e8-052a-41d9-8506-f61752e88cfa} + + + {034c19ed-376c-4107-93e5-ba0861c07235} + + + {55d69045-d7bf-4a69-b84a-7538dfe6baed} + + + {f448e746-ba3c-42bb-a0c1-f7e93508834f} + + + {2372ff4c-f72f-43de-a65d-ba9f537b2913} + + + {a657e19a-8af2-4f0b-955c-9ccbae2e77d8} + + + {1f90661a-bc0a-441b-b342-2d6eedf9fb94} + + + {0ae62a5f-d0c5-4ff6-89ca-ab170103c2bc} + + + {5ab5bf42-9891-4d5f-a9ff-9ece8a4270c2} + + + {3d2e3e56-48d8-430f-94de-9b8a57dfe4c9} + + + {93fe5f86-d0f9-4c43-8e81-cddae4227244} + + + {584eed9a-47e5-4c37-8bca-a95d75c29f62} + + + {bffcb066-b578-490f-aaf9-fbf8aef6d4c4} + + + {dd59272c-3349-40ef-b44b-7347bbf4392d} + + + {15a4a353-d120-484d-a13a-6ba08eb013b8} + + + {c56b1f21-e4b6-4135-b990-c5530dd83d03} + + + {b9f8ab37-1686-49b1-b338-ba9dd90c6886} + + + {339b1b9e-0d18-4483-a452-2d7dac4e9774} + + + {c9c62206-3924-43e8-b900-a52671601a0e} + + + {f7c32081-1c3e-4652-8fdc-999024d3abab} + + + {92bac19f-25cb-4864-ad12-9454ca805370} + + + {bfa00cac-2671-4338-a525-dc2a7a3cb835} + + + {e4c9b2bb-dd6a-4bc2-ad09-ca5df8a0f6b8} + + + {5b8e1124-eb86-4fdb-80b1-e62a9038d935} + + + {d3a01f56-464b-4976-813a-80569bcdac9b} + + + {e8d5e893-f323-4825-991f-51c02ed78ecd} + + + {e0ab39aa-1910-4819-a602-0fdd17d66456} + + + {6ef901fe-7701-4453-b9c4-d7472c0de173} + + + {ff1b723c-3fc3-46c9-8cef-2cc3425816c7} + + + {a415d0c3-a60e-464c-81cc-706cb07d84d9} + + + {2a37a25a-a795-41d6-b223-ab458cea036f} + + + {29e11ac1-6f08-4aaa-a395-e3848eb70331} + + + {5e435b28-0443-4721-8b76-88aa15f8328f} + + + {1e142ae9-c0c9-48cc-9c21-ff3aa423aa04} + + + {39f17d69-60a7-4065-b4a4-7c29811564e7} + + + {8d268021-ee46-4bee-9f81-7c6abfc875a4} + + + {e86e6bdd-0733-456f-9887-0255316e7467} + + + {781a8fb4-cac7-4473-a69c-1756a64cfd49} + + + {c861b738-eb90-48e4-968a-772d679f3cc6} + + + {b0ee89d7-6347-4058-8480-871ff274503e} + + + {aee2537a-d56d-463e-9f3d-0cb76daf7c94} + + + {763bb103-bdcd-4a05-be99-8424a41baedc} + + + {590fffe6-e7cb-4003-881e-cb8e3de02a0e} + + + {94de1687-3b46-432c-b0db-2f3d91b37c79} + + + {24e3748f-b780-4f9b-b819-c3d885bd2794} + + + {aec2a760-6241-4542-9d9d-fd2c87cb86bf} + + + {76ae14a7-78a1-4352-873a-7ec4c1673336} + + + {13b72f8c-f0f6-4cdc-a8ae-a9dfee4b3b84} + + + {8d9f0dc3-353d-4ea1-999b-d0c58e5d8237} + + + {44c64431-48ef-4126-865d-e7eedbe2d502} + + + {7ecf8a6f-c16e-4ab5-a085-15df6e952981} + + + {94eb27c8-6d80-4b08-8f0e-36c68aa47651} + + + {6bbae227-6fd3-40d9-8e81-64146c137948} + + + {56124eba-f9ec-4c7b-ab97-d0d84b0e32ed} + + + {6f852919-472d-458b-8ac5-5f8d41657372} + + + {40ebbca9-2cf3-41fe-bf4b-7265a5d488ca} + + + {6b36cef2-2356-41af-b817-f508f96d78d5} + + + {0291d685-9a9b-485b-9aae-744d4afbde70} + + + {1edcd4ab-1ef7-48fb-a49c-35b3a9a3be5c} + + + {a80e512f-2eda-4ae3-978f-9030b568f8da} + + + {19b8ce8d-6a1f-4eca-8e27-4e84a9be5014} + + + {fb9b1379-7d7c-4066-a819-80a2e55dd0d6} + + + {a9dc66ed-6c31-4fae-a36c-9743ba4a33a3} + + + {70d07248-75c9-493f-b244-fe2e839114d8} + + + + + Ressource Files + + + \ No newline at end of file diff --git a/Src/Plugins/General/gen_ff/gen_ff_ipc.h b/Src/Plugins/General/gen_ff/gen_ff_ipc.h new file mode 100644 index 00000000..dd279d8d --- /dev/null +++ b/Src/Plugins/General/gen_ff/gen_ff_ipc.h @@ -0,0 +1,20 @@ +#ifndef _GEN_FF_IPC_H +#define _GEN_FF_IPC_H + +#include "ff_ipc.h" + +void ff_ipc_getSkinColor(ff_skincolor *sc); +void ff_ipc_genSkinBitmap(ff_skinbitmap *sb); +HBITMAP ff_genwa2skinbitmap(); +HWND ff_ipc_getContentWnd(HWND w); + +class ColorThemeMonitor : public SkinCallbackI { + public: + ColorThemeMonitor(); + virtual ~ColorThemeMonitor(); + int skincb_onColorThemeChanged(const wchar_t *colortheme); +}; + +extern ColorThemeMonitor *colorThemeMonitor; + +#endif \ No newline at end of file diff --git a/Src/Plugins/General/gen_ff/main.cpp b/Src/Plugins/General/gen_ff/main.cpp new file mode 100644 index 00000000..728d99a4 --- /dev/null +++ b/Src/Plugins/General/gen_ff/main.cpp @@ -0,0 +1,3527 @@ +#include "precomp__gen_ff.h" +#include +#include + +#include "main.h" +#include "resource.h" +#include "prefs.h" +#include "wa2frontend.h" +#include "wa2groupdefs.h" +#include "wa2wndembed.h" +#include "wa2cfgitems.h" +#include "wa2coreactions.h" +#include "wa2pledit.h" +#include "embedwndguid.h" +#include "gen_ff_ipc.h" +#include "fsmonitor.h" +#include +#include "../winamp/wa_ipc.h" +#include "../gen_hotkeys/wa_hotkeys.h" +#include +#include + +#include +#include +#include +#include +#include "menuactions.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "skininfo.h" +#include + +#include + +#include "wa2core.h" +#include + +#include +#include +#include +#include + +#include "../nu/AutoWide.h" +#include +#include +//wtf? +#define _WAFE_H_ +#define ___HOTAMP3_H___ +#include "../gen_hotkeys/hotkey.h" + +int embedCreateProc(embedWindowState *p, embedEnumStruct *parms); + +DECLARE_MODULE_SVCMGR; + +#define VERSION L"1.55" +//#define VIDDEBUG +//#define DEBUG_CAPTURES + +#include "../gen_ml/ml_ipc.h" +librarySendToMenuStruct mainSendTo; + +#include "../Agave/Language/api_language.h" +// wasabi based services for localisation support +api_language *WASABI_API_LNG = 0; +HINSTANCE WASABI_API_LNG_HINST = 0, WASABI_API_ORIG_HINST; +static wchar_t szDescription[256]; + +//----------------------------------------------------------------------------------------------- +#define GET_X_LPARAM(lp) ((int)(short)LOWORD(lp)) +#define GET_Y_LPARAM(lp) ((int)(short)HIWORD(lp)) + +#define ID_FILE_SHOWLIBRARY 40379 +#define WINAMP_OPTIONS_PREFS 40012 +#define WINAMP_HELP_ABOUT 40041 +#define WINAMP_LIGHTNING_CLICK 40339 +#define WINAMP +#define UPDATE_EGG 0xC0DE+2 +#define VIEWPORT 0xC0DE+3 +static int DEFERREDCALLBACKMSG; +static int UPDATEDIALOGBOXPARENTMSG; + +//----------------------------------------------------------------------------------------------- +void config(); +void quit(); +int init(); +void quit_inst(); +void init_inst(); +void ToggleLayout(const wchar_t *containerName); +void RestoreClassicWinamp(int was_loaded); +extern "C" winampGeneralPurposePlugin plugin = +{ + GPPHDR_VER_U, + "nullsoft(gen_ff.dll)", + init, + config, + quit, +}; + +//----------------------------------------------------------------------------------------------- + +int wantContainerInMenu(Container *cont) +{ + wchar_t tmpBuf[96] = {0}; + GUID g = cont->getGUID(); + if (WCSCASEEQLSAFE(cont->getId(), L"main")) return 0; + if (cont->getNoMenu()) return 0; + if (g == pleditWndGuid) return 0; + if (g == videoWndGuid) return 0; + if (g == avs_guid) return 0; + if (g == library_guid) return 0; + if (WCSCASEEQLSAFE(cont->getName(), L":componenttitle")) return 0; + if (WCSCASEEQLSAFE(cont->getName(), L"Playlist Editor") || WCSCASEEQLSAFE(cont->getName(), L"Playlist") || WCSCASEEQLSAFE(cont->getId(), L"pledit")) return 0; + if (WCSCASEEQLSAFE(cont->getName(), L"Video Window") || WCSCASEEQLSAFE(cont->getName(), L"Video") || WCSCASEEQLSAFE(cont->getId(), L"Video")) return 0; + if (WCSCASEEQLSAFE(cont->getName(), WASABI_API_LNG->GetStringFromGUIDW(GenMlLangGUID,plugin.hDllInstance,18)) || + WCSCASEEQLSAFE(cont->getName(), L"Media Library") || + WCSCASEEQLSAFE(cont->getId(), L"Library") || + WCSCASEEQLSAFE(cont->getName(),WASABI_API_LNGSTRINGW_BUF(IDS_MEDIA_LIBRARY,tmpBuf,96))) return 0; + if (WCSCASEEQLSAFE(cont->getName(), L"AVS") || WCSCASEEQLSAFE(cont->getName(), WASABI_API_LNGSTRINGW(IDS_VISUALIZATIONS)) || WCSCASEEQLSAFE(cont->getName(), L"Vis") || WCSCASEEQLSAFE(cont->getId(), L"Avs") || WCSCASEEQLSAFE(cont->getId(), L"Vis")) return 0; + return 1; +} + + +//----------------------------------------------------------------------------------------------- + +api_playlists *AGAVE_API_PLAYLISTS = 0; +api_playlistmanager *AGAVE_API_PLAYLISTMANAGER = 0; +api_albumart *AGAVE_API_ALBUMART = 0; +api_downloadManager *WAC_API_DOWNLOADMANAGER = 0; +api_colorthemes *WASABI_API_COLORTHEMES = 0; +api_palette *WASABI_API_PALETTE = 0; +api_threadpool *WASABI_API_THREADPOOL = 0; + +StringW m_lastskin_nam, m_lastskin_dir; + +inline int lumidiff(int a, int b) { + int r1 = (a & 0xFF0000) >> 16; + int r2 = (b & 0xFF0000) >> 16; + int g1 = (a & 0xFF00) >> 8; + int g2 = (b & 0xFF00) >> 8; + int b1 = a & 0xFF; + int b2 = b & 0xFF; + return MIN((ABS(r1 - r2), ABS(g1 - g2)), ABS(b1 - b2)); +} + +static void doplaylistcolors() +{ + int interpolate = 0; + if (SkinParser::getSkinVersion()*10 < 10) interpolate = 1; + + // update colors + int buf[6] = {0}; + waSetPlColorsStruct s = {0, }; + extern COLORREF getWindowBackground(COLORREF *); + COLORREF windowbackground; + buf[5] = getWindowBackground(&windowbackground); // auto inverted + + int need_cols = 1, need_bms = 1; + + if (!m_lastskin_dir.isempty()) + { + StringPathCombine bitmapfilename(m_lastskin_dir, L"pledit.bmp"); + HANDLE h = CreateFileW(bitmapfilename, 0, 0, 0, OPEN_EXISTING, 0, 0); + if (h != INVALID_HANDLE_VALUE) + { + CloseHandle(h); + need_bms = 0; + } + + bitmapfilename = StringPathCombine(m_lastskin_dir, L"pledit.txt"); + h = CreateFileW(bitmapfilename, 0, 0, 0, OPEN_EXISTING, 0, 0); + if (h != INVALID_HANDLE_VALUE) + { + CloseHandle(h); + need_cols = 0; + } + } + + if (need_cols) + { + s.numElems = 6; + s.elems = buf; + + // list background + if (WASABI_API_SKIN->skin_getColorElementRef(L"wasabi.list.background")) + buf[2] = SkinColor(L"wasabi.list.background") & 0xFFFFFF; + else + buf[2] = RGBTOBGR(WASABI_API_SKIN->skin_getBitmapColor(L"wasabi.list.background")) & 0xFFFFFF; // inverted coz coming from bitmap + + // normal text + if (WASABI_API_SKIN->skin_getColorElementRef(L"wasabi.list.text")) + buf[0] = SkinColor(L"wasabi.list.text") & 0xFFFFFF; + else + buf[0] = SkinColor(L"wasabi.edit.text") & 0xFFFFFF; + + if (interpolate) + { + int c = buf[0]; + c = lumidiff(c, buf[2]) < 0x1F ? Blenders::BLEND_AVG(buf[0], 0xFF7F7F7F) : c; + c = lumidiff(c, buf[2]) < 0x1F ? Blenders::BLEND_AVG(buf[0], 0xFF101010) : c; + buf[0] = c & 0xFFFFFF; + } + + COLORREF selected; + + // selected items background + if (WASABI_API_SKIN->skin_getColorElementRef(L"wasabi.list.text.selected.background")) + { + selected = SkinColor(L"wasabi.list.text.selected.background"); + buf[3] = selected; + } + else + { + // inverted twice, see bellow + if (WASABI_API_SKIN->skin_getColorElementRef(L"wasabi.list.item.selected")) + selected = RGBTOBGR(SkinColor(L"wasabi.list.item.selected")); + else + selected = RGBTOBGR(SkinColor(SKINCOLOR_TREE_SELITEMBKG)); + + COLORREF col = RGBTOBGR(SkinColor(SKINCOLOR_LIST_COLUMNBKG)); + int a = MAX((col & 0xFF0000) >> 16, MAX((col & 0xFF00) >> 8, col & 0xFF)); + col = a < 0x1F ? Blenders::BLEND_AVG(windowbackground, 0xFF000000) : col; + int listbkg = RGBTOBGR(buf[2]); + selected = lumidiff(selected, listbkg) < 0x2F ? Blenders::BLEND_AVG(windowbackground, 0xFF7F7F7F) : selected; + selected = lumidiff(selected, listbkg) < 0x2F ? Blenders::BLEND_AVG(listbkg, 0xFF7F7F7F) : selected; + selected = lumidiff(selected, listbkg) < 0x2F ? Blenders::BLEND_AVG(windowbackground, 0xFFF0F0F0) : selected; + selected = lumidiff(selected, listbkg) < 0x2F ? Blenders::BLEND_AVG(listbkg, 0xFFF0F0F0) : selected; + selected = lumidiff(selected, listbkg) < 0x2F ? Blenders::BLEND_AVG(col, 0xFFF0F0F0) : selected; + selected = lumidiff(selected, listbkg) < 0x2F ? Blenders::BLEND_AVG(col, 0xFF101010) : selected; + selected = lumidiff(selected, RGBTOBGR(buf[0])) < 0x1F ? Blenders::BLEND_AVG(selected, 0xFF101010) : selected; + selected = lumidiff(selected, RGBTOBGR(buf[0])) < 0x1F ? Blenders::BLEND_AVG(selected, 0xFFF0F0F0) : selected; + selected &= 0xFFFFFF; + buf[3] = RGBTOBGR(selected); + } + + // active text + if (WASABI_API_SKIN->skin_getColorElementRef(L"wasabi.list.text.current")) + buf[1] = SkinColor(L"wasabi.list.text.current"); + else + { + COLORREF act = Blenders::BLEND_AVG(selected, buf[0]); + act = (lumidiff(act, buf[0]) < 0x3F || lumidiff(act, buf[2]) < 0x2F) ? Blenders::BLEND_AVG(buf[2], 0xFFFFFFFF) : act; + act = (lumidiff(act, buf[0]) < 0x3F || lumidiff(act, buf[2]) < 0x2F) ? Blenders::BLEND_AVG(buf[2], 0xFF101010) : act; + act = (lumidiff(act, buf[0]) < 0x3F || lumidiff(act, buf[2]) < 0x2F) ? Blenders::BLEND_AVG(selected, buf[0]) : act; + act = (lumidiff(act, buf[0]) < 0x3F || lumidiff(act, buf[2]) < 0x2F) ? Blenders::BLEND_AVG(selected, buf[2]) : act; + act = (lumidiff(act, buf[0]) < 0x3F || lumidiff(act, buf[2]) < 0x2F) ? Blenders::BLEND_AVG(buf[0], buf[2]) : act; + buf[1] = act & 0xFFFFFF; + } + } + + if (need_bms) + { + BltCanvas c(280, 186); + + c.fillBits(windowbackground); // already inverted + + void blitButtonToCanvas(int w, int h, int state, const wchar_t *overlayelement, int xpos, int ypos, BltCanvas *c); + + blitButtonToCanvas(8, 18, 0, L"wasabi.scrollbar.vertical.grip", 52, 53, &c); + blitButtonToCanvas(8, 18, 1, L"wasabi.scrollbar.vertical.grip", 61, 53, &c); + + int xpos, ypos; + //w=8,h=29 + int cols[2]; + int *fb = (int*)c.getBits() + 280 * 42; + + // inverted colors because going into bitmap + + if (WASABI_API_SKIN->skin_getColorElementRef(L"wasabi.scrollbar.background.inverted")) + cols[0] = RGBTOBGR(SkinColor(L"wasabi.scrollbar.background.inverted")); + else + { + if (WASABI_API_SKIN->skin_getColorElementRef(L"wasabi.list.column.empty")) + cols[0] = RGBTOBGR(SkinColor(L"wasabi.list.column.empty")); + else + { + COLORREF col = SkinColor(SKINCOLOR_LIST_COLUMNBKG); + int a = MAX((col & 0xFF0000) >> 16, MAX((col & 0xFF00) >> 8, col & 0xFF)); + col = a < 0x1F ? Blenders::BLEND_AVG(windowbackground, 0xFF000000) : col; + cols[0] = RGBTOBGR(Blenders::BLEND_AVG(col, 0xFF000000)); + } + } + + if (WASABI_API_SKIN->skin_getColorElementRef(L"wasabi.border.sunken")) + cols[1] = RGBTOBGR(SkinColor(L"wasabi.border.sunken")); + else + { + int a = MAX((windowbackground & 0xFF0000) >> 16, MAX((windowbackground & 0xFF00) >> 8, windowbackground & 0xFF)); + cols[1] = Blenders::BLEND_AVG(windowbackground, a > 0xE0 ? 0xFF000000 : 0xFFFFFFFF); + } + + for (ypos = 42;ypos < 42 + 29; ypos ++) + { + for (xpos = 36; xpos < 36 + 8; xpos ++) + { + fb[xpos] = cols[(xpos == 36 || xpos == 36 + 8 - 1)]; + } + fb += 280; + } + + extern HBITMAP CreateBitmapDIB(int w, int h, int planes, int bpp, void *data); + s.bm = CreateBitmapDIB(280, 186, 1, 32, c.getBits()); + } + if (s.bm || s.elems) SendMessageW(wa2.getMainWindow(), WM_WA_IPC, (WPARAM)&s, IPC_SETPLEDITCOLORS); +} + +static prefsDlgRecW ffPrefsItem; + +//----------------------------------------------------------------------------------------------- + +// a guid for our app : {4BE592C7-6937-426a-A388-ACF0EBC88E93} +static const GUID GEN_FREEFORM = + { 0x4be592c7, 0x6937, 0x426a, { 0xa3, 0x88, 0xac, 0xf0, 0xeb, 0xc8, 0x8e, 0x93 } }; + +Wa2Groupdefs *groups; +static WNDPROC wa_oldWndProc; + +Wa2CfgItems *cfgitems=0; + +HINSTANCE hInstance = NULL; +HWND last_dlg_parent = NULL; +void populateWindowsMenus(); +void unpopulateWindowsMenus(); +void addWindowOptionsToContextMenu(ifc_window *w); +void removeWindowOptionsFromContextMenu(); +void controlOpacity(int v); +void controlScaling(double v); +void customScaling(); +void customOpacity(); +void autoOpacifyHover(); +void autoOpacifyFocus(); +ifc_window *g_controlMenuTarget = NULL; +HMENU controlmenu = NULL; +void lockScaling(int lock); +int ffwindowsitempos = -1; +int ffoptionstop = -1; +int ffwoptionstop = -1; +int ffwindowstop = -1; +int ffwindowsitempos2 = -1; +int eqremoved = 0; +void removeEq(); +void restoreEq(); +int g_timedisplaymode = 0; +ifc_window *lastFocused = NULL; +int before_startup_callback = 0; +void loadExtraColorThemes(); +extern _int last_page; +int gothrueqmsg = 0; +void unhookOutputIPC(); +int we_have_ml = 0; +void checkMlPresent(); +char eggstr[9] = {0}; +int eggstat = 0; +int eggfallout = 0; +void initEgg(); +void toggleEgg(); +Layout *lastlayoutegg = NULL; +int disable_send_visrandom = 0; +void registerGlobalHotkeys(); +int processGenericHotkey(const char *hk); +const char *getSkinInfo(); +const wchar_t *getSkinInfoW(); +void controlAppBar(int side); +void controlAppBarAOT(); +void controlAppBarAH(); +void updateAppBarMenu(ifc_window *w); +void startFSMonitor(); +void stopFSMonitor(); +void onGoFullscreen(); +void onCancelFullscreen(); +FullScreenMonitor *g_fsmonitor = NULL; +class Wa5FSCallback : public FSCallback +{ +public: + virtual void onGoFullscreen() + { + ::onGoFullscreen(); + } + virtual void onCancelFullscreen() + { + ::onCancelFullscreen(); + } +}; +Wa5FSCallback *g_fscallback = NULL; +int getAOTTempDisable() +{ + return g_fsmonitor->isFullScreen(); +} + +LRESULT CallWinampWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + return CallWindowProcW(wa_oldWndProc, hwnd, msg, wParam, lParam); +} + +//----------------------------------------------------------------------------------------------- +BOOL WINAPI dll_main_raw_fn(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) +{ + hInstance = hinstDLL; + Wasabi::Std::Initialize(); + return TRUE; +} + +extern "C" +{ + void *_pRawDllMain = &dll_main_raw_fn; +}; + +__declspec(dllexport) BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) +{ + hInstance = hinstDLL; + return TRUE; +} + +//----------------------------------------------------------------------------------------------- +// core actions implement "play", "stop", etc. wndEmbedded embed wa2 windows into GUID-based wnds +//----------------------------------------------------------------------------------------------- +BEGIN_SERVICES(Winamp2_Svcs); +DECLARE_SERVICETSINGLE(svc_action, CoreActions); +DECLARE_SERVICETSINGLE(svc_action, MenuActions); +DECLARE_SERVICETSINGLE(svc_windowCreate, Wa2WndEmbed); +END_SERVICES(Winamp2_Svcs, _Winamp2_Svcs); + +//----------------------------------------------------------------------------------------------- +// functions called by the overriden windowproc to create and destroy wndembedders +//----------------------------------------------------------------------------------------------- +ifc_window *plWnd = NULL; +#ifdef MINIBROWSER_SUPPORT +ifc_window *mbWnd = NULL; +#endif +ifc_window *vidWnd = NULL; +TList forcedoffwnds; + +int going_freeform = 0; +int going_fixedform = 0; + +wchar_t *INI_FILE = 0, *INI_DIR = 0; + +void ToggleLayout(const wchar_t *containerName) +{ + Container *c = SkinParser::getContainer(containerName); + if (c) + { + int numLayouts = c->getNumLayouts(); + + for (int i = 0;i < numLayouts;i++) + { + if (c->enumLayout(i) == c->getCurrentLayout()) + { + int nextLayout = (i + 1) % numLayouts; + Layout *layout = c->enumLayout(nextLayout); + c->switchToLayout(layout->getId()); + return ; + } + } + } +} + +int updatePl() +{ + // find out if this GUID is already shown. + WindowHolder *wh = skinEmbedder->getSuitableWindowHolder(pleditWndGuid, NULL, NULL, NULL, 1, -1, 0, 1, -1); + if (wh != NULL) + { + wh->cancelDeferredRemove(); + GuiObject *go = wh->getRootWndPtr()->getGuiObject(); + if (go) + { + Layout *l = go->guiobject_getParentLayout(); + if (l) + { + Container *c = l->getParentContainer(); + if (c) + { + DebugStringW(L"Container is %s\n", c->getId()); + SkinEmbedder::cancelDestroyContainer(c); + } + } + } + plWnd = wh->getRootWndPtr(); //->getDesktopParent(); + return 1; + } + return 0; +} + +int createPl() +{ + if (updatePl()) + { + ShowWindow(wa2.getWnd(IPC_GETWND_PE), SW_SHOWNA); + return 1; + } + ReentryFilter f(&wndMsgFilter, IPC_GETWND_PE); + if (f.mustLeave()) return 1; + plWnd = WASABI_API_WNDMGR->skinwnd_createByGuid(pleditWndGuid, L"resizable_status"); + if (plWnd != NULL) + { + plWnd->setVisible(1); + } + return plWnd != NULL; +} + +static ifc_window *updateEmb(GUID thisguid, embedWindowState *ws) +{ + WindowHolder *wh = skinEmbedder->getSuitableWindowHolder(thisguid, NULL, NULL, NULL, 1, -1, 0, 1, -1); + if (wh != NULL && !(ws->flags & EMBED_FLAGS_LEGACY_WND)) + { + ifc_window *p = wh->getRootWndPtr(); //->getDesktopParent(); + + // p->getGuiScriptObject()->vcpu_setInterface(embeddedWndStateGuid, ws, INTERFACE_GENERICVOIDPTR); + ws->extra_data[EMBED_STATE_EXTRA_FFROOTWND] = (intptr_t)p; + return p; + } + return 0; +} + +static ifc_window *createEmb(embedWindowState *ws, bool startHidden) +{ + GUID thisguid = EmbedWndGuid(ws).getGuid(); + if (thisguid == INVALID_GUID || (ws->flags & EMBED_FLAGS_LEGACY_WND)) + return 0; + + //thisguid.Data1 = (int)ws->me; + ifc_window *update = updateEmb(thisguid, ws); + if (update) + return update; + + ReentryFilter f(&wndMsgFilter, (intptr_t)ws); + if (f.mustLeave()) + return 0; + + RECT r; + if (!GetWindowRect(ws->me, &r)) + SetRectEmpty(&r); + + ifc_window *p = NULL; + if (ws->flags & EMBED_FLAGS_NORESIZE) + p = WASABI_API_WNDMGR->skinwnd_createByGuid(thisguid, L"static", 0, NULL, 0, startHidden, NULL); + else + p = WASABI_API_WNDMGR->skinwnd_createByGuid(thisguid, L"resizable_nostatus", 0, NULL, 0, startHidden, NULL); + + ws->extra_data[EMBED_STATE_EXTRA_FFROOTWND] = (intptr_t) p; + if (p != NULL) + { + // p->getGuiScriptObject()->vcpu_setInterface(embeddedWndStateGuid, ws, INTERFACE_GENERICVOIDPTR); + if (NULL != WASABI_API_APP) + WASABI_API_APP->app_unregisterGlobalWindow(p->getDesktopParent()->gethWnd()); + + if (!startHidden) + p->setVisible(1); + + GuiObject *go = p->getGuiObject(); + if (go) + { + Layout *l = go->guiobject_getParentLayout(); + if (l) + { + Container *c = l->getParentContainer(); + if (c) + { + if (ws->flags & EMBED_FLAGS_NOWINDOWMENU) + c->setXmlParam(L"nomenu", L"1"); + else + c->setXmlParam(L"nomenu", L"0"); + } + } + } + } + if (p == NULL) + { + RECT r; + GetWindowRect(ws->me, &r); + //////SetWindowPos( ws->me, NULL, r.left - 20000, r.top - 20000, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOREDRAW | SWP_NOSENDCHANGING | SWP_NOZORDER | SWP_NOCOPYBITS ); + SetWindowPos( ws->me, NULL, r.left, r.top, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOREDRAW | SWP_NOSENDCHANGING | SWP_NOZORDER | SWP_NOCOPYBITS ); + forcedoffwnds.addItem(ws->me); + //if (0 != (WS_VISIBLE & windowStyle)) + //RedrawWindow(GetDesktopWindow(), &r, NULL, RDW_INVALIDATE | RDW_ALLCHILDREN | RDW_UPDATENOW | RDW_NOERASE); + } + + RedrawWindow(GetDesktopWindow(), &r, NULL, RDW_INVALIDATE | RDW_ALLCHILDREN | RDW_UPDATENOW | RDW_NOERASE); + return p; +} + +#ifdef MINIBROWSER_SUPPORT +int updateMb() +{ + WindowHolder *wh = skinEmbedder->getSuitableWindowHolder(minibrowserWndGuid, NULL, NULL, NULL, 1, -1, 0, 1, -1); + if (wh != NULL) + { + mbWnd = wh->getRootWndPtr(); //->getDesktopParent(); + ShowWindow(wa2.getWnd(IPC_GETWND_MB), SW_SHOWNA); + return 1; + } + return 0; +} + +int createMb() +{ + // see cratePl() + if (updateMb()) return 1; + ReentryFilter f(&wndMsgFilter, IPC_GETWND_MB); + if (f.mustLeave()) return 1; + mbWnd = WASABI_API_WNDMGR->skinwnd_createByGuid(minibrowserWndGuid, "resizable_status"); + if (mbWnd != NULL) mbWnd->setVisible(1); + return mbWnd != NULL; +} +#endif + +static HWND oldVideoWnd; +static WNDPROC oldVideoWndProc; +static DWORD WINAPI newVideoWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + if (uMsg == WM_USER + 0x100 && wParam == 1 && lParam) + return 0; + + return CallWindowProc(oldVideoWndProc, hwnd, uMsg, wParam, lParam); +} + +int updateVid() +{ + WindowHolder *wh = skinEmbedder->getSuitableWindowHolder(videoWndGuid, NULL, NULL, NULL, 1, -1, 0, 1, -1); + if (wh != NULL) + { +#ifdef VIDDEBUG + DebugString("Video : Host already exists and window already shown in it\n"); +#endif + wh->cancelDeferredRemove(); + GuiObject *go = wh->getRootWndPtr()->getGuiObject(); + if (go) + { + Layout *l = go->guiobject_getParentLayout(); + if (l) + { + Container *c = l->getParentContainer(); + if (c) + { + DebugStringW(L"Container is %s\n", c->getId()); + SkinEmbedder::cancelDestroyContainer(c); + } + } + } + vidWnd = wh->getRootWndPtr(); //->getDesktopParent(); + return 1; + } + return 0; +} + +int createVid() +{ + ShowWindow(WASABI_API_WND->main_getRootWnd()->gethWnd(), SW_RESTORE); + SetForegroundWindow(WASABI_API_WND->main_getRootWnd()->gethWnd()); + + if (updateVid()) + { + ShowWindow(wa2.getWnd(IPC_GETWND_VIDEO), SW_SHOWNA); + return 1; + } + + if (vidWnd == NULL) + { + ReentryFilter f(&wndMsgFilter, IPC_GETWND_VIDEO); + if (f.mustLeave()) return 1; +#ifdef VIDDEBUG + DebugString("Video : Trying to find a host or creating it if needed\n"); +#endif + vidWnd = WASABI_API_WNDMGR->skinwnd_createByGuid(videoWndGuid, L"resizable_status"); + } + + if (vidWnd != NULL) + { + vidWnd->setVisible(1); + } + return vidWnd != NULL; +} + +int destroyPl(HWND hwndDlg, int uMsg, WPARAM wParam, LPARAM lParam) +{ + ReentryFilter f(&wndMsgFilter, IPC_GETWND_PE); + int r = CallWinampWndProc(hwndDlg, uMsg, wParam, lParam); + if (f.mustLeave()) return r; + if (plWnd != NULL && WASABI_API_WND->rootwndIsValid(plWnd)) + { + WASABI_API_WNDMGR->skinwnd_destroy(plWnd); + r = !WASABI_API_WND->rootwndIsValid(plWnd); + } + else + r = !SOM::checkAbortShowHideWindow(pleditWndGuid, 0); + if (r) + plWnd = NULL; + return r; +} + +int destroyEmb(HWND hwndDlg, int uMsg, WPARAM wParam, LPARAM lParam, embedWindowState *ws) +{ + ReentryFilter f(&wndMsgFilter, (intptr_t)ws); + int r = CallWinampWndProc(hwndDlg, uMsg, wParam, lParam); + if (f.mustLeave() || (ws->flags & EMBED_FLAGS_LEGACY_WND)) return r; + if (ws->extra_data[EMBED_STATE_EXTRA_FFROOTWND] == NULL) return r; + if (ws->extra_data[EMBED_STATE_EXTRA_REPARENTING] == 1) return r; + ifc_window *w = (ifc_window*)ws->extra_data[EMBED_STATE_EXTRA_FFROOTWND]; +#if 1 + if (WASABI_API_WND->rootwndIsValid(w)) + { + WASABI_API_WNDMGR->skinwnd_destroy(w); + r = !WASABI_API_WND->rootwndIsValid(w); + } + else + { + r = !SOM::checkAbortShowHideWindow(embedWndGuidMgr.getGuid(ws), 0); + } + if (r) + ws->extra_data[EMBED_STATE_EXTRA_FFROOTWND] = NULL; +#else + if (WASABI_API_WND->rootwndIsValid(w)) WASABI_API_WNDMGR->skinwnd_destroy(w); + else SOM::checkAbortShowHideWindow(embedWndGuidMgr.getGuid(ws), 0); + ws->extra_data[EMBED_STATE_EXTRA_FFROOTWND] = NULL; +#endif + return r; +} + +#ifdef MINIBROWSER_SUPPORT +int destroyMb(HWND hwndDlg, int uMsg, WPARAM wParam, LPARAM lParam) +{ + ReentryFilter f(&wndMsgFilter, IPC_GETWND_MB); + int r = CallWinampWndProc(hwndDlg, uMsg, wParam, lParam); + if (f.mustLeave()) return r; + + if (mbWnd != NULL && WASABI_API_WND->rootwndIsValid(mbWnd)) WASABI_API_WNDMGR->skinwnd_destroy(mbWnd); + else SOM::checkAbortShowHideWindow(minibrowserWndGuid, 0); + mbWnd = NULL; + return r; +} +#endif + +int destroyVid(HWND hwndDlg, int uMsg, WPARAM wParam, LPARAM lParam) +{ + ReentryFilter f(&wndMsgFilter, IPC_GETWND_VIDEO); + int r = CallWinampWndProc(hwndDlg, uMsg, wParam, lParam); + + if (f.mustLeave()) return r; + + if (vidWnd != NULL && WASABI_API_WND->rootwndIsValid(vidWnd)) + { + WASABI_API_WNDMGR->skinwnd_destroy(vidWnd); + r = !WASABI_API_WND->rootwndIsValid(vidWnd); + } + else + r = !SOM::checkAbortShowHideWindow(videoWndGuid, 0); + + if (r) + vidWnd = NULL; + + return r; +} + + +int m_loading_at_startup; +int m_are_we_loaded; + +#define WINAMP_REFRESHSKIN 40291 +#define WINAMP_OPTIONS_EQ 40036 +#define WINAMP_OPTIONS_DSIZE 40165 +#define WINAMP_OPTIONS_ELAPSED 40037 +#define WINAMP_OPTIONS_REMAINING 40038 + +//----------------------------------------------------------------------------------------------- +class MainLayoutMonitor : public H_Layout +{ + public: + MainLayoutMonitor(HWND _w, ScriptObject *o) : H_Layout(o) { hwnd= _w; } + void hook_onResize(int x, int y, int w, int h); + HWND hwnd; +}; + +void MainLayoutMonitor::hook_onResize(int x, int y, int w, int h) +{ + PostMessage(wa2.getMainWindow(), WM_WA_IPC, (WPARAM)hwnd, UPDATEDIALOGBOXPARENTMSG); +} + +//----------------------------------------------------------------------------------------------- +// must be called after switching to a new freeform skin +// should also be called when the main container changes its visible layout +//----------------------------------------------------------------------------------------------- +Layout *GetMainLayout() +{ + Container *container = SkinParser::getContainer(L"main"); + if (container != NULL) + { + return container->getCurrentLayout(); + } + return 0; +} + +HWND GetMainContainerHWND() +{ + Layout *l = GetMainLayout(); + if (l != NULL) + return l->gethWnd(); + + return 0; +} +static MainLayoutMonitor *mainLayoutMonitor = 0; +void setDialogBoxesParent() +{ + Layout *l = GetMainLayout(); + if (l != NULL) + { + HWND hwnd = l->gethWnd(); + delete mainLayoutMonitor; + mainLayoutMonitor = new MainLayoutMonitor(hwnd, l->getScriptObject()); + wa2.setDialogBoxParent(hwnd); + PostMessage(wa2.getMainWindow(), WM_WA_IPC, (WPARAM)hwnd, UPDATEDIALOGBOXPARENTMSG); + lastFocused = l; + } +} + +static void removeSkinExtension(StringW &skinname) +{ + int x = skinname.len() - 4; + if (x > 0) + { + const wchar_t *p = ((const wchar_t *)skinname) + x; + if (!_wcsicmp(p, L".zip") || !_wcsicmp(p, L".wal") || !_wcsicmp(p, L".wsz")) + skinname.trunc(x); + } +} + +extern HWND subWnd, tabwnd; +extern int subWndId; +extern int toggle_from_wa2; +int switching_skin = 0; +void shutdownFFApi(); + + +/*----------------------------------------------------------------------------------------------- +* Winamp2 message processor +*/ + +static void RerouteMessage(MSG *pMsg, ifc_window *wnd) +{ + if (NULL == wnd) + return; + + HWND hReroute = NULL; + if (pMsg->hwnd == GetMainContainerHWND()) + { + hReroute = WASABI_API_WND->main_getRootWnd()->gethWnd(); + if (NULL != hReroute) + { + pMsg->hwnd = hReroute; + } + return; + } + if (wnd->gethWnd() != pMsg->hwnd) + return; + + ifc_window *parent = wnd->getRootParent(); + if (NULL == parent) + return; + + Layout *l = static_cast(parent->getInterface(layoutGuid)); + if (NULL == l) + return; + + Container *c = l->getParentContainer(); + if (NULL == c) + return; + + GUID g = c->getDefaultContent(); + if (INVALID_GUID == g) + return; + + if (g == playerWndGuid) + hReroute = wa2.getMainWindow(); + else if (g == pleditWndGuid) + hReroute = wa2.getWnd(IPC_GETWND_PE); + else if (g == videoWndGuid) + hReroute = wa2.getWnd(IPC_GETWND_VIDEO); + else + { + embedWindowState *ews = embedWndGuidMgr.getEmbedWindowState(g); + if (ews) + hReroute = ews->me; + } + + if (NULL != hReroute) + pMsg->hwnd = hReroute; +} + +class WaMessageProccessor : public ifc_messageprocessor +{ +public: + WaMessageProccessor() {} + ~WaMessageProccessor(void) {} + +public: + bool ProcessMessage(MSG *pMsg) + { + if (WM_KEYDOWN == pMsg->message || + WM_KEYUP == pMsg->message || + WM_SYSKEYDOWN == pMsg->message || + WM_SYSKEYUP == pMsg->message) + { + + ifc_window *wnd; + HWND h = pMsg->hwnd; + do + { + wnd = WASABI_API_WND->rootWndFromOSHandle(h); + + } while (!wnd && NULL!= (h = GetAncestor(h, GA_PARENT))); + + if (wnd) + { + if (!wnd->isVirtual()) + { + while (wnd) + { + const wchar_t *pid = wnd->getId(); + if (pid && *pid) break; + wnd = wnd->getParent(); + } + } + if (wnd && wnd->getCurVirtualChildFocus()) + wnd = wnd->getCurVirtualChildFocus(); + } + + if (wnd) + DebugStringW(L"target wnd 0x%08X, id: %s", wnd, wnd->getId()); + + INT keyMsg = -1; + switch(pMsg->message) + { + case WM_KEYDOWN: + static int pos; + if (toupper((int)pMsg->wParam) == eggstr[pos]) + { + if (!eggstr[++pos]) + { + toggleEgg(); + pos = 0; + } + } + else pos = 0; + keyMsg = (0 != WASABI_API_WND->forwardOnKeyDown(wnd, (int)pMsg->wParam, pMsg->lParam)); + break; + case WM_KEYUP: keyMsg = (wnd && 0 != WASABI_API_WND->forwardOnKeyUp(wnd, (int)pMsg->wParam, pMsg->lParam)); break; + case WM_SYSKEYDOWN: keyMsg = ( 0 != WASABI_API_WND->forwardOnSysKeyDown(wnd, (int)pMsg->wParam, pMsg->lParam)); break; + case WM_SYSKEYUP: keyMsg = (wnd && 0 != WASABI_API_WND->forwardOnSysKeyUp(wnd, (int)pMsg->wParam, pMsg->lParam)); break; + + } + if (-1 != keyMsg) + { + if (keyMsg > 0) + return true; + + RerouteMessage(pMsg, wnd); + } + } + return false; + } + +protected: + RECVS_DISPATCH; +}; + +#define CBCLASS WaMessageProccessor +START_DISPATCH; +CB(IFC_MESSAGEPROCESSOR_PROCESS_MESSAGE, ProcessMessage) +END_DISPATCH; +#undef CBCLASS + +static WaMessageProccessor waMessageProcessor; + +#define TabCtrl_InsertItemW(hwnd, iItem, pitem) \ + (int)SNDMSG((hwnd), TCM_INSERTITEMW, (WPARAM)(int)(iItem), (LPARAM)(const TC_ITEMW *)(pitem)) + +//----------------------------------------------------------------------------------------------- +void onSkinSwitch() +{ + switching_skin = 1; + int lastloaded = m_are_we_loaded; + + Wa2WndEmbed::rememberVisibleWindows(); + + // get skin name + wchar_t buf[MAX_PATH] = {0}; + wchar_t *p = (wchar_t *) SendMessageW(plugin.hwndParent, WM_WA_IPC, (WPARAM)buf, IPC_GETSKINW); + { + m_lastskin_nam.setValue(p); + m_lastskin_dir.setValue(buf); + + StringPathCombine t(m_lastskin_dir, L"skin.xml"); + + if (!m_lastskin_nam.isempty() + && _waccess(t.getValue(), 0)) + { + t.setValue(m_lastskin_dir); + StringW n = m_lastskin_nam; + removeSkinExtension(n); + t.AppendPath(n); + t.AppendPath(L"skin.xml"); + if (!_waccess(t.getValue(), 0)) + m_lastskin_dir.AppendPath(n); + } + + if (!_waccess(t.getValue(), 0)) + { + if (!m_are_we_loaded) init_inst(); + else + { + // load new skin + StringW skinname = m_lastskin_nam; + + removeSkinExtension(skinname); + + before_startup_callback = 1; + WASABI_API_SKIN->skin_switchSkin(skinname, m_lastskin_dir); + + if (DEFERREDCALLBACKMSG > 65536) + PostMessage(wa2.getMainWindow(), WM_WA_IPC, 2, DEFERREDCALLBACKMSG); + //embedEnumStruct cs = { embedCreateProc, 0 }; + //SendMessageW(plugin.hwndParent, WM_WA_IPC, (WPARAM)&cs, IPC_EMBED_ENUM); + + doplaylistcolors(); + setDialogBoxesParent(); + populateWindowsMenus(); + } + } + else + { + going_fixedform = 1; + quit_inst(); + } + } + if (subWnd != NULL && IsWindow(subWnd)) SendMessageW(subWnd, WM_INITDIALOG, 0, 0); + + if (IsWindow(tabwnd)) + { + if (!m_are_we_loaded) + { + if (subWndId == 5) + { + DestroyWindow(subWnd); + TabCtrl_SetCurSel(tabwnd,0); + _dosetsel(GetParent(tabwnd)); + } + + if (TabCtrl_GetItemCount(tabwnd) == 5) + { + TabCtrl_DeleteItem(tabwnd,4); + TabCtrl_DeleteItem(tabwnd,3); + } + } + else + { + if (TabCtrl_GetItemCount(tabwnd) == 3) + { + TCITEMW item = {0}; + item.mask=TCIF_TEXT; + item.pszText=WASABI_API_LNGSTRINGW(IDS_COLOR_THEMES); + TabCtrl_InsertItemW(tabwnd,3,&item); + item.pszText=WASABI_API_LNGSTRINGW(IDS_CURRENT_SKIN); + TabCtrl_InsertItemW(tabwnd,4,&item); + } + } + } + + if (m_are_we_loaded || lastloaded) // dont call this if going from classic->classic + Wa2WndEmbed::restoreVisibleWindows(); + + if (m_are_we_loaded && WASABI_API_SYSCB) + WASABI_API_SYSCB->syscb_issueCallback(SysCallback::GC, GarbageCollectCallback::GARBAGECOLLECT); + + if (WASABI_API_APP) + { + if (m_are_we_loaded) WASABI_API_APP->app_addMessageProcessor(&waMessageProcessor); + else WASABI_API_APP->app_removeMessageProcessor(&waMessageProcessor); + } + + /* TODO: benski> want to add this, but it's causing a weird crash + if (going_fixedform) + shutdownFFApi(); + */ + switching_skin = 0; + going_fixedform = 0; + before_startup_callback = 0; +} + +static int embedUpdateColorProc(embedWindowState *p, embedEnumStruct *parms) +{ + SendMessageW(p->me, WM_DISPLAYCHANGE, 0, 0); + return 0; +} + +int embedCreateProc(embedWindowState *ws, embedEnumStruct *parms) +{ + ifc_window *rw = reinterpret_cast(ws->extra_data[EMBED_STATE_EXTRA_FFROOTWND]); + + if (!rw && IsWindow(ws->me) && IsWindowVisible(ws->me) && !(ws->flags & EMBED_FLAGS_LEGACY_WND)) + { + ifc_window *windowParent = createEmb(ws, true); + if (NULL != windowParent) + { + GuiObject *uiObject = windowParent->getGuiObject(); + if (NULL != uiObject) + { + if (0 != (EMBED_FLAGS_FFCALLBACK & ws->flags) && + NULL != ws->callback) + { + ws->callback(ws, FFC_CREATEEMBED, (LPARAM)windowParent); + } + + Layout *layout = uiObject->guiobject_getParentLayout(); + if (NULL != layout) + { + Container *container = layout->getParentContainer(); + if (NULL != container) container->setVisible(1); + else layout->setVisible(1); + windowParent->setVisible(1); + } + } + } + + if (NULL == windowParent) + ShowWindow(ws->me, SW_HIDE); + } + + return 0; +} + +//----------------------------------------------------------------------------------------------- +void syncDoubleSize(int loading = 0) +{ + if (!cfg_uioptions_uselocks.getValueAsInt()) return ; + int isdsize = wa2.isDoubleSize(); + int i; + for (i = 0;i < SkinParser::getNumContainers();i++) + { + Container *c = SkinParser::enumContainer(i); + if (c != NULL) + { + int j; + for (j = 0;j < c->getNumLayouts();j++) + { + Layout *l = c->enumLayout(j); + if (l != NULL) + { + if (l->isScaleLocked()) continue; + l->setRenderRatio(isdsize ? 2.0f : 1.0f); + } + } + } + } +} + +//----------------------------------------------------------------------------------------------- +class syncDisplayModeEnumerator : public FindObjectCallbackI +{ +public: + int findobjectcb_matchObject(ifc_window *object) + { + ScriptObject *s = NULL; + GuiObject *g = object->getGuiObject(); + if (g) + { + ScriptObject *_s = g->guiobject_getScriptObject(); + if (_s) + { + void *v = _s->vcpu_getInterfaceObject(textGuid, &s); + if (v && !s) + { + (static_cast(v))->setTimeDisplayMode(g_timedisplaymode); + } + } + } + return 0; + } +}; + +//----------------------------------------------------------------------------------------------- +void syncDisplayMode() +{ + if (switching_skin) return ; + g_timedisplaymode = wa2.getTimeDisplayMode(); + int i; + syncDisplayModeEnumerator enumerator; + for (i = 0;i < SkinParser::getNumContainers();i++) + { + Container *c = SkinParser::enumContainer(i); + if (c != NULL) + { + int j; + for (j = 0;j < c->getNumLayouts();j++) + { + Layout *l = c->enumLayout(j); + if (!WASABI_API_WND->rootwndIsValid(l)) continue; + l->findWindowByCallback(&enumerator); + } + } + } +} + +static POINT viewPort; +VOID CALLBACK ViewPortChanged(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime) +{ + KillTimer(hwnd, idEvent); + if (viewPort.x && viewPort.y) + { + SystemObject::onViewPortChanged(viewPort.x, viewPort.y); + } +} + +//----------------------------------------------------------------------------------------------- +// Winamp2 main window subclass +//----------------------------------------------------------------------------------------------- +static LRESULT WINAPI wa_newWndProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + static int m_in_skinrefresh; + + if (uMsg == WM_COMMAND && LOWORD(wParam) == WINAMP_REFRESHSKIN) + { + HWND prefs = wa2.getPreferencesWindow(); + RECT prefrect = {0}; + if (prefs) + { + GetWindowRect(prefs, &prefrect); + HWND hOwner = (HWND)(LONG_PTR)GetWindowLongPtrW(prefs, GWLP_HWNDPARENT); + if (NULL != hOwner && hOwner != hwndDlg) + SetWindowLongPtrW(prefs, GWLP_HWNDPARENT, (LONG_PTR)hwndDlg); + } + m_in_skinrefresh++; + DWORD b = CallWinampWndProc(hwndDlg, uMsg, wParam, lParam); + onSkinSwitch(); + if (m_are_we_loaded) doplaylistcolors(); + m_in_skinrefresh--; + if (prefs && !wa2.getPreferencesWindow()) + { + SendMessageW(wa2.getMainWindow(), WM_COMMAND, WINAMP_OPTIONS_PREFS, 0); + SetWindowPos(wa2.getPreferencesWindow(), NULL, prefrect.left, prefrect.top, prefrect.right - prefrect.left, prefrect.bottom - prefrect.top, SWP_NOZORDER | SWP_NOACTIVATE | SWP_DEFERERASE); + } + + return b; + } + if (g_Core) + { + if (uMsg == WM_WA_IPC && lParam == IPC_CB_MISC) g_Core->gotCallback(wParam); +#define WINAMP_FILE_REPEAT 40022 +#define WINAMP_FILE_SHUFFLE 40023 +#define WINAMP_FILE_MANUALPLADVANCE 40395 +#define UPDATE_DISPLAY_TIMER 38 + if (uMsg == WM_TIMER) + { + if (LOWORD(wParam) == UPDATE_EGG) + { + for (int i = 0;i < SkinParser::getNumContainers();i++) + { + Container *cont = SkinParser::enumContainer(i); + if (cont->isMainContainer()) + { + Layout *l = cont->getCurrentLayout(); + if (lastlayoutegg && l != lastlayoutegg) + { + if (WASABI_API_WND->rootwndIsValid(lastlayoutegg)) + { + lastlayoutegg->updateTransparency(); + lastlayoutegg->setTransparencyOverride(-1); + } + } + lastlayoutegg = l; + int v = (WASABI_API_MEDIACORE->core_getLeftVuMeter(0) + WASABI_API_MEDIACORE->core_getRightVuMeter(0)) / 4; + eggfallout -= 4; + if (v > eggfallout) eggfallout = v; + lastlayoutegg->setTransparencyOverride(255 - eggfallout); + break; + } + } + return 0; + } + if (LOWORD(wParam) == UPDATE_DISPLAY_TIMER + 4) + { + syncDisplayMode(); + } + if (LOWORD(wParam) == 0xC0DE) + { + if (!wa2.isVisRunning()) + { + KillTimer(wa2.getMainWindow(), 0xC0DE); + KillTimer(wa2.getMainWindow(), 0xC0DE + 1); + wa2.toggleVis(); + } + } + if (LOWORD(wParam) == 0xC0DE + 1) + { + KillTimer(wa2.getMainWindow(), 0xC0DE); + KillTimer(wa2.getMainWindow(), 0xC0DE + 1); + } + } + if (!my_set && (uMsg == WM_COMMAND || uMsg == WM_SYSCOMMAND) && LOWORD(wParam) == WINAMP_FILE_SHUFFLE) + { + DWORD v = CallWinampWndProc(hwndDlg, uMsg, wParam, lParam); + int shuf = wa2.getShuffle(); + if (!shuffle.getValueAsInt() != !shuf) shuffle.setValueAsInt(shuf); + return v; + } + if (!my_set && (uMsg == WM_COMMAND || uMsg == WM_SYSCOMMAND) && + (LOWORD(wParam) == WINAMP_FILE_REPEAT) || + (LOWORD(wParam) == WINAMP_FILE_MANUALPLADVANCE)) + { + DWORD v = CallWinampWndProc(hwndDlg, uMsg, wParam, lParam); + int rep = wa2.getRepeat(); + int manadv = wa2.getManualPlaylistAdvance(); + int _v = (rep && manadv) ? -1 : rep; + if (repeat.getValueAsInt() != _v) repeat.setValueAsInt(_v); + return v; + } + if ((uMsg == WM_COMMAND || uMsg == WM_SYSCOMMAND) && LOWORD(wParam) == WINAMP_OPTIONS_DSIZE) + { + int v = CallWinampWndProc(hwndDlg, uMsg, wParam, lParam); + syncDoubleSize(); + return v; + } + if ((uMsg == WM_COMMAND || uMsg == WM_SYSCOMMAND) && (LOWORD(wParam) == WINAMP_OPTIONS_ELAPSED || LOWORD(wParam) == WINAMP_OPTIONS_REMAINING)) + { + int v = CallWinampWndProc(hwndDlg, uMsg, wParam, lParam); + syncDisplayMode(); + return v; + } + } + if (m_are_we_loaded) + { + if (uMsg == WM_COMMAND && LOWORD(wParam) == 40266) + { + ToggleLayout(L"pledit"); + return 0; + } + else if (uMsg == WM_COMMAND && LOWORD(wParam) == 40064) + { + ToggleLayout(L"main"); + return 0; + } + else if (uMsg == WM_CLOSE && wParam == 0xDEADBEEF && lParam == 0xDEADF00D) + { + //if (/*todo: configurable */ 1) + { + uMsg = WM_COMMAND; + wParam = WINAMP_MAIN_WINDOW; + } + } + else if (uMsg == WM_SETTINGCHANGE) + { + // used to track viewport changes so we can update + // e.g. when removing Win8's side-by-side app mode + if (wParam == SPI_SETWORKAREA) + { + KillTimer(wa2.getMainWindow(), VIEWPORT); + viewPort.x = -1; + viewPort.y = -1; + SetTimer(wa2.getMainWindow(), VIEWPORT, 100, ViewPortChanged); + } + } + else if (uMsg == WM_DISPLAYCHANGE) + { + DWORD b = CallWinampWndProc(hwndDlg, uMsg, wParam, lParam); + if (!m_in_skinrefresh) doplaylistcolors(); + + // filter out our internal skin updates + if (wParam && lParam) + { + KillTimer(wa2.getMainWindow(), VIEWPORT); + viewPort.x = LOWORD(lParam); + viewPort.y = HIWORD(lParam); + SetTimer(wa2.getMainWindow(), VIEWPORT, 100, ViewPortChanged); + } + + return b; + } + else if (uMsg == WM_INITMENUPOPUP) + { + HMENU hmenuPopup = (HMENU) wParam; + + if (hmenuPopup && hmenuPopup == mainSendTo.build_hMenu && mainSendTo.mode == 1) + { + int IPC_LIBRARY_SENDTOMENU = SendMessageW(plugin.hwndParent, WM_WA_IPC, (WPARAM)&"LibrarySendToMenu", IPC_REGISTER_WINAMP_IPCMESSAGE); + if (IPC_LIBRARY_SENDTOMENU > 65536 && SendMessageW(plugin.hwndParent, WM_WA_IPC, (WPARAM)&mainSendTo, IPC_LIBRARY_SENDTOMENU) == 0xffffffff) + mainSendTo.mode = 2; + } + + if(hmenuPopup == wa2.getMenuBarMenu(Winamp2FrontEnd::WA2_MAINMENUBAR_PLAY)) + { + CheckMenuItem((HMENU)wParam, WINAMP_FILE_SHUFFLE, wa2.getShuffle() ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem((HMENU)wParam, WINAMP_FILE_REPEAT, wa2.getRepeat() ? MF_CHECKED : MF_UNCHECKED); + } + + if (hmenuPopup == wa2.getPopupMenu() || + hmenuPopup == wa2.getMenuBarMenu(Winamp2FrontEnd::WA2_MAINMENUBAR_OPTIONS) || + hmenuPopup == wa2.getMenuBarMenu(Winamp2FrontEnd::WA2_MAINMENUBAR_WINDOWS)) + { + if (hmenuPopup == wa2.getMenuBarMenu(Winamp2FrontEnd::WA2_MAINMENUBAR_OPTIONS)) { + EnableMenuItem(hmenuPopup, WINAMP_OPTIONS_DSIZE, + MF_BYCOMMAND | (cfg_uioptions_uselocks.getValueAsInt() ? MF_ENABLED : MF_GRAYED)); + } + if (hmenuPopup == wa2.getPopupMenu()){ + EnableMenuItem(GetSubMenu(hmenuPopup, 11 + wa2.adjustOptionsPopupMenu(0)), WINAMP_OPTIONS_DSIZE, + MF_BYCOMMAND | (cfg_uioptions_uselocks.getValueAsInt() ? MF_ENABLED : MF_GRAYED)); + } + + populateWindowsMenus(); + + if ((HMENU)wParam == wa2.getPopupMenu()) + { + for (int i = 0;i < SkinParser::getNumContainers();i++) + { + Container *cont = SkinParser::enumContainer(i); + if (!_wcsicmp(cont->getId(), L"main")) + { + CheckMenuItem((HMENU)wParam, WINAMP_MAIN_WINDOW, cont->isVisible() ? MF_CHECKED : MF_UNCHECKED); + break; + } + } + } + } + } + else if (uMsg == WM_SETFOCUS) + { + if (m_are_we_loaded && !switching_skin) + { + Container *c = SkinParser::getContainer(L"main"); + if (c) + { + Layout *l = c->enumLayout(0); + if (l) l->setFocus(); + } + return 1; + } + } + else if (uMsg == WM_WA_IPC) + { + if (lParam == DEFERREDCALLBACKMSG && DEFERREDCALLBACKMSG > 65536) + { + if (wParam == 1) + { + setDialogBoxesParent(); + return 1; + } + else if (wParam == 2) + { + // embed the windows that are currently visible because at the time the window were originally shown, + // we hadn't been loaded yet + // but then again, we might if it's only a switch skin and not a app load, so let's check anyway + + populateWindowsMenus(); + + doplaylistcolors(); + + if (plWnd == NULL && wa2.isWindowVisible(IPC_GETWND_PE)) + if (!createPl()) wa2.setWindowVisible(IPC_GETWND_PE, 0); + +#ifdef MINIBROWSER_SUPPORT + if (mbWnd == NULL && wa2.isWindowVisible(IPC_GETWND_MB)) + if (!createMb()) wa2.setWindowVisible(IPC_GETWND_MB, 0); +#endif + if (vidWnd == NULL && wa2.isWindowVisible(IPC_GETWND_VIDEO)) + if (!createVid()) wa2.setWindowVisible(IPC_GETWND_VIDEO, 0); + + embedEnumStruct cs = { embedCreateProc, 0 }; + SendMessageW(hwndDlg, WM_WA_IPC, (WPARAM)&cs, IPC_EMBED_ENUM); + + for (int i = 0;i < SkinParser::getNumContainers();i++) + { + Container *cont = SkinParser::enumContainer(i); + if (!_wcsicmp(cont->getId(), L"main")) + { + for (int j = 0;j < cont->getNumLayouts();j++) + { + Layout *l = cont->enumLayout(j); + if (l && l->isVisible()) + { + SetFocus(l->gethWnd()); + break; + } + } + } + } + + Crossfader::onOutputChanged(); + + extern int make_sure_library_is_here_at_startup; + if (make_sure_library_is_here_at_startup) + SendMessageW(wa2.getMainWindow(), WM_COMMAND, ID_FILE_SHOWLIBRARY, 0); + make_sure_library_is_here_at_startup = 0; + + before_startup_callback = 0; + + registerGlobalHotkeys(); + + // force a title update since it's not always correct + // if gen_ml hasn't loaded fully when we're loading + if (wa2.isPlaying()) + g_Core->gotCallback(IPC_CB_MISC_TITLE, 2); + return 1; + } + } + else if (lParam == UPDATEDIALOGBOXPARENTMSG && UPDATEDIALOGBOXPARENTMSG > 65536) + { + wa2.updateDialogBoxParent((HWND)wParam); + } + else switch (lParam) + { + case IPC_GETSKININFO: + return (LRESULT)getSkinInfo(); + case IPC_GETSKININFOW: + return (LRESULT)getSkinInfoW(); + case IPC_SHOW_NOTIFICATION: + return SystemObject::onShowNotification(); + case IPC_CB_VISRANDOM: + { + int v = wParam; + extern _bool visrandom; + if (!!v == !!visrandom.getValueAsInt()) break; + disable_send_visrandom = 1; + visrandom.setValueAsInt(v); + disable_send_visrandom = 0; + break; + } + case IPC_CB_OUTPUTCHANGED: + Crossfader::onOutputChanged(); + break; + + case IPC_CB_ONTOGGLEAOT: + { + if ((WPARAM)cfg_options_alwaysontop.getValueAsInt() != wParam) + cfg_options_alwaysontop.setValueAsInt(wParam); + break; + } + + case IPC_SETIDEALVIDEOSIZE: + { + wa2.setIdealVideoSize(HIWORD(wParam), LOWORD(wParam)); + break; + } + // this is where we detect that wa2 wants to open one of its windows (thru popup menu, button, whatever) + // when this happens, we create a freeform wndembedder if one doesn't already exist. that embedder will + // reparent and resize the wa2 window on its own. when we return, winamp then shows the HWND inside our frame + // as it would show the HWND as a popup normally. + case IPC_CB_ONSHOWWND: + switch (wParam) + { + case IPC_CB_WND_PE: + if (!createPl()) return 0; + break; +#ifdef MINIBROWSER_SUPPORT + case IPC_CB_WND_MB: + if (!createMb()) return 0; + break; +#endif + case IPC_CB_WND_VIDEO: +#ifdef VIDDEBUG + DebugString("Video : Got IPC_ONSHOW\n"); +#endif + if (!createVid()) + { +#ifdef VIDDEBUG + DebugString("Video : SHOW was cancelled by script\n"); +#endif + return 0; + } + break; + default: + DebugStringW(L"embedWnd : Got IPC_ONSHOW\n"); + if (IsWindow((HWND)wParam)) + { + HWND hTarget = (HWND)wParam; + embedWindowState *ws = (embedWindowState*)GetWindowLongPtrW(hTarget, GWLP_USERDATA); + if (ws && ws->me == hTarget) + { + GUID thisguid = EmbedWndGuid(ws).getGuid(); + if (INVALID_GUID == thisguid || NULL == updateEmb(thisguid, ws)) + { + ifc_window *windowParent = createEmb(ws, true); + if (NULL != windowParent) + { + GuiObject *uiObject = windowParent->getGuiObject(); + if (NULL != uiObject) + { + if (0 != (EMBED_FLAGS_FFCALLBACK & ws->flags) && + NULL != ws->callback) + { + ws->callback(ws, FFC_CREATEEMBED, (LPARAM)windowParent); + } + + Layout *layout = uiObject->guiobject_getParentLayout(); + if (NULL != layout) + { + Container *container = layout->getParentContainer(); + if (NULL != container) container->setVisible(1); + else layout->setVisible(1); + windowParent->setVisible(1); + } + } + + } + return (NULL != windowParent); + } + } + } + break; + } + break; + // here we do the reverse, we detect that wa2 wants to close one of its windows, so we destroy our window + // embedder (it will reparent the wa2 window back to its former parent and resize it back to where it was + // on its own). when we return, winamp then hides the window. + + // NOTE! because of this, there might be a split second where the window is seen on the screen as a popup + // after you closed the window (this won't happen for static containers [ie: pledit/video in mmd3] since + // they are hidden rather than destroyed). this can be fixed in the future + case IPC_CB_ONHIDEWND: + switch (wParam) + { + case IPC_CB_WND_PE: + destroyPl(hwndDlg, uMsg, wParam, lParam); + break; +#ifdef MINIBROWSER_SUPPORT + case IPC_CB_WND_MB: + return destroyMb(hwndDlg, uMsg, wParam, lParam); +#endif + case IPC_CB_WND_VIDEO: +#ifdef VIDDEBUG + DebugString("Video : Got IPC_ONHIDE\n"); +#endif + return destroyVid(hwndDlg, uMsg, wParam, lParam); + default: + DebugStringW(L"embedWnd : Got IPC_ONHIDE\n"); + if (IsWindow((HWND)wParam)) + { + HWND h = (HWND)wParam; + embedWindowState *ws = (embedWindowState *)GetWindowLongPtrW(h, GWLP_USERDATA); + if (ws) return destroyEmb(hwndDlg, uMsg, wParam, lParam, ws); + } + break; + } + break; + case IPC_FF_ISMAINWND: + { + for (int i = 0;i < SkinParser::getNumContainers();i++) + { + Container *cont = SkinParser::enumContainer(i); + if (!_wcsicmp(cont->getId(), L"main")) + { + for (int j = 0;j < cont->getNumLayouts();j++) + { + if (cont->enumLayout(j)->gethWnd() == (HWND)wParam) return 1; + } + break; + } + } + return 0; + } + case IPC_FF_GETCONTENTWND: + return (LRESULT)ff_ipc_getContentWnd((HWND)wParam); + case IPC_FF_GETSKINCOLOR: + ff_ipc_getSkinColor((ff_skincolor*)wParam); + return 1; + case IPC_FF_GENSKINBITMAP: + ff_ipc_genSkinBitmap((ff_skinbitmap*)wParam); + return 1; + case IPC_FF_NOTIFYHOTKEY: + if (processGenericHotkey((const char *)wParam)) return 0; // prevent gen_hotkey from processing the hotkey + return 1; // let gen_hotkey process the hotkey + case IPC_GET_GENSKINBITMAP: + if (m_are_we_loaded) + { + if (wParam == 0) + { + if (m_lastskin_dir[0]) + { + HBITMAP bm; + wchar_t bitmapfilename[MAX_PATH] = {0}; + PathCombineW(bitmapfilename, m_lastskin_dir, L"genex.bmp"); + bm = (HBITMAP)LoadImageW(NULL, bitmapfilename, IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION | LR_LOADFROMFILE); + if (bm) return (LRESULT)bm; + } + return (LRESULT)ff_genwa2skinbitmap(); + } + else if (wParam == 4) + { + // TODO need to make sure we're covering everything needed for all of this!! + int buf[6] = {0}; + // active text + if (WASABI_API_SKIN->skin_getColorElementRef(L"wasabi.list.text.current")) + buf[1] = SkinColor(L"wasabi.list.text.current"); + /*else + { + COLORREF act = Blenders::BLEND_AVG(selected, buf[0]); + act = (lumidiff(act, buf[0]) < 0x3F || lumidiff(act, buf[2]) < 0x2F) ? Blenders::BLEND_AVG(buf[2], 0xFFFFFFFF) : act; + act = (lumidiff(act, buf[0]) < 0x3F || lumidiff(act, buf[2]) < 0x2F) ? Blenders::BLEND_AVG(buf[2], 0xFF101010) : act; + act = (lumidiff(act, buf[0]) < 0x3F || lumidiff(act, buf[2]) < 0x2F) ? Blenders::BLEND_AVG(selected, buf[0]) : act; + act = (lumidiff(act, buf[0]) < 0x3F || lumidiff(act, buf[2]) < 0x2F) ? Blenders::BLEND_AVG(selected, buf[2]) : act; + act = (lumidiff(act, buf[0]) < 0x3F || lumidiff(act, buf[2]) < 0x2F) ? Blenders::BLEND_AVG(buf[0], buf[2]) : act; + buf[1] = act & 0xFFFFFF; + }*/ + return buf[1]; + } + } + break; + case IPC_FF_ONCOLORTHEMECHANGED: + { + embedEnumStruct cs = { embedUpdateColorProc, 0 }; + SendMessageW(hwndDlg, WM_WA_IPC, (WPARAM)&cs, IPC_EMBED_ENUM); + if (wParam != 0xf00d) doplaylistcolors(); + } + break; + case IPC_PLAYLIST_MODIFIED: + { + Wa2PlaylistEditor::_onPlaylistModified(); + } + break; + } + } + else if (uMsg == WM_COMMAND && (LOWORD(wParam) == 40144 || LOWORD(wParam) == 40148)) + { + // seek left/right, we need to disable them if we are in a menu, because this is a + // plugin (ie: milkdrop) sending us the command manually as a forward rather than + // sending us the WM_KEY* directly + if (WASABI_API_WND->isKeyboardLocked()) return 0; + } + else if ((uMsg == WM_COMMAND || uMsg == WM_SYSCOMMAND) && LOWORD(wParam) >= 43000 && LOWORD(wParam) < ffwindowstop) + { + int id = LOWORD(wParam) - 43000; + + int n = SkinParser::getNumContainers(); + if (id < n) + { + Container *cont = SkinParser::enumContainer(id); + if (cont) cont->toggle(); + return 1; + } + id -= n; + + n = WASABI_API_WNDMGR->autopopup_getNumGuids(); + if (id < n) + { + GUID guid = WASABI_API_WNDMGR->autopopup_enumGuid(id); + if (guid != INVALID_GUID) + WASABI_API_WNDMGR->skinwnd_toggleByGuid(guid); + return 1; + } + id -= n; + + n = WASABI_API_WNDMGR->autopopup_getNumGroups(); + if (id < n) + { + const wchar_t *gid = WASABI_API_WNDMGR->autopopup_enumGroup(id); + if (gid && *gid) + WASABI_API_WNDMGR->skinwnd_toggleByGroupId(gid); + return 1; + } + + /* for (int c=0;cgetName() == NULL) continue; + if (id == 0) { + if (cont) + cont->toggle(); + return 1; + } + id--; + } + } + int n = WASABI_API_WNDMGR->autopopup_getNumGuids(); + for (c=0;cautopopup_enumGuid(c); + const char *groupdesc = WASABI_API_WNDMGR->autopopup_enumGuidDescription(c); + if (guid != INVALID_GUID && groupdesc && *groupdesc) { + if (id == 0) { + WASABI_API_WNDMGR->skinwnd_toggleByGuid(guid); + return 1; + } + id--; + } + } + n = WASABI_API_WNDMGR->autopopup_getNumGroups(); + for (c=0;cautopopup_enumGroup(c); + const char *groupdesc = WASABI_API_WNDMGR->autopopup_enumGroupDescription(c); + if (id && groupdesc && *gid && *groupdesc) { + if (id == 0) { + WASABI_API_WNDMGR->skinwnd_toggleByGroupId(gid); + return 1; + } + id--; + } + }*/ + return 0; + } + else if ((uMsg == WM_COMMAND || uMsg == WM_SYSCOMMAND) && LOWORD(wParam) >= 44000 && LOWORD(wParam) < ffoptionstop) + { + int id = LOWORD(wParam); + MenuActions::toggleOption(id - 44000); + } + else if ((uMsg == WM_COMMAND || uMsg == WM_SYSCOMMAND) && LOWORD(wParam) >= 44500 && LOWORD(wParam) < 45000) + { + int id = LOWORD(wParam) - 44500; + WASABI_API_SKIN->colortheme_setColorSet(WASABI_API_SKIN->colortheme_enumColorSet(id)); + } + else if ((uMsg == WM_COMMAND || uMsg == WM_SYSCOMMAND) && LOWORD(wParam) >= 42000 && LOWORD(wParam) < ffwoptionstop) + { + int id = LOWORD(wParam); + MenuActions::toggleWindowOption(id - 42000); + } + else if ((uMsg == WM_COMMAND || uMsg == WM_SYSCOMMAND) && LOWORD(wParam) == WINAMP_MAIN_WINDOW) + { + for (int i = 0;i < SkinParser::getNumContainers();i++) + { + Container *cont = SkinParser::enumContainer(i); + if (!_wcsicmp(cont->getId(), L"main")) + { + cont->toggle(); + return 1; + } + } + } + else if ((uMsg == WM_COMMAND || uMsg == WM_SYSCOMMAND) && LOWORD(wParam) == WINAMP_OPTIONS_EQ) + { + if (!gothrueqmsg) + return 0; + } + else if ((uMsg == WM_COMMAND || uMsg == WM_SYSCOMMAND)) + { + switch (LOWORD(wParam)) + { + case ID_CONTROLMENU_OPACITY_10: controlOpacity(10); return 0; + case ID_CONTROLMENU_OPACITY_20: controlOpacity(20); return 0; + case ID_CONTROLMENU_OPACITY_30: controlOpacity(30); return 0; + case ID_CONTROLMENU_OPACITY_40: controlOpacity(40); return 0; + case ID_CONTROLMENU_OPACITY_50: controlOpacity(50); return 0; + case ID_CONTROLMENU_OPACITY_60: controlOpacity(60); return 0; + case ID_CONTROLMENU_OPACITY_70: controlOpacity(70); return 0; + case ID_CONTROLMENU_OPACITY_80: controlOpacity(80); return 0; + case ID_CONTROLMENU_OPACITY_90: controlOpacity(90); return 0; + case ID_CONTROLMENU_OPACITY_100: controlOpacity(100); return 0; + case ID_CONTROLMENU_SCALING_50: controlScaling(0.5); return 0; + case ID_CONTROLMENU_SCALING_75: controlScaling(0.75); return 0; + case ID_CONTROLMENU_SCALING_100: controlScaling(1.0); return 0; + case ID_CONTROLMENU_SCALING_125: controlScaling(1.25); return 0; + case ID_CONTROLMENU_SCALING_150: controlScaling(1.5); return 0; + case ID_CONTROLMENU_SCALING_200: controlScaling(2.0); return 0; + case ID_CONTROLMENU_SCALING_250: controlScaling(2.5); return 0; + case ID_CONTROLMENU_SCALING_300: controlScaling(3.0); return 0; + case ID_CONTROLMENU_SCALING_LOCKED: lockScaling(1); return 0; + case ID_CONTROLMENU_SCALING_FOLLOWDOUBLESIZE: lockScaling(0); return 0; + case ID_CONTROLMENU_SCALING_CUSTOM: customScaling(); return 0; + case ID_CONTROLMENU_OPACITY_CUSTOM: customOpacity(); return 0; + case ID_CONTROLMENU_OPACITY_AUTO100_HOVER: autoOpacifyHover(); return 0; + case ID_CONTROLMENU_OPACITY_AUTO100_FOCUS: autoOpacifyFocus(); return 0; + case SC_MOVE: + { + Layout *l = SkinParser::getMainLayout(); + if (l != NULL) + { + Container *c = l->getParentContainer(); + if (c) + { + Layout *ll = c->getCurrentLayout(); + if (ll) l = ll; + if (l) + { + if (!l->isVisible()) l->setVisible(1); + return SendMessageW(l->gethWnd(), WM_SYSCOMMAND, wParam, lParam); + } + } + } + break; + } + case ID_CONTROLMENU_TOOLBAR_DISABLED: controlAppBar(APPBAR_NOTDOCKED); break; + case ID_CONTROLMENU_TOOLBAR_TOP: controlAppBar(APPBAR_TOP); break; + case ID_CONTROLMENU_TOOLBAR_LEFT: controlAppBar(APPBAR_LEFT); break; + case ID_CONTROLMENU_TOOLBAR_RIGHT: controlAppBar(APPBAR_RIGHT); break; + case ID_CONTROLMENU_TOOLBAR_BOTTOM: controlAppBar(APPBAR_BOTTOM); break; + case ID_CONTROLMENU_TOOLBAR_ALWAYSONTOP: controlAppBarAOT(); break; + case ID_CONTROLMENU_TOOLBAR_AUTOHIDE: controlAppBarAH(); break; + case ID_CONTROLMENU_TOOLBAR_AUTODOCKONDRAG: cfg_options_appbarondrag = !cfg_options_appbarondrag; break; + } + } + } + + return CallWinampWndProc(hwndDlg, uMsg, wParam, lParam); +} + +void onLayoutChanged() +{ + if (DEFERREDCALLBACKMSG > 65536) + PostMessage(wa2.getMainWindow(), WM_WA_IPC, 1, DEFERREDCALLBACKMSG); +} + +#ifdef DEBUG_CAPTURES +int tid = 0; + +VOID TimerProc(HWND hwnd, UINT uMsg, UINT idEvent, DWORD time) +{ + DebugString("Capture belongs to %x (foregroundwnd = %x)\n", GetCapture(), GetForegroundWindow()); +} +#endif + +template +static void ServiceBuild(api_T *&api_t, GUID factoryGUID_t) +{ + if (WASABI_API_SVC) + { + waServiceFactory *factory = WASABI_API_SVC->service_getServiceByGuid(factoryGUID_t); + if (factory) + api_t = reinterpret_cast( factory->getInterface() ); + } +} + +template +static void ServiceRelease(api_T *api_t, GUID factoryGUID_t) +{ + if (WASABI_API_SVC && api_t) + { + waServiceFactory *factory = WASABI_API_SVC->service_getServiceByGuid(factoryGUID_t); + if (factory) + factory->releaseInterface(api_t); + } + api_t = NULL; +} + +//----------------------------------------------------------------------------------------------- +// initializes freeform library +//----------------------------------------------------------------------------------------------- +int m_loaded_at_all = 0; +StringW g_resourcepath; + +void initFFApi() +{ + if ( !m_loaded_at_all ) + { + m_loaded_at_all = 1; + api_service *svc = (api_service *)SendMessageW( wa2.getMainWindow(), WM_WA_IPC, 0, IPC_GET_API_SERVICE ); + + ServiceBuild( AGAVE_API_PLAYLISTS, api_playlistsGUID ); + ServiceBuild( AGAVE_API_ALBUMART, albumArtGUID ); + ServiceBuild( AGAVE_API_PLAYLISTMANAGER, api_playlistmanagerGUID ); + ServiceBuild( WAC_API_DOWNLOADMANAGER, DownloadManagerGUID ); + ServiceBuild( WASABI_API_COLORTHEMES, ColorThemesAPIGUID ); + ServiceBuild( WASABI_API_PALETTE, PaletteManagerGUID ); + ServiceBuild( WASABI_API_THREADPOOL, ThreadPoolGUID ); + + ApiInit::init( hInstance, wa2.getMainWindow(), svc ); + cfgitems = new Wa2CfgItems(); + +#ifdef DEBUG_CAPTURES + tid = SetTimer( NULL, 0x159, 50, (TIMERPROC)TimerProc ); +#endif + + wchar_t filename[ WA_MAX_PATH ] = { 0 }; + GetModuleFileNameW( hInstance, filename, WA_MAX_PATH ); + PathParserW pp( filename ); + StringW path; + for ( int i = 0; i < pp.getNumStrings() - 1; i++ ) + { + path.AppendPath( pp.enumString( i ) ); + } + path.AppendPath( L"freeform" ); + path.AppendFolder( L"wacs" ); + + // we can load a somewhat restricted version of the wac format + + ComponentManager::loadAll( path ); + ComponentManager::postLoad(); + + ApiInit::widgets->loadResources(); + + startFSMonitor(); + } + cfg_options_alwaysontop.setValueAsInt( wa2.isOnTop() ); +} + +//----------------------------------------------------------------------------------------------- +// shutdown ff lib +//----------------------------------------------------------------------------------------------- +void shutdownFFApi() +{ + if (m_loaded_at_all) + { + ServiceRelease( AGAVE_API_PLAYLISTS, api_playlistsGUID); + ServiceRelease( AGAVE_API_ALBUMART, albumArtGUID); + ServiceRelease( AGAVE_API_PLAYLISTMANAGER, api_playlistmanagerGUID); + ServiceRelease( WAC_API_DOWNLOADMANAGER, DownloadManagerGUID); + ServiceRelease( WASABI_API_COLORTHEMES, ColorThemesAPIGUID); + ServiceRelease( WASABI_API_PALETTE, PaletteManagerGUID); + ServiceRelease( WASABI_API_THREADPOOL, ThreadPoolGUID); + + stopFSMonitor(); + + // shutdown the library +#ifdef DEBUG_CAPTURES + KillTimer(NULL, tid); +#endif + + delete cfgitems; cfgitems = NULL; + ApiInit::shutdown(); +ComponentManager::unloadAll(); + + m_loaded_at_all = 0; + } +} + +static void MakeControlMenu() +{ + if (controlmenu) + return ; + + controlmenu = WASABI_API_LOADMENU(IDR_CONTROLMENU); + + if (!Wasabi::Std::Wnd::isDesktopAlphaAvailable()) + EnableMenuItem(controlmenu, 0, MF_BYPOSITION | MF_GRAYED); +} + +//----------------------------------------------------------------------------------------------- +// go freeform +//----------------------------------------------------------------------------------------------- +void init_inst() +{ + if ( m_are_we_loaded ) + return; + + // set the classic main window to be transparent + // so we can move it around the screen to allow + // certain Windows 8 modes as well as improving + // where it is placed for some multi-mon setups + Wasabi::Std::Wnd::setLayeredWnd( wa2.getMainWindow(), 1 ); + Wasabi::Std::Wnd::setLayeredAlpha( wa2.getMainWindow(), 0 ); + + initEgg(); + + wa2.setDrawBorders( 0 ); + wa2.disableSkinnedCursors( 1 ); + + int wasminimized = IsIconic( wa2.getMainWindow() ); + if ( wasminimized ) + ShowWindow( wa2.getMainWindow(), SW_RESTORE ); + + MakeControlMenu(); + + going_freeform = 1; + + Wa2WndEmbed::rememberVisibleWindows(); + + if ( wa2.isWindowVisible( IPC_GETWND_PE ) ) + { + if ( !m_loading_at_startup ) WritePrivateProfileStringW( L"gen_ff", L"classicpe", L"1", INI_FILE ); + } + else + if ( !m_loading_at_startup ) WritePrivateProfileStringW( L"gen_ff", L"classicpe", L"0", INI_FILE ); + + if ( wa2.isWindowVisible( IPC_GETWND_EQ ) ) + { + gothrueqmsg = 1; + SendMessageW( plugin.hwndParent, WM_COMMAND, WINAMP_OPTIONS_EQ, 0 ); + gothrueqmsg = 0; + if ( !m_loading_at_startup ) WritePrivateProfileStringW( L"gen_ff", L"classiceq", L"1", INI_FILE ); + } + else + if ( !m_loading_at_startup ) WritePrivateProfileStringW( L"gen_ff", L"classiceq", L"0", INI_FILE ); + + removeEq(); + + if ( SendMessageW( plugin.hwndParent, WM_WA_IPC, 0, IPC_ISMAINWNDVISIBLE ) ) + { + SendMessageW( plugin.hwndParent, WM_COMMAND, WINAMP_MAIN_WINDOW, 0 ); + if ( !m_loading_at_startup ) WritePrivateProfileStringW( L"gen_ff", L"classicmw", L"1", INI_FILE ); + } + else + if ( !m_loading_at_startup ) WritePrivateProfileStringW( L"gen_ff", L"classicmw", L"0", INI_FILE ); + + m_are_we_loaded = 1; + + initFFApi(); + + // redirect drag and drop to winamp2 by default + WASABI_API_WND->setDefaultDropTarget( (void *)wa2.getDropTarget() ); + + // this installs a bunch of predefined groups to map wa3 to wa2 functionnality + groups = new Wa2Groupdefs(); + + // now load a skin ! the path to the skin is extracted and temporarilly becomes the official skins directory + + StringW skinname = m_lastskin_nam; + + removeSkinExtension( skinname ); + + before_startup_callback = 1; + WASABI_API_SKIN->skin_switchSkin( skinname, m_lastskin_dir ); + + /* // if we wanted to have drag&drop support for just the main window, we'd do it this way :-) + Container *maincontainer = SkinParser::getContainer("main"); + if (maincontainer != NULL) { + for (int i=0;igetNumLayouts();i++) { + Layout *layout = maincontainer->enumLayout(i); + if (layout != NULL) { + layout->setDropTarget((void *)wa2.getDropTarget()); + } + } + }*/ + + // send event for dialog parent + onLayoutChanged(); + + if ( wa2.export_sa_setreq ) + wa2.export_sa_setreq( 1 ); + + shuffle.setValueAsInt( wa2.getShuffle() ); + + int rep = wa2.getRepeat(); + int manadv = wa2.getManualPlaylistAdvance(); + + disable_set_wa2_repeat = 1; + repeat.setValueAsInt( ( rep && manadv ) ? -1 : rep ); + disable_set_wa2_repeat = 0; + + if ( DEFERREDCALLBACKMSG > 65536 ) + PostMessage( wa2.getMainWindow(), WM_WA_IPC, 2, DEFERREDCALLBACKMSG ); + + // so if some embedwindows are already visible, they update their look + PostMessage( wa2.getMainWindow(), WM_WA_IPC, 0xf00d, IPC_FF_ONCOLORTHEMECHANGED ); + + // monitor color theme + colorThemeMonitor = new ColorThemeMonitor(); + + syncDoubleSize( 1 ); + syncDisplayMode(); + + going_freeform = 0; + + if ( wa2.getWnd( IPC_GETWND_VIDEO ) ) + { + oldVideoWnd = wa2.getWnd( IPC_GETWND_VIDEO ); + if ( !oldVideoWndProc ) + oldVideoWndProc = (WNDPROC)SetWindowLongPtrW( oldVideoWnd, GWLP_WNDPROC, (LONG_PTR)newVideoWndProc ); + } + + if ( wasminimized ) + ShowWindow( wa2.getMainWindow(), SW_MINIMIZE ); + + if ( eggstat ) + SetTimer( wa2.getMainWindow(), UPDATE_EGG, 25, NULL ); +} + +void quit_inst() +{ + if (!m_are_we_loaded) return ; + + KillTimer(wa2.getMainWindow(), UPDATE_EGG); + + wa2.setDrawBorders(1); + wa2.disableSkinnedCursors(0); + + KillTimer(wa2.getMainWindow(), 0xC0DE); + KillTimer(wa2.getMainWindow(), 0xC0DE + 1); + + EnableMenuItem(wa2.getMenuBarMenu(Winamp2FrontEnd::WA2_MAINMENUBAR_OPTIONS), WINAMP_OPTIONS_DSIZE, MF_ENABLED); + EnableMenuItem(GetSubMenu(wa2.getPopupMenu(), 11 + wa2.adjustOptionsPopupMenu(0)), WINAMP_OPTIONS_DSIZE, MF_ENABLED); + + if (oldVideoWnd && oldVideoWndProc) + SetWindowLongPtrW(oldVideoWnd, GWLP_WNDPROC, (LONG_PTR)oldVideoWndProc); + oldVideoWndProc = 0; + oldVideoWnd = 0; + + removeWindowOptionsFromContextMenu(); + + delete mainLayoutMonitor; + mainLayoutMonitor=0; + + wa2.setDialogBoxParent(NULL); + unpopulateWindowsMenus(); + restoreEq(); + + // unload the skin -- skinspath is restored to default + WASABI_API_SKIN->skin_unloadSkin(); + + // delete predefined groups service + delete groups; groups = NULL; + + // delete options + delete ffoptions; ffoptions = NULL; + + // stop monitoring color theme + delete colorThemeMonitor; colorThemeMonitor = NULL; + + if (wa2.export_sa_setreq) wa2.export_sa_setreq(0); + + m_are_we_loaded = 0; + + int classicmw = GetPrivateProfileIntW(L"gen_ff", L"classicmw", 1, INI_FILE); + if (classicmw && !SendMessageW(plugin.hwndParent, WM_WA_IPC, 0, IPC_ISMAINWNDVISIBLE)) + SendMessageW(plugin.hwndParent, WM_COMMAND, WINAMP_MAIN_WINDOW, 0); + + CheckMenuItem(wa2.getPopupMenu(), WINAMP_MAIN_WINDOW, classicmw ? MF_CHECKED : MF_UNCHECKED); + + if (GetPrivateProfileIntW(L"gen_ff", L"classiceq", 1, INI_FILE) && !wa2.isWindowVisible(IPC_GETWND_EQ)) + { + gothrueqmsg = 1; + SendMessageW(plugin.hwndParent, WM_COMMAND, WINAMP_OPTIONS_EQ, 0); + gothrueqmsg = 0; + } + + if (GetPrivateProfileIntW(L"gen_ff", L"classicpe", 1, INI_FILE) && !wa2.isWindowVisible(IPC_GETWND_PE)) + { + SendMessageW(plugin.hwndParent, WM_COMMAND, WINAMP_OPTIONS_PLEDIT, 0); + } + + // restore the classic main window to be solid + Wasabi::Std::Wnd::setLayeredWnd(wa2.getMainWindow(), 0); +} + +//----------------------------------------------------------------------------------------------- +// init (from Winamp2) +//----------------------------------------------------------------------------------------------- +int init() +{ + // loader so that we can get the localisation service api for use + WASABI_API_SVC = (api_service*) SendMessageW(plugin.hwndParent, WM_WA_IPC, 0, IPC_GET_API_SERVICE); + if (WASABI_API_SVC == (api_service*)1) WASABI_API_SVC = NULL; + if (!WASABI_API_SVC || WASABI_API_SVC == (api_service *)1) + return GEN_INIT_FAILURE; + + static wchar_t modskin[128]; + INI_FILE = (wchar_t*) SendMessageW(plugin.hwndParent, WM_WA_IPC, 0, IPC_GETINIFILEW); + INI_DIR = (wchar_t*) SendMessageW(plugin.hwndParent, WM_WA_IPC, 0, IPC_GETINIDIRECTORYW); + + waServiceFactory *sf = WASABI_API_SVC->service_getServiceByGuid(languageApiGUID); + if (sf) WASABI_API_LNG = reinterpret_cast(sf->getInterface()); + + // need to have this initialised before we try to do anything with localisation features + WASABI_API_START_LANG(plugin.hDllInstance,GenFFLangGUID); + + swprintf(szDescription, ARRAYSIZE(szDescription), + WASABI_API_LNGSTRINGW(IDS_NULLSOFT_MODERN_SKINS), VERSION); + plugin.description = (char*)szDescription; + + wa2.init(plugin.hwndParent); + + DEFERREDCALLBACKMSG = SendMessageW(plugin.hwndParent, WM_WA_IPC, (WPARAM)"gen_ff_deferred", IPC_REGISTER_WINAMP_IPCMESSAGE); + UPDATEDIALOGBOXPARENTMSG = SendMessageW(plugin.hwndParent, WM_WA_IPC, (WPARAM)"gen_ff_update", IPC_REGISTER_WINAMP_IPCMESSAGE); + + // subclass the Winamp2 main window to receive our callbacks + wa_oldWndProc = (WNDPROC) SetWindowLongPtrW(wa2.getMainWindow(), GWLP_WNDPROC, (LONG_PTR)wa_newWndProc); + + ffPrefsItem.dlgID = IDD_PREFS; + ffPrefsItem.name = WASABI_API_LNGSTRINGW_BUF(IDS_MODERN_SKINS,modskin,128); + ffPrefsItem.proc = (void*)ffPrefsProc; + ffPrefsItem.hInst = WASABI_API_LNG_HINST; + ffPrefsItem.where = 2; //skins subtreeitem + SendMessageW(plugin.hwndParent, WM_WA_IPC, (WPARAM)&ffPrefsItem, IPC_ADD_PREFS_DLGW); + + checkMlPresent(); + + m_loading_at_startup = 1; + onSkinSwitch(); + m_loading_at_startup = 0; + return 0; +} + +void RestoreClassicWinamp(int was_loaded) +{ + // JF> my proposed fix to the problems :) + // investigating doing this from winamp.exe on startup, gotta figure it out + if (INI_FILE && was_loaded) // restore winamp.ini to about what it shoulda been + { + int classicpews = GetPrivateProfileIntW(L"gen_ff", L"classicplws", 0, INI_FILE); + int classicwidth = GetPrivateProfileIntW(L"gen_ff", L"classicplwidth", 275, INI_FILE); + int classicheight = GetPrivateProfileIntW(L"gen_ff", L"classicplheight", 145, INI_FILE); + int classicmw = GetPrivateProfileIntW(L"gen_ff", L"classicmw", 1, INI_FILE); + int classiceq = GetPrivateProfileIntW(L"gen_ff", L"classiceq", 1, INI_FILE); + wchar_t buf[64] = {0}; + + wsprintfW(buf, L"%d", classicheight); + if (classicpews) + { + WritePrivateProfileStringW(L"winamp", L"pe_height", L"14", INI_FILE); + WritePrivateProfileStringW(L"winamp", L"pe_height_ws", buf, INI_FILE); + } + else + { + WritePrivateProfileStringW(L"winamp", L"pe_height", buf, INI_FILE); + WritePrivateProfileStringW(L"winamp", L"pe_height_ws", L"", INI_FILE); + } + wsprintfW(buf, L"%d", classicwidth); + WritePrivateProfileStringW(L"winamp", L"pe_width", buf, INI_FILE); + + WritePrivateProfileStringW(L"winamp", L"eq_open", classiceq ? L"1" : L"0", INI_FILE); + WritePrivateProfileStringW(L"winamp", L"mw_open", classicmw ? L"1" : L"0", INI_FILE); + } +} + +//----------------------------------------------------------------------------------------------- +// quit (from Winamp 2) +//----------------------------------------------------------------------------------------------- +void quit() +{ + int was_loaded = m_are_we_loaded; + quit_inst(); + + RestoreClassicWinamp(was_loaded); + // restore wa2's windowproc + //SetWindowLong(wa2.getMainWindow(), GWL_WNDPROC, (LONG)wa_oldWndProc); + shutdownFFApi(); +} + +//----------------------------------------------------------------------------------------------- +// About box wndproc +//----------------------------------------------------------------------------------------------- +ifc_window *about_group = NULL; +StringW oldrenderer; +BOOL CALLBACK aboutProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch (uMsg) + { + case WM_INITDIALOG: + { + oldrenderer = cfg_options_fontrenderer.getValue(); + if (!WCSCASEEQLSAFE(cfg_options_fontrenderer.getValue(), L"FreeType")) + cfg_options_fontrenderer.setValue(L"FreeType"); + about_group = WASABI_API_SKIN->group_create(L"wasabi.gen_ff.about"); + about_group->setVirtual(0); + HWND w = GetDlgItem(hwndDlg, IDC_STATIC_GROUP); + about_group->setStartHidden(1); + about_group->init(WASABI_API_WND->main_getRootWnd(), 1); + SetWindowLong(about_group->gethWnd(), GWL_STYLE, GetWindowLong(about_group->gethWnd(), GWL_STYLE) | WS_CHILD); + SetParent(about_group->gethWnd(), w); + SetWindowLong(w, GWL_STYLE, GetWindowLong(w, GWL_STYLE) | WS_CLIPCHILDREN); + RECT r; + GetClientRect(w, &r); + about_group->resize(r.left + 1, r.top + 1, r.right - r.left - 2, r.bottom - r.top - 2); + about_group->setVisible(1); + C_Group g(about_group->getGuiObject()->guiobject_getScriptObject()); + ScriptObject *ver = g.findObject(L"version"); + if (ver) + { + C_Text t(ver); + t.setText(StringPrintfW(L". © 2003-2023 Winamp SA %s", VERSION)); + } + ShowWindow(about_group->gethWnd(), SW_NORMAL); + return 1; + } + case WM_DESTROY: + WASABI_API_SKIN->group_destroy(about_group); + about_group = NULL; + break; + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDOK: case IDCANCEL: + if (!WCSCASEEQLSAFE(cfg_options_fontrenderer.getValue(), oldrenderer)) + cfg_options_fontrenderer.setValue(oldrenderer); + EndDialog(hwndDlg, 0); + return 0; + } + return 0; + } + return 0; +} + +//----------------------------------------------------------------------------------------------- +// configure plugin (from Winamp 2) +//----------------------------------------------------------------------------------------------- +void config() +{ + if (m_loaded_at_all) + { + StringW skin = WASABI_API_SKIN->getSkinName(); + wchar_t _skName[64] = {0}; + WASABI_API_LNGSTRINGW_BUF(IDS_NO_SKIN_LOADED_,_skName,64); + + if ((!skin.iscaseequal(_skName)) || guiTree->getNumObject() > 0) + { + WASABI_API_DIALOGBOXW(IDD_ABOUT, wa2.getPreferencesWindow(), aboutProc); + return ; + } + } + + MSGBOXPARAMSW msgbx = {sizeof(MSGBOXPARAMS),0}; + msgbx.lpszText = WASABI_API_LNGSTRINGW(IDS_MODERN_SKIN_SUPPORT_CLASSIC); + msgbx.lpszCaption = szDescription; + msgbx.lpszIcon = MAKEINTRESOURCEW(102); + msgbx.hInstance = GetModuleHandle(0); + msgbx.dwStyle = MB_USERICON; + MessageBoxIndirectW(&msgbx); +} + +//----------------------------------------------------------------------------------------------- +// expose the genpurp plugin interface to dll +//----------------------------------------------------------------------------------------------- +extern "C" __declspec(dllexport) winampGeneralPurposePlugin * winampGetGeneralPurposePlugin() { return &plugin; } + +//----------------------------------------------------------------------------------------------- +// a window was right clicked where there was no custom context menu available, spawn Wa2's menu +//----------------------------------------------------------------------------------------------- +void appContextMenu(ifc_window *w) +{ + if (!WASABI_API_WND->rootwndIsValid(w)) return ; + WASABI_API_WND->appdeactivation_setbypass(1); + SetCursor(LoadCursor(NULL, IDC_ARROW)); + w->setFocus(); + DWORD p = GetMessagePos(); + int x = GET_X_LPARAM(p); + int y = GET_Y_LPARAM(p); + addWindowOptionsToContextMenu(w); + wa2.triggerPopupMenu(x, y); + WASABI_API_WND->appdeactivation_setbypass(0); +} + +//----------------------------------------------------------------------------------------------- +void updateControlMenu(ifc_window *w) +{ + int curalpha = 255; + double curratio = 1.; + int opacitysafe = 0; + int scalelocked = 0; + int auto100_hover = 0; + int auto100_focus = 0; + + if (g_controlMenuTarget == NULL) return ; + + if (w) + { + ifc_window *rootparent = g_controlMenuTarget->getDesktopParent(); + if (rootparent && rootparent->getInterface(layoutGuid)) + { + Layout *l = static_cast(rootparent); + opacitysafe = l->isTransparencySafe(); + scalelocked = l->isScaleLocked(); + curratio = w->getRenderRatio(); + if (!cfg_uioptions_linkallalpha.getValueAsInt()) + { + auto100_hover = l->getAutoOpacify() == 1; + auto100_focus = l->getAutoOpacify() == 2; + curalpha = l->getAlpha(); + } + else + { + auto100_hover = l->getAlphaMgr()->getAutoOpacify() == 1; + auto100_focus = l->getAlphaMgr()->getAutoOpacify() == 2; + curalpha = l->getAlphaMgr()->getGlobalAlpha(); + } + } + } + + HMENU ctrlmenu = GetSubMenu(controlmenu, 0); + EnableMenuItem(ctrlmenu, 0, MF_BYPOSITION | (opacitysafe ? MF_ENABLED : MF_GRAYED)); + HMENU scalemenu = GetSubMenu(ctrlmenu, 1); + HMENU alphamenu = GetSubMenu(ctrlmenu, 0); + int uselocks = cfg_uioptions_uselocks.getValueAsInt(); + CheckMenuItem(scalemenu, ID_CONTROLMENU_SCALING_LOCKED, MF_BYCOMMAND | ((scalelocked || !uselocks) ? MF_CHECKED : MF_UNCHECKED)); + EnableMenuItem(scalemenu, ID_CONTROLMENU_SCALING_LOCKED, MF_BYCOMMAND | (uselocks ? MF_ENABLED : MF_GRAYED)); + CheckMenuItem(scalemenu, ID_CONTROLMENU_SCALING_FOLLOWDOUBLESIZE, MF_BYCOMMAND | (scalelocked || !uselocks ? MF_UNCHECKED : MF_CHECKED)); + EnableMenuItem(scalemenu, ID_CONTROLMENU_SCALING_FOLLOWDOUBLESIZE, MF_BYCOMMAND | (uselocks ? MF_ENABLED : MF_GRAYED)); + CheckMenuItem(alphamenu, ID_CONTROLMENU_OPACITY_AUTO100_HOVER, MF_BYCOMMAND | (auto100_hover ? MF_CHECKED : MF_UNCHECKED)); + CheckMenuItem(alphamenu, ID_CONTROLMENU_OPACITY_AUTO100_FOCUS, MF_BYCOMMAND | (auto100_focus ? MF_CHECKED : MF_UNCHECKED)); + + int v = (int)((curalpha / 255.0f * 100.0f) + 0.5f); + int u = (int)((curratio * 100.0f) + 0.5f); + + CheckMenuItem(alphamenu, ID_CONTROLMENU_OPACITY_10, MF_BYCOMMAND | (v == 10 ? MF_CHECKED : MF_UNCHECKED)); + CheckMenuItem(alphamenu, ID_CONTROLMENU_OPACITY_20, MF_BYCOMMAND | (v == 20 ? MF_CHECKED : MF_UNCHECKED)); + CheckMenuItem(alphamenu, ID_CONTROLMENU_OPACITY_30, MF_BYCOMMAND | (v == 30 ? MF_CHECKED : MF_UNCHECKED)); + CheckMenuItem(alphamenu, ID_CONTROLMENU_OPACITY_40, MF_BYCOMMAND | (v == 40 ? MF_CHECKED : MF_UNCHECKED)); + CheckMenuItem(alphamenu, ID_CONTROLMENU_OPACITY_50, MF_BYCOMMAND | (v == 50 ? MF_CHECKED : MF_UNCHECKED)); + CheckMenuItem(alphamenu, ID_CONTROLMENU_OPACITY_60, MF_BYCOMMAND | (v == 60 ? MF_CHECKED : MF_UNCHECKED)); + CheckMenuItem(alphamenu, ID_CONTROLMENU_OPACITY_70, MF_BYCOMMAND | (v == 70 ? MF_CHECKED : MF_UNCHECKED)); + CheckMenuItem(alphamenu, ID_CONTROLMENU_OPACITY_80, MF_BYCOMMAND | (v == 80 ? MF_CHECKED : MF_UNCHECKED)); + CheckMenuItem(alphamenu, ID_CONTROLMENU_OPACITY_90, MF_BYCOMMAND | (v == 90 ? MF_CHECKED : MF_UNCHECKED)); + CheckMenuItem(alphamenu, ID_CONTROLMENU_OPACITY_100, MF_BYCOMMAND | (v == 100 ? MF_CHECKED : MF_UNCHECKED)); + CheckMenuItem(scalemenu, ID_CONTROLMENU_SCALING_50, MF_BYCOMMAND | (u == 50 ? MF_CHECKED : MF_UNCHECKED)); + CheckMenuItem(scalemenu, ID_CONTROLMENU_SCALING_75, MF_BYCOMMAND | (u == 75 ? MF_CHECKED : MF_UNCHECKED)); + CheckMenuItem(scalemenu, ID_CONTROLMENU_SCALING_100, MF_BYCOMMAND | (u == 100 ? MF_CHECKED : MF_UNCHECKED)); + CheckMenuItem(scalemenu, ID_CONTROLMENU_SCALING_125, MF_BYCOMMAND | (u == 125 ? MF_CHECKED : MF_UNCHECKED)); + CheckMenuItem(scalemenu, ID_CONTROLMENU_SCALING_150, MF_BYCOMMAND | (u == 150 ? MF_CHECKED : MF_UNCHECKED)); + CheckMenuItem(scalemenu, ID_CONTROLMENU_SCALING_200, MF_BYCOMMAND | (u == 200 ? MF_CHECKED : MF_UNCHECKED)); + CheckMenuItem(scalemenu, ID_CONTROLMENU_SCALING_250, MF_BYCOMMAND | (u == 250 ? MF_CHECKED : MF_UNCHECKED)); + CheckMenuItem(scalemenu, ID_CONTROLMENU_SCALING_300, MF_BYCOMMAND | (u == 300 ? MF_CHECKED : MF_UNCHECKED)); + + if (u != 50 && u != 75 && u != 100 && u != 125 && u != 150 && u != 200 && u != 250 && u != 300) + { + ModifyMenuW(scalemenu, ID_CONTROLMENU_SCALING_CUSTOM, MF_BYCOMMAND | MF_STRING, ID_CONTROLMENU_SCALING_CUSTOM, StringPrintfW(WASABI_API_LNGSTRINGW(IDS_CUSTOM_X_PERCENT), u)); + CheckMenuItem(scalemenu, ID_CONTROLMENU_SCALING_CUSTOM, MF_BYCOMMAND | MF_CHECKED); + } + else + { + ModifyMenuW(scalemenu, ID_CONTROLMENU_SCALING_CUSTOM, MF_BYCOMMAND | MF_STRING, ID_CONTROLMENU_SCALING_CUSTOM, WASABI_API_LNGSTRINGW(IDS_CUSTOM)); + CheckMenuItem(scalemenu, ID_CONTROLMENU_SCALING_CUSTOM, MF_BYCOMMAND | MF_UNCHECKED); + } + if (((float)v / 10.0) - (v / 10) != 0.0) + { + ModifyMenuW(alphamenu, ID_CONTROLMENU_OPACITY_CUSTOM, MF_BYCOMMAND | MF_STRING, ID_CONTROLMENU_OPACITY_CUSTOM, StringPrintfW(WASABI_API_LNGSTRINGW(IDS_CUSTOM_X_PERCENT), v)); + CheckMenuItem(alphamenu, ID_CONTROLMENU_OPACITY_CUSTOM, MF_BYCOMMAND | MF_CHECKED); + } + else + { + ModifyMenuW(alphamenu, ID_CONTROLMENU_OPACITY_CUSTOM, MF_BYCOMMAND | MF_STRING, ID_CONTROLMENU_OPACITY_CUSTOM, WASABI_API_LNGSTRINGW(IDS_CUSTOM)); + CheckMenuItem(alphamenu, ID_CONTROLMENU_OPACITY_CUSTOM, MF_BYCOMMAND | MF_UNCHECKED); + } +} + +//----------------------------------------------------------------------------------------------- +// a window has triggered the control menu (control scale & alpha) +//----------------------------------------------------------------------------------------------- +void appControlMenu(ifc_window *w) +{ + WASABI_API_WND->appdeactivation_setbypass(1); + int x, y; + if (w) + { + Wasabi::Std::getMousePos(&x, &y); + g_controlMenuTarget = w; + updateControlMenu(w); + updateAppBarMenu(w); + HMENU ctrlmenu = GetSubMenu(controlmenu, 0); + DoTrackPopup(ctrlmenu, TPM_LEFTALIGN | TPM_TOPALIGN | TPM_LEFTBUTTON, x, y, wa2.getMainWindow()); + } + WASABI_API_WND->appdeactivation_setbypass(0); +} + +//----------------------------------------------------------------------------------------------- +// a close application button was clicked +//----------------------------------------------------------------------------------------------- +void appQuit() +{ + if (!wa2.isExitEnabled()) return ; + wa2.quit(); // this is in fact *posting* a quit message +} + + +//----------------------------------------------------------------------------------------------- +// a fatal skin error occured, we should revert to the wa2 skin (deferred) +//----------------------------------------------------------------------------------------------- +#define DCB_UNLOADSKIN 64 + +class deferredUnloadSkin : public TimerClientDI +{ +public: + deferredUnloadSkin() + { + timerclient_postDeferredCallback(DCB_UNLOADSKIN, 0); + } + + virtual int timerclient_onDeferredCallback(intptr_t param1, intptr_t param2) + { + if (param1 == DCB_UNLOADSKIN) + { + quit_inst(); + delete this; + return 1; + } + else return TimerClientDI::timerclient_onDeferredCallback(param1, param2); + } +}; + +//----------------------------------------------------------------------------------------------- +// this is the actual event for skin fata errors, it is called when a try/except block failed +// upon calling a skin operation which should not be able to fail (ie: calling a script function pointer) +//----------------------------------------------------------------------------------------------- +void onFatalSkinError() +{ + new deferredUnloadSkin(); +} + + +//----------------------------------------------------------------------------------------------- +// map a GUID to the toggling of a window for which there is no permanent wndCreationService, +// ie: library, prefs, avs +//----------------------------------------------------------------------------------------------- + +// called by a skinwnd_toggle call when the guid isn't found in the wndcreation services +int onCreateExternalWindowGuid(GUID g) +{ + if (g == library_guid) + { + SendMessageW(wa2.getMainWindow(), WM_COMMAND, ID_FILE_SHOWLIBRARY, 0); + return 1; + } + else if (g == preferences_guid) + { + SendMessageW(wa2.getMainWindow(), WM_COMMAND, WINAMP_OPTIONS_PREFS, 0); + return 1; + } + else if (g == about_guid) + { + SendMessageW(wa2.getMainWindow(), WM_COMMAND, WINAMP_HELP_ABOUT, 0); + return 1; + } + else if (g == lightning_bolt_guid) + { + SendMessageW(wa2.getMainWindow(), WM_COMMAND, WINAMP_LIGHTNING_CLICK, 0); + return 1; + } + else if (g == colorthemes_guid) + { + last_page.setData(L"3"); + SendMessageW(plugin.hwndParent, WM_WA_IPC, (WPARAM)&ffPrefsItem, IPC_OPENPREFSTOPAGE); + return 1; + } + else if (g == avs_guid) + { + if (wa2.isVisRunning()) + { + SetTimer(wa2.getMainWindow(), 0xC0DE, 50, NULL); + SetTimer(wa2.getMainWindow(), 0xC0DE + 1, 5000, NULL); + } + else + wa2.toggleVis(); + return 1; + } + return 0; +} + +//----------------------------------------------------------------------------------------------- +void onToggleDesktopAlpha(int v) +{ + if (!toggle_from_wa2 && subWndId == 0 && IsWindow(subWnd)) SendMessageW(subWnd, WM_INITDIALOG, 0, 0); +} + + +//----------------------------------------------------------------------------------------------- +// changes the name of a container so that static containers with the wrong name don't load the config +// values off the wrong name. this should match the WindowText of the embededwindow. note: this is only +// necessary for static containers holding an internal embedwnd (ie: library, avs, but not third party wnds +// as they can't be refered to by GUID anyway [if they can't be refered to by GUID, you can't make a static +// container for them]) +//----------------------------------------------------------------------------------------------- + +const wchar_t *onTweakContainerNameW(const wchar_t *name) +{ + static wchar_t tweaked[96]; + ZERO(tweaked); + if (!_wcsicmp(name, WASABI_API_LNG->GetStringFromGUIDW(GenMlLangGUID, plugin.hDllInstance, 18, tweaked, sizeof(tweaked)/sizeof(wchar_t))) || + !_wcsicmp(name, L"Media Library") || + !_wcsicmp(name, L"Winamp Library") || + !_wcsicmp(name, L"Library")) + { + return WASABI_API_LNGSTRINGW_BUF(IDS_MEDIA_LIBRARY,tweaked, sizeof(tweaked)/sizeof(wchar_t)); + } + if (!_wcsicmp(name, L"Avs")) return WASABI_API_LNGSTRINGW_BUF(IDS_VISUALIZATIONS,tweaked, sizeof(tweaked)/sizeof(wchar_t)); + return name; +} + +const wchar_t *GetMenuItemString(HMENU menu, int id, int bypos) +{ + static StringW rtn; + rtn.trunc(0); + MENUITEMINFOW info = {sizeof(info), MIIM_DATA | MIIM_TYPE | MIIM_STATE | MIIM_ID, MFT_STRING, }; + GetMenuItemInfoW(menu, id, bypos, &info); + if (info.cch > 0) + { + info.dwTypeData = WMALLOC(++info.cch + 1); + GetMenuItemInfoW(menu, id, bypos, &info); + info.dwTypeData[info.cch] = 0; + rtn = info.dwTypeData; + FREE(info.dwTypeData); + } + return rtn; +} + +StringW eqmenustring; + +//----------------------------------------------------------------------------------------------- +void removeEq() +{ + if (eqremoved) return ; + eqremoved = 1; + eqmenustring = GetMenuItemString(wa2.getPopupMenu(), WINAMP_OPTIONS_EQ, FALSE); + RemoveMenu(wa2.getPopupMenu(), WINAMP_OPTIONS_EQ, MF_BYCOMMAND); + wa2.adjustOptionsPopupMenu(-1); +} + +//----------------------------------------------------------------------------------------------- +void restoreEq() +{ + if (!eqremoved) return ; + MENUITEMINFOW i = {sizeof(i), MIIM_ID | MIIM_STATE | MIIM_TYPE, MFT_STRING, wa2.isWindowVisible(IPC_GETWND_EQ) ? MFS_CHECKED : 0, WINAMP_OPTIONS_EQ}; + i.dwTypeData = eqmenustring.getNonConstVal(); + InsertMenuItemW(wa2.getPopupMenu(), 8, TRUE, &i); + wa2.adjustOptionsPopupMenu(1); + eqremoved = 0; +} + +//----------------------------------------------------------------------------------------------- +void unpopulateWindowsMenus() +{ + if (ffwindowsitempos == -1) return ; + + HMENU menuBarMenu = wa2.getMenuBarMenu(Winamp2FrontEnd::WA2_MAINMENUBAR_WINDOWS); + for(int i = GetMenuItemCount(menuBarMenu)-1; i >= 0; i--) + { + MENUITEMINFOW info = {sizeof(info), MIIM_DATA, 0, }; + if(GetMenuItemInfoW(menuBarMenu,i,TRUE,&info)) + { + if(info.dwItemData == 0xD01){ + RemoveMenu(menuBarMenu,i,MF_BYPOSITION); + wa2.adjustFFWindowsMenu(-1); + } + } + } + + HMENU menuPopupMenu = wa2.getPopupMenu(); + for(int i = GetMenuItemCount(menuPopupMenu)-1; i >= 0; i--) + { + MENUITEMINFOW info = {sizeof(info), MIIM_DATA, 0, }; + if(GetMenuItemInfoW(menuPopupMenu,i,TRUE,&info)) + { + if(info.dwItemData == 0xD01){ + RemoveMenu(menuPopupMenu,i,MF_BYPOSITION); + wa2.adjustOptionsPopupMenu(-1); + } + } + } + + ffwindowsitempos = -1; + ffwindowsitempos2 = -1; + + MenuActions::removeSkinWindowOptions(); + MenuActions::removeSkinOptions(); +} + +//----------------------------------------------------------------------------------------------- +void populateWindowsMenus() +{ + if (ffwindowsitempos != -1) unpopulateWindowsMenus(); + MenuActions::installSkinOptions(); + MenuActions::installSkinWindowOptions(); + + ffwindowsitempos = wa2.adjustFFWindowsMenu(0) + NUMSTATICWINDOWS; + ffwindowsitempos2 = wa2.adjustOptionsPopupMenu(0) + 6 + NUMSTATICWINDOWS + 1; + + MENUITEMINFOW i = {sizeof(i), }; + i.fMask = MIIM_TYPE | MIIM_DATA | MIIM_ID | MIIM_STATE; + i.fType = MFT_STRING; + i.wID = 43000; + i.dwItemData = 0xD01; // use this as a check so we're only removing the correct items!! + int pos = ffwindowsitempos; + int pos2 = ffwindowsitempos2; + PtrListQuickSorted items; + + HMENU hMenu = wa2.getMenuBarMenu(Winamp2FrontEnd::WA2_MAINMENUBAR_WINDOWS); + HMENU hMenu2 = wa2.getPopupMenu(); + for (int c = 0;c < SkinParser::getNumContainers();c++) + { + Container *cont = SkinParser::enumContainer(c); + if (cont && wantContainerInMenu(cont)) + { + i.dwTypeData = const_cast(cont->getName()); + if (i.dwTypeData != NULL) + { + if (!items.findItem(i.dwTypeData)) + { + items.addItem(new StringW(i.dwTypeData)); + i.cch = wcslen(i.dwTypeData); + i.fState = cont->isVisible() ? MFS_CHECKED : 0; + InsertMenuItemW(hMenu, pos++, TRUE, &i); + wa2.adjustFFWindowsMenu(1); + InsertMenuItemW(hMenu2, pos2++, TRUE, &i); + wa2.adjustOptionsPopupMenu(1); + } + } + } + i.wID++; + } + + int n = WASABI_API_WNDMGR->autopopup_getNumGuids(); + for (int c = 0;c < n;c++) + { + GUID guid = WASABI_API_WNDMGR->autopopup_enumGuid(c); + const wchar_t *groupdesc = WASABI_API_WNDMGR->autopopup_enumGuidDescription(c); + if (guid != INVALID_GUID && groupdesc && *groupdesc) + { + i.dwTypeData = const_cast(groupdesc); + if (!items.findItem(i.dwTypeData)) + { + items.addItem(new StringW(i.dwTypeData)); + i.cch = wcslen(i.dwTypeData); + i.fState = WASABI_API_WNDMGR->skinwnd_getNumByGuid(guid) ? MFS_CHECKED : 0; + InsertMenuItemW(hMenu, pos++, TRUE, &i); + wa2.adjustFFWindowsMenu(1); + InsertMenuItemW(hMenu2, pos2++, TRUE, &i); + wa2.adjustOptionsPopupMenu(1); + } + } + i.wID++; + } + + n = WASABI_API_WNDMGR->autopopup_getNumGroups(); + for (int c = 0;c < n;c++) + { + const wchar_t *id = WASABI_API_WNDMGR->autopopup_enumGroup(c); + const wchar_t *groupdesc = WASABI_API_WNDMGR->autopopup_enumGroupDescription(c); + if (id && groupdesc && *id && *groupdesc) + { + i.dwTypeData = const_cast(groupdesc); + // allow localisation of the color editor menu item + i.dwTypeData = const_cast(MenuActions::localizeSkinWindowName(i.dwTypeData)); + if (!items.findItem(i.dwTypeData)) + { + items.addItem(new StringW(i.dwTypeData)); + i.cch = wcslen(i.dwTypeData); + i.fState = WASABI_API_WNDMGR->skinwnd_getNumByGroupId(id) ? MFS_CHECKED : 0; + InsertMenuItemW(hMenu, pos++, TRUE, &i); + wa2.adjustFFWindowsMenu(1); + InsertMenuItemW(hMenu2, pos2++, TRUE, &i); + wa2.adjustOptionsPopupMenu(1); + } + } + i.wID++; + } + + items.deleteAll(); + ffwindowstop = i.wID; +} + +//----------------------------------------------------------------------------------------------- +void switchSkin(const wchar_t *skinname) +{ + wa2.switchSkin(skinname); +} + +//----------------------------------------------------------------------------------------------- +void addWindowOptionsToContextMenu(ifc_window *w) +{ + if (g_controlMenuTarget != NULL) + removeWindowOptionsFromContextMenu(); + if (w == NULL && WASABI_API_WND->rootwndIsValid(lastFocused)) + w = lastFocused; + g_controlMenuTarget = w; + + if (g_controlMenuTarget == NULL) return ; + + /*int opacitysafe = 1; + int scalelocked = 0; + + ifc_window *rootparent = g_controlMenuTarget->getDesktopParent(); + if (rootparent && rootparent->getInterface(layoutGuid)) + { + Layout *l = static_cast(rootparent); + opacitysafe = l->isTransparencySafe(); + scalelocked = l->isScaleLocked(); + }*/ + + updateControlMenu(w); + updateAppBarMenu(w); + + HMENU menu = wa2.getPopupMenu(); + HMENU ctrlmenu = GetSubMenu(controlmenu, 0); + + // JF> Francis, made this use command IDs (inserts it right before the help ID in the main menu). + // does this look OK? if you want, revert back to the old one. I just thought this seemed a tiny + // bit cleaner... :) +#define ID_HELP_HELPTOPICS 40347 + InsertMenuW(menu, ID_HELP_HELPTOPICS, MF_BYCOMMAND | MF_POPUP, (UINT_PTR)ctrlmenu, WASABI_API_LNGSTRINGW(IDS_WINDOW_SETTINGS)); + InsertMenu(menu, ID_HELP_HELPTOPICS, MF_BYCOMMAND | MF_SEPARATOR, 0, NULL); + +} + +//----------------------------------------------------------------------------------------------- +void removeWindowOptionsFromContextMenu() +{ + if (g_controlMenuTarget == NULL) return ; + g_controlMenuTarget = NULL; + HMENU menu = wa2.getPopupMenu(); + HMENU ctrlmenu = GetSubMenu(controlmenu, 0); + if (ctrlmenu) + { + int l = GetMenuItemCount(menu); + while (l-- > 0 && GetSubMenu(menu, l) != ctrlmenu); + if (l >= 0) + { + RemoveMenu(menu, l, MF_BYPOSITION); + RemoveMenu(menu, l, MF_BYPOSITION); // remove sep + } + } +} + +//----------------------------------------------------------------------------------------------- +void controlOpacity(int v) +{ + if (!g_controlMenuTarget) return ; + if (!WASABI_API_WND->rootwndIsValid(g_controlMenuTarget)) return ; + v = (int)(((float)v / 100.0f) * 255.0f + 0.5f); + ifc_window *rootparent = g_controlMenuTarget->getDesktopParent(); + if (rootparent && rootparent->getInterface(layoutGuid)) + { + Layout *l = static_cast(rootparent); + if (!cfg_uioptions_linkallalpha.getValueAsInt()) + { + l->setAlpha(v); + } + else + { + cfg_uioptions_linkedalpha.setValueAsInt(v); + } + } +} + +//----------------------------------------------------------------------------------------------- +void controlScaling(double v) +{ + if (!g_controlMenuTarget) return ; + if (!WASABI_API_WND->rootwndIsValid(g_controlMenuTarget)) return ; + ifc_window *rootparent = g_controlMenuTarget->getDesktopParent(); + if (rootparent) rootparent->setRenderRatio(v); +} + +//----------------------------------------------------------------------------------------------- +void lockScaling(int lock) +{ + ifc_window *rootparent = g_controlMenuTarget->getDesktopParent(); + if (!WASABI_API_WND->rootwndIsValid(g_controlMenuTarget)) return ; + if (rootparent && rootparent->getInterface(layoutGuid)) + { + Layout *l = static_cast(rootparent); + l->lockScale(lock); + } +} + +//----------------------------------------------------------------------------------------------- +void controlAppBar(int side) +{ + ifc_window *rootparent = g_controlMenuTarget->getDesktopParent(); + if (rootparent) + { + AppBar *ab = (AppBar *)rootparent->getInterface(appBarGuid); + if (ab) + { + ab->appbar_dock(side); + } + } +} + +//----------------------------------------------------------------------------------------------- +void controlAppBarAOT() +{ + ifc_window *rootparent = g_controlMenuTarget->getDesktopParent(); + if (rootparent) + { + Layout *l = (Layout *)rootparent->getInterface(layoutGuid); + if (l) + { + int curaot = l->getAppBarAlwaysOnTop(); + l->setAppBarAlwaysOnTop(!curaot); + } + } +} + +//----------------------------------------------------------------------------------------------- +void controlAppBarAH() +{ + ifc_window *rootparent = g_controlMenuTarget->getDesktopParent(); + if (rootparent) + { + Layout *l = (Layout *)rootparent->getInterface(layoutGuid); + if (l) + { + int curah = l->getAppBarAutoHide(); + l->setAppBarAutoHide(!curah); + } + } +} + +//----------------------------------------------------------------------------------------------- +void updateAppBarMenu(ifc_window *w) +{ + if (g_controlMenuTarget == NULL) return ; + ifc_window *rootparent = g_controlMenuTarget->getDesktopParent(); + if (rootparent) + { + Layout *l = (Layout *)rootparent->getInterface(layoutGuid); + if (l) + { + HMENU ctrlmenu = GetSubMenu(controlmenu, 0); + HMENU appbarmenu = GetSubMenu(ctrlmenu, 2); + if (l->appbar_getEnabledSides() == 0) + { + EnableMenuItem(ctrlmenu, 2, MF_BYPOSITION | MF_GRAYED); + return ; + } + else + EnableMenuItem(ctrlmenu, 2, MF_BYPOSITION | MF_ENABLED); + int docked = l->appbar_isDocked(); + int side = l->appbar_getSide(); + CheckMenuItem(appbarmenu, ID_CONTROLMENU_TOOLBAR_ALWAYSONTOP, MF_BYCOMMAND | (l->appbar_wantAlwaysOnTop() ? MF_CHECKED : MF_UNCHECKED)); + CheckMenuItem(appbarmenu, ID_CONTROLMENU_TOOLBAR_AUTOHIDE, MF_BYCOMMAND | (l->appbar_wantAutoHide() ? MF_CHECKED : MF_UNCHECKED)); + CheckMenuItem(appbarmenu, ID_CONTROLMENU_TOOLBAR_LEFT, MF_BYCOMMAND | ((docked && side == APPBAR_LEFT) ? MF_CHECKED : MF_UNCHECKED)); + CheckMenuItem(appbarmenu, ID_CONTROLMENU_TOOLBAR_TOP, MF_BYCOMMAND | ((docked && side == APPBAR_TOP) ? MF_CHECKED : MF_UNCHECKED)); + CheckMenuItem(appbarmenu, ID_CONTROLMENU_TOOLBAR_RIGHT, MF_BYCOMMAND | ((docked && side == APPBAR_RIGHT) ? MF_CHECKED : MF_UNCHECKED)); + CheckMenuItem(appbarmenu, ID_CONTROLMENU_TOOLBAR_BOTTOM, MF_BYCOMMAND | ((docked && side == APPBAR_BOTTOM) ? MF_CHECKED : MF_UNCHECKED)); + CheckMenuItem(appbarmenu, ID_CONTROLMENU_TOOLBAR_DISABLED, MF_BYCOMMAND | (!docked ? MF_CHECKED : MF_UNCHECKED)); + EnableMenuItem(appbarmenu, ID_CONTROLMENU_TOOLBAR_AUTOHIDE, MF_BYCOMMAND | ((!docked || l->appbar_isSideAutoHideSafe(side)) ? MF_ENABLED : MF_GRAYED)); + EnableMenuItem(appbarmenu, ID_CONTROLMENU_TOOLBAR_LEFT, MF_BYCOMMAND | (l->appbar_isSideEnabled(APPBAR_LEFT) ? MF_ENABLED : MF_GRAYED)); + EnableMenuItem(appbarmenu, ID_CONTROLMENU_TOOLBAR_TOP, MF_BYCOMMAND | (l->appbar_isSideEnabled(APPBAR_TOP) ? MF_ENABLED : MF_GRAYED)); + EnableMenuItem(appbarmenu, ID_CONTROLMENU_TOOLBAR_RIGHT, MF_BYCOMMAND | (l->appbar_isSideEnabled(APPBAR_RIGHT) ? MF_ENABLED : MF_GRAYED)); + EnableMenuItem(appbarmenu, ID_CONTROLMENU_TOOLBAR_BOTTOM, MF_BYCOMMAND | (l->appbar_isSideEnabled(APPBAR_BOTTOM) ? MF_ENABLED : MF_GRAYED)); + CheckMenuItem(appbarmenu, ID_CONTROLMENU_TOOLBAR_AUTODOCKONDRAG, MF_BYCOMMAND | (cfg_options_appbarondrag ? MF_CHECKED : MF_UNCHECKED)); + } + } +} + +//----------------------------------------------------------------------------------------------- +double onTweakRenderRatio(double v) +{ + if (!cfg_uioptions_uselocks.getValueAsInt()) + return v; + return wa2.isDoubleSize() ? 2.0 : 1.0; +} + +//----------------------------------------------------------------------------------------------- +void onCustomAltF4() +{ + SendMessageW(wa2.getMainWindow(), WM_CLOSE, 0, 0); +} + +int isSkinStillLoading() +{ + return before_startup_callback; +} + +//----------------------------------------------------------------------------------------------- +void loadExtraColorThemes() +{ + wchar_t filename[WA_MAX_PATH] = {0}; + GetModuleFileNameW(hInstance, filename, WA_MAX_PATH); + PathParserW pp(filename); + StringW path; + for (int i = 0;i < pp.getNumStrings() - 1;i++) + { + path.AppendPath(pp.enumString(i)); + } + StringW file = path; + file.AppendPath(L"ColorThemes"); + file.AppendPath(WASABI_API_SKIN->getSkinName()); + file.AppendPath(L"*.xml"); + WASABI_API_SKIN->loadSkinFile(file); +} + +double oldscale = 1.; + +//----------------------------------------------------------------------------------------------- +static BOOL CALLBACK customScaleProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch (uMsg) + { + case WM_INITDIALOG: + { + //CUT: double curratio=1.; + ifc_window *rootparent = g_controlMenuTarget->getDesktopParent(); + if (!rootparent || !WASABI_API_WND->rootwndIsValid(rootparent)) + { + g_controlMenuTarget = NULL; return TRUE; + } + + oldscale = rootparent->getRenderRatio(); + + int u = (int)((oldscale * 100.0f) + 0.5f); + + SendMessageW(GetDlgItem(hwndDlg, IDC_SLIDER_CUSTOMSCALE), TBM_SETRANGEMAX, 0, 300); + SendMessageW(GetDlgItem(hwndDlg, IDC_SLIDER_CUSTOMSCALE), TBM_SETRANGEMIN, 0, 10); + SendMessageW(GetDlgItem(hwndDlg, IDC_SLIDER_CUSTOMSCALE), TBM_SETPOS, 1, u); + SetDlgItemTextW(hwndDlg, IDC_STATIC_SCALE, StringPrintfW(WASABI_API_LNGSTRINGW(IDS_SCALE_X_PERCENT), u)); + return TRUE; + } + case WM_HSCROLL: + { + int t = (int)SendMessageW((HWND) lParam, TBM_GETPOS, 0, 0); + if ((HWND) lParam == GetDlgItem(hwndDlg, IDC_SLIDER_CUSTOMSCALE)) + { + SetDlgItemTextW(hwndDlg, IDC_STATIC_SCALE, StringPrintfW(WASABI_API_LNGSTRINGW(IDS_SCALE_X_PERCENT), t)); + controlScaling((double)t / 100.0); + if (g_controlMenuTarget) + { + ifc_window *w = g_controlMenuTarget->getDesktopParent(); + UpdateWindow(w->gethWnd()); + } + } + break; + } + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDOK: + EndDialog(hwndDlg, IDOK); + return 0; + case IDCANCEL: + EndDialog(hwndDlg, IDCANCEL); + return 0; + } + break; + } + return FALSE; +} + +double oldalpha = 255.; + +//----------------------------------------------------------------------------------------------- +static BOOL CALLBACK customAlphaProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch (uMsg) + { + case WM_INITDIALOG: + { + ifc_window *rootparent = g_controlMenuTarget->getDesktopParent(); + if (!rootparent || !WASABI_API_WND->rootwndIsValid(rootparent)) + { + g_controlMenuTarget = NULL; return TRUE; + } + + int v = 100; + if (!cfg_uioptions_linkallalpha.getValueAsInt()) + { + Layout *l = static_cast(rootparent); + if (l != NULL) + oldalpha = static_cast(l->getAlpha()); + } + else + { + oldalpha = static_cast(cfg_uioptions_linkedalpha.getValueAsInt()); + } + v = (int)((oldalpha / 255.0f * 100.0f) + 0.5f); + SendMessageW(GetDlgItem(hwndDlg, IDC_SLIDER_CUSTOMALPHA), TBM_SETRANGEMAX, 0, 100); + SendMessageW(GetDlgItem(hwndDlg, IDC_SLIDER_CUSTOMALPHA), TBM_SETRANGEMIN, 0, 10); + SendMessageW(GetDlgItem(hwndDlg, IDC_SLIDER_CUSTOMALPHA), TBM_SETPOS, 1, v); + SetDlgItemTextW(hwndDlg, IDC_STATIC_ALPHA, StringPrintfW(WASABI_API_LNGSTRINGW(IDS_OPACITY_X_PERCENT), v)); + return TRUE; + } + case WM_HSCROLL: + { + int t = (int)SendMessageW((HWND) lParam, TBM_GETPOS, 0, 0); + if ((HWND) lParam == GetDlgItem(hwndDlg, IDC_SLIDER_CUSTOMALPHA)) + { + SetDlgItemTextW(hwndDlg, IDC_STATIC_ALPHA, StringPrintfW(WASABI_API_LNGSTRINGW(IDS_OPACITY_X_PERCENT), t)); + controlOpacity(t); + } + break; + } + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDOK: + EndDialog(hwndDlg, IDOK); + return 0; + case IDCANCEL: + EndDialog(hwndDlg, IDCANCEL); + return 0; + } + break; + } + return FALSE; +} + +void customScaling() +{ + if (!g_controlMenuTarget) return ; + if (!WASABI_API_WND->rootwndIsValid(g_controlMenuTarget)) return ; + ifc_window *w = g_controlMenuTarget->getDesktopParent(); + if (!w || !WASABI_API_WND->rootwndIsValid(w)) return ; + int r = WASABI_API_DIALOGBOXW(IDD_CUSTOMSCALE, w->gethWnd(), customScaleProc); + if (r != IDOK) + controlScaling(oldscale); +} + +void customOpacity() +{ + if (!g_controlMenuTarget) return ; + if (!WASABI_API_WND->rootwndIsValid(g_controlMenuTarget)) return ; + ifc_window *w = g_controlMenuTarget->getDesktopParent(); + if (!w || !WASABI_API_WND->rootwndIsValid(w)) return ; + int r = WASABI_API_DIALOGBOXW(IDD_CUSTOMALPHA, w->gethWnd(), customAlphaProc); + if (r != IDOK) + controlOpacity((int)((oldalpha / 255.0f * 100.0f) + 0.5f)); +} + +void autoOpacifyHover() +{ + if (!g_controlMenuTarget) return ; + if (!WASABI_API_WND->rootwndIsValid(g_controlMenuTarget)) return ; + ifc_window *w = g_controlMenuTarget->getDesktopParent(); + Layout *l = static_cast(w->getInterface(layoutGuid)); + if (l) + { + if (!cfg_uioptions_linkallalpha.getValueAsInt()) + { + int a = l->getAutoOpacify(); + if (a == 2) a = 1; + else a = !a; + l->setAutoOpacify(a); + } + else + { + int a = cfg_uioptions_autoopacitylinked.getValueAsInt(); + if (a == 2) a = 1; + else a = !a; + cfg_uioptions_autoopacitylinked.setValueAsInt(a); + } + } +} + +void autoOpacifyFocus() +{ + if (!g_controlMenuTarget) return ; + if (!WASABI_API_WND->rootwndIsValid(g_controlMenuTarget)) return ; + ifc_window *w = g_controlMenuTarget->getDesktopParent(); + Layout *l = static_cast(w->getInterface(layoutGuid)); + if (l) + { + if (!cfg_uioptions_linkallalpha.getValueAsInt()) + { + int a = l->getAutoOpacify(); + if (a == 1) a = 2; + else if (a == 0) a = 2; + else if (a == 2) a = 0; + l->setAutoOpacify(a); + } + else + { + int a = cfg_uioptions_autoopacitylinked.getValueAsInt(); + if (a == 1) a = 2; + else if (a == 0) a = 2; + else if (a == 2) a = 0; + cfg_uioptions_autoopacitylinked.setValueAsInt(a); + } + } +} + +StringW langpackfilename; + +const wchar_t *localesCustomGetFile() +{ + const wchar_t *langDir = WASABI_API_LNG->GetLanguageFolder(); + if (!langDir || !*langDir) + return NULL; + + langpackfilename = StringPathCombine(langDir, L"freeform"); + return langpackfilename; +#if 0 // old code + wchar_t buf[256] = L""; + GetPrivateProfileStringW(L"Winamp", L"langpack", L"", buf, 256, AutoWide(INI_FILE)); // TODO: maybe we should change all ini file stuff to W versions + if (*buf == 0) + return NULL; + wchar_t *p = wcschr(buf, '.'); + if (p) + *p = 0; + + wchar_t filename[WA_MAX_PATH] = {0}; + GetModuleFileNameW(hInstance, filename, WA_MAX_PATH); + PathParserW pp(filename); + langpackfilename = L""; + + for (int i = 0;i < pp.getNumStrings() - 1;i++) + { + langpackfilename.AppendPath(pp.enumString(i)); + } + langpackfilename.AppendPath(L"freeform"); + langpackfilename.AppendPath(L"langpacks"); + langpackfilename.AppendPath(StringPrintfW(L"%s.xml", buf)); + + return langpackfilename; +#endif +} + +const wchar_t *getCustomVar(const wchar_t *var) +{ + static StringW ret; + if (WCSCASEEQLSAFE(var, L"@HAVE_LIBRARY@")) + { + ret = StringPrintfW(L"%d", we_have_ml); + return ret; + } + return NULL; +} + +void checkMlPresent() +{ + wchar_t filename[WA_MAX_PATH] = {0}; + GetModuleFileNameW(hInstance, filename, WA_MAX_PATH); + PathParserW pp(filename); + StringW path; + for (int i = 0;i < pp.getNumStrings() - 1;i++) + { + path.AppendPath(pp.enumString(i)); + } + path.AppendPath(L"gen_ml.dll"); + we_have_ml = !WACCESS(path, 0); +} + +void initEgg() +{ + eggstr[0] = ~'N'; + eggstr[1] = ~'U'; + eggstr[2] = ~'L'; + eggstr[3] = ~'L'; + eggstr[4] = ~'S'; + eggstr[5] = ~'O'; + eggstr[6] = ~'F'; + eggstr[7] = ~'T'; + eggstr[8] = 0; + + { + int x; + for (x = 0; x < 8; x ++) eggstr[x] ^= 255; + } +} + +void toggleEgg() +{ + eggstat = !eggstat; + if (!eggstat) + { + KillTimer(wa2.getMainWindow(), UPDATE_EGG); + if (lastlayoutegg && WASABI_API_WND->rootwndIsValid(lastlayoutegg)) lastlayoutegg->setTransparencyOverride(-1); + lastlayoutegg = NULL; + } + else SetTimer(wa2.getMainWindow(), UPDATE_EGG, 25, NULL); +} + +void getCustomMetaData(const wchar_t *field, wchar_t *buf, int len) +{ + StringW curfile = WASABI_API_MEDIACORE->core_getCurrent(0); + if (curfile.isempty()) + { + buf[0] = 0; + return ; + } + if (!_wcsnicmp(curfile, L"file://", 7)) + curfile = StringW(curfile.getValue() + 7); + buf[0] = 0; + + if (WCSCASEEQLSAFE(field, L"filename")) + { + WCSCPYN(buf, curfile, len); + } + else + { + wa2.getMetaData(curfile, field, buf, len); + } +} + +void registerGlobalHotkeys() +{ + static int registered = 0; + if (!registered) + { + static wchar_t ghkStr[96]; + wa2.registerGlobalHotkey((char*)WASABI_API_LNGSTRINGW_BUF(IDS_GHK_SHOW_NOTIFICATION,ghkStr,96), WM_WA_IPC, 0, IPC_SHOW_NOTIFICATION, HKF_UNICODE_NAME, "genff shn"); + registered = 1; + } +} + +const wchar_t *getSongInfoText() +{ + return Core::getSongInfoText(); +} + +const wchar_t *getSongInfoTextTranslated() +{ + return Core::getSongInfoTextTranslated(); +} + +OSWINDOWHANDLE getKeyboardForwardWnd(GUID g) +{ + if (g != INVALID_GUID) + { + if (g == playerWndGuid) + return wa2.getMainWindow(); + else if (g == pleditWndGuid) + return wa2.getWnd(IPC_GETWND_PE); + else if (g == videoWndGuid) + return wa2.getWnd(IPC_GETWND_VIDEO); + else + { + embedWindowState *ews = embedWndGuidMgr.getEmbedWindowState(g); + if (ews && wa2.isValidEmbedWndState(ews)) return ews->me; + } + } + return WASABI_API_WND->main_getRootWnd()->gethWnd(); +} + +void onAppBarDockChanged(ifc_window *w) +{ + setDialogBoxesParent(); +} + +void onMainLayoutMove(HWND w) +{ + // for Winamp to appear on the correct taskbar on Windows 8 + // its necessary to set the classic main window to appear on + // that monitor and to be on-screen (but hidden) otherwise + // Windows will ignore it and it then makes us look buggy. + RECT r; + Wasabi::Std::getViewport(&r, w, 1); + SetWindowPos(wa2.getMainWindow(), NULL, r.left, r.bottom - 1, 0, 0, SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOREDRAW); +} + +void updateAll() +{ + updatePl(); +#ifdef MINIBROWSER_SUPPORT + updateMb(); +#endif + updateVid(); + int n = embedWndGuidMgr.getNumWindowStates(); + for (int i = 0;i < n;i++) + { + embedWindowState *ws = NULL; + GUID g = embedWndGuidMgr.enumWindowState(i, &ws); + if (g != INVALID_GUID) + { + updateEmb(g, ws); + } + } +} + +void onReParent(HWND wnd) +{ + updateAll(); +} + +void onReInit(HWND wnd) +{ + updateAll(); +} + +void startFSMonitor() +{ + g_fsmonitor = new FullScreenMonitor(); + g_fscallback = new Wa5FSCallback(); + g_fsmonitor->registerCallback(g_fscallback); +} + +void stopFSMonitor() +{ + delete g_fsmonitor; + g_fsmonitor = NULL; + delete g_fscallback; + g_fscallback = NULL; +} + +void updateParentlessOnTop() +{ + int i; + for (i = 0;i < SkinParser::getNumContainers();i++) + { + Container *c = SkinParser::enumContainer(i); + if (c != NULL) + { + int j; + for (j = 0;j < c->getNumLayouts();j++) + { + Layout *l = c->enumLayout(j); + if (l != NULL) + { + // skip windows owned by winamp + // skip appbars, they take care of themselves + if (l->getNoParent() && !l->appbar_isDocked()) + { + l->updateOnTop(); + } + } + } + } + } +} + +void onGoFullscreen() +{ + // hidden windows will not receive APPBAR_CALLBACK, so forward it to winamp's main + SendMessageW(wa2.getMainWindow(), APPBAR_CALLBACK, ABN_FULLSCREENAPP, 1); + // update ontop flag for windows that are not parented to winamp + updateParentlessOnTop(); +} + +void onCancelFullscreen() +{ + // hidden windows will not receive APPBAR_CALLBACK, so forward it to winamp's main + SendMessageW(wa2.getMainWindow(), APPBAR_CALLBACK, ABN_FULLSCREENAPP, 0); + // update ontop flag for windows that are not owned by winamp + updateParentlessOnTop(); +} + +int processGenericHotkey(const char *hk) +{ + if (!m_are_we_loaded) return 0; + + SystemObject::onKeyDown(AutoWide(StringPrintf("HOTKEY: %s", hk))); + if (VCPU::getComplete()) + { + DebugStringW(L"HOTKEY: %s trapped by script\n", hk); + return 1; + } + return 0; +} + +int canExitWinamp() +{ + return wa2.isExitEnabled(); +} + +int fsMonitorIsFS() +{ + return g_fsmonitor->isFullScreen(); +} + +void modalPush() +{ + wa2.pushExitDisabled(); +} + +void modalPop() +{ + wa2.popExitDisabled(); +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_ff/main.h b/Src/Plugins/General/gen_ff/main.h new file mode 100644 index 00000000..d831919b --- /dev/null +++ b/Src/Plugins/General/gen_ff/main.h @@ -0,0 +1,29 @@ +#ifndef __GEN_FF_MAIN_H +#define __GEN_FF_MAIN_H + +class ifc_window; + +extern ifc_window *plWnd; +extern ifc_window *mbWnd; +extern ifc_window *vidWnd; + +#define NUMSTATICWINDOWS 3 + +#include "../playlist/api_playlists.h" +extern api_playlists *playlistsApi; +#define AGAVE_API_PLAYLISTS playlistsApi + +#include "../Agave/AlbumArt/api_albumart.h" +extern api_albumart *albumArtApi; +#define AGAVE_API_ALBUMART albumArtApi + +#include "../playlist/api_playlistmanager.h" +extern api_playlistmanager *playlistManagerApi; +#define AGAVE_API_PLAYLISTMANAGER playlistManagerApi + +#include "../Components/wac_downloadManager/wac_downloadManager_api.h" + +#include "../Winamp/gen.h" +extern "C" winampGeneralPurposePlugin plugin; + +#endif \ No newline at end of file diff --git a/Src/Plugins/General/gen_ff/menuactions.cpp b/Src/Plugins/General/gen_ff/menuactions.cpp new file mode 100644 index 00000000..266dd74a --- /dev/null +++ b/Src/Plugins/General/gen_ff/menuactions.cpp @@ -0,0 +1,787 @@ +#include "precomp__gen_ff.h" +#include "menuactions.h" +#include "wa2frontend.h" +#include +#include "wa2cfgitems.h" +#include "main.h" +#include "resource.h" +#include +#include "../gen_ml/ml_ipc.h" +#include "../winamp/gen.h" +#include "../Agave/Language/api_language.h" +#include "../gen_ml/menufucker.h" +#include "../Winamp/strutil.h" + +extern librarySendToMenuStruct mainSendTo; +#define WINAMP_OPTIONS_DSIZE 40165 +extern void addWindowOptionsToContextMenu(ifc_window *w); +extern void removeWindowOptionsFromContextMenu(); +extern ifc_window *g_controlMenuTarget; +extern HMENU controlmenu; +int lowest_itempos = 0; +int highest_itempos = -1; +int lowest_witempos = 0; +int lowest_witempos2 = 0; +int highest_witempos = -1; +int highest_witempos2 = -1; +int optionsmenu_wa = 1; +TList menulist; +TList wmenulist; +int in_menu = 0; + + +//----------------------------------------------------------------------------------------------- +MenuActions::MenuActions() +{ + registerAction(L"menu", _ACTION_MENU); + registerAction(L"sysmenu", _ACTION_SYSMENU); + registerAction(L"controlmenu", _ACTION_CONTROLMENU); + registerAction(L"MENU:WA5:File", ACTION_WA5FILEMENU); + registerAction(L"MENU:WA5:Play", ACTION_WA5PLAYMENU); + registerAction(L"MENU:WA5:Options", ACTION_WA5OPTIONSMENU); + registerAction(L"MENU:WA5:Windows", ACTION_WA5WINDOWSMENU); + registerAction(L"MENU:WA5:Help", ACTION_WA5HELPMENU); + registerAction(L"MENU:WA5:PE_File", ACTION_WA5PEFILEMENU); + registerAction(L"MENU:WA5:PE_Playlist", ACTION_WA5PEPLAYLISTMENU); + registerAction(L"MENU:WA5:PE_Sort", ACTION_WA5PESORTMENU); + registerAction(L"MENU:WA5:PE_Help", ACTION_WA5PEHELPMENU); + registerAction(L"MENU:WA5:ML_File", ACTION_WA5MLFILEMENU); + registerAction(L"MENU:WA5:ML_View", ACTION_WA5MLVIEWMENU); + registerAction(L"MENU:WA5:ML_Help", ACTION_WA5MLHELPMENU); + registerAction(L"PE_Add", ACTION_PEADD); + registerAction(L"PE_Rem", ACTION_PEREM); + registerAction(L"PE_Sel", ACTION_PESEL); + registerAction(L"PE_Misc", ACTION_PEMISC); + registerAction(L"PE_List", ACTION_PELIST); + registerAction(L"PE_ListOfLists", ACTION_PELISTOFLISTS); + registerAction(L"VID_FS", ACTION_VIDFS); + registerAction(L"VID_1X", ACTION_VID1X); + registerAction(L"VID_2X", ACTION_VID2X); + registerAction(L"VID_TV", ACTION_VIDTV); + registerAction(L"VID_Misc", ACTION_VIDMISC); + registerAction(L"VIS_Next", ACTION_VISNEXT); + registerAction(L"VIS_Prev", ACTION_VISPREV); + registerAction(L"VIS_FS", ACTION_VISFS); + registerAction(L"VIS_CFG", ACTION_VISCFG); + registerAction(L"VIS_Menu", ACTION_VISMENU); + registerAction(L"trackinfo", ACTION_TRACKINFO); + registerAction(L"trackmenu", ACTION_TRACKMENU); + registerAction(L"ML_SendTo", ACTION_SENDTO); +} + +//----------------------------------------------------------------------------------------------- +MenuActions::~MenuActions() +{} + +static LRESULT sendMlIpc(int msg, WPARAM param) { + static HWND mlwnd = NULL; + if(!IsWindow(mlwnd)) { + int IPC_GETMLWINDOW = (INT)SendMessageW(plugin.hwndParent, WM_WA_IPC, (WPARAM)&"LibraryGetWnd", IPC_REGISTER_WINAMP_IPCMESSAGE); + if(IPC_GETMLWINDOW > 65536) mlwnd = (HWND)SendMessageW(plugin.hwndParent,WM_WA_IPC,0,IPC_GETMLWINDOW); + } + if(!IsWindow(mlwnd)) return 0; + return SendMessageW(mlwnd,WM_ML_IPC,param,msg); +} + +//----------------------------------------------------------------------------------------------- +int MenuActions::onActionId(int pvtid, const wchar_t *action, const wchar_t *param /* =NULL */, int p1 /* =0 */, int p2 /* =0 */, void *data /* =NULL */, int datalen /* =0 */, ifc_window *source /* =NULL */) +{ + SetCursor(LoadCursor(NULL, IDC_ARROW)); + RECT r = {0, 0, 0, 0}; + if (source) source->getWindowRect(&r); + int height = r.bottom - r.top; + int width = r.right - r.left; + in_menu = 1; + switch (pvtid) + { + case _ACTION_MENU: + { + if (!_wcsicmp(param, L"presets")) + { + wa2.triggerEQPresetMenu(p1, p2); + } + } + break; + case _ACTION_SYSMENU: + { + addWindowOptionsToContextMenu(source); + wa2.triggerPopupMenu(p1, p2); + break; + } + case _ACTION_CONTROLMENU: + { + if (g_controlMenuTarget != NULL) + removeWindowOptionsFromContextMenu(); + addWindowOptionsToContextMenu(g_controlMenuTarget); + g_controlMenuTarget = source; + + HMENU ctrlmenu = GetSubMenu(controlmenu, 0); + DoTrackPopup(ctrlmenu, TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_RIGHTBUTTON, p1, p2, wa2.getMainWindow()); + break; + } + case ACTION_WA5FILEMENU: + { + wa2.triggerFileMenu(p1, p2, width, height); + break; + } + case ACTION_WA5PLAYMENU: + { + wa2.triggerPlayMenu(p1, p2, width, height); + break; + } + case ACTION_WA5OPTIONSMENU: + { + wa2.triggerOptionsMenu(p1, p2, width, height); + break; + } + case ACTION_WA5WINDOWSMENU: + { + wa2.triggerWindowsMenu(p1, p2, width, height); + break; + } + case ACTION_WA5HELPMENU: + { + wa2.triggerHelpMenu(p1, p2, width, height); + break; + } + case ACTION_WA5PEFILEMENU: + { + wa2.triggerPEFileMenu(p1, p2, width, height); + break; + } + case ACTION_WA5PEPLAYLISTMENU: + { + wa2.triggerPEPlaylistMenu(p1, p2, width, height); + break; + } + case ACTION_WA5PESORTMENU: + { + wa2.triggerPESortMenu(p1, p2, width, height); + break; + } + case ACTION_WA5PEHELPMENU: + { + wa2.triggerPEHelpMenu(p1, p2, width, height); + break; + } + case ACTION_WA5MLFILEMENU: + { + wa2.triggerMLFileMenu(p1, p2, width, height); + break; + } + case ACTION_WA5MLVIEWMENU: + { + wa2.triggerMLViewMenu(p1, p2, width, height); + break; + } + case ACTION_WA5MLHELPMENU: + { + wa2.triggerMLHelpMenu(p1, p2, width, height); + break; + } + case ACTION_PEADD: + { + wa2.sendPlCmd(Winamp2FrontEnd::WA2_PLEDITPOPUP_ADD, r.left, r.top, TPM_BOTTOMALIGN | TPM_LEFTALIGN); + break; + } + case ACTION_PEREM: + { + wa2.sendPlCmd(Winamp2FrontEnd::WA2_PLEDITPOPUP_REM, r.left, r.top, TPM_BOTTOMALIGN | TPM_LEFTALIGN); + break; + } + case ACTION_PESEL: + { + wa2.sendPlCmd(Winamp2FrontEnd::WA2_PLEDITPOPUP_SEL, r.left, r.top, TPM_BOTTOMALIGN | TPM_LEFTALIGN); + break; + } + case ACTION_PEMISC: + { + wa2.sendPlCmd(Winamp2FrontEnd::WA2_PLEDITPOPUP_MISC, r.left, r.top, TPM_BOTTOMALIGN | TPM_LEFTALIGN); + break; + } + case ACTION_PELIST: + { + wa2.sendPlCmd(Winamp2FrontEnd::WA2_PLEDITPOPUP_LIST, r.right, r.top, TPM_BOTTOMALIGN | TPM_RIGHTALIGN); + break; + } + case ACTION_PELISTOFLISTS: + { + wa2.triggerPEListOfListsMenu(r.left, r.top); + break; + } + case ACTION_VIDFS: + { + wa2.sendVidCmd(Winamp2FrontEnd::WA2_VIDCMD_FULLSCREEN); + break; + } + case ACTION_VID1X: + { + wa2.sendVidCmd(Winamp2FrontEnd::WA2_VIDCMD_1X); + break; + } + case ACTION_VID2X: + { + wa2.sendVidCmd(Winamp2FrontEnd::WA2_VIDCMD_2X); + break; + } + case ACTION_VIDTV: + { + wa2.sendVidCmd(Winamp2FrontEnd::WA2_VIDCMD_LIB); + break; + } + case ACTION_VIDMISC: + { + wa2.sendVidCmd(Winamp2FrontEnd::WA2_VIDPOPUP_MISC, r.right, r.top, TPM_BOTTOMALIGN | TPM_RIGHTALIGN); + break; + } + case ACTION_VISNEXT: + { + wa2.visNext(); + break; + } + case ACTION_VISPREV: + { + wa2.visPrev(); + break; + } + case ACTION_VISFS: + { + wa2.visFullscreen(); + break; + } + case ACTION_VISCFG: + { + wa2.visConfig(); + break; + } + case ACTION_VISMENU: + { + wa2.visMenu(); + break; + } + case ACTION_TRACKMENU: + { + extern const wchar_t *GetMenuItemString(HMENU menu, int id, int bypos); +#define WINAMP_TOGGLE_AUTOSCROLL 40189 +#define ID_RATING5 40396 +#define ID_RATING4 40397 +#define ID_RATING3 40398 +#define ID_RATING2 40399 +#define ID_RATING1 40400 +#define ID_RATING0 40401 + HMENU top_menu = wa2.getTopMenu(); + HMENU contextmenus = GetSubMenu(top_menu, 3); + HMENU songinfomenu = GetSubMenu(contextmenus, 0); + HMENU ratingmenu = GetSubMenu(songinfomenu, 5); + int rating = wa2.getCurTrackRating(); + CheckMenuItem(ratingmenu, ID_RATING5, (rating == 5) ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(ratingmenu, ID_RATING4, (rating == 4) ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(ratingmenu, ID_RATING3, (rating == 3) ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(ratingmenu, ID_RATING2, (rating == 2) ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(ratingmenu, ID_RATING1, (rating == 1) ? MF_CHECKED : MF_UNCHECKED); + CheckMenuItem(ratingmenu, ID_RATING0, (rating == 0) ? MF_CHECKED : MF_UNCHECKED); + StringW olditemstr = GetMenuItemString(songinfomenu, 3, TRUE); + RemoveMenu(songinfomenu, 3, MF_BYPOSITION); + + LRESULT IPC_LIBRARY_SENDTOMENU = SendMessageW(plugin.hwndParent, WM_WA_IPC, (WPARAM)&"LibrarySendToMenu", IPC_REGISTER_WINAMP_IPCMESSAGE); + HMENU menu = 0; + memset(&mainSendTo, 0, sizeof(mainSendTo)); + if (IPC_LIBRARY_SENDTOMENU > 65536 && SendMessageW(plugin.hwndParent, WM_WA_IPC, (WPARAM)0, IPC_LIBRARY_SENDTOMENU) == 0xffffffff) + { + char stStr[32] = {0}; + MENUITEMINFOA mii = {sizeof(mii), MIIM_SUBMENU | MIIM_TYPE, MFT_STRING, }; + mii.hSubMenu = menu = CreatePopupMenu(); + mii.dwTypeData = WASABI_API_LNGSTRING_BUF(IDS_SEND_TO,stStr,32); + mii.cch = strlen((char*)mii.dwTypeData); + + InsertMenuItemA(songinfomenu, 3, TRUE, &mii); + + mainSendTo.mode = 1; + mainSendTo.hwnd = plugin.hwndParent; // TODO??? + mainSendTo.data_type = ML_TYPE_FILENAMESW; + mainSendTo.build_hMenu = menu; + } + + menufucker_t mf = {sizeof(mf),MENU_SONGTICKER,songinfomenu,0x3000,0x4000,0}; + pluginMessage message_build = {SendMessageW(plugin.hwndParent, WM_WA_IPC, (WPARAM)&"menufucker_build", IPC_REGISTER_WINAMP_IPCMESSAGE),(intptr_t)&mf,0}; + sendMlIpc(ML_IPC_SEND_PLUGIN_MESSAGE,(WPARAM)&message_build); + + int ret = DoTrackPopup(songinfomenu, TPM_LEFTALIGN | TPM_TOPALIGN | TPM_LEFTBUTTON | TPM_RIGHTBUTTON|TPM_RETURNCMD, p1, p2, wa2.getMainWindow()); + + pluginMessage message_result = {SendMessageW(plugin.hwndParent, WM_WA_IPC, (WPARAM)&"menufucker_result", IPC_REGISTER_WINAMP_IPCMESSAGE),(intptr_t)&mf,ret,0}; + sendMlIpc(ML_IPC_SEND_PLUGIN_MESSAGE,(WPARAM)&message_result); + + if (menu) + { + if (mainSendTo.mode == 2) + { + mainSendTo.menu_id = ret; + if (SendMessageW(plugin.hwndParent, WM_WA_IPC, (WPARAM)&mainSendTo, IPC_LIBRARY_SENDTOMENU) == 0xffffffff) + { + wchar_t buf[FILENAME_SIZE + 1] = {0}; + wchar_t *end=buf; + size_t endSize = 0; + const wchar_t *entry = wa2.getFileW(wa2.getCurPlaylistEntry()); + if (entry && *entry) + { + StringCchCopyExW(buf, FILENAME_SIZE, entry, &end, &endSize, 0); + + mainSendTo.mode = 3; + mainSendTo.data = buf; + mainSendTo.data_type = ML_TYPE_FILENAMESW; + SendMessageW(plugin.hwndParent, WM_WA_IPC, (WPARAM)&mainSendTo, IPC_LIBRARY_SENDTOMENU); + } + } + } + // remove sendto + DeleteMenu(songinfomenu, 3, MF_BYPOSITION); + } + if (mainSendTo.mode) + { + mainSendTo.mode = 4; + SendMessageW(plugin.hwndParent, WM_WA_IPC, (WPARAM)&mainSendTo, IPC_LIBRARY_SENDTOMENU); // cleanup + memset(&mainSendTo, 0, sizeof(mainSendTo)); + } + InsertMenuW(songinfomenu, 3, MF_BYPOSITION | MF_STRING, WINAMP_TOGGLE_AUTOSCROLL, olditemstr); + if (ret) SendMessageW(wa2.getMainWindow(), WM_COMMAND, ret, 0); // TODO? + + break; + } + case ACTION_SENDTO: + { + LRESULT IPC_LIBRARY_SENDTOMENU = SendMessageW(plugin.hwndParent, WM_WA_IPC, (WPARAM)&"LibrarySendToMenu", IPC_REGISTER_WINAMP_IPCMESSAGE); + HMENU menu = 0; + memset(&mainSendTo, 0, sizeof(mainSendTo)); + if (IPC_LIBRARY_SENDTOMENU > 65536 && SendMessageW(plugin.hwndParent, WM_WA_IPC, (WPARAM)0, IPC_LIBRARY_SENDTOMENU) == 0xffffffff) + { + menu = CreatePopupMenu(); + + mainSendTo.mode = 1; + mainSendTo.hwnd = plugin.hwndParent; // TODO??? + mainSendTo.data_type = ML_TYPE_FILENAMESW; + mainSendTo.build_hMenu = menu; + } + int ret = DoTrackPopup(menu, TPM_LEFTALIGN | TPM_TOPALIGN | TPM_LEFTBUTTON | TPM_RIGHTBUTTON|TPM_RETURNCMD, p1, p2, wa2.getMainWindow()); + if (menu) + { + if (mainSendTo.mode == 2) + { + mainSendTo.menu_id = ret; + if (SendMessageW(plugin.hwndParent, WM_WA_IPC, (WPARAM)&mainSendTo, IPC_LIBRARY_SENDTOMENU) == 0xffffffff) + { + wchar_t buf[FILENAME_SIZE + 1] = {0}; + wchar_t *end=buf; + size_t endSize = 0; + const wchar_t *entry = wa2.getFileW(wa2.getCurPlaylistEntry()); + if (entry && *entry) + { + StringCchCopyExW(buf, FILENAME_SIZE, entry, &end, &endSize, 0); + + mainSendTo.mode = 3; + mainSendTo.data = buf; + mainSendTo.data_type = ML_TYPE_FILENAMESW; + SendMessageW(plugin.hwndParent, WM_WA_IPC, (WPARAM)&mainSendTo, IPC_LIBRARY_SENDTOMENU); + } + } + } + } + if (mainSendTo.mode) + { + mainSendTo.mode = 4; + SendMessageW(plugin.hwndParent, WM_WA_IPC, (WPARAM)&mainSendTo, IPC_LIBRARY_SENDTOMENU); // cleanup + memset(&mainSendTo, 0, sizeof(mainSendTo)); + } + + } + break; + case ACTION_TRACKINFO: + { + wa2.openTrackInfo(); + break; + } + } + in_menu = 0; + return 1; +} + +HMENU MenuActions::makeSkinOptionsSubMenu(GUID g, int *cmdoffset) +{ + CfgItem *item = WASABI_API_CONFIG->config_getCfgItemByGuid(g); + if (item != NULL) + { + HMENU menu = CreatePopupMenu(); + int n = MIN(item->getNumAttributes(), 500); + for (int i = 0;i < n;i++) + { + const wchar_t *attr = item->enumAttribute(i); + if (attr && *attr) + { + HMENU submenu = NULL; + wchar_t txt[256] = {0}; + item->getData(attr, txt, 256); + GUID g = nsGUID::fromCharW(txt); + if (g != INVALID_GUID) + { // submenu ! + submenu = makeSkinOptionsSubMenu(g, cmdoffset); + } + int v = item->getDataAsInt(attr, 0); + if (WCSCASEEQLSAFE(txt, L"-")) + { + InsertMenu(menu, i, MF_SEPARATOR | MF_BYPOSITION, 0, NULL); + } + else + InsertMenuW(menu, i, ((v && !submenu) ? MF_CHECKED : MF_UNCHECKED) | MF_STRING | MF_BYPOSITION | (submenu ? MF_POPUP : 0), submenu ? (UINT_PTR)submenu : 44000 + *cmdoffset, _(attr)); + } + (*cmdoffset)++; + } + return menu; + } + return NULL; +} + +void MenuActions::installSkinOptions(HMENU menu) +{ + optionsmenu_wa = 0; + HMENU omenu = NULL; + if (menu == NULL) + { + optionsmenu_wa = 1; + menu = wa2.getMenuBarMenu(Winamp2FrontEnd::WA2_MAINMENUBAR_OPTIONS); + omenu = GetSubMenu(wa2.getPopupMenu(), 11 + wa2.adjustOptionsPopupMenu(0)); + } + int pos2 = 12; + int cmdoffset = 0; + int pos = optionsmenu_wa ? wa2.adjustFFOptionsMenu(0) + 6 + NUMSTATICWINDOWS /*+ 1*//* + 9*/ : 0; + lowest_itempos = pos; + int insertedline = 0; + if (menu && optionsmenuitems) + { + int n = MIN(optionsmenuitems->getNumAttributes(), 500); + for (int i = 0;i < n;i++) + { + const wchar_t *attr = optionsmenuitems->enumAttribute(i); + if (attr && *attr) + { + HMENU submenu = NULL; + wchar_t txt[256] = {0}; + optionsmenuitems->getData(attr, txt, 256); + GUID g = nsGUID::fromCharW(txt); + if (g != INVALID_GUID) + { // submenu ! + submenu = makeSkinOptionsSubMenu(g, &cmdoffset); + if (submenu) + menulist.addItem(submenu); + } + int v = optionsmenuitems->getDataAsInt(attr, 0); + if (optionsmenu_wa && !insertedline) + { + wa2.adjustFFOptionsMenu(1); + insertedline = 1; + InsertMenu(menu, pos++, MF_SEPARATOR | MF_BYPOSITION, 0, NULL); + if (omenu) InsertMenu(omenu, pos2++, MF_SEPARATOR | MF_BYPOSITION, 0, NULL); + } + if (optionsmenu_wa) wa2.adjustFFOptionsMenu(1); + if (WCSCASEEQLSAFE(txt, L"-")) + { + InsertMenu(menu, pos++, MF_SEPARATOR | MF_BYPOSITION, 0, NULL); + if (omenu) InsertMenu(omenu, pos2++, MF_SEPARATOR | MF_BYPOSITION, 0, NULL); + } + else + InsertMenuW(menu, pos++, (v ? MF_CHECKED : MF_UNCHECKED) | MF_STRING | MF_BYPOSITION | (submenu ? MF_POPUP : 0), submenu ? (UINT_PTR)submenu : 44000 + cmdoffset, _(attr)); + if (omenu) InsertMenuW(omenu, pos2++, (v ? MF_CHECKED : MF_UNCHECKED) | MF_STRING | MF_BYPOSITION | (submenu ? MF_POPUP : 0), submenu ? (UINT_PTR)submenu : 44000 + cmdoffset, _(attr)); + } + cmdoffset++; + } + ffoptionstop = 44000 + cmdoffset; + } + // insert colorthemes submenu + if (omenu) + { + PtrListQuickSorted sortedthemes; + int fn = MIN(WASABI_API_SKIN->colortheme_getNumColorSets(), 500); + for (int t = 0;t < fn;t++) + sortedthemes.addItem(new ColorThemeSlot(WASABI_API_SKIN->colortheme_enumColorSet(t), t)); + if (!insertedline) + { + wa2.adjustFFOptionsMenu(1); + InsertMenu(menu, pos++, MF_SEPARATOR | MF_BYPOSITION, 0, NULL); + if (omenu) InsertMenu(omenu, pos2++, MF_SEPARATOR | MF_BYPOSITION, 0, NULL); + } + HMENU submenu = CreatePopupMenu(); + if (WASABI_API_SKIN->colortheme_getNumColorSets() == 0) + { + InsertMenuW(submenu, 0, MF_GRAYED | MF_STRING | MF_BYPOSITION, 0, WASABI_API_LNGSTRINGW(IDS_NO_THEME_AVAILABLE)); + } + else + { + int n = sortedthemes.getNumItems(); + for (int i = 0;i < n;i++) + { + const wchar_t *ct = sortedthemes.enumItem(i)->name; + int entry = sortedthemes.enumItem(i)->entry; + int iscurrent = WCSCASEEQLSAFE(ct, WASABI_API_SKIN->colortheme_getColorSet()); + InsertMenuW(submenu, i, (iscurrent ? MF_CHECKED : MF_UNCHECKED) | MF_STRING | MF_BYPOSITION, 44500 + entry, ct); + } + } + if (optionsmenu_wa) wa2.adjustFFOptionsMenu(1); + InsertMenuW(menu, pos++, MF_STRING | MF_BYPOSITION | (submenu ? MF_POPUP : 0), submenu ? (UINT_PTR)submenu : 44000 + cmdoffset, WASABI_API_LNGSTRINGW(IDS_COLOR_THEMES)); + if (omenu) InsertMenuW(omenu, pos2++, MF_STRING | MF_BYPOSITION | (submenu ? MF_POPUP : 0), submenu ? (UINT_PTR)submenu : 44000 + cmdoffset, WASABI_API_LNGSTRINGW(IDS_COLOR_THEMES)); + cmdoffset++; + sortedthemes.deleteAll(); + } + highest_itempos = pos; +} + +void MenuActions::removeSkinOptions() +{ + if (highest_itempos == -1 || !optionsmenu_wa) return ; + for (int j = 0;j < menulist.getNumItems();j++) + DestroyMenu(menulist.enumItem(j)); + menulist.removeAll(); + + HMENU menu = wa2.getMenuBarMenu(Winamp2FrontEnd::WA2_MAINMENUBAR_OPTIONS); + HMENU omenu = GetSubMenu(wa2.getPopupMenu(), 11 + wa2.adjustOptionsPopupMenu(0)); + if (menu && optionsmenuitems) + { + for (int i = lowest_itempos;i < highest_itempos;i++) + { + RemoveMenu(menu, lowest_itempos, MF_BYPOSITION); + if (omenu) RemoveMenu(omenu, 12, MF_BYPOSITION); + wa2.adjustFFOptionsMenu( -1); + } + } + highest_itempos = -1; +} + +int MenuActions::toggleOption(int n, GUID guid, int *cmdoffset) +{ + int _cmdoffset = 0; + if (!cmdoffset) cmdoffset = &_cmdoffset; + CfgItem *item = NULL; + if (guid == INVALID_GUID) + item = optionsmenuitems; + else + item = WASABI_API_CONFIG->config_getCfgItemByGuid(guid); + + if (!item) // not sure why this happens, but it happened in a crash report so I'm going to check for it. + return 1; // TODO: guru + + for (int i = 0; i < item->getNumAttributes();i++) + { + const wchar_t *name = item->enumAttribute(i); + if (name && *name) + { + wchar_t txt[256] = {0}; + item->getData(name, txt, 256); + GUID g = nsGUID::fromCharW(txt); + if (g != INVALID_GUID) + { // submenu ! + if (toggleOption(n, g, cmdoffset)) return 1; + } + if (*cmdoffset == n) + { + int newv = item->getDataAsInt(name) ? 0 : 1; + item->setDataAsInt(name, newv); + return 1; + } + } + (*cmdoffset)++; + } + return 0; +} + +const wchar_t* MenuActions::localizeSkinWindowName(const wchar_t* attr) +{ + static wchar_t tweaked_attr[96]; + ZERO(tweaked_attr); + + // allows us to map some of the common actions to localised versions (primarily with bundled skins) + if(!_wcsicmp(attr,L"Equalizer\tAlt+G")) + { + // if there's a match then we can force things to use the previous menu + // string (fixes localisation inconsistancy without altering the scripts!) + lstrcpynW(tweaked_attr, eqmenustring, 96); + } + else if(!_wcsicmp(attr,L"Skin Settings\tAlt+C")) + { + WASABI_API_LNGSTRINGW_BUF(IDS_SKIN_SETTINGS,tweaked_attr,96); + } + else if(!_wcsicmp(attr,L"Web Browser\tAlt+X")) + { + WASABI_API_LNGSTRINGW_BUF(IDS_WEB_BROWSER,tweaked_attr,96); + } + else if(!_wcsicmp(attr,L"Album Art\tAlt+A")) + { + WASABI_API_LNGSTRINGW_BUF(IDS_ALBUM_ART,tweaked_attr,96); + } + else if(!_wcsicmp(attr,L"Color Editor")) + { + WASABI_API_LNGSTRINGW_BUF(IDS_COLOR_EDITOR,tweaked_attr,96); + } + else + { + return attr; + } + return tweaked_attr; +} + +// FIX ME - menu weirdness going on!! +void MenuActions::installSkinWindowOptions() +{ + HMENU menu = wa2.getMenuBarMenu(Winamp2FrontEnd::WA2_MAINMENUBAR_WINDOWS); + int pos = lowest_witempos = wa2.adjustFFWindowsMenu(0) + NUMSTATICWINDOWS; + HMENU omenu = wa2.getPopupMenu(); + int pos2 = lowest_witempos2 = wa2.adjustOptionsPopupMenu(0) + 6 + NUMSTATICWINDOWS + 1; + + MENUITEMINFOW mii = {sizeof(mii), }; + mii.fMask = MIIM_TYPE | MIIM_DATA | MIIM_ID | MIIM_STATE | MIIM_SUBMENU; + mii.fType = MFT_STRING; + mii.wID = 42000; + mii.dwItemData = 0xD02; // use this as a check so we're only removing the correct items!! + + if (menu && windowsmenuitems) + { + int n = MIN(windowsmenuitems->getNumAttributes(), 500); + int cmdoffset = 0; + for (int i = 0;i < n;i++) + { + const wchar_t *attr = windowsmenuitems->enumAttribute(i); + + if (attr && *attr) + { + HMENU submenu = NULL; + wchar_t txt[256] = {0}; + windowsmenuitems->getData(attr, txt, 256); + GUID g = nsGUID::fromCharW(txt); + if (g != INVALID_GUID) + { // submenu ! + submenu = makeSkinOptionsSubMenu(g, &cmdoffset); + if (submenu) + wmenulist.addItem(submenu); + } + int v = windowsmenuitems->getDataAsInt(attr, 0); + wa2.adjustFFWindowsMenu(1); + wa2.adjustOptionsPopupMenu(1); + + attr = localizeSkinWindowName(attr); + const wchar_t* t = _(attr); + mii.dwTypeData = const_cast(t); + + if (WCSCASEEQLSAFE(txt, L"-")) + { + InsertMenu(menu, pos++, MF_SEPARATOR | MF_BYPOSITION, 0, NULL); + InsertMenu(omenu, pos2++, MF_SEPARATOR | MF_BYPOSITION, 0, NULL); + } + else{ + mii.cch = wcslen(mii.dwTypeData); + mii.fState = (v ? MFS_CHECKED : 0); + mii.wID = 42000 + cmdoffset; + mii.hSubMenu = submenu; + InsertMenuItemW(menu, pos++, TRUE, &mii); + } + InsertMenuItemW(omenu, pos2++, TRUE, &mii); + } + cmdoffset++; + } + ffwoptionstop = 42000 + cmdoffset; + } + highest_witempos = pos; + highest_witempos2 = pos2; +} + +void MenuActions::removeSkinWindowOptions() +{ + if (highest_witempos == -1) return ; + for (int j = 0;j < wmenulist.getNumItems();j++) + DestroyMenu(wmenulist.enumItem(j)); + wmenulist.removeAll(); + + HMENU menu = wa2.getMenuBarMenu(Winamp2FrontEnd::WA2_MAINMENUBAR_WINDOWS); + HMENU omenu = wa2.getPopupMenu(); + if (menu && windowsmenuitems) + { + for(int i = GetMenuItemCount(menu)-1; i != 0; i--) + { + MENUITEMINFOW info = {sizeof(info), MIIM_DATA, 0, }; + if(GetMenuItemInfoW(menu,i,TRUE,&info)) + { + if(info.dwItemData == 0xD02){ + RemoveMenu(menu,i,MF_BYPOSITION); + wa2.adjustFFWindowsMenu(-1); + } + } + } + + for(int i = GetMenuItemCount(omenu)-1; i != 0; i--) + { + MENUITEMINFOW info = {sizeof(info), MIIM_DATA, 0, }; + if(GetMenuItemInfoW(omenu,i,TRUE,&info)) + { + if(info.dwItemData == 0xD02){ + RemoveMenu(omenu,i,MF_BYPOSITION); + wa2.adjustOptionsPopupMenu(-1); + } + } + } + + /*for (int i = highest_witempos;i >= lowest_witempos;i--) + { + RemoveMenu(menu, i, MF_BYPOSITION); + wa2.adjustFFWindowsMenu(-1); + } + + for (int i = highest_witempos2;i >= lowest_witempos2;i--) + { + RemoveMenu(menu, i, MF_BYPOSITION); + wa2.adjustOptionsPopupMenu(-1); + } + /*/ + /*for (int i = lowest_witempos;i < highest_witempos;i++) + { + RemoveMenu(menu, lowest_witempos, MF_BYPOSITION); + wa2.adjustFFWindowsMenu(-1); + } + + for (int i = lowest_witempos2;i < highest_witempos2;i++) + { + RemoveMenu(omenu, lowest_witempos2, MF_BYPOSITION); + wa2.adjustOptionsPopupMenu(-1); + }/**/ + } + highest_witempos = -1; + highest_witempos2 = -1; +} + +int MenuActions::toggleWindowOption(int n, GUID guid, int *cmdoffset) +{ + int _cmdoffset = 0; + if (!cmdoffset) cmdoffset = &_cmdoffset; + CfgItem *item = NULL; + if (guid == INVALID_GUID) + item = windowsmenuitems; + else + item = WASABI_API_CONFIG->config_getCfgItemByGuid(guid); + + for (int i = 0; i < item->getNumAttributes();i++) + { + const wchar_t *name = item->enumAttribute(i); + if (name && *name) + { + wchar_t txt[256] = {0}; + item->getData(name, txt, 256); + GUID g = nsGUID::fromCharW(txt); + if (g != INVALID_GUID) + { // submenu ! + if (toggleWindowOption(n, g, cmdoffset)) return 1; + } + if (*cmdoffset == n) + { + int newv = item->getDataAsInt(name) ? 0 : 1; + item->setDataAsInt(name, newv); + return 1; + } + } + (*cmdoffset)++; + } + return 0; +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_ff/menuactions.h b/Src/Plugins/General/gen_ff/menuactions.h new file mode 100644 index 00000000..bc940145 --- /dev/null +++ b/Src/Plugins/General/gen_ff/menuactions.h @@ -0,0 +1,91 @@ +#ifndef _MENUACTIONS_H +#define _MENUACTIONS_H + +#include + +extern int ffoptionstop; +extern int ffwoptionstop; +extern int in_menu; + +class MenuActions : public svc_actionI { + public : + MenuActions(); + virtual ~MenuActions(); + + static const char *getServiceName() { return "Menu Actions"; } + virtual int onActionId(int pvtid, const wchar_t *action, const wchar_t *param=NULL, int p1=0, int p2=0, void *data=NULL, int datalen=0, ifc_window *source=NULL); + + static void installSkinOptions(HMENU menu=NULL); + static void removeSkinOptions(); + static int toggleOption(int n, GUID g=INVALID_GUID, int *cmdoffset=NULL); + + static void installSkinWindowOptions(); + static void removeSkinWindowOptions(); + static int toggleWindowOption(int n, GUID g=INVALID_GUID, int *cmdoffset=NULL); + + static HMENU makeSkinOptionsSubMenu(GUID g, int *cmdoffset); + + static const wchar_t* localizeSkinWindowName(const wchar_t*); + + enum { + _ACTION_MENU = 0, + _ACTION_SYSMENU, + _ACTION_CONTROLMENU, + ACTION_WA5FILEMENU, + ACTION_WA5PLAYMENU, + ACTION_WA5OPTIONSMENU, + ACTION_WA5WINDOWSMENU, + ACTION_WA5HELPMENU, + ACTION_WA5PEFILEMENU, + ACTION_WA5PEPLAYLISTMENU, + ACTION_WA5PESORTMENU, + ACTION_WA5PEHELPMENU, + ACTION_WA5MLFILEMENU, + ACTION_WA5MLVIEWMENU, + ACTION_WA5MLHELPMENU, + ACTION_PEADD, + ACTION_PEREM, + ACTION_PESEL, + ACTION_PEMISC, + ACTION_PELIST, + ACTION_PELISTOFLISTS, + ACTION_VIDFS, + ACTION_VID1X, + ACTION_VID2X, + ACTION_VIDTV, + ACTION_VIDMISC, + ACTION_VISNEXT, + ACTION_VISPREV, + ACTION_VISRANDOM, + ACTION_VISFS, + ACTION_VISCFG, + ACTION_VISMENU, + ACTION_TRACKINFO, + ACTION_TRACKMENU, + ACTION_SENDTO, + }; +}; + + +class ColorThemeSlot +{ +public: + ColorThemeSlot(const wchar_t *_name, int _entry) : name(_name), entry(_entry) {} + virtual ~ColorThemeSlot() {} + StringW name; + int entry; +}; + +class ColorThemeSlotSort { +public: + // comparator for sorting + static int compareItem(ColorThemeSlot *p1, ColorThemeSlot *p2) { + return wcscmp(p1->name, p2->name); + } + // comparator for searching + static int compareAttrib(const wchar_t *attrib, ColorThemeSlot *item) { + return wcscmp(attrib, item->name); + } +}; + +#endif \ No newline at end of file diff --git a/Src/Plugins/General/gen_ff/minibrowserCOM.cpp b/Src/Plugins/General/gen_ff/minibrowserCOM.cpp new file mode 100644 index 00000000..e3d896d6 --- /dev/null +++ b/Src/Plugins/General/gen_ff/minibrowserCOM.cpp @@ -0,0 +1,93 @@ +#include "precomp__gen_ff.h" +#include "minibrowserCOM.h" + +MinibrowserCOM::MinibrowserCOM(BrowserWnd* brw) +{ + this->brw = brw; +} + +HRESULT __stdcall MinibrowserCOM::GetTypeInfoCount(unsigned int FAR* pctinfo) +{ + return E_NOTIMPL; +} + +HRESULT __stdcall MinibrowserCOM::GetTypeInfo(unsigned int iTInfo, LCID lcid, ITypeInfo FAR* FAR* ppTInfo) +{ + return E_NOTIMPL; +} + +HRESULT __stdcall MinibrowserCOM::GetIDsOfNames( REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgDispId) +{ + int notFound = false; + for (unsigned int i = 0; i != cNames; i++) + { + if (!_wcsicmp(rgszNames[i], L"messageToMaki")) + { + rgDispId[i] = MINIBROWSERCOM_MAKI_MESSAGETOMAKI; + continue; + } + notFound = true; + } + + if (!notFound) + return S_OK; + + return DISP_E_UNKNOWNNAME; +} + +HRESULT __stdcall MinibrowserCOM::Invoke(DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, EXCEPINFO FAR * pexecinfo, unsigned int FAR *puArgErr) +{ + switch (dispid) + { + case MINIBROWSERCOM_MAKI_MESSAGETOMAKI: + /* var ret = window.external.messageToMaki(str1, str2, int1, int2, int3) */ + /* keep in mind that the args are passed in reverse order! */ + + /*if (wFlags != DISPATCH_METHOD) + return DISP_E_MEMBERNOTFOUND;*/ + if (pdispparams->cArgs != 5) + return DISP_E_BADPARAMCOUNT; + + const wchar_t * ret = this->brw->messageToMaki(pdispparams->rgvarg[4].bstrVal, pdispparams->rgvarg[3].bstrVal, pdispparams->rgvarg[2].iVal, pdispparams->rgvarg[1].iVal, pdispparams->rgvarg[0].iVal); + + // (mpdeimos) we need to check this here since in JS one can omit the return value. In this case we would get a NPE here. + if (pvarResult != NULL) + { + BSTR returnValue = SysAllocString(ret); + VariantInit(pvarResult); + V_VT(pvarResult) = VT_BSTR; + V_BSTR(pvarResult) = returnValue; + } + return S_OK; + } + + return DISP_E_MEMBERNOTFOUND; +} + +STDMETHODIMP MinibrowserCOM::QueryInterface(REFIID riid, PVOID *ppvObject) +{ + if (!ppvObject) + return E_POINTER; + else if (IsEqualIID(riid, IID_IDispatch)) + *ppvObject = (IDispatch *)this; + else if (IsEqualIID(riid, IID_IUnknown)) + *ppvObject = this; + else + { + *ppvObject = NULL; + return E_NOINTERFACE; + } + + AddRef(); + return S_OK; +} + +ULONG MinibrowserCOM::AddRef(void) +{ + return 0; +} + +ULONG MinibrowserCOM::Release(void) +{ + return 0; +} diff --git a/Src/Plugins/General/gen_ff/minibrowserCOM.h b/Src/Plugins/General/gen_ff/minibrowserCOM.h new file mode 100644 index 00000000..e237b40f --- /dev/null +++ b/Src/Plugins/General/gen_ff/minibrowserCOM.h @@ -0,0 +1,25 @@ +#pragma once +#include +#include + +class MinibrowserCOM : IDispatch +{ +public: + MinibrowserCOM(BrowserWnd* brw); + /** IUnknown */ + STDMETHOD(QueryInterface)(REFIID riid, PVOID *ppvObject); + STDMETHOD_(ULONG, AddRef)(void); + STDMETHOD_(ULONG, Release)(void); + /** IDispatch */ + STDMETHOD (GetTypeInfoCount)(unsigned int FAR* pctinfo); + STDMETHOD (GetTypeInfo)(unsigned int iTInfo, LCID lcid, ITypeInfo FAR* FAR* ppTInfo); + STDMETHOD (GetIDsOfNames)(REFIID riid,LPOLESTR __RPC_FAR *rgszNames,UINT cNames,LCID lcid,DISPID __RPC_FAR *rgDispId); + STDMETHOD (Invoke)(DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, EXCEPINFO FAR * pexecinfo, unsigned int FAR *puArgErr); + + enum + { + MINIBROWSERCOM_MAKI_MESSAGETOMAKI = 666, + }; +private: + BrowserWnd* brw; +}; diff --git a/Src/Plugins/General/gen_ff/precomp__gen_ff.cpp b/Src/Plugins/General/gen_ff/precomp__gen_ff.cpp new file mode 100644 index 00000000..26e242df --- /dev/null +++ b/Src/Plugins/General/gen_ff/precomp__gen_ff.cpp @@ -0,0 +1,7 @@ +// precomp__gen_ff.cpp : source file that includes just the standard includes +// TheLibrary.pch will be the pre-compiled header +// precomp.obj will contain the pre-compiled type information + +#include "precomp__gen_ff.h" + + diff --git a/Src/Plugins/General/gen_ff/precomp__gen_ff.h b/Src/Plugins/General/gen_ff/precomp__gen_ff.h new file mode 100644 index 00000000..dd83219d --- /dev/null +++ b/Src/Plugins/General/gen_ff/precomp__gen_ff.h @@ -0,0 +1,76 @@ +// precomp__gen_ff.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#ifndef NULLSOFT_GEN_FF_PRECOMP_H +#define NULLSOFT_GEN_FF_PRECOMP_H + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers + +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0500 +#endif +#ifndef WINVER +#define WINVER 0x0501 +#endif + + +// Most used wasabi headers + +#include +//#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include "wa2core.h" +#include "wa2frontend.h" +#include "wa2wndembed.h" + +#include +#include +#include + +#include +#include +#include + +#include + +#include + + + +#endif // _PRECOMP_H diff --git a/Src/Plugins/General/gen_ff/prefs.cpp b/Src/Plugins/General/gen_ff/prefs.cpp new file mode 100644 index 00000000..998d3b50 --- /dev/null +++ b/Src/Plugins/General/gen_ff/prefs.cpp @@ -0,0 +1,136 @@ +#include "precomp__gen_ff.h" +#include "main.h" +#include "resource.h" +#include "prefs.h" +#include "wa2cfgitems.h" +#include "wa2frontend.h" +#include "../Agave/Language/api_language.h" +#include "gen.h" +#include +#include + +void turnonoff(HWND wnd, int *t, int n, int v) { + for (int i=0;i= 0 && (sel != last_page.getValueAsInt() || !subWnd)) + { + last_page.setValueAsInt(sel); + if (subWnd) DestroyWindow(subWnd); + subWnd = NULL; + subWndId = -1; + + UINT t=0; + DLGPROC p=0; + switch (sel) + { + case 0: t=IDD_PREFS_GENERAL; p=ffPrefsProc1; subWndId = 0; break; + case 1: t=IDD_PREFS_WINDOWS; p=ffPrefsProc4; subWndId = 1; break; + case 2: t=IDD_PREFS_FONTS; p=ffPrefsProc2; subWndId = 2; break; + case 3: t=IDD_PREFS_THEMES; p=ffPrefsProc3; subWndId = 3; break; + case 4: t=IDD_PREFS_SKIN; p=ffPrefsProc5; subWndId = 5; break; + } + if (t) subWnd=WASABI_API_CREATEDIALOGW(t,hwndDlg,p); + + if (IsWindow(subWnd)) + { + RECT r = {0}; + GetClientRect(tabwnd,&r); + TabCtrl_AdjustRect(tabwnd,FALSE,&r); + SetWindowPos(subWnd,HWND_TOP,r.left,r.top,r.right-r.left,r.bottom-r.top,SWP_NOACTIVATE); + ShowWindow(subWnd,SW_SHOWNA); + } + + if(!SendMessageW(plugin.hwndParent,WM_WA_IPC,IPC_ISWINTHEMEPRESENT,IPC_USE_UXTHEME_FUNC)) + { + SendMessageW(plugin.hwndParent,WM_WA_IPC,(WPARAM)tabwnd,IPC_USE_UXTHEME_FUNC); + SendMessageW(plugin.hwndParent,WM_WA_IPC,(WPARAM)subWnd,IPC_USE_UXTHEME_FUNC); + } + } +} + +#define TabCtrl_InsertItemW(hwnd, iItem, pitem) \ + (int)SNDMSG((hwnd), TCM_INSERTITEMW, (WPARAM)(int)(iItem), (LPARAM)(const TC_ITEMW *)(pitem)) + +// frame proc +INT_PTR CALLBACK ffPrefsProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam) { + switch (uMsg) + { + case WM_INITDIALOG: + { + if (WASABI_API_APP == NULL) + { + // wasabi is not initialized ! we need to init before we can access cfgitems otherwise we'd have + // to mirror their values with winamp.ini and that'd be seriously crappy + initFFApi(); + } + + if (!ffoptions) + ffoptions = new Wa2FFOptions(); + + TCITEMW item = {0}; + HWND tabwnd=GetDlgItem(hwndDlg,IDC_TAB1); + item.mask=TCIF_TEXT; + item.pszText=WASABI_API_LNGSTRINGW(IDS_GENERAL); + TabCtrl_InsertItemW(tabwnd,0,&item); + item.pszText=WASABI_API_LNGSTRINGW(IDS_WINDOW_SETTINGS); + TabCtrl_InsertItemW(tabwnd,1,&item); + item.pszText=WASABI_API_LNGSTRINGW(IDS_FONT_RENDERING); + TabCtrl_InsertItemW(tabwnd,2,&item); + if (m_are_we_loaded) + { + item.pszText=WASABI_API_LNGSTRINGW(IDS_COLOR_THEMES); + TabCtrl_InsertItemW(tabwnd,3,&item); + item.pszText=WASABI_API_LNGSTRINGW(IDS_CURRENT_SKIN); + TabCtrl_InsertItemW(tabwnd,4,&item); + } + + TabCtrl_SetCurSel(tabwnd,last_page.getValueAsInt()); + _dosetsel(hwndDlg); + } + return 0; + + case WM_NOTIFY: + { + LPNMHDR p=(LPNMHDR) lParam; + if (p->idFrom == IDC_TAB1 && p->code == TCN_SELCHANGE) + { + _dosetsel(hwndDlg); + return 0; + } + } + break; + + case WM_DESTROY: + subWnd=NULL; + return 0; + } + return 0; +} + +Wa2FFOptions::Wa2FFOptions() : CfgItemI(L"Winamp5", Wa2FFOptionsGuid) { + registerAttribute(&last_page); +} + +int ComboBox_AddStringW(HWND list, const wchar_t *string) +{ + return SendMessageW(list, CB_ADDSTRING, 0, (LPARAM)string); +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_ff/prefs.h b/Src/Plugins/General/gen_ff/prefs.h new file mode 100644 index 00000000..e90aaa33 --- /dev/null +++ b/Src/Plugins/General/gen_ff/prefs.h @@ -0,0 +1,30 @@ +#ifndef _FF_PREFS_H +#define _FF_PREFS_H + +#include + +extern INT_PTR CALLBACK ffPrefsProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam); +extern INT_PTR CALLBACK ffPrefsProc1(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam); +extern INT_PTR CALLBACK ffPrefsProc2(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam); +extern INT_PTR CALLBACK ffPrefsProc3(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam); +extern INT_PTR CALLBACK ffPrefsProc4(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam); +extern INT_PTR CALLBACK ffPrefsProc5(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam); + +void _dosetsel(HWND hwndDlg); +HWND +ActiveChildWindowFromPoint(HWND hwnd, POINTS cursor_s, const int *controls, size_t controlsCount); + +#define WA2FFOPTIONS_PARENT CfgItemI + +// {68A2EFD7-0FBB-4ef9-9D3A-590F943C2A73} +static const GUID Wa2FFOptionsGuid = +{ 0x68a2efd7, 0xfbb, 0x4ef9, { 0x9d, 0x3a, 0x59, 0xf, 0x94, 0x3c, 0x2a, 0x73 } }; + +class Wa2FFOptions : public WA2FFOPTIONS_PARENT { +public: + Wa2FFOptions (); +}; + +extern Wa2FFOptions *ffoptions; + +#endif \ No newline at end of file diff --git a/Src/Plugins/General/gen_ff/prefs_about.cpp b/Src/Plugins/General/gen_ff/prefs_about.cpp new file mode 100644 index 00000000..a9e5ca90 --- /dev/null +++ b/Src/Plugins/General/gen_ff/prefs_about.cpp @@ -0,0 +1,98 @@ +#include "precomp__gen_ff.h" +#include "gen.h" +#include "resource.h" +#include "menuactions.h" +#include "wa2frontend.h" +#include "../Agave/Language/api_language.h" + +extern const wchar_t *getSkinInfoW(); +extern int m_are_we_loaded; +ifc_window *skin_about_group = NULL; + +void destroyskinabout() +{ + if (skin_about_group) + WASABI_API_SKIN->group_destroy(skin_about_group); + skin_about_group = NULL; +} + +INT_PTR CALLBACK ffPrefsProc5(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch (uMsg) + { + case WM_INITDIALOG: + destroyskinabout(); + if (m_are_we_loaded) + { + if (WASABI_API_SKIN->group_exists(L"skin.about.group")) + { + skin_about_group = WASABI_API_SKIN->group_create(L"skin.about.group"); + if (skin_about_group) + { + skin_about_group->setVirtual(0); + HWND w = GetDlgItem(hwndDlg, IDC_STATIC_GROUP); + skin_about_group->setStartHidden(1); + skin_about_group->init(WASABI_API_WND->main_getRootWnd(), 1); + SetWindowLong(skin_about_group->gethWnd(), GWL_STYLE, GetWindowLong(skin_about_group->gethWnd(), GWL_STYLE) | WS_CHILD); + SetParent(skin_about_group->gethWnd(), w); + SetWindowLong(w, GWL_STYLE, GetWindowLong(w, GWL_STYLE) | WS_CLIPCHILDREN); + RECT r; + GetClientRect(w, &r); + skin_about_group->resize(r.left, r.top, r.right - r.left, r.bottom - r.top); + skin_about_group->setVisible(1); + ShowWindow(skin_about_group->gethWnd(), SW_NORMAL); + ShowWindow(GetDlgItem(hwndDlg, IDC_STATIC_EMPTY), SW_HIDE); + } + else + { + SetDlgItemTextA(hwndDlg, IDC_STATIC_EMPTY, WASABI_API_LNGSTRING(IDS_ERROR_WHILE_LOADING_SKIN_WINDOW)); + } + } + else + { + SetDlgItemTextW(hwndDlg, IDC_STATIC_EMPTY, getSkinInfoW()); + } + } + else + { + SetDlgItemTextA(hwndDlg, IDC_STATIC_EMPTY, WASABI_API_LNGSTRING(IDS_NO_SKIN_LOADED)); + } + return 1; + case WM_DESTROY: + destroyskinabout(); + break; + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDOK: case IDCANCEL: + EndDialog(hwndDlg, 0); + return 0; + case IDC_BUTTON_SKINSPECIFIC: + extern void unpopulateWindowsMenus(); + extern TList menulist; + HMENU menu = CreatePopupMenu(); + unpopulateWindowsMenus(); + MenuActions::installSkinOptions(menu); + menulist.addItem(menu); + HWND w = GetDlgItem(hwndDlg, IDC_BUTTON_SKINSPECIFIC); + RECT r; + GetWindowRect(w, &r); + int n = GetMenuItemCount(menu); + if (n == 1) + { + HMENU submenu = GetSubMenu(menu, 0); + if (submenu != NULL) menu = submenu; + } + else if (n == 0) + { + InsertMenuW(menu, 0, MF_BYPOSITION | MF_STRING | MF_GRAYED, 0, WASABI_API_LNGSTRINGW(IDS_NO_OPTIONS_AVAILABLE_FOR_THIS_SKIN)); + } + //DoTrackPopup(menu, TPM_LEFTALIGN | TPM_BOTTOMALIGN | TPM_LEFTBUTTON, r.left, r.top, wa2.getMainWindow()); + TrackPopupMenu(menu, TPM_LEFTALIGN | TPM_BOTTOMALIGN | TPM_LEFTBUTTON, r.left, r.top, 0, wa2.getMainWindow(), NULL); + MenuActions::removeSkinOptions(); + return 0; + } + break; + } + return 0; +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_ff/prefs_alpha.cpp b/Src/Plugins/General/gen_ff/prefs_alpha.cpp new file mode 100644 index 00000000..54b21b29 --- /dev/null +++ b/Src/Plugins/General/gen_ff/prefs_alpha.cpp @@ -0,0 +1,280 @@ +#include "precomp__gen_ff.h" +#include "resource.h" +#include "prefs.h" +#include "wa2cfgitems.h" +#include +#include "../Agave/Language/api_language.h" +#include +#include +void turnonoff(HWND wnd, int *t, int n, int v); +extern int toggle_from_wa2; +int opacity_all_on[] = {IDC_SLIDER_CUSTOMALPHA, IDC_STATIC_TRANSP, IDC_STATIC_ALPHA, IDC_STATIC_OPAQUE, IDC_COMBO_OPACITY}; +int opacity_all_off[] = {IDC_CHECK_LINKALPHA, }; +int opacity_unavail[] = {IDC_STATIC_OPACITY, IDC_CHECK_LINKALLALPHA, IDC_COMBO_OPACITY, IDC_SLIDER_CUSTOMALPHA, + IDC_STATIC_TRANSP,IDC_STATIC_ALPHA,IDC_STATIC_OPAQUE,IDC_CHECK_LINKALPHA,IDC_STATIC_AUTOON, + IDC_STATIC_AUTOONTXT,IDC_STATIC_FADEIN2,IDC_SLIDER_FADEIN,IDC_STATIC_FADEIN,IDC_STATIC_HOLD2, + IDC_SLIDER_HOLD,IDC_STATIC_HOLD,IDC_STATIC_FADEOUT2,IDC_SLIDER_FADEOUT,IDC_STATIC_FADEOUT, + IDC_STATIC_EXTENDBOX,IDC_EDIT_EXTEND,IDC_STATIC_EXTEND,IDC_SPIN_EXTEND}; +static int ratio_all_on[] = {IDC_SLIDER_CUSTOMSCALE, IDC_COMBO_SCALE, IDC_STATIC_SCALE}; +static int ratio_all_off[] = {IDC_CHECK_LINKRATIO, }; +static int spin_extend = 0; + +static UINT get_logslider(HWND wnd) { + int z = SendMessageW(wnd, TBM_GETPOS, 0, 0); + long a = (long)(0.5 + 303.03 * pow(100.0, (double)z/30303.0) - 303.03); + return a; +} + +void set_logslider(HWND wnd, int b) { + long a = (long) (0.5 + 30303.0 * log((double)(b+303.0)/303.03)/log(100.0)); + SendMessageW(wnd, TBM_SETPOS, 1, a); +} + +int ResizeComboBoxDropDown(HWND hwndDlg, UINT id, const wchar_t * str, int width) { + SIZE size = {0}; + HWND control = (id ? GetDlgItem(hwndDlg, id) : hwndDlg); + HDC hdc = GetDC(control); + // get and select parent dialog's font so that it'll calculate things correctly + HFONT font = (HFONT)SendMessageW((id ? hwndDlg : GetParent(hwndDlg)), WM_GETFONT, 0, 0), + oldfont = (HFONT)SelectObject(hdc, font); + GetTextExtentPoint32W(hdc, str, lstrlenW(str)+1, &size); + + int ret = width; + if(size.cx > width) + { + if (id) + SendDlgItemMessageW(hwndDlg, id, CB_SETDROPPEDWIDTH, size.cx, 0); + else + SendMessageW(hwndDlg, CB_SETDROPPEDWIDTH, size.cx, 0); + + ret = size.cx; + } + + SelectObject(hdc, oldfont); + ReleaseDC(control, hdc); + return ret; +} + +INT_PTR CALLBACK ffPrefsProc4(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam) { + switch (uMsg) { + case WM_INITDIALOG: + { + spin_extend = 0; + + int transpavail = Wasabi::Std::Wnd::isTransparencyAvailable(); + if (!transpavail) turnonoff(hwndDlg, opacity_unavail, sizeof(opacity_unavail)/sizeof(int), 0); + CheckDlgButton(hwndDlg, IDC_CHECK_LINKALPHA, cfg_uioptions_linkalpha.getValueAsInt()); + CheckDlgButton(hwndDlg, IDC_CHECK_LINKALLALPHA, cfg_uioptions_linkallalpha.getValueAsInt()); + Layout *main = SkinParser::getMainLayout(); + int curalpha = 255; + if (main) { + if (!WASABI_API_WND->rootwndIsValid(main)) { return 0; } + curalpha = cfg_uioptions_linkedalpha.getValueAsInt(); + } + int v = (int)((curalpha / 255.0f * 100.0f)+0.5f); + wchar_t msStr[16] = {0}; + WASABI_API_LNGSTRINGW_BUF(IDS_MS,msStr,16); + SendMessageW(GetDlgItem(hwndDlg,IDC_SLIDER_CUSTOMALPHA),TBM_SETRANGEMAX,0,100); + SendMessageW(GetDlgItem(hwndDlg,IDC_SLIDER_CUSTOMALPHA),TBM_SETRANGEMIN,0,10); + SendMessageW(GetDlgItem(hwndDlg,IDC_SLIDER_CUSTOMALPHA),TBM_SETPOS,1,v); + + wchar_t *name = 0; + SendDlgItemMessageW(hwndDlg, IDC_COMBO_OPACITY, CB_ADDSTRING, 0, (LPARAM)(name = WASABI_API_LNGSTRINGW(IDS_OPAQUE_FOCUS))); + int width = ResizeComboBoxDropDown(hwndDlg, IDC_COMBO_OPACITY, name, 0); + SendDlgItemMessageW(hwndDlg, IDC_COMBO_OPACITY, CB_SETITEMDATA, 0, 2); + SendDlgItemMessageW(hwndDlg, IDC_COMBO_OPACITY, CB_ADDSTRING, 0, (LPARAM)(name = WASABI_API_LNGSTRINGW(IDS_OPAQUE_HOVER))); + width = ResizeComboBoxDropDown(hwndDlg, IDC_COMBO_OPACITY, name, width); + SendDlgItemMessageW(hwndDlg, IDC_COMBO_OPACITY, CB_SETITEMDATA, 1, 1); + SendDlgItemMessageW(hwndDlg, IDC_COMBO_OPACITY, CB_ADDSTRING, 0, (LPARAM)(name = WASABI_API_LNGSTRINGW(IDS_NO_OPACITY))); + ResizeComboBoxDropDown(hwndDlg, IDC_COMBO_OPACITY, name, width); + SendDlgItemMessageW(hwndDlg, IDC_COMBO_OPACITY, CB_SETITEMDATA, 2, 0); + + int item = 2; + switch(cfg_uioptions_autoopacitylinked.getValueAsInt()) + { + case 1: + item = 1; + break; + case 2: + item = 0; + break; + default: + item = 2; + break; + } + SendDlgItemMessageW(hwndDlg, IDC_COMBO_OPACITY, CB_SETCURSEL, item, 0); + + SetDlgItemTextA(hwndDlg, IDC_STATIC_ALPHA, StringPrintf("%d%%", v)); + turnonoff(hwndDlg, opacity_all_on, sizeof(opacity_all_on)/sizeof(int), cfg_uioptions_linkallalpha.getValueAsInt() && transpavail); + turnonoff(hwndDlg, opacity_all_off, sizeof(opacity_all_off)/sizeof(int), !cfg_uioptions_linkallalpha.getValueAsInt() && transpavail); + SendMessageW(GetDlgItem(hwndDlg,IDC_SLIDER_HOLD),TBM_SETRANGEMAX,0,30303); + SendMessageW(GetDlgItem(hwndDlg,IDC_SLIDER_HOLD),TBM_SETRANGEMIN,0,0); + set_logslider(GetDlgItem(hwndDlg,IDC_SLIDER_HOLD),cfg_uioptions_autoopacitytime.getValueAsInt()); + SetDlgItemTextW(hwndDlg, IDC_STATIC_HOLD, StringPrintfW(L"%d%s", cfg_uioptions_autoopacitytime.getValueAsInt(),msStr)); + SendMessageW(GetDlgItem(hwndDlg,IDC_SLIDER_FADEIN),TBM_SETRANGEMAX,0,30303); + SendMessageW(GetDlgItem(hwndDlg,IDC_SLIDER_FADEIN),TBM_SETRANGEMIN,0,0); + set_logslider(GetDlgItem(hwndDlg,IDC_SLIDER_FADEIN),cfg_uioptions_autoopacityfadein.getValueAsInt()); + SetDlgItemTextW(hwndDlg, IDC_STATIC_FADEIN, StringPrintfW(L"%d%s", cfg_uioptions_autoopacityfadein.getValueAsInt(),msStr)); + SendMessageW(GetDlgItem(hwndDlg,IDC_SLIDER_FADEOUT),TBM_SETRANGEMAX,0,30303); + SendMessageW(GetDlgItem(hwndDlg,IDC_SLIDER_FADEOUT),TBM_SETRANGEMIN,0,0); + set_logslider(GetDlgItem(hwndDlg,IDC_SLIDER_FADEOUT),cfg_uioptions_autoopacityfadeout.getValueAsInt()); + SetDlgItemTextW(hwndDlg, IDC_STATIC_FADEOUT, StringPrintfW(L"%d%s", cfg_uioptions_autoopacityfadeout.getValueAsInt(),msStr)); + + SendMessageW(GetDlgItem(hwndDlg,IDC_SPIN_EXTEND),UDM_SETRANGE,0,MAKELONG(100,0)); + SetDlgItemInt(hwndDlg, IDC_EDIT_EXTEND, cfg_uioptions_extendautoopacity.getValueAsInt(), FALSE); + + CheckDlgButton(hwndDlg, IDC_CHECK_LINKALLRATIO, cfg_uioptions_linkallratio.getValueAsInt()); + CheckDlgButton(hwndDlg, IDC_CHECK_LINKRATIO, cfg_uioptions_linkratio.getValueAsInt()); + + SendDlgItemMessageW(hwndDlg, IDC_COMBO_SCALE, CB_ADDSTRING, 0, (LPARAM)(name = WASABI_API_LNGSTRINGW(IDS_USELOCK))); + width = ResizeComboBoxDropDown(hwndDlg, IDC_COMBO_SCALE, name, 0); + SendDlgItemMessageW(hwndDlg, IDC_COMBO_SCALE, CB_ADDSTRING, 0, (LPARAM)(name = WASABI_API_LNGSTRINGW(IDS_ALLLOCKED))); + ResizeComboBoxDropDown(hwndDlg, IDC_COMBO_SCALE, name, width); + SendDlgItemMessageW(hwndDlg, IDC_COMBO_SCALE, CB_SETCURSEL, !!cfg_uioptions_uselocks.getValueAsInt(), 0); + + int oldscale = 1; + if (main) + { + if (!WASABI_API_WND->rootwndIsValid(main)) { return 0; } + oldscale = (int)main->getRenderRatio(); + } + int u = (int)((oldscale * 100.0f) + 0.5f); + SendMessageW(GetDlgItem(hwndDlg, IDC_SLIDER_CUSTOMSCALE), TBM_SETRANGEMAX, 0, 300); + SendMessageW(GetDlgItem(hwndDlg, IDC_SLIDER_CUSTOMSCALE), TBM_SETRANGEMIN, 0, 10); + SendMessageW(GetDlgItem(hwndDlg, IDC_SLIDER_CUSTOMSCALE), TBM_SETPOS, 1, u); + SetDlgItemTextA(hwndDlg, IDC_STATIC_SCALE, StringPrintf("%d%%", u)); + turnonoff(hwndDlg, ratio_all_on, sizeof(ratio_all_on) / sizeof(int), cfg_uioptions_linkallratio.getValueAsInt()); + turnonoff(hwndDlg, ratio_all_off, sizeof(ratio_all_off) / sizeof(int), !cfg_uioptions_linkallratio.getValueAsInt()); + + spin_extend = 1; + return 1; + } + case WM_HSCROLL: + { + char msStr[16] = {0}; + WASABI_API_LNGSTRING_BUF(IDS_MS,msStr,16); + HWND ctrl = (HWND)lParam; + int t=(int)SendMessageW((HWND) lParam,TBM_GETPOS,0,0); + if (ctrl == GetDlgItem(hwndDlg,IDC_SLIDER_CUSTOMALPHA)) { + SetDlgItemTextA(hwndDlg, IDC_STATIC_ALPHA, StringPrintf("%d%%", t)); + int v = (int)(t / 100.0 * 255 + 0.5); + if (v == 254) v = 255; + cfg_uioptions_linkedalpha.setValueAsInt(v); + } + else if (ctrl == GetDlgItem(hwndDlg, IDC_SLIDER_HOLD)) { + t = get_logslider(ctrl); + SetDlgItemTextA(hwndDlg, IDC_STATIC_HOLD, StringPrintf("%d%s", t, msStr)); + cfg_uioptions_autoopacitytime.setValueAsInt(t); + } + else if (ctrl == GetDlgItem(hwndDlg, IDC_SLIDER_FADEIN)) { + t = get_logslider(ctrl); + SetDlgItemTextA(hwndDlg, IDC_STATIC_FADEIN, StringPrintf("%d%s", t, msStr)); + cfg_uioptions_autoopacityfadein.setValueAsInt(t); + } + else if (ctrl == GetDlgItem(hwndDlg, IDC_SLIDER_FADEOUT)) { + int t = get_logslider(ctrl); + SetDlgItemTextA(hwndDlg, IDC_STATIC_FADEOUT, StringPrintf("%d%s", t, msStr)); + cfg_uioptions_autoopacityfadeout.setValueAsInt(t); + } + else if (ctrl == GetDlgItem(hwndDlg, IDC_SLIDER_CUSTOMSCALE)) + { + SetDlgItemTextA(hwndDlg, IDC_STATIC_SCALE, StringPrintf("%d%%", t)); + Layout *main = SkinParser::getMainLayout(); + if (main) + { + main->setRenderRatio((float)t / 100.0); + if (cfg_uioptions_linkratio.getValueAsInt()) + { + int nc = SkinParser::getNumContainers(); + for (int i = 0;i < nc;i++) + { + Container *c = SkinParser::enumContainer(i); + if (c) + { + int nl = c->getNumLayouts(); + for (int j = 0;j < nl;j++) + { + Layout *l = c->enumLayout(j); + if (l) + { + UpdateWindow(l->gethWnd()); + } + } + } + } + } + } + return 0; + } + break; + } + case WM_COMMAND: + toggle_from_wa2 = 1; + switch (LOWORD(wParam)) { + case IDC_CHECK_LINKALPHA: + cfg_uioptions_linkalpha.setValueAsInt(IsDlgButtonChecked(hwndDlg, IDC_CHECK_LINKALPHA)); + return 0; + case IDC_CHECK_LINKALLALPHA: + cfg_uioptions_linkallalpha.setValueAsInt(IsDlgButtonChecked(hwndDlg, IDC_CHECK_LINKALLALPHA)); + turnonoff(hwndDlg, opacity_all_on, sizeof(opacity_all_on)/sizeof(int), cfg_uioptions_linkallalpha.getValueAsInt()); + turnonoff(hwndDlg, opacity_all_off, sizeof(opacity_all_off)/sizeof(int), !cfg_uioptions_linkallalpha.getValueAsInt()); + return 0; + case IDC_EDIT_EXTEND: + if (HIWORD(wParam) == EN_CHANGE && spin_extend) { + int t, a = GetDlgItemInt(hwndDlg,IDC_EDIT_EXTEND,&t,0); + if (t) cfg_uioptions_extendautoopacity.setValueAsInt(MAX(a,0)); + if (a < 0) + { + char msStr[16] = {0}; + SetDlgItemTextA(hwndDlg, IDC_STATIC_HOLD, + StringPrintf("%d%s", cfg_uioptions_autoopacitytime.getValueAsInt(),WASABI_API_LNGSTRING_BUF(IDS_MS,msStr,16))); + } + } + return 0; + case IDC_CHECK_LINKALLRATIO: + cfg_uioptions_linkallratio.setValueAsInt(IsDlgButtonChecked(hwndDlg, IDC_CHECK_LINKALLRATIO)); + turnonoff(hwndDlg, ratio_all_on, sizeof(ratio_all_on) / sizeof(int), cfg_uioptions_linkallratio.getValueAsInt()); + turnonoff(hwndDlg, ratio_all_off, sizeof(ratio_all_off) / sizeof(int), !cfg_uioptions_linkallratio.getValueAsInt()); + return 0; + case IDC_COMBO_OPACITY: + if (HIWORD(wParam) == CBN_SELCHANGE) + { + int sel = SendDlgItemMessageW(hwndDlg, IDC_COMBO_OPACITY, CB_GETCURSEL, 0, 0); + if (sel != CB_ERR) + { + cfg_uioptions_autoopacitylinked.setValueAsInt(SendDlgItemMessageW(hwndDlg, IDC_COMBO_OPACITY, CB_GETITEMDATA, sel, 0)); + } + } + return 0; + case IDC_COMBO_SCALE: + if (HIWORD(wParam) == CBN_SELCHANGE) + { + int sel = SendDlgItemMessageW(hwndDlg, IDC_COMBO_SCALE, CB_GETCURSEL, 0, 0); + if (sel != CB_ERR) + { + cfg_uioptions_uselocks.setValueAsInt(!!sel); + } + } + return 0; + case IDC_CHECK_LINKRATIO: + cfg_uioptions_linkratio.setValueAsInt(IsDlgButtonChecked(hwndDlg, IDC_CHECK_LINKRATIO)); + return 0; + } + case WM_DESTROY: + spin_extend = 0; + return 0; + } + + const int controls[] = + { + IDC_SLIDER_CUSTOMALPHA, + IDC_SLIDER_HOLD, + IDC_SLIDER_FADEIN, + IDC_SLIDER_FADEOUT, + IDC_SLIDER_CUSTOMSCALE, + }; + if (FALSE != WASABI_API_APP->DirectMouseWheel_ProcessDialogMessage(hwndDlg, uMsg, wParam, lParam, controls, ARRAYSIZE(controls))) + return TRUE; + + return 0; +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_ff/prefs_colorthemes.cpp b/Src/Plugins/General/gen_ff/prefs_colorthemes.cpp new file mode 100644 index 00000000..9c8a5333 --- /dev/null +++ b/Src/Plugins/General/gen_ff/prefs_colorthemes.cpp @@ -0,0 +1,101 @@ +#include "precomp__gen_ff.h" +#include "resource.h" +#include + +extern int m_are_we_loaded; +extern int toggle_from_wa2; +extern HWND subWnd; + +#define ListBox_AddStringW(hwndCtl, lpsz) ((int)(DWORD)SendMessageW((hwndCtl), LB_ADDSTRING, 0L, (LPARAM)(LPCWSTR)(lpsz))) +#define ListBox_FindStringW(hwndCtl, indexStart, lpszFind) ((int)(DWORD)SendMessageW((hwndCtl), LB_FINDSTRING, (WPARAM)(int)(indexStart), (LPARAM)(LPCWSTR)(lpszFind))) +#define ListBox_GetTextW(hwndCtl, index, lpszBuffer) ((int)(DWORD)SendMessageW((hwndCtl), LB_GETTEXT, (WPARAM)(int)(index), (LPARAM)(LPWSTR)(lpszBuffer))) +#define ListBox_GetItemDataW(hwndCtl, index) ((LRESULT)(ULONG_PTR)SendMessageW((hwndCtl), LB_GETITEMDATA, (WPARAM)(int)(index), 0L)) +#define ListBox_SetItemDataW(hwndCtl, index, data) ((int)(DWORD)SendMessageW((hwndCtl), LB_SETITEMDATA, (WPARAM)(int)(index), (LPARAM)(data))) + + +static void fillColorThemesList(HWND list) +{ + ListBox_ResetContent(list); + if (!m_are_we_loaded) return ; + int numsets = WASABI_API_SKIN->colortheme_getNumColorSets(); + for (int i = 0; i < numsets; i++) + { + const wchar_t *set = WASABI_API_SKIN->colortheme_enumColorSet(i); + if (!_wcsnicmp(set, L"{coloredit}", 11)) + { + int pos = ListBox_AddStringW(list, set + 11); + ListBox_SetItemDataW(list, pos, 1); + } + else + { + ListBox_AddStringW(list, set); + } + } + const wchar_t *curset = WASABI_API_SKIN->colortheme_getColorSet(); + int cur = ListBox_FindStringW(list, 0, curset); + ListBox_SetCurSel(list, cur); +} + +INT_PTR CALLBACK ffPrefsProc3(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch (uMsg) + { + case WM_NOTIFYFORMAT: + return NFR_UNICODE; + case WM_INITDIALOG: + EnableWindow(GetDlgItem(hwndDlg, IDC_BUTTON_SETTHEME), m_are_we_loaded); + { + HWND listWindow; + listWindow = GetDlgItem(hwndDlg, IDC_LIST1); + if (NULL != listWindow) + { + EnableWindow(listWindow, m_are_we_loaded); + fillColorThemesList(listWindow); + if (NULL != WASABI_API_APP) + WASABI_API_APP->DirectMouseWheel_EnableConvertToMouseWheel(listWindow, TRUE); + } + } + return 1; + case WM_COMMAND: + { + toggle_from_wa2 = 1; + int id = (int) LOWORD(wParam); + int msg = (int)HIWORD(wParam); + if (id == IDC_LIST1 && msg == LBN_DBLCLK || id == IDC_BUTTON_SETTHEME) + { + HWND ctrl = GetDlgItem(hwndDlg, IDC_LIST1); + int sel = ListBox_GetCurSel(ctrl); + if (sel != -1) + { + wchar_t newset[256 + 11] = L""; + ListBox_GetTextW(ctrl, sel, newset); + newset[255] = 0; + if (*newset) + { + int p = ListBox_GetItemDataW(ctrl, sel); + if (p) + { + WCSCPYN(newset, StringPrintfW(L"{coloredit}%s", newset), 256 + 11); + } + WASABI_API_SKIN->colortheme_setColorSet(newset); + } + } + return 0; + } + toggle_from_wa2 = 0; + break; + } + case WM_DESTROY: + subWnd = NULL; + if (NULL != WASABI_API_APP) + { + HWND listWindow; + listWindow = GetDlgItem(hwndDlg, IDC_LIST1); + if (NULL != listWindow) + WASABI_API_APP->DirectMouseWheel_EnableConvertToMouseWheel(listWindow, FALSE); + } + return 0; + } + return 0; +} + diff --git a/Src/Plugins/General/gen_ff/prefs_font.cpp b/Src/Plugins/General/gen_ff/prefs_font.cpp new file mode 100644 index 00000000..ccb5c586 --- /dev/null +++ b/Src/Plugins/General/gen_ff/prefs_font.cpp @@ -0,0 +1,860 @@ +#include "precomp__gen_ff.h" +#include +#include +#include +#include "resource.h" +#include +#include +#include "../nu/ListView.h" +#include "prefs.h" +#include "gen.h" +#include "wa2cfgitems.h" +#include +#include "../Agave/Language/api_language.h" + +extern HWND subWnd; +#define ComboBox_SetItemDataW(hwndCtl, index, data) ((int)(DWORD)SendMessageW((hwndCtl), CB_SETITEMDATA, (WPARAM)(int)(index), (LPARAM)(data))) +#define ComboBox_GetLBTextLenW(hwndCtl, index) ((int)(DWORD)SendMessageW((hwndCtl), CB_GETLBTEXTLEN, (WPARAM)(int)(index), 0L)) +#define ComboBox_GetLBTextW(hwndCtl, index, lpszBuffer) ((int)(DWORD)SendMessageW((hwndCtl), CB_GETLBTEXT, (WPARAM)(int)(index), (LPARAM)(LPCWSTR)(lpszBuffer))) +#define ComboBox_SelectStringW(hwndCtl, indexStart, lpszSelect) ((int)(DWORD)SendMessageW((hwndCtl), CB_SELECTSTRING, (WPARAM)(int)(indexStart), (LPARAM)(LPCWSTR)(lpszSelect))) + +static W_ListView mappingList; +int fonts_loaded = 0; +int ComboBox_AddStringW(HWND list, const wchar_t *string); + +class font_entry +{ +public: + StringW filename; + StringW face; +}; + +class font_entry_comparator1 +{ +public: + // comparator for sorting + static int compareItem(font_entry *p1, font_entry* p2) + { + return wcscmp(p1->face, p2->face); + } + // comparator for searching + static int compareAttrib(const wchar_t *attrib, font_entry *item) + { + return wcscmp(attrib, item->face); + } +}; +class font_entry_comparator2 +{ +public: + // comparator for sorting + static int compareItem(font_entry *p1, font_entry* p2) + { + return _wcsicmp(p1->filename, p2->filename); + } + // comparator for searching + static int compareAttrib(const wchar_t *attrib, font_entry *item) + { + return _wcsicmp(attrib, item->filename); + } +}; + +int ResizeComboBoxDropDown(HWND hwndDlg, UINT id, const wchar_t * str, int width); + +PtrListQuickSorted fontlist; +PtrListQuickSorted fontlist_byfilename; + +PtrListQuickSorted skin_fontlist; +PtrListQuickSorted skin_fontlist_byfilename; + +void fillFontLists(HWND list, HWND list2, int selectdefault = 1) +{ + fontlist.deleteAll(); + fontlist_byfilename.removeAll(); + + ComboBox_ResetContent(list); + ComboBox_ResetContent(list2); + SendMessageW(list, CB_INITSTORAGE, 400, 32); + SendMessageW(list2, CB_INITSTORAGE, 400, 32); + + wchar_t *txt = WMALLOC(WA_MAX_PATH); + Wasabi::Std::getFontPath(WA_MAX_PATH, txt); + StringW path = txt; + StringW deffont = cfg_options_ttfoverridefont.getValue(); + FREE(txt); + StringPathCombine mask(path, L"*.ttf"); + WIN32_FIND_DATAW fd = {0}; + HANDLE fh; + int width = 0, width2 = 0; + if ((fh = FindFirstFileW(mask, &fd)) != INVALID_HANDLE_VALUE) + { + while (1) + { + StringW fullpath = fd.cFileName; + if (_wcsicmp(fullpath, L".") && _wcsicmp(fullpath, L"..")) + { + fullpath = StringPathCombine(path, fullpath); + StringW fontname = TrueTypeFont_Win32::filenameToFontFace(fullpath); + if (!fontname.isempty()) + { + if (fontlist_byfilename.findItem(fd.cFileName)) continue; + font_entry *fe = new font_entry; + fe->face = fontname; + fe->filename = fd.cFileName; + fontlist.addItem(fe); + fontlist_byfilename.addItem(fe); + int idx = ComboBox_AddStringW(list, fontname); + ComboBox_SetItemData(list, idx, fe); + width = ResizeComboBoxDropDown(list, 0, fontname, width); + + idx = ComboBox_AddStringW(list2, fontname); + ComboBox_SetItemData(list2, idx, fe); + width2 = ResizeComboBoxDropDown(list2, 0, fontname, width2); + } + } + if (!FindNextFileW(fh, &fd)) break; + } + } + if (fh != INVALID_HANDLE_VALUE) FindClose(fh); + int pos = -1; + font_entry *fe = fontlist_byfilename.findItem(deffont.v(), NULL); + if (fe) + { + fontlist.sort(1); + pos = fontlist.searchItem(fe); + } + ComboBox_SetCurSel(list, pos); +} + + +int fm_validMapping(HWND wnd) +{ + if (ComboBox_GetCurSel(GetDlgItem(wnd, IDC_COMBO_SKINFONTS)) == -1) return 0; + if (ComboBox_GetCurSel(GetDlgItem(wnd, IDC_COMBO_FONTS)) == -1) return 0; + return 1; +} + +int fm_hasSelection(HWND wnd) +{ + int n = mappingList.GetCount(); + for (int i = 0;i < n;i++) + if (mappingList.GetSelected(i)) + return 1; + return 0; +} + +void fm_itemClicked(HWND wnd, int pos) +{ + HWND combo1 = GetDlgItem(wnd, IDC_COMBO_SKINFONTS); + HWND combo2 = GetDlgItem(wnd, IDC_COMBO_FONTS); + + int n = mappingList.GetCount(); + for (int i = 0;i < n;i++) + { + if (mappingList.GetSelected(i)) + { + wchar_t txt[4096] = {0}; + mappingList.GetText(i, 0, txt, 4096); + ComboBox_SetCurSel(combo1, -1); + if (skin_fontlist.findItem(txt)) + ComboBox_SelectStringW(combo1, -1, StringPrintfW(L" %s", txt)); + else + ComboBox_SelectStringW(combo1, -1, txt); + mappingList.GetText(i, 1, txt, 4096); + + ComboBox_SetCurSel(combo2, -1); + ComboBox_SelectStringW(combo2, -1, txt); + + wchar_t type[64] = {0}; + mappingList.GetText(i, 3, type, 64); + + CheckDlgButton(wnd, IDC_RADIO_THISSKIN, WCSCASEEQLSAFE(type, WASABI_API_LNGSTRINGW(IDS_THIS_SKIN))); + CheckDlgButton(wnd, IDC_RADIO_ALLSKINS, !WCSCASEEQLSAFE(type, WASABI_API_LNGSTRINGW(IDS_THIS_SKIN))); + + wchar_t scale[64] = {0}; + mappingList.GetText(i, 2, scale, 64); + int s = WTOI(scale); + SendMessageW(GetDlgItem(wnd, IDC_SLIDER_SCALE), TBM_SETPOS, 1, s); + SetDlgItemTextW(wnd, IDC_STATIC_SCALE, StringPrintfW(L"%d%%", s)); + break; + } + } + EnableWindow(GetDlgItem(wnd, IDC_BUTTON_SET), fm_validMapping(wnd) && fm_hasSelection(wnd)); + EnableWindow(GetDlgItem(wnd, IDC_BUTTON_NEW), fm_validMapping(wnd)); + EnableWindow(GetDlgItem(wnd, IDC_BUTTON_DEL), fm_hasSelection(wnd)); +} + +void fm_invalidate() +{ + Font::uninstallAll(1); + WASABI_API_WNDMGR->wndTrackInvalidateAll(); +} + +void fm_scanMapping(HWND ctrl, const wchar_t *id) +{ + int global = 0; + wchar_t t[256] = L""; + int scale; + StringW tmp; + tmp.printf(L"Skin:%s/Font Mapping/%s", WASABI_API_SKIN->getSkinName(), id); + WASABI_API_CONFIG->getStringPrivate(tmp, t, 250, L""); + + tmp.printf(L"Skin:%s/Font Mapping/%s_scale", WASABI_API_SKIN->getSkinName(), id); + scale = WASABI_API_CONFIG->getIntPrivate(tmp, -1); + + if (!*t) + { + global = 1; + tmp.printf(L"Font Mapping/%s", id); + WASABI_API_CONFIG->getStringPrivate(tmp, t, 250, L""); + tmp.printf(L"Skin:%s/Font Mapping/%s_scale", WASABI_API_SKIN->getSkinName(), id); + scale = WASABI_API_CONFIG->getIntPrivate(tmp, -1); + } + if (t && *t) + { + if (!WCSCASESTR(t, L".ttf")) wcscat(t, L".ttf"); + font_entry *fe = fontlist_byfilename.findItem(t); + if (fe) + { + int pos = mappingList.InsertItem(0, id, 0); + mappingList.SetItemText(pos, 1, fe->face.getValue()); + mappingList.SetItemText(pos, 2, StringPrintfW(L"%d%%", scale != -1 ? scale : 100).getValue()); + mappingList.SetItemText(pos, 3, WASABI_API_LNGSTRINGW(global ? IDS_ALL_SKINS : IDS_THIS_SKIN)); + } + } +} + +void fm_rescanList(HWND lv) +{ + mappingList.Clear(); + + wchar_t t[4096] = L""; + WASABI_API_CONFIG->getStringPrivate(StringPrintfW(L"Skin:%s/Font Mapping/Mapped", WASABI_API_SKIN->getSkinName()), t, 4096, L""); + ParamParser pp(t); + for (int i = 0;i < pp.getNumItems();i++) + { + fm_scanMapping(lv, pp.enumItem(i)); + } + WASABI_API_CONFIG->getStringPrivate(L"Font Mapping/Mapped", t, 4096, L""); + ParamParser pp2(t); + for (int i = 0;i < pp2.getNumItems();i++) + { + fm_scanMapping(lv, pp2.enumItem(i)); + } +} + +void fm_saveMappingLists(HWND wnd) +{ + PtrListQuickSorted global_list; + PtrListQuickSorted skin_list; + + int n = mappingList.GetCount(); + for (int i = 0;i < n;i++) + { + wchar_t txt[4096] = {0}; + mappingList.GetText(i, 0, txt, 4096); + + wchar_t type[64] = {0}; + mappingList.GetText(i, 3, type, 64); + + if (!_wcsicmp(type, WASABI_API_LNGSTRINGW(IDS_THIS_SKIN))) + { + if (skin_list.findItem(txt) != NULL) continue; + skin_list.addItem(new StringW(txt)); + } + else + { + if (global_list.findItem(txt) != NULL) continue; + global_list.addItem(new StringW(txt)); + } + } + StringW s = L""; + foreach(global_list) + if (!s.isempty()) + s += L";"; + s += global_list.getfor()->getValue(); + endfor; + WASABI_API_CONFIG->setStringPrivate(L"Font Mapping/Mapped", s); + + s = L""; + foreach(skin_list) + if (!s.isempty()) s += L";"; + s += skin_list.getfor()->getValue(); + endfor; + WASABI_API_CONFIG->setStringPrivate(StringPrintfW(L"Skin:%s/Font Mapping/Mapped", WASABI_API_SKIN->getSkinName()), s); + + global_list.deleteAll(); + skin_list.deleteAll(); +} + +void fm_delMapping(HWND wnd, int pos = -1) +{ + HWND list = GetDlgItem(wnd, IDC_LIST_MAPPINGS); + int n = mappingList.GetCount(); + int start = 0; + if (pos != -1) { start = pos; n = pos + 1; } + for (int i = start;i < n;i++) + { + if (pos != -1 || mappingList.GetSelected(i)) + { + wchar_t txt[4096] = {0}; + mappingList.GetText(i, 0, txt, 4096); + + wchar_t m[64] = {0}; + mappingList.GetText(i, 3, m, 64); + + if (WCSCASEEQLSAFE(m, WASABI_API_LNGSTRINGW(IDS_THIS_SKIN))) + { + WASABI_API_CONFIG->setStringPrivate(StringPrintfW(L"Skin:%s/Font Mapping/%s", WASABI_API_SKIN->getSkinName(), txt), L""); + WASABI_API_CONFIG->setStringPrivate(StringPrintfW(L"Skin:%s/Font Mapping/%s_scale", WASABI_API_SKIN->getSkinName(), txt), L"100"); + } + else + { + WASABI_API_CONFIG->setStringPrivate(StringPrintfW(L"Font Mapping/%s", txt), L""); + WASABI_API_CONFIG->setStringPrivate(StringPrintfW(L"Font Mapping/%s_scale", txt), L"100"); + } + mappingList.DeleteItem(i); + break; + } + } + fm_saveMappingLists(wnd); + fm_rescanList(list); + fm_invalidate(); + if (pos == -1) + { + EnableWindow(GetDlgItem(wnd, IDC_BUTTON_DEL), 0); + EnableWindow(GetDlgItem(wnd, IDC_BUTTON_SET), 0); + } +} + +void fm_newMapping(HWND wnd) +{ + HWND combo1 = GetDlgItem(wnd, IDC_COMBO_SKINFONTS); + HWND combo2 = GetDlgItem(wnd, IDC_COMBO_FONTS); + int global = !IsDlgButtonChecked(wnd, IDC_RADIO_THISSKIN); + + int pos = ComboBox_GetCurSel(combo1); + int l = ComboBox_GetLBTextLenW(combo1, pos); + wchar_t *txt = WMALLOC(l + 1); + ComboBox_GetLBTextW(combo1, pos, txt); + if (!*txt) + { + FREE(txt); + return ; + } + wchar_t *delme = txt; + if (*txt == ' ') txt++; + font_entry *fe = skin_fontlist.findItem(txt); + if (!fe) fe = fontlist.findItem(txt); + if (!fe) return ; + FREE(delme); + + + int n = mappingList.GetCount(); + int oldpos = -1; + for (int i = 0;i < n;i++) + { + wchar_t txt[4096] = {0}; + mappingList.GetText(i, 0, txt, 4096); + if (!_wcsicmp(fe->face, txt)) { oldpos = i; break; } + } + + if (oldpos != -1) fm_delMapping(wnd, oldpos); + + pos = ComboBox_GetCurSel(combo2); + l = ComboBox_GetLBTextLenW(combo2, pos); + txt = WMALLOC(l + 1); + ComboBox_GetLBTextW(combo2, pos, txt); + if (!*txt) + { + FREE(txt); + return ; + } + font_entry *femap = fontlist.findItem(txt); + if (!femap) return ; + FREE(txt); + + StringW file = femap->filename; + + int v = (int)SendMessageW(GetDlgItem(wnd, IDC_SLIDER_SCALE), TBM_GETPOS, 0, 0); + + if (!global) + { + WASABI_API_CONFIG->setStringPrivate(StringPrintfW(L"Skin:%s/Font Mapping/%s", WASABI_API_SKIN->getSkinName(), fe->face), file); + WASABI_API_CONFIG->setIntPrivate(StringPrintfW(L"Skin:%s/Font Mapping/%s_scale", WASABI_API_SKIN->getSkinName(), fe->face), v); + } + else + { + WASABI_API_CONFIG->setStringPrivate(StringPrintfW(L"Font Mapping/%s", fe->face), file); + WASABI_API_CONFIG->setIntPrivate(StringPrintfW(L"Font Mapping/%s_scale", fe->face), v); + } + + fm_scanMapping(GetDlgItem(wnd, IDC_LIST_MAPPINGS), fe->face); + fm_saveMappingLists(wnd); + fm_invalidate(); + + if (oldpos == -1) + { + int n = mappingList.GetCount(); + for (int i = 0;i < n;i++) + { + wchar_t txt[4096] = {0}; + mappingList.GetText(i, 0, txt, 4096); + if (!_wcsicmp(fe->face, txt)) { oldpos = i; break; } + } + } + + if (oldpos != -1) + { + mappingList.SetSelected(oldpos); + EnableWindow(GetDlgItem(wnd, IDC_BUTTON_DEL), 1); + EnableWindow(GetDlgItem(wnd, IDC_BUTTON_SET), 1); + } +} + +void fm_modifyMapping(HWND wnd) +{ + int n = mappingList.GetCount(); + int i; + for (i = 0;i < n;i++) + { + if (mappingList.GetSelected(i)) + { + wchar_t txt[4096] = {0}; + mappingList.GetText(i, 0, txt, 4096); + + wchar_t m[64] = {0}; + mappingList.GetText(i, 3, m, 64); + + if (WCSCASEEQLSAFE(m, WASABI_API_LNGSTRINGW(IDS_THIS_SKIN))) + WASABI_API_CONFIG->setStringPrivate(StringPrintfW(L"Skin:%s/Font Mapping/%s", WASABI_API_SKIN->getSkinName(), txt), L""); + else + WASABI_API_CONFIG->setStringPrivate(StringPrintfW(L"Font Mapping/%s", txt), L""); + mappingList.DeleteItem(i); + break; + } + } + if (i == n) return ; + fm_newMapping(wnd); + mappingList.SetSelected(i); +} + +void fillFontList2(HWND list, int reset = 1) +{ + if (reset) + ComboBox_ResetContent(list); + + int width = 0; + foreach(fontlist) + const wchar_t *str; + int idx = ComboBox_AddStringW(list, (str = fontlist.getfor()->face.v())); + ComboBox_SetItemDataW(list, idx, fontlist.getfor()); + width = ResizeComboBoxDropDown(list, 0, str, width); + endfor; +} + +void fillSkinFontList(HWND list) +{ + ComboBox_ResetContent(list); + SendMessageW(list, CB_INITSTORAGE, 400, 32); + skin_fontlist.deleteAll(); + skin_fontlist_byfilename.removeAll(); + int n = Font::getNumFontDefs(); + for (int i = 0;i < n;i++) + { + FontDef *fd = Font::enumFontDef(i); + if (!fd) continue; + if (!fd->allowmapping) continue; + font_entry *fe = new font_entry; + fe->filename = fd->filename; + fe->face = fd->id; + skin_fontlist.addItem(fe); + skin_fontlist_byfilename.addItem(fe); + int idx = ComboBox_AddStringW(list, StringPrintfW(L" %s", fe->face)); + ComboBox_SetItemDataW(list, idx, fe); + } + fillFontList2(list, 0); +} + +VOID CALLBACK FontMappingLoader(HWND hwndDlg, UINT uMsg, UINT_PTR idEvent, DWORD dwTime) +{ + KillTimer(hwndDlg, idEvent); + + HWND ctrl = GetDlgItem(hwndDlg, IDC_COMBO_FONTS); + HWND ctrl2 = GetDlgItem(hwndDlg, IDC_COMBO_SKINFONTS); + + fillFontList2(ctrl, 1); + fillSkinFontList(ctrl2); + + EnableWindow(ctrl, TRUE); + EnableWindow(ctrl2, TRUE); +} + +BOOL CALLBACK fontMapperProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch (uMsg) + { + case WM_INITDIALOG: + { + HWND lv = GetDlgItem(hwndDlg, IDC_LIST_MAPPINGS); + mappingList.setwnd(lv); + + mappingList.AddCol(WASABI_API_LNGSTRINGW(IDS_FONT), 140); + mappingList.AddCol(WASABI_API_LNGSTRINGW(IDS_MAPPING), 139); + mappingList.AddCol(WASABI_API_LNGSTRINGW(IDS_SCALE), 48); + mappingList.AddCol(WASABI_API_LNGSTRINGW(IDS_TYPE), 64); + + HWND ctrl = GetDlgItem(hwndDlg, IDC_COMBO_FONTS); + ComboBox_AddStringW(ctrl, WASABI_API_LNGSTRINGW(IDS_LOADING)); + ComboBox_SetCurSel(ctrl, 0); + EnableWindow(ctrl, FALSE); + ctrl = GetDlgItem(hwndDlg, IDC_COMBO_SKINFONTS); + ComboBox_AddStringW(ctrl, WASABI_API_LNGSTRINGW(IDS_LOADING)); + ComboBox_SetCurSel(ctrl, 0); + EnableWindow(ctrl, FALSE); + SetTimer(hwndDlg, 0xC0DF, 1, FontMappingLoader); + + fm_rescanList(lv); + CheckDlgButton(hwndDlg, IDC_RADIO_THISSKIN, 1); + EnableWindow(GetDlgItem(hwndDlg, IDC_BUTTON_DEL), 0); + EnableWindow(GetDlgItem(hwndDlg, IDC_BUTTON_NEW), 0); + EnableWindow(GetDlgItem(hwndDlg, IDC_BUTTON_SET), 0); + SendMessageW(GetDlgItem(hwndDlg, IDC_SLIDER_SCALE), TBM_SETRANGEMAX, 0, 200); + SendMessageW(GetDlgItem(hwndDlg, IDC_SLIDER_SCALE), TBM_SETRANGEMIN, 0, 25); + SendMessageW(GetDlgItem(hwndDlg, IDC_SLIDER_SCALE), TBM_SETPOS, 1, 100); + return 1; + } + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDCANCEL: + EndDialog(hwndDlg, 0); + return 0; + case IDOK: + EndDialog(hwndDlg, 1); + return 0; + case IDC_BUTTON_NEW: + fm_newMapping(hwndDlg); + return 0; + case IDC_BUTTON_DEL: + fm_delMapping(hwndDlg); + return 0; + case IDC_BUTTON_SET: + fm_modifyMapping(hwndDlg); + return 0; + case IDC_COMBO_FONTS: + case IDC_COMBO_SKINFONTS: + EnableWindow(GetDlgItem(hwndDlg, IDC_BUTTON_SET), fm_validMapping(hwndDlg) && fm_hasSelection(hwndDlg)); + EnableWindow(GetDlgItem(hwndDlg, IDC_BUTTON_NEW), fm_validMapping(hwndDlg)); + return 0; + } + case WM_HSCROLL: + { + if ((HWND) lParam == GetDlgItem(hwndDlg, IDC_SLIDER_SCALE)) + { + int t = (int)SendMessageW(GetDlgItem(hwndDlg, IDC_SLIDER_SCALE), TBM_GETPOS, 0, 0); + SetDlgItemTextA(hwndDlg, IDC_STATIC_SCALE, StringPrintf("%d%%", t)); + return 0; + } + break; + } + case WM_NOTIFY: + { + int ctrl = (int)wParam; + NMHDR *pnmh = (NMHDR*)lParam; + if (ctrl == IDC_LIST_MAPPINGS) + { + if (pnmh->code == NM_CLICK) + { + NMLISTVIEW *nml = (NMLISTVIEW*)lParam; + fm_itemClicked(hwndDlg, nml->iItem); + break; + } + } + } + } + + const int controls[] = + { + IDC_SLIDER_SCALE, + }; + if (FALSE != WASABI_API_APP->DirectMouseWheel_ProcessDialogMessage(hwndDlg, uMsg, wParam, lParam, controls, ARRAYSIZE(controls))) + return TRUE; + + return 0; +} + +void updateBFReplacement(HWND hwndDlg) +{ + int replace = !IsDlgButtonChecked(hwndDlg, IDC_CHECK_ALLOWBITMAPFONTS); + EnableWindow(GetDlgItem(hwndDlg, IDC_COMBO_TTFOVERRIDE), (replace && fonts_loaded)); + EnableWindow(GetDlgItem(hwndDlg, IDC_SLIDER_SCALETTFOVERRIDE), replace); + EnableWindow(GetDlgItem(hwndDlg, IDC_STATIC_DECREASESIZE), replace); + EnableWindow(GetDlgItem(hwndDlg, IDC_STATIC_TTFSCALE), replace); + EnableWindow(GetDlgItem(hwndDlg, IDC_STATIC_INCREASESIZE), replace); + EnableWindow(GetDlgItem(hwndDlg, IDC_NO_7BIT_OVERRIDE), replace); +} + +void updateFreetypeVisible(HWND hwndDlg) +{ + if (!IsDlgButtonChecked(hwndDlg, IDC_CHECK_FREETYPETTF)) + { + EnableWindow(GetDlgItem(hwndDlg, IDC_STATIC_CHARMAP), FALSE); + EnableWindow(GetDlgItem(hwndDlg, IDC_COMBO_CHARMAP), FALSE); + } + else + { + EnableWindow(GetDlgItem(hwndDlg, IDC_STATIC_CHARMAP), TRUE); + EnableWindow(GetDlgItem(hwndDlg, IDC_COMBO_CHARMAP), TRUE); + } +} + + +void updateAltFontsVisible(HWND hwndDlg) +{ + if (!IsDlgButtonChecked(hwndDlg, IDC_CHECK_ALTFONTS)) + { + EnableWindow(GetDlgItem(hwndDlg, IDC_CHECK_NO_ALT_7BIT_OVERRIDE), FALSE); + } + else + { + EnableWindow(GetDlgItem(hwndDlg, IDC_CHECK_NO_ALT_7BIT_OVERRIDE), TRUE); + } +} + +// {504060F6-7D8C-4ebe-AE1D-A8BDF5EA1881} +static const GUID freetypeFontRendererGUID = +{ 0x504060f6, 0x7d8c, 0x4ebe, { 0xae, 0x1d, 0xa8, 0xbd, 0xf5, 0xea, 0x18, 0x81 } }; + +VOID CALLBACK FontLoader(HWND hwndDlg, UINT uMsg, UINT_PTR idEvent, DWORD dwTime) +{ + KillTimer(hwndDlg, idEvent); + + HWND ctrl = GetDlgItem(hwndDlg, IDC_COMBO_TTFOVERRIDE); + HWND ctrl2 = GetDlgItem(hwndDlg, IDC_COMBO_DEFAULTFONT); + + fillFontLists(ctrl, ctrl2); + + int pos = -1; + font_entry *fe = fontlist_byfilename.findItem(cfg_options_ttfoverridefont.getValue(), NULL); + if (fe) + { + fontlist.sort(1); + pos = fontlist.searchItem(fe); + } + ComboBox_SetCurSel(ctrl, pos); + + pos = -1; + fe = fontlist_byfilename.findItem(cfg_options_defaultfont.getValue(), NULL); + if (fe) + { + fontlist.sort(1); + pos = fontlist.searchItem(fe); + } + ComboBox_SetCurSel(ctrl2, pos); + + EnableWindow(ctrl, (!IsDlgButtonChecked(hwndDlg, IDC_CHECK_ALLOWBITMAPFONTS))); + EnableWindow(ctrl2, TRUE); + EnableWindow(ctrl2, TRUE); + fonts_loaded = 1; +} + +INT_PTR CALLBACK ffPrefsProc2(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch (uMsg) + { + case WM_INITDIALOG: + { + fonts_loaded = 0; + + CheckDlgButton(hwndDlg, IDC_CHECK_ALLOWBITMAPFONTS, cfg_options_allowbitmapfonts.getValueAsInt()); + CheckDlgButton(hwndDlg, IDC_CHECK_ALTFONTS, cfg_options_altfonts.getValueAsInt()); + + // delay the loading of the font lists until after the dialog has loaded to improve ui response + HWND ctrl = GetDlgItem(hwndDlg, IDC_COMBO_TTFOVERRIDE); + ComboBox_AddStringW(ctrl, WASABI_API_LNGSTRINGW(IDS_LOADING)); + ComboBox_SetCurSel(ctrl, 0); + EnableWindow(ctrl, FALSE); + + ctrl = GetDlgItem(hwndDlg, IDC_COMBO_DEFAULTFONT); + ComboBox_AddStringW(ctrl, WASABI_API_LNGSTRINGW(IDS_LOADING)); + ComboBox_SetCurSel(ctrl, 0); + EnableWindow(ctrl, FALSE); + SetTimer(hwndDlg, 0xC0DE, 1, FontLoader); + + SendMessageW(GetDlgItem(hwndDlg, IDC_SLIDER_SCALETTFOVERRIDE), TBM_SETRANGEMAX, 0, 200); + SendMessageW(GetDlgItem(hwndDlg, IDC_SLIDER_SCALETTFOVERRIDE), TBM_SETRANGEMIN, 0, 25); + SendMessageW(GetDlgItem(hwndDlg, IDC_SLIDER_SCALETTFOVERRIDE), TBM_SETPOS, 1, cfg_options_ttfoverridescale.getValueAsInt()); + SetDlgItemTextA(hwndDlg, IDC_STATIC_TTFSCALE, StringPrintf("%d%%", cfg_options_ttfoverridescale.getValueAsInt())); + SendMessageW(GetDlgItem(hwndDlg, IDC_SLIDER_SCALEDEFAULTFONT), TBM_SETRANGEMAX, 0, 200); + SendMessageW(GetDlgItem(hwndDlg, IDC_SLIDER_SCALEDEFAULTFONT), TBM_SETRANGEMIN, 0, 25); + SendMessageW(GetDlgItem(hwndDlg, IDC_SLIDER_SCALEDEFAULTFONT), TBM_SETPOS, 1, cfg_options_defaultfontscale.getValueAsInt()); + SetDlgItemTextA(hwndDlg, IDC_STATIC_SCALEDEFAULTFONT, StringPrintf("%d%%", cfg_options_defaultfontscale.getValueAsInt())); + + HWND charmaps = GetDlgItem(hwndDlg, IDC_COMBO_CHARMAP); + if (WASABI_API_SVC->service_getServiceByGuid(freetypeFontRendererGUID) == 0) // no freetype + { + EnableWindow(GetDlgItem(hwndDlg, IDC_STATIC_FREETYPE), FALSE); + EnableWindow(GetDlgItem(hwndDlg, IDC_CHECK_FREETYPETTF), FALSE); + EnableWindow(charmaps, FALSE); + } + else + { + const wchar_t *v = cfg_options_fontrenderer.getValue(); + CheckDlgButton(hwndDlg, IDC_CHECK_FREETYPETTF, WCSCASEEQLSAFE(v, L"FreeType")); + + ComboBox_AddStringW(charmaps, WASABI_API_LNGSTRINGW(IDS_AUTO_UNICODE_LATIN1_ASCII)); + ComboBox_AddStringW(charmaps, L"Unicode"); + ComboBox_AddStringW(charmaps, L"Apple Roman"); + ComboBox_AddStringW(charmaps, L"Adobe Latin-1"); + ComboBox_AddStringW(charmaps, L"Adobe Standard"); + ComboBox_AddStringW(charmaps, L"Adobe Custom"); + ComboBox_AddStringW(charmaps, L"Adobe Expert"); + ComboBox_AddStringW(charmaps, L"SJIS"); + ComboBox_AddStringW(charmaps, L"Big5"); + ComboBox_AddStringW(charmaps, L"Wansung"); + ComboBox_AddStringW(charmaps, L"Johab"); + ComboBox_SetCurSel(charmaps, cfg_options_freetypecharmap.getValueAsInt() + 1); + } + + CheckDlgButton(hwndDlg, IDC_NO_7BIT_OVERRIDE, cfg_options_no7bitsttfoverride.getValueAsInt()); + CheckDlgButton(hwndDlg, IDC_CHECK_NO_ALT_7BIT_OVERRIDE, cfg_options_noalt7bitsttfoverride.getValueAsInt()); + + updateBFReplacement(hwndDlg); + updateFreetypeVisible(hwndDlg); + updateAltFontsVisible(hwndDlg); + EnableWindow(GetDlgItem(hwndDlg, IDC_BUTTON_FONTMAPPER), cfg_options_usefontmapper.getValueAsInt()); + CheckDlgButton(hwndDlg, IDC_CHECK_USEFONTMAPPER, cfg_options_usefontmapper.getValueAsInt()); + return 1; + } + case WM_COMMAND: + { + int id = (int) LOWORD(wParam); + int msg = (int)HIWORD(wParam); + switch (id) + { + case IDC_CHECK_ALLOWBITMAPFONTS: + updateBFReplacement(hwndDlg); + cfg_options_allowbitmapfonts.setValueAsInt(IsDlgButtonChecked(hwndDlg, IDC_CHECK_ALLOWBITMAPFONTS)); + return 0; + case IDC_CHECK_ALTFONTS: + updateAltFontsVisible(hwndDlg); + cfg_options_altfonts.setValueAsInt(IsDlgButtonChecked(hwndDlg, IDC_CHECK_ALTFONTS)); + return 0; + case IDC_CHECK_FREETYPETTF: + updateFreetypeVisible(hwndDlg); + if (!IsDlgButtonChecked(hwndDlg, IDC_CHECK_FREETYPETTF)) + cfg_options_fontrenderer.setValue(L"Win32 TextOut"); + else + cfg_options_fontrenderer.setValue(L"FreeType"); + return 0; + case IDC_NO_7BIT_OVERRIDE: + cfg_options_no7bitsttfoverride.setValueAsInt(IsDlgButtonChecked(hwndDlg, IDC_NO_7BIT_OVERRIDE)); + return 0; + case IDC_CHECK_NO_ALT_7BIT_OVERRIDE: + cfg_options_noalt7bitsttfoverride.setValueAsInt(IsDlgButtonChecked(hwndDlg, IDC_CHECK_NO_ALT_7BIT_OVERRIDE)); + return 0; + case IDC_BUTTON_FONTMAPPER: + WASABI_API_DIALOGBOXW(IDD_FONTMAPPER, hwndDlg, fontMapperProc); + return 0; + case IDC_CHECK_USEFONTMAPPER: + cfg_options_usefontmapper.setValueAsInt(IsDlgButtonChecked(hwndDlg, IDC_CHECK_USEFONTMAPPER)); + EnableWindow(GetDlgItem(hwndDlg, IDC_BUTTON_FONTMAPPER), cfg_options_usefontmapper.getValueAsInt()); + return 0; + case IDC_COMBO_TTFOVERRIDE: + { + if (msg == CBN_SELCHANGE) + { + HWND list = GetDlgItem(hwndDlg, IDC_COMBO_TTFOVERRIDE); + int idx = ComboBox_GetCurSel(list); + if (idx != -1) + { + font_entry *fe = (font_entry *)ComboBox_GetItemData(list, idx); + if (fe && fontlist.haveItem(fe)) + { + cfg_options_ttfoverridefont.setValue(fe->filename); + } + } + return 0; + } + } + case IDC_COMBO_DEFAULTFONT: + { + if (msg == CBN_SELCHANGE) + { + HWND list = GetDlgItem(hwndDlg, IDC_COMBO_DEFAULTFONT); + int idx = ComboBox_GetCurSel(list); + if (idx != -1) + { + font_entry *fe = (font_entry *)ComboBox_GetItemData(list, idx); + if (fe && fontlist.haveItem(fe)) + { + cfg_options_defaultfont.setValue(fe->filename); + } + } + return 0; + } + } + case IDC_COMBO_CHARMAP: + { + if (msg == CBN_SELCHANGE) + { + int idx = ComboBox_GetCurSel(GetDlgItem(hwndDlg, IDC_COMBO_CHARMAP)); + if (idx >= 0) + { + cfg_options_freetypecharmap.setValueAsInt(idx - 1); + } + return 0; + } + } + } + break; + } + case WM_HSCROLL: + { + if ((HWND) lParam == GetDlgItem(hwndDlg, IDC_SLIDER_SCALETTFOVERRIDE)) + { + int t = (int)SendMessageW((HWND)lParam, TBM_GETPOS, 0, 0); + SetDlgItemTextA(hwndDlg, IDC_STATIC_TTFSCALE, StringPrintf("%d%%", t)); + SetTimer(hwndDlg, 100, 100, NULL); + return 0; + } + if ((HWND) lParam == GetDlgItem(hwndDlg, IDC_SLIDER_SCALEDEFAULTFONT)) + { + int t = (int)SendMessageW((HWND)lParam, TBM_GETPOS, 0, 0); + SetDlgItemTextA(hwndDlg, IDC_STATIC_SCALEDEFAULTFONT, StringPrintf("%d%%", t)); + SetTimer(hwndDlg, 101, 100, NULL); + return 0; + } + break; + } + case WM_TIMER: + { + if (wParam == 100) + { + KillTimer(hwndDlg, 100); + int t = (int)SendMessageW(GetDlgItem(hwndDlg, IDC_SLIDER_SCALETTFOVERRIDE), TBM_GETPOS, 0, 0); + cfg_options_ttfoverridescale.setValueAsInt(t); + } + else if (wParam == 101) + { + KillTimer(hwndDlg, 101); + int t = (int)SendMessageW(GetDlgItem(hwndDlg, IDC_SLIDER_SCALEDEFAULTFONT), TBM_GETPOS, 0, 0); + cfg_options_defaultfontscale.setValueAsInt(t); + } + return 0; + } + case WM_DESTROY: + subWnd = NULL; + return 0; + } + + const int controls[] = + { + IDC_SLIDER_SCALETTFOVERRIDE, + IDC_SLIDER_SCALEDEFAULTFONT, + }; + if (FALSE != WASABI_API_APP->DirectMouseWheel_ProcessDialogMessage(hwndDlg, uMsg, wParam, lParam, controls, ARRAYSIZE(controls))) + return TRUE; + + return 0; +} diff --git a/Src/Plugins/General/gen_ff/prefs_general.cpp b/Src/Plugins/General/gen_ff/prefs_general.cpp new file mode 100644 index 00000000..1c5ab1dc --- /dev/null +++ b/Src/Plugins/General/gen_ff/prefs_general.cpp @@ -0,0 +1,273 @@ +#include "precomp__gen_ff.h" +#include "resource.h" +#include +#include "wa2cfgitems.h" +#include "gen.h" +#include "prefs.h" +#include "../Agave/Language/api_language.h" +#include + +void turnonoff(HWND wnd, int *t, int n, int v); +extern int toggle_from_wa2; +extern HWND subWnd; + +static int auto_res = -1; +static int cur_res = 10; +static DWORD cur_res_last = 0; +static int cur_res_total = 0; +static int cur_res_num = 0; +static int old_res = 0; +static int dock_dist = 0; +static int dock_dist2 = 0; +static int spin_inc = 0; +static int spin_show = 0; +static int spin_hide = 0; +int desktopalpha_unavail[] = {IDC_STATIC_DA1, IDC_CHECK_DESKTOPALPHA}; + +static const wchar_t *getScrollTextSpeedW(float v) +{ + int skipn = (int)((1.0f / v) - 1 + 0.5f); + static wchar_t buf[64]; + ZERO(buf); + switch (skipn) + { + case 0: return WASABI_API_LNGSTRINGW_BUF(IDS_FASTER, buf, 64); + case 1: return WASABI_API_LNGSTRINGW_BUF(IDS_FAST, buf, 64); + case 2: return WASABI_API_LNGSTRINGW_BUF(IDS_AVERAGE, buf, 64); + case 3: return WASABI_API_LNGSTRINGW_BUF(IDS_SLOW, buf, 64); + case 4: return WASABI_API_LNGSTRINGW_BUF(IDS_SLOWER, buf, 64); + } + return WASABI_API_LNGSTRINGW_BUF(IDS_N_A, buf, 64); +} + +static void nextRes(HWND dlg) +{ + if (cur_res == 250) + { + cfg_uioptions_timerresolution.setValueAsInt(old_res); + SetDlgItemTextW(dlg, IDC_TXT, WASABI_API_LNGSTRINGW(IDS_FAILED_TO_DETECT_OPTIMAL_RESOLUTION)); + } + else + { + if (cur_res >= 100) + cur_res += 5; + else if (cur_res >= 50) + cur_res += 2; + else + cur_res++; + SetDlgItemTextW(dlg, IDC_TXT, StringPrintfW(WASABI_API_LNGSTRINGW(IDS_AUTO_DETECTING), cur_res)); + cur_res_last = Wasabi::Std::getTickCount(); + cur_res_total = 0; + cur_res_num = 0; + SetTimer(dlg, 1, cur_res, NULL); + } +} + +static INT_PTR CALLBACK autoTimerResProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch (uMsg) + { + case WM_INITDIALOG: + { + old_res = cfg_uioptions_timerresolution.getValueAsInt(); + cur_res = -1; + return 1; + } + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDCANCEL: + cfg_uioptions_timerresolution.setValueAsInt(old_res); + EndDialog(hwndDlg, 0); + return 0; + case IDOK: + if (cur_res == -1) + { + cfg_uioptions_timerresolution.setValueAsInt(250); + EnableWindow(GetDlgItem(hwndDlg, IDOK), FALSE); + SetDlgItemTextW(hwndDlg, IDC_TXT, WASABI_API_LNGSTRINGW(IDS_PREPARING_AUTO_DETECTION)); + SetTimer(hwndDlg, 2, 1000, NULL); + } + else EndDialog(hwndDlg, IDOK); + return 0; + } + break; + case WM_TIMER: + { + if (wParam == 1) + { + DWORD now = Wasabi::Std::getTickCount(); + cur_res_total += now - cur_res_last; + cur_res_num++; + cur_res_last = now; + int m = 5; + if (cur_res >= 100) m = 2; + else if (cur_res >= 50) m = 3; + if (cur_res_num == m) + { + float average = (float)cur_res_total / (float)m; + if (average <= (float)cur_res*1.1) + { + auto_res = cur_res; + cfg_uioptions_timerresolution.setValueAsInt(old_res); + SetDlgItemTextW(hwndDlg, IDC_TXT, StringPrintfW(WASABI_API_LNGSTRINGW(IDS_AUTO_DETECTION_SUCCESSFUL), cur_res)); + SetDlgItemTextW(hwndDlg, IDOK, WASABI_API_LNGSTRINGW(IDS_ACCEPT)); + EnableWindow(GetDlgItem(hwndDlg, IDOK), TRUE); + } + else + { + nextRes(hwndDlg); + } + } + return 0; + } + else if (wParam == 2) + { + KillTimer(hwndDlg, 2); + cur_res = 9; + nextRes(hwndDlg); + return 0; + } + break; + } + } + return FALSE; +} + +static void autoTimerRes(HWND dlg) +{ + INT_PTR r = WASABI_API_DIALOGBOXW(IDD_AUTOTIMERRES, dlg, autoTimerResProc); + if (r == IDOK) + { + cfg_uioptions_timerresolution.setValueAsInt(auto_res); + SendMessageW(GetDlgItem(dlg, IDC_SLIDER_TIMERRESOLUTION), TBM_SETPOS, 1, auto_res); + SetDlgItemTextW(dlg, IDC_STATIC_TIMERRES, StringPrintfW(WASABI_API_LNGSTRINGW(IDS_TIMERS_RESOLUTION), auto_res)); + } +} + +INT_PTR CALLBACK ffPrefsProc1(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch (uMsg) + { + case WM_INITDIALOG: + { + dock_dist = dock_dist2 = spin_inc = spin_show = spin_hide = 0; + + CheckDlgButton(hwndDlg, IDC_CHECK_DESKTOPALPHA, cfg_uioptions_desktopalpha.getValueAsInt()); + if (!Wasabi::Std::Wnd::isDesktopAlphaAvailable()) turnonoff(hwndDlg, desktopalpha_unavail, sizeof(desktopalpha_unavail)/sizeof(int), 0); + SendMessageW(GetDlgItem(hwndDlg, IDC_SLIDER_TIMERRESOLUTION), TBM_SETRANGEMAX, 0, 250); + SendMessageW(GetDlgItem(hwndDlg, IDC_SLIDER_TIMERRESOLUTION), TBM_SETRANGEMIN, 0, 10); + SendMessageW(GetDlgItem(hwndDlg, IDC_SLIDER_TIMERRESOLUTION), TBM_SETPOS, 1, cfg_uioptions_timerresolution.getValueAsInt()); + SetDlgItemTextW(hwndDlg, IDC_STATIC_TIMERRES, StringPrintfW(WASABI_API_LNGSTRINGW(IDS_TIMERS_RESOLUTION), cfg_uioptions_timerresolution.getValueAsInt())); + CheckDlgButton(hwndDlg, IDC_CHECK_TOOLTIPS, cfg_uioptions_tooltips.getValueAsInt()); + CheckDlgButton(hwndDlg, IDC_CHECK_DOCKING, cfg_options_docking.getValueAsInt()); + CheckDlgButton(hwndDlg, IDC_CHECK_DOCKING2, cfg_options_appbarondrag.getValueAsInt()); + + SendMessageW(GetDlgItem(hwndDlg,IDC_SPIN_DOCKDISTANCE),UDM_SETRANGE,0,MAKELONG(1000,0)); + SetDlgItemInt(hwndDlg, IDC_EDIT_DOCKDISTANCE, cfg_options_dockingdistance.getValueAsInt(), 0); + SendMessageW(GetDlgItem(hwndDlg,IDC_SPIN_DOCKDISTANCE2),UDM_SETRANGE,0,MAKELONG(1000,0)); + SetDlgItemInt(hwndDlg, IDC_EDIT_DOCKDISTANCE2, cfg_options_appbardockingdistance.getValueAsInt(), 0); + + SetDlgItemTextA(hwndDlg, IDC_EDIT_INCREMENT, StringPrintf("%d", cfg_uioptions_textincrement.getValueAsInt())); + SendMessageW(GetDlgItem(hwndDlg, IDC_SLIDER_TICKERSPEED), TBM_SETRANGEMAX, 0, 4); + SendMessageW(GetDlgItem(hwndDlg, IDC_SLIDER_TICKERSPEED), TBM_SETRANGEMIN, 0, 0); + SendMessageW(GetDlgItem(hwndDlg, IDC_SLIDER_TICKERSPEED), TBM_SETPOS, 1, (int)(1.0f / cfg_uioptions_textspeed.getValueAsDouble() - 1.0f + 0.5f)); + SetDlgItemTextW(hwndDlg, IDC_STATIC_TICKER, StringPrintfW(WASABI_API_LNGSTRINGW(IDS_TEXT_SCROLL_SPEED), getScrollTextSpeedW((float)cfg_uioptions_textspeed.getValueAsDouble()))); + + SendMessageW(GetDlgItem(hwndDlg,IDC_SPIN_SHOWTIME),UDM_SETRANGE,0,MAKELONG(9999,0)); + SetDlgItemInt(hwndDlg, IDC_EDIT_SHOWTIME, cfg_uioptions_appbarsshowtime.getValueAsInt(), 0); + SendMessageW(GetDlgItem(hwndDlg,IDC_SPIN_HIDETIME),UDM_SETRANGE,0,MAKELONG(9999,0)); + SetDlgItemInt(hwndDlg, IDC_EDIT_HIDETIME, cfg_uioptions_appbarshidetime.getValueAsInt(), 0); + + dock_dist = dock_dist2 = spin_inc = spin_show = spin_hide = 1; + return 1; + } + case WM_COMMAND: + toggle_from_wa2 = 1; + switch (LOWORD(wParam)) + { + case IDC_CHECK_DESKTOPALPHA: + cfg_uioptions_desktopalpha.setValueAsInt(IsDlgButtonChecked(hwndDlg, IDC_CHECK_DESKTOPALPHA)); + return 0; + case IDC_BUTTON_AUTOTIMERRES: + autoTimerRes(hwndDlg); + return 0; + case IDC_CHECK_TOOLTIPS: + cfg_uioptions_tooltips.setValueAsInt(IsDlgButtonChecked(hwndDlg, IDC_CHECK_TOOLTIPS)); + return 0; + case IDC_CHECK_DOCKING: + cfg_options_docking.setValueAsInt(IsDlgButtonChecked(hwndDlg, IDC_CHECK_DOCKING)); + return 0; + case IDC_CHECK_DOCKING2: + cfg_options_appbarondrag.setValueAsInt(IsDlgButtonChecked(hwndDlg, IDC_CHECK_DOCKING2)); + return 0; + case IDC_EDIT_DOCKDISTANCE: + if (HIWORD(wParam) == EN_CHANGE && dock_dist) + { + int t, a = GetDlgItemInt(hwndDlg, IDC_EDIT_DOCKDISTANCE, &t, 0); + if (t) cfg_options_dockingdistance.setValueAsInt(a); + } + return 0; + case IDC_EDIT_DOCKDISTANCE2: + if (HIWORD(wParam) == EN_CHANGE && dock_dist2) + { + int t, a = GetDlgItemInt(hwndDlg, IDC_EDIT_DOCKDISTANCE2, &t, 0); + if (t) cfg_options_appbardockingdistance.setValueAsInt(a); + } + return 0; + case IDC_EDIT_INCREMENT: + if (HIWORD(wParam) == EN_CHANGE && spin_inc) + { + int t, a = GetDlgItemInt(hwndDlg, IDC_EDIT_INCREMENT, &t, 0); + if (t) cfg_uioptions_textincrement.setValueAsInt(a); + } + return 0; + case IDC_EDIT_SHOWTIME: + if (HIWORD(wParam) == EN_CHANGE && spin_show) + { + int t, a = GetDlgItemInt(hwndDlg, IDC_EDIT_SHOWTIME, &t, 0); + if (t) cfg_uioptions_appbarsshowtime.setValueAsInt(a); + } + return 0; + case IDC_EDIT_HIDETIME: + if (HIWORD(wParam) == EN_CHANGE && spin_hide) + { + int t, a = GetDlgItemInt(hwndDlg, IDC_EDIT_HIDETIME, &t, 0); + if (t) cfg_uioptions_appbarshidetime.setValueAsInt(a); + } + return 0; + } + toggle_from_wa2 = 0; + break; + case WM_HSCROLL: + { + int t = (int)SendMessageW((HWND) lParam, TBM_GETPOS, 0, 0); + HWND ctrl = (HWND) lParam; + if (ctrl == GetDlgItem(hwndDlg, IDC_SLIDER_TIMERRESOLUTION)) + { + cfg_uioptions_timerresolution.setValueAsInt(t); + SetDlgItemTextW(hwndDlg, IDC_STATIC_TIMERRES, StringPrintfW(WASABI_API_LNGSTRINGW(IDS_TIMERS_RESOLUTION), cfg_uioptions_timerresolution.getValueAsInt())); + } + else if (ctrl == GetDlgItem(hwndDlg, IDC_SLIDER_TICKERSPEED)) + { + cfg_uioptions_textspeed.setValueAsDouble(1.0 / (float)(t + 1)); + SetDlgItemTextW(hwndDlg, IDC_STATIC_TICKER, StringPrintfW(WASABI_API_LNGSTRINGW(IDS_TEXT_SCROLL_SPEED), getScrollTextSpeedW((float)cfg_uioptions_textspeed.getValueAsDouble()))); + } + break; + } + case WM_DESTROY: + subWnd = NULL; + dock_dist = dock_dist2 = spin_inc = spin_show = spin_hide = 0; + return 0; + } + + const int controls[] = + { + IDC_SLIDER_TIMERRESOLUTION, + IDC_SLIDER_TICKERSPEED, + }; + if (FALSE != WASABI_API_APP->DirectMouseWheel_ProcessDialogMessage(hwndDlg, uMsg, wParam, lParam, controls, ARRAYSIZE(controls))) + return TRUE; + + return 0; +} diff --git a/Src/Plugins/General/gen_ff/resource.h b/Src/Plugins/General/gen_ff/resource.h new file mode 100644 index 00000000..1f91ed89 --- /dev/null +++ b/Src/Plugins/General/gen_ff/resource.h @@ -0,0 +1,261 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by gen_ff.rc +// +#define IDS_SEND_TO 1 +#define IDS_NO_THEME_AVAILABLE 2 +#define IDS_COLOR_THEMES 3 +#define IDS_CURRENT_SKIN 4 +#define IDS_NO_SKIN_LOADED 5 +#define IDS_MODERN_SKIN_SUPPORT_CLASSIC 6 +#define IDS_MODERN_SKIN_SUPPORT 7 +#define IDS_CUSTOM_X_PERCENT 8 +#define IDS_CUSTOM 9 +#define IDS_WINDOW_SETTINGS 10 +#define IDS_SCALE_X_PERCENT 11 +#define IDS_OPACITY_X_PERCENT 12 +#define IDS_GHK_SHOW_NOTIFICATION 13 +#define IDS_MODERN_SKINS 14 +#define IDS_GENERAL 15 +#define IDS_ALPHA_BLENDING 16 +#define IDS_FONT_RENDERING 17 +#define IDS_ERROR_WHILE_LOADING_SKIN_WINDOW 18 +#define IDS_NO_OPTIONS_AVAILABLE_FOR_THIS_SKIN 19 +#define IDS_AUTO_UNICODE_LATIN1_ASCII 20 +#define IDS_FONT 21 +#define IDS_MAPPING 22 +#define IDS_SCALE 23 +#define IDS_TYPE 24 +#define IDS_THIS_SKIN 25 +#define IDS_ALL_SKINS 26 +#define IDS_FASTER 27 +#define IDS_FAST 28 +#define IDS_AVERAGE 29 +#define IDS_SLOW 30 +#define IDS_SLOWER 31 +#define IDS_FAILED_TO_DETECT_OPTIMAL_RESOLUTION 32 +#define IDS_AUTO_DETECTING 33 +#define IDS_PREPARING_AUTO_DETECTION 34 +#define IDS_AUTO_DETECTION_SUCCESSFUL 35 +#define IDS_ACCEPT 36 +#define IDS_N_A 37 +#define IDS_TIMERS_RESOLUTION 38 +#define IDS_TEXT_SCROLL_SPEED 39 +#define IDS_CROSSFADER_ONLY_UNDER_OUT_DS 40 +#define IDS_NOT_SUPPORTED 41 +#define IDS_PLAYLIST_EDITOR 42 +#define IDS_VIDEO 43 +#define IDS_MONO 44 +#define IDS_STEREO 45 +#define IDS_X_CHANNELS 46 +#define IDS_BY_SPACE 47 +#define IDS_COULD_NOT_FIND_WINAMP 48 +#define IDS_ERROR 49 +#define IDS_WINAMP_NOT_FOUND 50 +#define IDS_VISUALIZATIONS 51 +#define IDS_STRING52 52 +#define IDS_MEDIA_LIBRARY 52 +#define IDS_NO_SET 53 +#define IDS_SLOT_X_X 54 +#define IDS_COLOR_EDITOR 55 +#define IDS_SKIN_SETTINGS 56 +#define IDS_WEB_BROWSER 57 +#define IDS_STRING58 58 +#define IDS_ALBUM_ART 58 +#define IDS_NO_VISUALISATION 59 +#define IDS_SPECTRUM_ANALYZER 60 +#define IDS_STRING61 61 +#define IDS_OSCILLOSCOPE 61 +#define IDS_SKIN_LOAD_FORMAT_OLD 62 +#define IDS_SKIN_LOAD_FORMAT_TOO_RECENT 63 +#define IDS_SKIN_LOAD_WARNING 64 +#define IDS_SKIN_LOAD_NOT_SUPPORTED 65 +#define IDS_NO_SKIN_LOADED_ 66 +#define IDS_XUITHEME_LOAD 67 +#define IDS_XUITHEME_SAVE 68 +#define IDS_MS 69 +#define IDS_KHZ 70 +#define IDS_STRING71 71 +#define IDS_KBPS 71 +#define IDS_USELOCK 72 +#define IDS_ALLLOCKED 73 +#define IDS_OPAQUE_FOCUS 74 +#define IDS_OPAQUE_HOVER 75 +#define IDS_NO_OPACITY 76 +#define IDS_LOADING 77 +#define IDB_PLEDIT_TAB_NORMAL 110 +#define IDB_PLEDIT_TAB_HILITED 111 +#define IDB_PLEDIT_TAB_SELECTED 112 +#define IDB_VIDEO_TAB_NORMAL 120 +#define IDB_VIDEO_TAB_HILITED 121 +#define IDB_VIDEO_TAB_SELECTED 122 +#define IDD_ABOUT 124 +#define IDR_SONGINFO 128 +#define IDB_MB_TAB_NORMAL 130 +#define IDB_MB_TAB_HILITED 131 +#define IDB_MB_TAB_SELECTED 132 +#define IDB_ML_TAB_NORMAL 133 +#define IDD_DIALOG1 133 +#define IDB_ML_TAB_HILITED 134 +#define IDD_FONTMAPPER 134 +#define IDB_ML_TAB_SELECTED 135 +#define IDB_VIS_TAB_NORMAL 136 +#define IDB_VIS_TAB_HILITED 137 +#define IDB_VIS_TAB_SELECTED 138 +#define IDD_MEDIA_DOWNLOADER 141 +#define IDD_PREFS_WINDOWS 142 +#define IDC_CHECK_DESKTOPALPHA 1002 +#define IDC_STATIC_DA1 1003 +#define IDC_STATIC_DA3 1005 +#define IDC_TAB1 1006 +#define IDC_LIST1 1007 +#define IDC_BUTTON_SETTHEME 1008 +#define IDC_VERSTR 1009 +#define IDC_CHECK_LINKALPHA 1011 +#define IDC_CHECK_LINKRATIO 1012 +#define IDC_SLIDER_TIMERRESOLUTION 1013 +#define IDC_STATIC_TIMERRES 1014 +#define IDC_CHECK_TOOLTIPS 1015 +#define IDC_CHECK_DOCKING 1016 +#define IDC_EDIT_DOCKDISTANCE 1017 +#define IDC_BUTTON_SKINSPECIFIC 1018 +#define IDC_CHECK_DOCKING2 1018 +#define IDC_SLIDER_TICKERSPEED 1019 +#define IDC_STATIC_TICKER 1020 +#define IDC_BUTTON_AUTOTIMERRES 1021 +#define IDC_EDIT_DOCKDISTANCE2 1022 +#define IDC_TXT 1023 +#define IDC_EDIT_INCREMENT 1023 +#define IDC_SLIDER_CUSTOMSCALE 1025 +#define IDC_STATIC_SCALE 1026 +#define IDC_EDIT_SHOWTIME 1027 +#define IDC_EDIT_HIDETIME 1028 +#define IDC_RECT 1186 +#define IDR_CONTROLMENU 1281 +#define IDD_PREFS_THEMES 1282 +#define IDC_CHECK_ALLOWBITMAPFONTS 1283 +#define IDD_PREFS 1284 +#define IDC_CHECK_ALTFONTS 1284 +#define IDD_AUTOTIMERRES 1285 +#define IDD_CUSTOMSCALE 1286 +#define IDC_COMBO_TTFOVERRIDE 1286 +#define IDD_PREFS_FONTS 1287 +#define IDC_STATIC_TTFOVERRIDE 1287 +#define IDC_SLIDER_SCALETTFOVERRIDE 1288 +#define IDC_STATIC_TTFSCALE 1289 +#define IDD_CUSTOMALPHA 1289 +#define IDC_CHECK_FREETYPETTF 1290 +#define IDD_PREFS_SKIN 1290 +#define IDC_COMBO_CHARMAP 1291 +#define IDC_STATIC_FREETYPE 1292 +#define IDC_STATIC_DECREASESIZE 1293 +#define IDC_STATIC_INCREASESIZE 1294 +#define IDC_NO_7BIT_OVERRIDE 1295 +#define IDC_CHECK_NO_ALT_7BIT_OVERRIDE 1296 +#define IDC_STATIC_CHARMAP 1297 +#define IDC_STATIC_ALTFONTS 1298 +#define IDC_COMBO_DEFAULTFONT 1299 +#define IDC_SLIDER_SCALEDEFAULTFONT 1300 +#define IDC_CHECK_LINKALLALPHA 1301 +#define IDC_STATIC_DECREASESIZE2 1301 +#define IDC_CHECK_LINKALLRATIO 1302 +#define IDC_STATIC_SCALEDEFAULTFONT 1302 +#define IDC_STATIC_INCREASESIZE2 1303 +#define IDC_STATIC_SCALEDEFAULTFONT2 1305 +#define IDC_RADIO_USELOCK 1306 +#define IDC_RADIO_ALLLOCKED 1307 +#define IDC_SLIDER_CUSTOMALPHA 1309 +#define IDC_STATIC_ALPHA 1311 +#define IDC_SLIDER_HOLD 1313 +#define IDC_STATIC_TRANSP 1314 +#define IDC_STATIC_OPAQUE 1315 +#define IDC_SLIDER_FADEIN 1316 +#define IDC_SLIDER_FADEOUT 1317 +#define IDC_STATIC_SCALE11 1318 +#define IDC_STATIC_HOLD 1319 +#define IDC_STATIC_FADEIN 1320 +#define IDC_STATIC_FADEOUT 1321 +#define IDC_STATIC_SCALE301 1322 +#define IDC_EDIT_EXTEND 1323 +#define IDC_STATIC_EXTEND 1324 +#define IDC_STATIC_GROUP 1326 +#define IDC_CHECK_USEFONTMAPPER 1330 +#define IDC_BUTTON_FONTMAPPER 1331 +#define IDC_LIST_MAPPINGS 1332 +#define IDC_RADIO_THISSKIN 1333 +#define IDC_RADIO_ALLSKINS 1334 +#define IDC_BUTTON_NEW 1335 +#define IDC_BUTTON_DEL 1336 +#define IDC_COMBO_SKINFONTS 1337 +#define IDC_COMBO_FONTS 1338 +#define IDC_SLIDER_SCALE 1339 +#define IDC_BUTTON_SET 1340 +#define IDC_STATIC_EMPTY 1344 +#define IDC_STATIC_OPACITY 1345 +#define IDC_STATIC_AUTOON 1346 +#define IDC_STATIC_AUTOONTXT 1347 +#define IDC_STATIC_FADEIN2 1348 +#define IDC_STATIC_FADEOUT2 1349 +#define IDC_STATIC_HOLD2 1350 +#define IDC_STATIC_EXTENDBOX 1351 +#define IDC_URL 1352 +#define IDC_PROGRESS 1353 +#define IDC_DOWNLOADTO 1354 +#define IDC_SPIN_EXTEND 1356 +#define IDC_SPIN_DOCKDISTANCE 1357 +#define IDC_SPIN_DOCKDISTANCE2 1358 +#define IDC_COMBO_SCALE 1358 +#define IDC_SPIN_INCREMENT 1359 +#define IDC_COMBO_OPACITY 1359 +#define IDC_SPIN_SHOWTIME 1360 +#define IDC_SPIN_HIDETIME 1361 +#define IDD_PREFS_GENERAL 1362 +#define WINAMP_EDIT_ID3 40188 +#define WINAMP_JUMP 40193 +#define WINAMP_JUMPFILE 40194 +#define ID_CONTROLMENU_OPACITY_10 42202 +#define ID_CONTROLMENU_OPACITY_20 42203 +#define ID_CONTROLMENU_OPACITY_30 42204 +#define ID_CONTROLMENU_OPACITY_40 42205 +#define ID_CONTROLMENU_OPACITY_50 42206 +#define ID_CONTROLMENU_OPACITY_60 42207 +#define ID_CONTROLMENU_OPACITY_70 42208 +#define ID_CONTROLMENU_OPACITY_80 42209 +#define ID_CONTROLMENU_OPACITY_90 42210 +#define ID_CONTROLMENU_OPACITY_100 42211 +#define ID_CONTROLMENU_SCALING_25 42213 +#define ID_CONTROLMENU_SCALING_50 42214 +#define ID_CONTROLMENU_SCALING_75 42215 +#define ID_CONTROLMENU_SCALING_100 42216 +#define ID_CONTROLMENU_SCALING_200 42217 +#define ID_CONTROLMENU_SCALING_250 42218 +#define ID_CONTROLMENU_SCALING_300 42219 +#define ID_CONTROLMENU_SCALING_150 42222 +#define ID_CONTROLMENU_SCALING_LOCKED 42223 +#define ID_CONTROLMENU_SCALING_CUSTOM 42224 +#define ID_CONTROLMENU_SCALING_FOLLOWDOUBLESIZE 42225 +#define ID_CONTROLMENU_OPACITY_AUTO100 42226 +#define ID_CONTROLMENU_OPACITY_AUTO100_HOVER 42226 +#define ID_CONTROLMENU_OPACITY_CUSTOM 42227 +#define ID_CONTROLMENU_OPACITY_AUTO100_FOCUS 42228 +#define ID_CONTROLMENU_TOOLBAR_TOP 42229 +#define ID_CONTROLMENU_TOOLBAR_RIGHT 42231 +#define ID_CONTROLMENU_TOOLBAR_BOTTOM 42232 +#define ID_CONTROLMENU_TOOLBAR_DISABLED 42233 +#define ID_CONTROLMENU_TOOLBAR_ALWAYSONTOP 42234 +#define ID_CONTROLMENU_TOOLBAR_AUTOHIDE 42235 +#define ID_CONTROLMENU_TOOLBAR_LEFT 42236 +#define ID_CONTROLMENU_TOOLBAR_AUTODOCKONDRAG 42237 +#define ID_CONTROLMENU_SCALING_125 42238 +#define IDS_NULLSOFT_MODERN_SKINS 65534 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 143 +#define _APS_NEXT_COMMAND_VALUE 42238 +#define _APS_NEXT_CONTROL_VALUE 1359 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/Src/Plugins/General/gen_ff/servicelink.cpp b/Src/Plugins/General/gen_ff/servicelink.cpp new file mode 100644 index 00000000..f7b17444 --- /dev/null +++ b/Src/Plugins/General/gen_ff/servicelink.cpp @@ -0,0 +1,72 @@ +#include + +// these are pragmas to force a reference to objects that otherwise are entirely decoupled from the rest of the +// program except for their static constructor code -- in this case, if the code is in a lib, the object gets +// optimized out, and we definitly do not want that +// +// generally you want to add more of these pragmas for services declared through the BEGIN_SERVICES/END_SERVICES +// macros which you want to link with + +// color themes list xui object +#ifdef WASABI_COMPILE_COLORTHEMES +#pragma comment(linker, "/include:__link_ColorThemesListXuiSvc") +#endif + +// config script objects +#ifdef WASABI_COMPILE_CONFIG +#pragma comment(linker, "/include:__link_ConfigObjectSvc") +#endif + +// minibrowser service +#ifdef WASABI_WIDGETS_BROWSER +#pragma comment(linker, "/include:__link_MbSvc") +#endif + +// skinned tooltips +#ifdef WASABI_WIDGETS_TOOLTIPS +#pragma comment(linker, "/include:__link_GroupTipsSvc") +#endif + +// freetype font renderer +#ifdef WASABI_FONT_RENDERER_USE_FREETYPE +//#pragma comment(linker, "/include:__link_FreeTypeFontRenderer_Svc") +#endif + +// pldir svc +#pragma comment(linker, "/include:__link_wa2PlDirObj_Svcs") + +// pleditor xuiobject +#pragma comment(linker, "/include:__link_Wa2PleditXuiSvc") + +// song ticker xui object +#pragma comment(linker, "/include:__link_wa2SongTicker_Svcs") + +// Winamp Config script object +#pragma comment(linker, "/include:__link_WinampConfig_svcs") + +// progress grid xui object +#ifdef WASABI_WIDGETS_MEDIASLIDERS +#pragma comment(linker, "/include:__link_ProgressGridXuiSvc") +#endif + +// gradient xui object +#ifdef WASABI_WIDGETS_MEDIASLIDERS +#pragma comment(linker, "/include:__link_GradientXuiSvc") +#endif + +#pragma comment(linker, "/include:__link_GroupXFadeXuiSvc") + +#pragma comment(linker, "/include:__link_GradientGen_Svc") + +#pragma comment(linker, "/include:__link_OsEdgeGen_Svc") + +#pragma comment(linker, "/include:__link_PolyGen_Svc") + +#pragma comment(linker, "/include:__link_SolidGen_Svc") + +#pragma comment(linker, "/include:__link_ScriptCore_Svc") + + + +//#pragma comment(linker, "/include:__link_ColorEditor_Svc") + diff --git a/Src/Plugins/General/gen_ff/skininfo.cpp b/Src/Plugins/General/gen_ff/skininfo.cpp new file mode 100644 index 00000000..520bd767 --- /dev/null +++ b/Src/Plugins/General/gen_ff/skininfo.cpp @@ -0,0 +1,141 @@ +#include +#include "skininfo.h" +#include "../xml/obj_xml.h" +#include +#include +#include "../nu/AutoChar.h" +#include "gen.h" +#include "resource.h" +#include "../Agave/Language/api_language.h" + +SkinInfosXmlReader::SkinInfosXmlReader(const wchar_t *skinname) : SkinInfoBlock(skinname) +{ + waServiceFactory *parserFactory = WASABI_API_SVC->service_getServiceByGuid(obj_xmlGUID); + if (parserFactory) + { + obj_xml *parser = (obj_xml *)parserFactory->getInterface(); + if (parser) + { + { + StringPathCombine skinPath(WASABI_API_SKIN->getSkinsPath(), skinname); + + XMLAutoInclude include(parser, skinPath); + parser->xmlreader_registerCallback(L"WinampAbstractionLayer",this); + //parser->xmlreader_registerCallback(L"WinampAbstractionLayer\fSkinInfo", this); + parser->xmlreader_registerCallback(L"WinampAbstractionLayer\fSkinInfo\f*", this); + parser->xmlreader_registerCallback(L"WasabiXML",this); + //parser->xmlreader_registerCallback(L"WasabiXML\fSkinInfo", this); + parser->xmlreader_registerCallback(L"WasabiXML\fSkinInfo\f*", this); + parser->xmlreader_open(); + + skinPath.AppendPath(L"skin.xml"); + LoadXmlFile(parser, skinPath); + parser->xmlreader_unregisterCallback(this); + } + parser->xmlreader_close(); + parserFactory->releaseInterface(parser); + parser = 0; + } + } +} + +void SkinInfosXmlReader::xmlReaderOnStartElementCallback(const wchar_t *xmlpath, const wchar_t *xmltag, ifc_xmlreaderparams *params) +{ + if (!_wcsicmp(xmltag, L"WinampAbstractionLayer") || !_wcsicmp(xmltag, L"WasabiXML")) + { + const wchar_t *version = params->getItemValue(L"version"); + if (version) + walversion = version; + } +} + +void SkinInfosXmlReader::xmlReaderOnCharacterDataCallback(const wchar_t *xmlpath, const wchar_t *xmltag, const wchar_t *s) +{ + if (!wcscmp(xmltag, L"name")) { fullname = s;} + else if (!wcscmp(xmltag, L"version")) { version = s;} + else if (!wcscmp(xmltag, L"comment")) { comment = s;} + else if (!wcscmp(xmltag, L"author")) { author = s;} + else if (!wcscmp(xmltag, L"email")) { email = s;} + else if (!wcscmp(xmltag, L"homepage")) { homepage = s;} + else if (!wcscmp(xmltag, L"parentskin")) { parentskin = s;} + else if (!wcscmp(xmltag, L"screenshot")) { screenshot = s;} +} + +extern int m_are_we_loaded; +static StringW skin; + +const wchar_t *getSkinInfoW() +{ + static StringW skininfoW; + + // these checks is repeated in getSkinInfo, also + if (!m_are_we_loaded) return NULL; + if (skin.iscaseequal(WASABI_API_SKIN->getSkinName()) && !skininfoW.isempty()) return skininfoW; + + skininfoW = L""; + SkinInfosXmlReader r(WASABI_API_SKIN->getSkinName()); + + /* skin name */ + const wchar_t *name = r.getFullName(); + if (name && *name) + skininfoW += name; + else + skininfoW += WASABI_API_SKIN->getSkinName(); + + /* skin version */ + const wchar_t *ver = r.getVersion(); + if (ver && *ver) + { + skininfoW += L" v"; + skininfoW += ver; + } + skininfoW += L"\r\n"; + + /* skin author & e-mail */ + const wchar_t *aut = r.getAuthor(); + if (aut && *aut) + { + skininfoW += WASABI_API_LNGSTRINGW(IDS_BY_SPACE); + skininfoW += aut; + + const wchar_t *email = r.getEmail(); + if (email && *email) + { + skininfoW += L" ("; + skininfoW += email; + skininfoW += L")"; + } + skininfoW += L"\r\n"; + } + + /* skin homepage */ + const wchar_t *web = r.getHomepage(); + if (web && *web) + { + skininfoW += web; + skininfoW += L"\r\n"; + } + skininfoW += L"\r\n"; + const wchar_t *comment = r.getComment(); + if (comment && *comment) + skininfoW += comment; + + skin = WASABI_API_SKIN->getSkinName(); + return skininfoW; +} + +const char *getSkinInfo() +{ + static String skininfo; + + // these checks is repeated in getSkinInfoW, also + if (!m_are_we_loaded) return NULL; + if (skin.iscaseequal(WASABI_API_SKIN->getSkinName()) && !skininfo.isempty()) return skininfo; + + // get the wide character version + const wchar_t *wideSkinInfo = getSkinInfoW(); + + // and convert + skininfo = AutoChar(wideSkinInfo); + return skininfo; +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_ff/skininfo.h b/Src/Plugins/General/gen_ff/skininfo.h new file mode 100644 index 00000000..33d77553 --- /dev/null +++ b/Src/Plugins/General/gen_ff/skininfo.h @@ -0,0 +1,42 @@ +#ifndef _SKININFO_H +#define _SKININFO_H + +#include "../xml/obj_xml.h" +#include "../xml/ifc_xmlreadercallbacki.h" +#include + +class SkinInfoBlock { +public: + SkinInfoBlock(const wchar_t *name) : name(name),fullname(name) { } + const wchar_t *getName() { return name; } + const wchar_t *getParentSkin() { return parentskin; } + const wchar_t *getFullName() { return fullname; } + const wchar_t *getVersion() { return version; } + const wchar_t *getComment() { return comment; } + const wchar_t *getAuthor() { return author; } + const wchar_t *getEmail() { return email; } + const wchar_t *getHomepage() { return homepage; } + const wchar_t *getScreenshot() { return screenshot; } +protected: + StringW name; + StringW walversion; + StringW parentskin; + + StringW fullname; + StringW version; + StringW comment; + StringW author; + StringW email; + StringW homepage; + StringW screenshot; +}; + +class SkinInfosXmlReader : public SkinInfoBlock, public ifc_xmlreadercallbackI +{ +public: + SkinInfosXmlReader(const wchar_t *skinname); + void xmlReaderOnStartElementCallback(const wchar_t *xmlpath, const wchar_t *xmltag, ifc_xmlreaderparams *params); + void xmlReaderOnCharacterDataCallback(const wchar_t *xmlpath, const wchar_t *xmltag, const wchar_t *s); +}; + +#endif diff --git a/Src/Plugins/General/gen_ff/wa2buckitems.cpp b/Src/Plugins/General/gen_ff/wa2buckitems.cpp new file mode 100644 index 00000000..0fb57bba --- /dev/null +++ b/Src/Plugins/General/gen_ff/wa2buckitems.cpp @@ -0,0 +1,96 @@ +#include +#include "wa2buckitems.h" +#include "wa2frontend.h" +#include "wa2wndembed.h" +#include "gen.h" +#include "../Agave/Language/api_language.h" + + +//------------------------------------------------------------------------------------------- +Wa2BucketItem::Wa2BucketItem(GUID g, const wchar_t *t) : BucketItemT (g, t) { +} + +Wa2BucketItem::~Wa2BucketItem() { +} + +//------------------------------------------------------------------------------------------- + +PlBucketItem::PlBucketItem() : Wa2BucketItem(INVALID_GUID, L"Playlist Editor") { +} + +PlBucketItem::~PlBucketItem() { +} + +void PlBucketItem::onLeftPush(int x, int y) { + BUCKETITEM_PARENT::onLeftPush(x, y); + wa2.setWindowVisible(IPC_GETWND_PE, !wa2.isWindowVisible(IPC_GETWND_PE)); +} + + +//------------------------------------------------------------------------------------------- +EmbedBucketItem::EmbedBucketItem() : Wa2BucketItem(INVALID_GUID, L"Embed?") { +} + +EmbedBucketItem::~EmbedBucketItem() { +} + +void EmbedBucketItem::onLeftPush(int x, int y) { + BUCKETITEM_PARENT::onLeftPush(x, y); + //wa2.setWindowVisible(IPC_GETWND_PE, !wa2.isWindowVisible(IPC_GETWND_PE)); +} + +//------------------------------------------------------------------------------------------- +#ifdef MINIBROWSER_SUPPORT + +MbBucketItem::MbBucketItem() : Wa2BucketItem(INVALID_GUID, L"Minibrowser") { +} + +MbBucketItem::~MbBucketItem() { +} + +void MbBucketItem::onLeftPush(int x, int y) { + BUCKETITEM_PARENT::onLeftPush(x, y); + wa2.setWindowVisible(IPC_GETWND_MB, !wa2.isWindowVisible(IPC_GETWND_MB)); +} +#endif + +//------------------------------------------------------------------------------------------- +VidBucketItem::VidBucketItem() : Wa2BucketItem(INVALID_GUID, L"Video Window") { +} + +VidBucketItem::~VidBucketItem() { +} + +//------------------------------------------------------------------------------------------- +void VidBucketItem::onLeftPush(int x, int y) { + BUCKETITEM_PARENT::onLeftPush(x, y); + wa2.setWindowVisible(IPC_GETWND_VIDEO, !wa2.isWindowVisible(IPC_GETWND_VIDEO)); +} + +//------------------------------------------------------------------------------------------- + +MlBucketItem::MlBucketItem() : Wa2BucketItem(INVALID_GUID, L"Winamp Library") { +} + +MlBucketItem::~MlBucketItem() { +} + +#define WA_MEDIALIB_MENUITEM_ID 23123 + +void MlBucketItem::onLeftPush(int x, int y) { + BUCKETITEM_PARENT::onLeftPush(x, y); + SendMessageW(wa2.getMainWindow(), WM_COMMAND, WA_MEDIALIB_MENUITEM_ID, 0); +} + +//------------------------------------------------------------------------------------------- + +VisBucketItem::VisBucketItem() : Wa2BucketItem(INVALID_GUID, L"Visualizations") { +} + +VisBucketItem::~VisBucketItem() { +} + +void VisBucketItem::onLeftPush(int x, int y) { + BUCKETITEM_PARENT::onLeftPush(x, y); + wa2.toggleVis(); +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_ff/wa2buckitems.h b/Src/Plugins/General/gen_ff/wa2buckitems.h new file mode 100644 index 00000000..2238aa49 --- /dev/null +++ b/Src/Plugins/General/gen_ff/wa2buckitems.h @@ -0,0 +1,70 @@ +#ifndef __WA2BUCKETITEMS_H +#define __WA2BUCKETITEMS_H + +#include + +#define WA2BUCKETITEM_PARENT Wa2BucketItem + +//------------------------------------------------------------------------------------------- +class Wa2BucketItem : public BucketItemT { + public: + Wa2BucketItem(GUID g=INVALID_GUID, const wchar_t *text=NULL); + virtual ~Wa2BucketItem(); +}; + +//------------------------------------------------------------------------------------------- +class PlBucketItem : public Wa2BucketItem { + public: + PlBucketItem(); + virtual ~PlBucketItem(); + void onLeftPush(int x, int y); +}; + +//------------------------------------------------------------------------------------------- +class EmbedBucketItem : public Wa2BucketItem { + public: + EmbedBucketItem(); + virtual ~EmbedBucketItem(); + void onLeftPush(int x, int y); +}; + +#ifdef MINIBROWSER_SUPPORT + +//------------------------------------------------------------------------------------------- +class MbBucketItem : public Wa2BucketItem { + public: + MbBucketItem(); + virtual ~MbBucketItem(); + void onLeftPush(int x, int y); +}; + +#endif + +//------------------------------------------------------------------------------------------- +class VidBucketItem : public Wa2BucketItem { + public: + VidBucketItem(); + virtual ~VidBucketItem(); + void onLeftPush(int x, int y); +}; + +//------------------------------------------------------------------------------------------- +class VisBucketItem : public Wa2BucketItem { + public: + VisBucketItem(); + virtual ~VisBucketItem(); + void onLeftPush(int x, int y); +}; + +//------------------------------------------------------------------------------------------- +class MlBucketItem : public Wa2BucketItem { + public: + MlBucketItem(); + virtual ~MlBucketItem(); + void onLeftPush(int x, int y); +}; + + + + +#endif \ No newline at end of file diff --git a/Src/Plugins/General/gen_ff/wa2cfgitems.cpp b/Src/Plugins/General/gen_ff/wa2cfgitems.cpp new file mode 100644 index 00000000..a225df8c --- /dev/null +++ b/Src/Plugins/General/gen_ff/wa2cfgitems.cpp @@ -0,0 +1,306 @@ +#include +#include "wa2cfgitems.h" +#include "wa2wndembed.h" +#include "wa2core.h" +#include +#include "wa2frontend.h" +#include "../../Plugins/Output/out_ds/ds_ipc.h" +#include +#include "gen.h" +#include "resource.h" +#include "../Agave/Language/api_language.h" + + +// cfg item callbacks +void setCrossfader(int crossfade); +void onWa2CrossfadeChange(int crossfade); +void setOnTop(int ontop); +void onWa2OnTopChanged(int ontop); + +int my_set = 0; +int disable_set_wa2_repeat = 0; + +//--------------------------------------------------------- +// playlist editor cfg items +//--------------------------------------------------------- +_bool shuffle(L"Shuffle", FALSE); +_int repeat(L"Repeat", 0); +_bool visrandom(L"Random", FALSE); + +Wa2PlEditCfgItems::Wa2PlEditCfgItems() : CfgItemI(L"Playlist Editor", pleditWndGuid) { + registerAttribute(&shuffle, new int_attrCB(Wa2PlEditCfgItems::onSetShuffle)); + registerAttribute(&repeat, new int_attrCB(Wa2PlEditCfgItems::onSetRepeat)); + WASABI_API_CONFIG->config_registerCfgItem(this); + shuffle.setValueAsInt(g_Core->getShuffle()); + disable_set_wa2_repeat = 1; + repeat.setValueAsInt(g_Core->getRepeat()); + disable_set_wa2_repeat = 0; +} + +Wa2PlEditCfgItems::~Wa2PlEditCfgItems() { + WASABI_API_CONFIG->config_deregisterCfgItem(this); +} + +void Wa2PlEditCfgItems::onSetRepeat(int set) { + my_set = 1; + ASSERT(g_Core != NULL); + if (!disable_set_wa2_repeat) { + // if (!!set != !!g_Core->getRepeat()) // FG> do NOT uncomment this + g_Core->setRepeat(set); + } + my_set = 0; +} + +void Wa2PlEditCfgItems::onSetShuffle(BOOL set) { + my_set = 1; + ASSERT(g_Core != NULL); + if (!!set != !!g_Core->getShuffle()) + g_Core->setShuffle(set); + my_set = 0; +} + +//--------------------------------------------------------- +static _bool prevent_videostoponclose(L"Prevent video playback Stop on video window Close", FALSE); +static _bool prevent_videoresize(L"Prevent video resize", FALSE); // 5.32+ +static _bool show_videownd_on_play(L"Show video wnd on play"); // 5.36+ +static _bool hide_videownd_on_stop(L"Hide video wnd on stop"); // 5.36+ +int preventvc_savedvalued = -1; +int preventvresize_savedvalued = -1; + +SkinTweaks::SkinTweaks() : CfgItemI(L"SkinTweaks", skinTweaksGuid) +{ + registerAttribute(&prevent_videostoponclose, new int_attrCB(SkinTweaks::onPreventVideoStopChanged)); + registerAttribute(&prevent_videoresize, new int_attrCB(SkinTweaks::onPreventVideoResize)); +} + +//--------------------------------------------------------- +AvsCfg::AvsCfg() : CfgItemI(L"Avs", avs_guid) { + registerAttribute(&visrandom, new int_attrCB(AvsCfg::onVisRandomChanged)); +} + +//--------------------------------------------------------- +void SkinTweaks::onPreventVideoStopChanged(BOOL set) { + if (set) { + if (preventvc_savedvalued == -1) { + preventvc_savedvalued = wa2.getStopOnVideoClose(); + wa2.setStopOnVideoClose(0); + } + } else { + if (preventvc_savedvalued != -1) { + wa2.setStopOnVideoClose(preventvc_savedvalued); + preventvc_savedvalued = -1; + } + } +} + +void SkinTweaks::onPreventVideoResize(BOOL set) { + if (set) { + if (preventvresize_savedvalued == -1) { + preventvresize_savedvalued = wa2.GetVideoResize(); + wa2.SetVideoResize(0); + } + } else { + if (preventvresize_savedvalued != -1) { + wa2.SetVideoResize(preventvresize_savedvalued); + preventvresize_savedvalued = -1; + } + } +} + + +//--------------------------------------------------------- +void AvsCfg::onVisRandomChanged(BOOL set) { + extern int disable_send_visrandom; + if (!disable_send_visrandom) { + disable_send_visrandom = 1; + wa2.visRandom(set); + disable_send_visrandom = 0; + } +} + +//--------------------------------------------------------- +Options *options; +CustomOptionsMenuItems *optionsmenuitems; +CustomWindowsMenuItems *windowsmenuitems; +SkinTweaks *skintweaks; +Crossfader *crossfader; +AvsCfg *avscfg; +//--------------------------------------------------------- + +Wa2CfgItems::Wa2CfgItems() { + // set up main options + WASABI_API_CONFIG->config_registerCfgItem((options = new Options())); + + // set up custom options menu items + WASABI_API_CONFIG->config_registerCfgItem((optionsmenuitems = new CustomOptionsMenuItems())); + + // set up custom windows menu items + WASABI_API_CONFIG->config_registerCfgItem((windowsmenuitems = new CustomWindowsMenuItems())); + + // set up skintweaks options menu items + WASABI_API_CONFIG->config_registerCfgItem((skintweaks = new SkinTweaks())); + + // set up crossfader options menu item + WASABI_API_CONFIG->config_registerCfgItem((crossfader = new Crossfader())); + + // set up avs cfg item + WASABI_API_CONFIG->config_registerCfgItem((avscfg = new AvsCfg())); +} + +Wa2CfgItems::~Wa2CfgItems() { + WASABI_API_CONFIG->config_deregisterCfgItem(options); + WASABI_API_CONFIG->config_deregisterCfgItem(optionsmenuitems); + WASABI_API_CONFIG->config_deregisterCfgItem(windowsmenuitems); + WASABI_API_CONFIG->config_deregisterCfgItem(skintweaks); + WASABI_API_CONFIG->config_deregisterCfgItem(crossfader); + WASABI_API_CONFIG->config_deregisterCfgItem(avscfg); + delete options; + options = NULL; + delete optionsmenuitems; + optionsmenuitems = NULL; + delete windowsmenuitems; + windowsmenuitems = NULL; + delete skintweaks; + skintweaks = NULL; + delete crossfader; + crossfader = NULL; + delete avscfg; + avscfg = NULL; +} + +//----------------------------------------------------------------------------------------------- +// always on top was toggled in freeform skin +//----------------------------------------------------------------------------------------------- +void setOnTop(int ontop) { + if (!!ontop != !!wa2.isOnTop()) wa2.setOnTop(!!ontop); +} + +//----------------------------------------------------------------------------------------------- +// setontop was toggled in wa2 +//----------------------------------------------------------------------------------------------- +void onWa2OnTopChanged(int ontop) { + cfg_options_alwaysontop.setValueAsInt(!!ontop); +} + +//----------------------------------------------------------------------------------------------- +// Crossfader cfgitem - monitors out_ds +//----------------------------------------------------------------------------------------------- +_int crossfade_time(L"Crossfade time", 2); // in seconds + +HWND output_ipc = NULL; +static WNDPROC oldOutputIPCProc; +int syncing_crossfade = 0; +int using_dsound = 0; +extern HWND last_dlg_parent; + +// sync out_ds with gen_ff +void syncOutputCrossfade() { + if (output_ipc == NULL) return; + SendMessageW(output_ipc, WM_DS_IPC, crossfade_time.getValueAsInt() * 1000, DS_IPC_SET_CROSSFADE_TIME); + if (using_dsound) SendMessageW(output_ipc, WM_DS_IPC, cfg_audiooptions_crossfader.getValueAsInt(), DS_IPC_SET_CROSSFADE); +} + +// sync gen_ff with out_ds, can't happen if out_ds isn't the selected plugin since this occurs when the user changes his config, and he needs to select out_ds to access its config +void syncGenFFCrossfade() { + syncing_crossfade = 1; + if (output_ipc == NULL) return; + crossfade_time.setValueAsInt(SendMessageW(output_ipc, WM_DS_IPC, 0, DS_IPC_GET_CROSSFADE_TIME) / 1000); + cfg_audiooptions_crossfader.setValueAsInt(SendMessageW(output_ipc, WM_DS_IPC, 0, DS_IPC_GET_CROSSFADE)); + syncing_crossfade = 0; +} + +// called when gen_ff changes the crossfade time setting +void onSetCrossfadeTime(int sectime) { + if (syncing_crossfade) return; + syncOutputCrossfade(); +} + +// called when gen_ff toggles crossfading +void setCrossfader(int crossfade) { + if (syncing_crossfade) return; + if (crossfade && (!output_ipc || !using_dsound)) { + cfg_audiooptions_crossfader.setValueAsInt(0); + char title[256] = {0}; + int r = MessageBoxA(last_dlg_parent ? last_dlg_parent : wa2.getMainWindow(), + WASABI_API_LNGSTRING(IDS_CROSSFADER_ONLY_UNDER_OUT_DS), + WASABI_API_LNGSTRING_BUF(IDS_NOT_SUPPORTED,title,256), MB_YESNO); + if (r == IDYES) { + SendMessageW(wa2.getMainWindow(),WM_WA_IPC,32,IPC_OPENPREFSTOPAGE); + } + } + syncOutputCrossfade(); +} + +// ----------------------- ipc hook +extern int m_are_we_loaded; + +static DWORD WINAPI outputIPCProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam) { + if (m_are_we_loaded) { + switch (uMsg) { + case WM_DS_IPC: + switch (lParam) { + case DS_IPC_CB_CFGREFRESH: + syncGenFFCrossfade(); + break; + case DS_IPC_CB_ONSHUTDOWN: + output_ipc = NULL; + break; + } + break; + } + } + return CallWindowProc(oldOutputIPCProc, hwndDlg, uMsg, wParam, lParam); +} + +void hookOutputIPC() { + oldOutputIPCProc = (WNDPROC)SetWindowLongPtrW(output_ipc, GWLP_WNDPROC, (LONG_PTR)outputIPCProc); +} + +// not called in fact, since we never need to let go of it +void unhookOutputIPC() { + SetWindowLongPtrW(output_ipc, GWLP_WNDPROC, (LONG_PTR)oldOutputIPCProc); +} + +BOOL CALLBACK findOutputIPCProc( HWND hwnd, LPARAM lParam ) +{ + char cn[ 256 ] = { 0 }; + GetClassNameA( hwnd, cn, 255 ); cn[ 255 ] = 0; // Must stay in ANSI mode + if ( STRCASEEQL( cn, DS_IPC_CLASSNAME ) ) + { + output_ipc = hwnd; + return FALSE; + } + return TRUE; +} + +// ------------------------- output plugin changed ! + +void Crossfader::onOutputChanged() { + if (!(output_ipc && IsWindow(output_ipc))) { + output_ipc = NULL; + EnumChildWindows(wa2.getMainWindow(), findOutputIPCProc, 0); + + if (output_ipc) { + hookOutputIPC(); + } + } + + const char* outputPluginPath = wa2.getOutputPlugin(); + PathParser pp(outputPluginPath); + using_dsound = STRCASEEQLSAFE(pp.getLastString(), "out_ds.dll"); + if (!using_dsound) { + cfg_audiooptions_crossfader.setValueAsInt(0); + } else + syncGenFFCrossfade(); +} + +// ------------------------------------------------------------------------ + +Crossfader::Crossfader() : CfgItemI(L"Crossfader", crossfaderGuid) { + registerAttribute(&crossfade_time, new int_attrCB(onSetCrossfadeTime)); +} + +Crossfader::~Crossfader() { + unhookOutputIPC(); +} + diff --git a/Src/Plugins/General/gen_ff/wa2cfgitems.h b/Src/Plugins/General/gen_ff/wa2cfgitems.h new file mode 100644 index 00000000..6d2959b2 --- /dev/null +++ b/Src/Plugins/General/gen_ff/wa2cfgitems.h @@ -0,0 +1,137 @@ +#ifndef _WA2CFGITEMS_H +#define _WA2CFGITEMS_H + +#include +#include +#include + +//--------------------------------------------------------- +// playlist editor cfg items +//--------------------------------------------------------- +class Wa2PlEditCfgItems : public CfgItemI { +public: + Wa2PlEditCfgItems(); + virtual ~Wa2PlEditCfgItems(); + + static void onSetShuffle(BOOL set); + static void onSetRepeat(BOOL set); +}; + +//--------------------------------------------------------- +// {1828D28F-78DD-4647-8532-EBA504B8FC04} +static const GUID customOptionsMenuitemGuid = +{ 0x1828d28f, 0x78dd, 0x4647, { 0x85, 0x32, 0xeb, 0xa5, 0x4, 0xb8, 0xfc, 0x4 } }; + +// {0542AFA4-48D9-4c9f-8900-5739D52C114F} +static const GUID skinTweaksGuid = +{ 0x542afa4, 0x48d9, 0x4c9f, { 0x89, 0x0, 0x57, 0x39, 0xd5, 0x2c, 0x11, 0x4f } }; + +// {6559CA61-7EB2-4415-A8A9-A2AEEF762B7F} +static const GUID customWindowsMenuitemGuid = +{ 0x6559ca61, 0x7eb2, 0x4415, { 0xa8, 0xa9, 0xa2, 0xae, 0xef, 0x76, 0x2b, 0x7f } }; + +// {F1239F09-8CC6-4081-8519-C2AE99FCB14C} +static const GUID crossfaderGuid = +{ 0xf1239f09, 0x8cc6, 0x4081, { 0x85, 0x19, 0xc2, 0xae, 0x99, 0xfc, 0xb1, 0x4c } }; + +//--------------------------------------------------------- +class CustomOptionsMenuItems : public CfgItemI { +public: + CustomOptionsMenuItems() : CfgItemI(L"Custom OptionsMenu Items", customOptionsMenuitemGuid) { }; + virtual ~CustomOptionsMenuItems() {} +}; + +//--------------------------------------------------------- +class Crossfader : public CfgItemI { +public: + Crossfader(); + virtual ~Crossfader(); + + static void onOutputChanged(); +}; + +//--------------------------------------------------------- +class AvsCfg : public CfgItemI { +public: + AvsCfg(); + virtual ~AvsCfg() {} + + static void onVisRandomChanged(BOOL set); +}; + +//--------------------------------------------------------- +class CustomWindowsMenuItems : public CfgItemI { +public: + CustomWindowsMenuItems() : CfgItemI(L"Custom WindowsMenu Items", customWindowsMenuitemGuid) { }; + virtual ~CustomWindowsMenuItems() {} +}; + +//--------------------------------------------------------- +class SkinTweaks : public CfgItemI { +public: + SkinTweaks(); + virtual ~SkinTweaks() {} + static void onPreventVideoStopChanged(BOOL set); + static void onPreventVideoResize(BOOL set); + //static void onDisplayVideoWndOnPlay(BOOL set); + //static void onCloseVideoWndOnStop(BOOL set); +}; + +//--------------------------------------------------------- +class Wa2CfgItems { +public: + Wa2CfgItems(); + virtual ~Wa2CfgItems(); + +private: + Wa2PlEditCfgItems pledit; +}; + +extern _bool shuffle; +extern _int repeat; +extern _bool cfg_audiooptions_crossfader; +extern _bool cfg_options_alwaysontop; +extern _bool cfg_uioptions_desktopalpha; +extern _bool cfg_uioptions_linkratio; +extern _bool cfg_uioptions_linkalpha; +extern _bool cfg_uioptions_linkallratio; +extern _bool cfg_uioptions_linkallalpha; +extern _bool cfg_uioptions_tooltips; +extern _float cfg_uioptions_textspeed; +extern _int cfg_uioptions_textincrement; +extern _int cfg_uioptions_appbarshidetime; +extern _int cfg_uioptions_appbarsshowtime; +extern _int cfg_uioptions_timerresolution; +extern _bool cfg_audiooptions_crossfader; +extern _bool cfg_options_altfonts; +extern _bool cfg_options_allowbitmapfonts; +extern _string cfg_options_defaultfont; +extern _string cfg_options_ttfoverridefont; +extern _int cfg_options_ttfoverridescale; +extern _int cfg_options_defaultfontscale; +extern _string cfg_options_fontrenderer; +extern _bool cfg_options_docking; +extern _int cfg_options_dockingdistance; +extern _bool cfg_options_appbarondrag; +extern _int cfg_options_appbardockingdistance; +extern _int cfg_options_freetypecharmap; +extern _bool cfg_options_no7bitsttfoverride; +extern _bool cfg_options_noalt7bitsttfoverride; +extern _bool cfg_uioptions_uselocks; +extern _int cfg_uioptions_autoopacitytime; +extern _int cfg_uioptions_autoopacitylinked; +extern _int cfg_uioptions_linkedalpha; +extern _int cfg_uioptions_autoopacityfadein; +extern _int cfg_uioptions_autoopacityfadeout; +extern _int cfg_uioptions_extendautoopacity; +extern _bool cfg_options_usefontmapper; + +extern CustomOptionsMenuItems *optionsmenuitems; +extern CustomWindowsMenuItems *windowsmenuitems; +extern SkinTweaks *skintweaks; +extern int disable_set_wa2_repeat; +extern StringW eqmenustring; + +extern int my_set; + +#endif \ No newline at end of file diff --git a/Src/Plugins/General/gen_ff/wa2core.cpp b/Src/Plugins/General/gen_ff/wa2core.cpp new file mode 100644 index 00000000..f9526e52 --- /dev/null +++ b/Src/Plugins/General/gen_ff/wa2core.cpp @@ -0,0 +1,930 @@ +#include +#include "wa2core.h" +#include "wa2frontend.h" +#include "../winamp/wa_ipc.h" +#include "wa2pledit.h" +#include "../nu/AutoWide.h" +#include "gen.h" +#include "../nu/ns_wc.h" +#include "../Agave/Language/api_language.h" +#include "../Winamp/in2.h" +#include "resource.h" +#include "../nu/AutoChar.h" +#include + + + +// {72409F84-BAF1-4448-8211-D84A30A1591A} +static const GUID eqConfigGroupGUID = +{ 0x72409f84, 0xbaf1, 0x4448, { 0x82, 0x11, 0xd8, 0x4a, 0x30, 0xa1, 0x59, 0x1a } }; + +// ----------------------------------------------------------------------------------------------------------------- +// Core implementation for wa2 +// ----------------------------------------------------------------------------------------------------------------- + +#define TIMER_POLL 0x8971 + +Core *g_Core = NULL; +api_core *g_core = NULL; +int g_coreref = 0; + +// this is called by the library when api->core_create is called + +api_core *createCustomCoreApi() +{ + g_coreref++; + if (g_core == NULL) { g_Core = new Core(); g_core = g_Core; } + return g_core; +} + +// this is called when api->core_destroy is called + +void destroyCustomCoreApi(api_core *core) +{ + if (--g_coreref == 0) + { + delete g_Core; + g_Core = NULL; + g_core = NULL; + } +} + +Core::Core() +{ + waServiceFactory *sf = WASABI_API_SVC->service_getServiceByGuid(Agave::AgaveConfigGUID); + if (sf) + config = (Agave::api_config *)sf->getInterface(); + + m_lastpeentry = -1; + m_laststatus = -1; + m_lastpos = -1; + m_lastvol = -1; + m_lastpan = -1; + m_lasttitle = L""; + m_lastsamplerate = -1; + m_lastchan = -1; + m_lastbitrate = -1; + for (int i = 0;i < 10;i++) + m_lasteqband[i] = 0xdead; + m_lasteq = -1; + m_lasteqauto = -1; + m_lasteqpreamp = -1; + m_lastfreqband = -1; + // timerclient_setTimer(TIMER_POLL, 250); + gotCallback(IPC_CB_MISC_TITLE, 1); + gotCallback(IPC_CB_MISC_VOLUME, 1); + gotCallback(IPC_CB_MISC_STATUS, 1); + gotCallback(IPC_CB_MISC_EQ, 1); + gotCallback(IPC_CB_MISC_INFO, 1); + gotCallback(IPC_CB_MISC_TITLE_RATING, 1); +} + +Core::~Core() +{ + waServiceFactory *sf = WASABI_API_SVC->service_getServiceByGuid(Agave::AgaveConfigGUID); + if (sf) + sf->releaseInterface(config); + //timerclient_killTimer(TIMER_POLL); +} + +void Core::addCallback(CoreCallback *cb) +{ + if (cb == NULL) return ; + if (callbacks.haveItem(cb)) return ; + callbacks.addItem(cb); + cb->ccb_notify(CoreCallback::REGISTER); +} + +void Core::delCallback(CoreCallback *cb) +{ + if (cb == NULL) return ; + if (callbacks.haveItem(cb)) + { + cb->ccb_notify(CoreCallback::DEREGISTER); + callbacks.removeItem(cb); + } +} + +void Core::sendCoreCallback(int message, int param1, int param2) +{ + ReentryFilter filter(&rf, message); + int l = callbacks.getNumItems(); + for (int i = 0; i < l;i++) + callbacks[i]->ccb_notify(message, param1, param2); +} + +int Core::getStatus() +{ + return m_laststatus; +} + +void Core::gotCallback(int wParam, int forcecb) +{ + if(wParam == IPC_CB_MISC_TITLE_RATING){ + forcecb = 1; + } + switch (wParam) + { + case IPC_CB_MISC_TITLE: + case IPC_CB_MISC_TITLE_RATING: + { + // check for title change + wa2.invalidateCache(); + StringW cur_title=wa2.GetCurrentTitle(); + if (cur_title.isempty()) + cur_title = L""; + + StringW cur_file = wa2.GetCurrentFile(); + + if (!m_lastfile.iscaseequal(cur_file) || forcecb) + { + m_lastfile.swap(cur_file); + sendCoreCallback(CoreCallback::URLCHANGE, (intptr_t)m_lastfile.getValue()); + } + + if (!m_lasttitle.iscaseequal(cur_title) || forcecb) + { + m_lasttitle.swap(cur_title); + //Martin> i dunno why we send a INFOCHANGE Callback here, but if we want to send this + // callback try to deliver the correct data + //sendCoreCallback((forcecb==2?CoreCallback::INFOCHANGE:CoreCallback::TITLECHANGE), + // (int)m_lasttitle.getValue(),(wParam == IPC_CB_MISC_TITLE_RATING)); + + if (forcecb==2) // this has led to INFOCHANGE Callback + { + sendCoreCallback(CoreCallback::INFOCHANGE, (intptr_t)getSongInfoText(), (wParam == IPC_CB_MISC_TITLE_RATING)); + } + sendCoreCallback(CoreCallback::TITLECHANGE, (intptr_t)m_lasttitle.getValue(), (wParam == IPC_CB_MISC_TITLE_RATING)); + } + + int plentry = wa2.getCurPlaylistEntry(); + int curpeentry = plentry + 1; + if (curpeentry != m_lastpeentry) + { + Wa2PlaylistEditor::_onNewCurrentIndex(curpeentry); + m_lastpeentry = curpeentry; + } + } + break; + case IPC_CB_MISC_STATUS: + { + int cur_status; + int resume = 0; + if (wa2.isPaused()) cur_status = STATUS_PAUSE; + else if (wa2.isPlaying()) + { + if (m_laststatus == STATUS_PAUSE) + resume = 1; + else + { + m_lastsamplerate = -1; + m_lastbitrate = -1; + m_lastchan = -1; + } + cur_status = STATUS_PLAY; + } + else cur_status = STATUS_STOP; + + if (m_laststatus != cur_status || forcecb) + { + m_laststatus = cur_status; + switch (cur_status) + { + case STATUS_PLAY: sendCoreCallback(resume ? CoreCallback::UNPAUSED : CoreCallback::STARTED); break; + case STATUS_STOP: + { + m_lastsamplerate = 0; + m_lastchan = 0; + m_lastbitrate = 0; + sendCoreCallback(CoreCallback::STOPPED); + break; + } + case STATUS_PAUSE: sendCoreCallback(CoreCallback::PAUSED); break; + } + } + } + break; + case IPC_CB_MISC_VOLUME: + { + // check for volume change + int cur_vol = wa2.getVolume(); + if (m_lastvol != cur_vol || forcecb) + { + m_lastvol = cur_vol; + sendCoreCallback(CoreCallback::VOLCHANGE, cur_vol); + } + int cur_pan = wa2.getPanning(); + if (m_lastpan != cur_pan || forcecb) + { + m_lastpan = cur_pan; + sendCoreCallback(CoreCallback::PANCHANGE, cur_pan); + } + } + break; + case IPC_CB_MISC_EQ: + { + // check if the band frequencies changed (winamp vs ISO) + int new_freqband = config->GetInt(eqConfigGroupGUID, L"frequencies", 0); + if (m_lastfreqband != new_freqband) + { + bool sendCallback = m_lastfreqband != -1; + + m_lastfreqband = new_freqband; // set before the callback so we don't run into any issues if the callback triggers another EQ change message + if (sendCallback) + sendCoreCallback(CoreCallback::EQFREQCHANGE, new_freqband, 0); + } + + // check for eq change + for (int i = 0;i < 10;i++) + { + int cur_band = wa2.getEqData(WA2_EQDATA_FIRSTBAND + i); + if (cur_band == 63 - ((m_lasteqband[i] + 127) / 4)) cur_band = m_lasteqband[i]; + else cur_band = (63 - cur_band) * 4 - 127; + if (m_lasteqband[i] != cur_band || forcecb) + { + m_lasteqband[i] = cur_band; + sendCoreCallback(CoreCallback::EQBANDCHANGE, i, cur_band); + } + } + int cur_eq = wa2.getEqData(WA2_EQDATA_ENABLED); + if (m_lasteq != cur_eq) + { + m_lasteq = cur_eq; + sendCoreCallback(CoreCallback::EQSTATUSCHANGE, cur_eq); + } + int cur_eqauto = wa2.getEqData(WA2_EQDATA_AUTO); + if (m_lasteqauto != cur_eqauto || forcecb) + { + m_lasteqauto = cur_eqauto; + sendCoreCallback(CoreCallback::EQAUTOCHANGE, cur_eqauto); + } + int cur_eqpreamp = wa2.getEqData(WA2_EQDATA_PREAMP); + if (cur_eqpreamp == 63 - ((cur_eqpreamp + 127) / 4)) { /*cur_eqpreamp = cur_eqpreamp;*/ } + else cur_eqpreamp = (63 - cur_eqpreamp) * 4 - 127; + if (m_lasteqpreamp != cur_eqpreamp || forcecb) + { + m_lasteqpreamp = cur_eqpreamp; + sendCoreCallback(CoreCallback::EQPREAMPCHANGE, cur_eqpreamp); + } + } + break; + + case IPC_CB_MISC_INFO: + { + int realrate = wa2.getSamplerate(); + int brate = wa2.getBitrate(); + int ch = wa2.getChannels(); + int any = 0; + if (realrate != m_lastsamplerate || forcecb) + { + m_lastsamplerate = realrate; + sendCoreCallback(CoreCallback::SAMPLERATECHANGE, realrate); + any = 1; + } + if (brate != m_lastbitrate || forcecb) + { + m_lastbitrate = brate; + sendCoreCallback(CoreCallback::BITRATECHANGE, m_lastbitrate); + any = 1; + } + if (ch != m_lastchan || forcecb) + { + m_lastchan = ch; + sendCoreCallback(CoreCallback::CHANNELSCHANGE, m_lastchan); + any = 1; + } + //DebugString("Got IPC_CB_MISC_INFO callback, numchans = %d, samplerate = %d, bitrate = %d\n", m_lastchan, m_lastsamplerate, m_lastbitrate); + if (any) + { + StringW txt = getSongInfoText(); + sendCoreCallback(CoreCallback::INFOCHANGE, (intptr_t)txt.getValue()); + } + break; + } + } +} + +StringW infotext; + +const wchar_t *Core::getSongInfoText() +{ + infotext = L""; + int srate = wa2.getSamplerate(); + int brate = wa2.getBitrate(); + int ch = wa2.getChannels(); + + if (srate == 0 && brate == 0) + { + return L""; + } + + infotext = StringPrintfW(L"%dkbps", brate); + + if (ch == 1) + { + infotext += L" mono"; + } + else if (ch == 2) + { + infotext += L" stereo"; + } + else if (ch > 2) + { + infotext += StringPrintfW(L" %d Channels", ch); + } + + infotext += StringPrintfW(L" %.1fkHz", (float)srate/1000.0f); + + if (wa2.isPlayingVideo()) + { + infotext.prepend(L"Video "); + } + return infotext; +} + +StringW infotextTranslated; + +const wchar_t *Core::getSongInfoTextTranslated() +{ + infotextTranslated = L""; + int srate = wa2.getSamplerate(); + int brate = wa2.getBitrate(); + int ch = wa2.getChannels(); + + if (srate == 0 && brate == 0) + { + return L""; + } + + infotextTranslated = StringPrintfW(L"%d%s", brate,WASABI_API_LNGSTRINGW(IDS_KBPS)); + + if (ch == 1) + { + infotextTranslated += WASABI_API_LNGSTRINGW(IDS_MONO); + } + else if (ch == 2) + { + infotextTranslated += WASABI_API_LNGSTRINGW(IDS_STEREO); + } + else if (ch > 2) + { + infotextTranslated += StringPrintfW(WASABI_API_LNGSTRINGW(IDS_X_CHANNELS), ch); + } + + infotextTranslated += StringPrintfW(L" %.1f", (float)srate/1000.0f); + infotextTranslated += WASABI_API_LNGSTRINGW(IDS_KHZ);// (doing this in the StringPrintfW(..) above causes a crash even in a seperate buffer) + + if (wa2.isPlayingVideo()) + { + infotextTranslated.prepend(StringPrintfW(L"%s ",WASABI_API_LNGSTRINGW(IDS_VIDEO))); + } + + return infotextTranslated; +} + +void Core::userButton(int button) +{ + int mod = Std::keyDown(VK_SHIFT) ? WA2_USERBUTTONMOD_SHIFT : (Std::keyDown(VK_SHIFT) ? WA2_USERBUTTONMOD_CTRL : WA2_USERBUTTONMOD_NONE); + switch (button) + { + case UserButton::PLAY: wa2.userButton(WA2_USERBUTTON_PLAY, mod); break; + case UserButton::STOP: wa2.userButton(WA2_USERBUTTON_STOP, mod); break; + case UserButton::PAUSE: wa2.userButton(WA2_USERBUTTON_PAUSE, mod); break; + case UserButton::NEXT: wa2.userButton(WA2_USERBUTTON_NEXT, mod); break; + case UserButton::PREV: wa2.userButton(WA2_USERBUTTON_PREV, mod); break; + } +} + +void Core::setVolume(int vol) +{ + wa2.setVolume(vol); +} + +int Core::getVolume() +{ + return wa2.getVolume(); +} + +void Core::setPosition(int ms) +{ + wa2.seekTo(ms); + sendCoreCallback(CoreCallback::SEEKED, ms); +} + +int Core::getPosition() +{ + if (m_laststatus == STATUS_STOP) return -1; + return wa2.getPosition(); +} + +int Core::getLength() +{ + if (m_laststatus == STATUS_STOP) return -1; + return wa2.getLength(); +} + +void Core::setPanning(int p) +{ + if (p > 127) p = 127; + wa2.setPanning(p); +} + +int Core::getPanning() +{ + return wa2.getPanning(); +} + +void Core::setShuffle(int shuffle) +{ + wa2.setShuffle(shuffle); +} + +int Core::getShuffle() +{ + return wa2.getShuffle(); +} + +void Core::setRepeat(int repeat) +{ + static int myset = 0; + if (!myset) + { + myset = 1; + int manadv = 0; + int rep = 0; + if (repeat == 0) + { + rep = 0; + manadv = 0; + } + else if (repeat > 0) + { + rep = 1; + manadv = 0; + } + else if (repeat < 0) + { + rep = 1; + manadv = 1; + } + if (!!wa2.getRepeat() != !!rep) wa2.setRepeat(rep); + if (!!wa2.getManualPlaylistAdvance() != !!manadv) wa2.setManualPlaylistAdvance(manadv); + myset = 0; + } +} + +int Core::getRepeat() +{ + int manadv = wa2.getManualPlaylistAdvance(); + int rep = wa2.getRepeat(); + int _v = (rep && manadv) ? -1 : rep; + return _v; +} + +int Core::getSamplerate(int wa2_getinfo) +{ + return wa2.getInfo(WA2_GETINFO_SAMPLERATE); +} + +int Core::getBitrate(int wa2_getinfo) +{ + return wa2.getInfo(WA2_GETINFO_BITRATE); +} + +int Core::getChannels(int wa2_getinfo) +{ + return wa2.getInfo(WA2_GETINFO_CHANNELS); +} + +int Core::getEqBand(int band) +{ + // int v = 63-wa2.getEqData(WA2_EQDATA_FIRSTBAND+band) * 4 - 127; + // v = MIN(-127, v); + // v = MAX(127, v); + return m_lasteqband[band]; //(v); +} + +void Core::setEqBand(int band, int val) +{ + val = MIN(127, MAX( -127, val)); + m_lasteqband[band] = val; + int v = 63 - ((val + 127) / 4); + v = MIN(63, v); + v = MAX(0, v); + wa2.setEqData(WA2_EQDATA_FIRSTBAND + band, v); + sendCoreCallback(CoreCallback::EQBANDCHANGE, band, val); +} + +int Core::getEQStatus() +{ + return wa2.getEqData(WA2_EQDATA_ENABLED); +} + +void Core::setEQStatus(int enable) +{ + wa2.setEqData(WA2_EQDATA_ENABLED, enable); +} + +int Core::getEQAuto() +{ + return wa2.getEqData(WA2_EQDATA_AUTO); +} + +void Core::setEQAuto(int enable) +{ + wa2.setEqData(WA2_EQDATA_AUTO, enable); +} + +int Core::getEQPreamp() +{ + // int v = 63-wa2.getEqData(WA2_EQDATA_PREAMP) * 4 - 127; + // v = MIN(-127, v); + // v = MAX(127, v); + return m_lasteqpreamp; +} + +void Core::setEQPreamp(int val) +{ + val = MIN(127, MAX( -127, val)); + m_lasteqpreamp = val; + int v = 63 - ((val + 127) / 4); + v = MIN(63, v); + v = MAX(0, v); + wa2.setEqData(WA2_EQDATA_PREAMP, v); + sendCoreCallback(CoreCallback::EQPREAMPCHANGE, val); +} + +const wchar_t *Core::getTitle() +{ + return m_lasttitle; // faster to use our cached ver + // int plentry = wa2.getCurPlaylistEntry(); + // if (plentry == -1) return NULL; + // return wa2.getTitle(plentry); +} + +void Core::setTitle(const wchar_t * new_title) +{ + StringW title = new_title; + if(title.isempty()) + title = L""; + m_lasttitle = title; +} + +const wchar_t *Core::getPlaystring() +{ + m_playstring = wa2.GetCurrentFile(); + if (m_playstring.getValue() == NULL) m_playstring = L""; + return m_playstring; +} + +int Core::getCurPlaylistEntry() +{ + return wa2.getCurPlaylistEntry(); +} + +// ----------------------------------------------------------------------------------------------------------------- +// api_core implementation +// ----------------------------------------------------------------------------------------------------------------- + +// this pointer is set automagically by ApiInit +api_core *coreApi = NULL; + +const wchar_t *Core::core_getSupportedExtensions() +{ + return L"mp3\x0"; +} + +const wchar_t *Core::core_getExtSupportedExtensions() +{ + return L"mp3\x0"; +} + +CoreToken Core::core_create() +{ + return 0; +} + +int Core::core_free(CoreToken core) +{ + return 0; +} + +int Core::core_setNextFile(CoreToken core, const wchar_t *playstring) +{ + return 0; +} + +int Core::core_getStatus(CoreToken core) +{ + switch (getStatus()) + { + case STATUS_STOP : return 0; + case STATUS_PLAY : return 1; + case STATUS_PAUSE : return -1; + } + return 0; // dunno, stopped i guess... +} + +const wchar_t *Core::core_getCurrent(CoreToken core) +{ + return getPlaystring(); +} + +int Core::core_getCurPlaybackNumber(CoreToken core) +{ + return getCurPlaylistEntry(); +} + +int Core::core_getPosition(CoreToken core) +{ + return getPosition(); +} + +int Core::core_getWritePosition(CoreToken core) +{ + return getPosition(); +} + +int Core::core_setPosition(CoreToken core, int ms) +{ + setPosition(ms); + return 1; +} + +int Core::core_getLength(CoreToken core) +{ + return getLength(); +} + +int Core::core_getPluginData(const wchar_t *playstring, const wchar_t *name, wchar_t *data, int data_len, int data_type) +{ + return 0; +} + +unsigned int Core::core_getVolume(CoreToken core) +{ + return getVolume(); +} + +void Core::core_setVolume(CoreToken core, unsigned int vol) +{ + setVolume(vol); +} + +int Core::core_getPan(CoreToken core) +{ + return getPanning(); +} + +void Core::core_setPan(CoreToken core, int val) +{ + setPanning(val); +} + +void Core::core_addCallback(CoreToken core, CoreCallback *cb) +{ + addCallback(cb); +} + +void Core::core_delCallback(CoreToken core, CoreCallback *cb) +{ + delCallback(cb); +} + +int Core::core_getVisData(CoreToken core, void *dataptr, int sizedataptr) +{ + int ret = 75*2; + // added this to attempt to cope with some poor / old input plug-ins + // which keep cropping up in the plug-in crash reports from users... + try + { + // todo + if (wa2.export_sa_get) + { + if (sizedataptr >= (75*2 + 8)) + wa2.export_sa_get((char *)dataptr); + else + { + char data[75*2 + 8]; + char *p = wa2.export_sa_get(data); + if (p) memcpy(dataptr, p, min(2*75, sizedataptr)); + } + } + else if (wa2.export_sa_get_deprecated) + { + char *p = wa2.export_sa_get_deprecated(); + if (p) memcpy(dataptr, p, min(2*75, sizedataptr)); + } + } + catch(...) + { + ret = 0; + } + return ret; +} + +int Core::core_getLeftVuMeter(CoreToken core) +{ + if (wa2.export_vu_get) + { + int vu = wa2.export_vu_get(0); + if (vu != -1) + return vu; + } + if (wa2.export_sa_get) + { + char data[75*2 + 8] = {0}; + char *p = (char *)wa2.export_sa_get(data); + if (!p) return 0; + + int m = 0; + for (int i = 75;i < 150;i++) // this is very gay but workish + m = max(abs(m), p[i]); + + return MIN(255, m*16); + } + else if (wa2.export_sa_get_deprecated) + { + char *p = (char *)wa2.export_sa_get_deprecated(); + if (!p) return 0; + + int m = 0; + for (int i = 75;i < 150;i++) // this is very gay but workish + m = max(abs(m), p[i]); + + return MIN(255, m*16); + } + else return 0; + +} + +int Core::core_getRightVuMeter(CoreToken core) +{ + if (wa2.export_vu_get) + { + int vu = wa2.export_vu_get(1); + if (vu == -1) + vu = wa2.export_vu_get(0); + if (vu != -1) + return vu; + } + + return core_getLeftVuMeter(core); +} + +int Core::core_registerSequencer(CoreToken core, ItemSequencer *seq) +{ + return 0; +} + +int Core::core_deregisterSequencer(CoreToken core, ItemSequencer *seq) +{ + return 0; +} + +void Core::core_userButton(CoreToken core, int button) +{ + userButton(button); +} + +int Core::core_getEqStatus(CoreToken core) +{ + return getEQStatus(); +} + +void Core::core_setEqStatus(CoreToken core, int enable) +{ + setEQStatus(enable); +} + +int Core::core_getEqPreamp(CoreToken core) +{ + return getEQPreamp(); +} + +void Core::core_setEqPreamp(CoreToken core, int pre) +{ + setEQPreamp(pre); +} + +int Core::core_getEqBand(CoreToken core, int band) +{ + return getEqBand(band); +} + +void Core::core_setEqBand(CoreToken core, int band, int val) +{ + setEqBand(band, val); +} + +int Core::core_getEqAuto(CoreToken core) +{ + return getEQAuto(); +} + +void Core::core_setEqAuto(CoreToken core, int enable) +{ + setEQAuto(enable); +} + +void Core::core_setCustomMsg(CoreToken core, const wchar_t *text) +{} + +void Core::core_registerExtension(const wchar_t *extensions, const wchar_t *extension_name, const wchar_t *family) +{} + + +const wchar_t *Core::core_getDecoderName(const wchar_t *filename) +{ + wchar_t fn[MAX_PATH+3] = {0}; + WCSNPRINTF(fn, MAX_PATH, L"hi.%s", filename); + In_Module *player = (In_Module *)wa2.CanPlay(fn); + if (player) + { + // cope nicely with the 5.64+ input plug-ins with unicode descriptions + static wchar_t decoder_name[512]; + int ver = ((player->version & ~IN_UNICODE) & ~IN_INIT_RET); + if (ver == IN_VER) + { + StringCchCopyW(decoder_name, 512, (wchar_t *)player->description); + } + else + { + MultiByteToWideCharSZ(CP_ACP, 0, player->description, -1, decoder_name, 512); + } + return decoder_name; + } + return NULL; +} + +const wchar_t *Core::core_getExtensionFamily(const wchar_t *extension) +{ + wchar_t fn[MAX_PATH] = {0}; + WCSNPRINTF(fn, MAX_PATH, L"hi.%s", extension); + In_Module *player = (In_Module *)wa2.CanPlay(fn); + if (player) + { + /* + char *p = player->FileExtensions; + const wchar_t *realExt = PathFindExtensionW(fn); + if (realExt && *realExt) + realExt++; + AutoChar ext(realExt); + while (p && *p) + { + char *b = p; + char *c; + do + { + char d[20] = {0}; + lstrcpyn(d, b, 15); + if ((c = strstr(b, ";"))) + { + if ((c-b)<15) + d[c - b] = 0; + } + + if (!_stricmp(ext, d)) + { + p += lstrlen(p) + 1; // skip to the name + MultiByteToWideCharSZ(CP_ACP, 0, p, -1, family, 512); + return family; + } + b = c + 1; + } + while (c); + p += lstrlen(p) + 1; + if (!*p) break; + p += lstrlen(p) + 1; + } + +*/ + static wchar_t family[512]; + MultiByteToWideCharSZ(CP_ACP, 0, player->description, -1, family, 512); + return family; + } + return NULL; +} + +void Core::core_unregisterExtension(const wchar_t *extensions) +{} + +const wchar_t *Core::core_getTitle(CoreToken core) +{ + return getTitle(); +} + +void Core::core_setTitle(const wchar_t *new_title) +{ + setTitle(new_title); +} + +int Core::core_getRating() +{ + return wa2.getCurTrackRating(); +} + +void Core::core_setRating(int newRating) +{ + wa2.setCurTrackRating(newRating); +} diff --git a/Src/Plugins/General/gen_ff/wa2core.h b/Src/Plugins/General/gen_ff/wa2core.h new file mode 100644 index 00000000..36fce556 --- /dev/null +++ b/Src/Plugins/General/gen_ff/wa2core.h @@ -0,0 +1,147 @@ +#ifndef __GENFF_CORE_H +#define __GENFF_CORE_H + +#include +//#include "../studio/bfc/timerclient.h" +#include +#include +#include +namespace Agave +{ + #include "../Agave/Config/api_config.h" +} + +#define STATUS_UNKNOWN -1 +#define STATUS_STOP 0 +#define STATUS_PLAY 1 +#define STATUS_PAUSE 2 + +class Core : public api_coreI//, public CoreCallbackI +{ //, public TimerClientDI { +public: + Core(); + virtual ~Core(); + + void gotCallback(int wParam, int forcecb = 0); + + void addCallback(CoreCallback *cb); + void delCallback(CoreCallback *cb); + + // virtual void timerclient_timerCallback(int id); + + int getStatus(); + void userButton(int button); + + void setVolume(int vol); + int getVolume(); + + void setPosition(int ms); + int getPosition(); + int getLength(); + + void setPanning(int p); + int getPanning(); + + void setShuffle(int shuffle); + int getShuffle(); + + void setRepeat(int repeat); + int getRepeat(); + + int getSamplerate(int wa2_getinfo); + int getBitrate(int wa2_getinfo); + int getChannels(int wa2_getinfo); + + int getEqBand(int band); + void setEqBand(int band, int val); + + int getEQStatus(); + void setEQStatus(int enable); + + int getEQAuto(); + void setEQAuto(int enable); + + int getEQPreamp(); + void setEQPreamp(int enable); + + const wchar_t *getTitle(); + void setTitle(const wchar_t * new_title); + const wchar_t *getPlaystring(); + + int getCurPlaylistEntry(); + + static const wchar_t *getSongInfoText(); + static const wchar_t *getSongInfoTextTranslated(); + + // api_core ------------------------------------------------------------------------ + + + virtual const wchar_t *core_getSupportedExtensions(); + virtual const wchar_t *core_getExtSupportedExtensions(); + virtual CoreToken core_create(); + virtual int core_free(CoreToken core); + virtual int core_setNextFile(CoreToken core, const wchar_t *playstring); + virtual int core_getStatus(CoreToken core); + virtual const wchar_t *core_getCurrent(CoreToken core); + virtual int core_getCurPlaybackNumber(CoreToken core); + virtual int core_getPosition(CoreToken core); + virtual int core_getWritePosition(CoreToken core); + virtual int core_setPosition(CoreToken core, int ms); + virtual int core_getLength(CoreToken core); + virtual int core_getPluginData(const wchar_t *playstring, const wchar_t *name, wchar_t *data, int data_len, int data_type = 0); + virtual unsigned int core_getVolume(CoreToken core); + virtual void core_setVolume(CoreToken core, unsigned int vol); + virtual int core_getPan(CoreToken core); + virtual void core_setPan(CoreToken core, int val); + virtual void core_addCallback(CoreToken core, CoreCallback *cb); + virtual void core_delCallback(CoreToken core, CoreCallback *cb); + virtual int core_getVisData(CoreToken core, void *dataptr, int sizedataptr); + virtual int core_getLeftVuMeter(CoreToken core); + virtual int core_getRightVuMeter(CoreToken core); + virtual int core_registerSequencer(CoreToken core, ItemSequencer *seq); + virtual int core_deregisterSequencer(CoreToken core, ItemSequencer *seq); + virtual void core_userButton(CoreToken core, int button); + virtual int core_getEqStatus(CoreToken core); + virtual void core_setEqStatus(CoreToken core, int enable); + virtual int core_getEqPreamp(CoreToken core); + virtual void core_setEqPreamp(CoreToken core, int pre); + virtual int core_getEqBand(CoreToken core, int band); + virtual void core_setEqBand(CoreToken core, int band, int val); + virtual int core_getEqAuto(CoreToken core); + virtual void core_setEqAuto(CoreToken core, int enable); + virtual void core_setCustomMsg(CoreToken core, const wchar_t *text); + virtual void core_registerExtension(const wchar_t *extensions, const wchar_t *extension_name, const wchar_t *family = NULL); + virtual const wchar_t *core_getExtensionFamily(const wchar_t *extension); + virtual void core_unregisterExtension(const wchar_t *extensions); + virtual const wchar_t *core_getTitle(CoreToken core); + virtual void core_setTitle(const wchar_t *new_title); + const wchar_t *core_getDecoderName(const wchar_t *Filename); + virtual int core_getRating(); + virtual void core_setRating(int newRating); + +private: + void sendCoreCallback(int message, int param1 = 0, int param2 = 0); + StringW m_lasttitle; + StringW m_playstring, m_lastfile; + int m_laststatus; + int m_lastpos; + int m_lastvol; + int m_lastpan; + int m_lasteqband[10]; + int m_lastfreqband; + int m_lasteq; + int m_lasteqauto; + int m_lasteqpreamp; + int m_lastchan; + int m_lastbitrate; + int m_lastsamplerate; + int m_lastpeentry; + + PtrList callbacks; + ReentryFilterObject rf; + Agave::api_config *config; +}; + +extern Core *g_Core; + +#endif diff --git a/Src/Plugins/General/gen_ff/wa2coreactions.cpp b/Src/Plugins/General/gen_ff/wa2coreactions.cpp new file mode 100644 index 00000000..218342b2 --- /dev/null +++ b/Src/Plugins/General/gen_ff/wa2coreactions.cpp @@ -0,0 +1,279 @@ +#include +#include "wa2frontend.h" +#include "wa2core.h" +#include "wa2coreactions.h" +#include "main.h" +#include +#include + +//----------------------------------------------------------------------------------------------- +CoreActions::CoreActions() +{ + registerAction( L"prev", ACTION_PREV ); + registerAction( L"play", ACTION_PLAY ); + registerAction( L"pause", ACTION_PAUSE ); + registerAction( L"stop", ACTION_STOP ); + registerAction( L"next", ACTION_NEXT ); + registerAction( L"eject", ACTION_EJECT ); + registerAction( L"eject_url", ACTION_EJECTURL ); + registerAction( L"eject_dir", ACTION_EJECTDIR ); + registerAction( L"seek", ACTION_SEEK ); + registerAction( L"volume", ACTION_VOLUME ); + registerAction( L"pan", ACTION_PAN ); + registerAction( L"volume_up", ACTION_VOLUME_UP ); + registerAction( L"volume_down", ACTION_VOLUME_DOWN ); + registerAction( L"rewind_5s", ACTION_REWIND_5S ); + registerAction( L"ffwd_5s", ACTION_FFWD_5S ); + registerAction( L"toggle_repeat", ACTION_TOGGLE_REPEAT ); + registerAction( L"toggle_shuffle", ACTION_TOGGLE_SHUFFLE ); + registerAction( L"toggle_crossfader", ACTION_TOGGLE_CROSSFADER ); + registerAction( L"mute", ACTION_MUTE ); + registerAction( L"eq_toggle", ACTION_EQ_TOGGLE ); + registerAction( L"eq_preamp", ACTION_EQ_PREAMP ); + registerAction( L"eq_band", ACTION_EQ_BAND ); + registerAction( L"eq_auto", ACTION_EQ_AUTO ); + registerAction( L"eq_reset", ACTION_EQ_RESET ); + // these were duped? + // yes, for wa3alpha skins compatibility + registerAction( L"toggle_repeat", ACTION_TOGGLE_REPEAT ); + registerAction( L"toggle_shuffle", ACTION_TOGGLE_SHUFFLE ); + registerAction( L"toggle_crossfader", ACTION_TOGGLE_CROSSFADER ); + for ( int i = 0; i < 4; i++ ) + registerAction( StringPrintfW( L"play_cd%d", i + 1 ), ACTION_PLAY_CD + i ); + // Martin> Moved from menuaction.cpp + registerAction( L"WA5:Prefs", ACTION_PREFS ); +} + +//----------------------------------------------------------------------------------------------- +CoreActions::~CoreActions() +{} + + +//----------------------------------------------------------------------------------------------- +int CoreActions::onActionId(int pvtid, const wchar_t *action, const wchar_t *param /* =NULL */, int p1 /* =0 */, int p2 /* =0 */, void *data /* =NULL */, int datalen /* =0 */, ifc_window *source /* =NULL */) +{ + if (g_Core == NULL) return 0; + int a = WTOI(param); + switch (pvtid) + { + case ACTION_PREV: + if (Std::keyDown(VK_SHIFT)) + { + wa2.rewind5s(); + } + else if (Std::keyDown(VK_CONTROL)) + { + wa2.startOfPlaylist(); + } + else + { + if (a) + wa2.previousPopupMenu(); + else + g_Core->userButton(UserButton::PREV); + } + break; + case ACTION_PLAY: + if (Std::keyDown(VK_SHIFT)) + { + wa2.openFileDialog(source ? source->gethWnd() : WASABI_API_WND->main_getRootWnd()->gethWnd()); + } + else if (Std::keyDown(VK_CONTROL)) + { + wa2.openUrlDialog(source ? source->gethWnd() : WASABI_API_WND->main_getRootWnd()->gethWnd()); + } + else + { + if (a) + wa2.playPopupMenu(); + else + g_Core->userButton(UserButton::PLAY); + } + break; + case ACTION_PAUSE: + if (a) + wa2.pausePopupMenu(); + else + g_Core->userButton(UserButton::PAUSE); + break; + case ACTION_STOP: + if (Std::keyDown(VK_SHIFT)) + { + wa2.stopWithFade(); + } + else if (Std::keyDown(VK_CONTROL)) + { + wa2.stopAfterCurrent(); + } + else + { + if (a) + wa2.stopPopupMenu(); + else + g_Core->userButton(UserButton::STOP); + } + break; + case ACTION_NEXT: + if (Std::keyDown(VK_SHIFT)) + { + wa2.forward5s(); + } + else if (Std::keyDown(VK_CONTROL)) + { + wa2.endOfPlaylist(); + } + else + { + if (a) + wa2.nextPopupMenu(); + else if (a == 0) + g_Core->userButton(UserButton::NEXT); + } + break; + case ACTION_EJECT: + if (Std::keyDown(VK_SHIFT)) + { + wa2.openDirectoryDialog(source ? source->gethWnd() : WASABI_API_WND->main_getRootWnd()->gethWnd()); + } + else if (Std::keyDown(VK_CONTROL)) + { + wa2.openUrlDialog(source ? source->gethWnd() : WASABI_API_WND->main_getRootWnd()->gethWnd()); + } + else + { + if (a) + wa2.ejectPopupMenu(); + else + wa2.openFileDialog(source ? source->gethWnd() : wa2.getMainWindow()); + } + break; + case ACTION_EJECTURL: + if (a == 0) + { + wa2.openUrlDialog(source ? source->gethWnd() : wa2.getMainWindow()); break; + } + else return 0; + case ACTION_EJECTDIR: + if (a == 0) + { + wa2.openDirectoryDialog(source ? source->gethWnd() : wa2.getMainWindow()); break; + } + else return 0; + case ACTION_VOLUME_UP: + if (a == 0) + { + g_Core->setVolume(MIN(g_Core->getVolume() + 5, 255)); break; + } + else return 0; + case ACTION_VOLUME_DOWN: + if (a == 0) + { + g_Core->setVolume(MIN(g_Core->getVolume() - 5, 255)); break; + } + else return 0; + case ACTION_REWIND_5S: + if (a == 0) + { + g_Core->setPosition(MAX(g_Core->getPosition() - 5000, 0)); break; + } + else return 0; + case ACTION_FFWD_5S: + if (a == 0) + { + g_Core->setPosition(MIN(g_Core->getPosition() + 5000, g_Core->getLength())); break; + } + else return 0; + case ACTION_EQ_AUTO: + if (a == 0) + { + g_Core->setEQAuto(!g_Core->getEQAuto()); break; + } + else return 0; + case ACTION_EQ_TOGGLE: + if (a == 0) + { + g_Core->setEQStatus(!g_Core->getEQStatus()); break; + } + else return 0; + case ACTION_EQ_RESET: + if (a == 0) + { + { + for (int i = 0;i < 10;i++) g_Core->setEqBand(i, 0); + } + break; + } + else return 0; + //case ACTION_MUTE: g_Core->setMute(!g_Core->getMute()); break; + case ACTION_TOGGLE_REPEAT: + if (a == 0) + { + wa2.setRepeat(!wa2.getRepeat()); + } + else return 0; + break; + case ACTION_TOGGLE_SHUFFLE: + if (a == 0) + { + wa2.setShuffle(!wa2.getShuffle()); + } + else + return 0; + break; + case ACTION_TOGGLE_CROSSFADER: + break; // todo + // Martin> Moved from menuactions.cpp + case ACTION_PREFS: + { + // check if param is set, otherwise we will get a crash + if (param != NULL) + { + int prefsPage = _wtoi(param); + SendMessageW(wa2.getMainWindow(),WM_WA_IPC,prefsPage,IPC_OPENPREFSTOPAGE); + } + break; + } + } + if (pvtid >= ACTION_PLAY_CD && pvtid < ACTION_PLAY_CD + 16) + { + if (a == 0) + { + wa2.playAudioCD(pvtid - ACTION_PLAY_CD); + } + else return 0; + } + return 1; +} + +const wchar_t *CoreActions::getHelp(int action) +{ + static StringW name; + switch (action) + { + // TODO: benski> move to win32 resources and let them get translated via gen_ff.lng + case ACTION_PREV: name = _(L"Previous track"); break; + case ACTION_PAUSE: name = _(L"Pause/Resume playback"); break; + case ACTION_STOP: name = _(L"Stop playback"); break; + case ACTION_NEXT: name = _(L"Next track"); break; + case ACTION_EJECT: name = _(L"Load file"); break; + case ACTION_EJECTURL: name = _(L"Load URL"); break; + case ACTION_EJECTDIR: name = _(L"Load directory"); break; + case ACTION_SEEK: name = _(L"Seek"); break; + case ACTION_VOLUME: name = _(L"Volume"); break; + case ACTION_PAN: name = _(L"Panning"); break; + case ACTION_EQ_TOGGLE: name = _(L"Toggle equalizer"); break; + case ACTION_EQ_PREAMP: name = _(L"Toggle preamplifier"); break; + case ACTION_EQ_BAND: name = _(L"Change equalizer band"); break; + case ACTION_EQ_AUTO: name = _(L"Auto equalizer"); break; + case ACTION_EQ_RESET: name = _(L"Reset equalizer"); break; + case ACTION_VOLUME_UP: name = _(L"Volume up"); break; + case ACTION_VOLUME_DOWN: name = _(L"Volume down"); break; + case ACTION_REWIND_5S: name = _(L"Rewind 5 seconds"); break; + case ACTION_FFWD_5S: name = _(L"Fast forward 5 seconds"); break; + case ACTION_MUTE: name = _(L"Mute/Unmute sound"); break; + case ACTION_TOGGLE_REPEAT: name = _(L"Toggle repeat"); break; + case ACTION_TOGGLE_SHUFFLE: name = _(L"Toggle shuffle"); break; + case ACTION_TOGGLE_CROSSFADER: name = _(L"Toggle crossfader"); break; + } + return name; +} diff --git a/Src/Plugins/General/gen_ff/wa2coreactions.h b/Src/Plugins/General/gen_ff/wa2coreactions.h new file mode 100644 index 00000000..eb405c48 --- /dev/null +++ b/Src/Plugins/General/gen_ff/wa2coreactions.h @@ -0,0 +1,6 @@ +#ifndef _WA2COREACTIONS_H +#define _WA2COREACTIONS_H + +#include + +#endif diff --git a/Src/Plugins/General/gen_ff/wa2frontend.cpp b/Src/Plugins/General/gen_ff/wa2frontend.cpp new file mode 100644 index 00000000..d8ec944d --- /dev/null +++ b/Src/Plugins/General/gen_ff/wa2frontend.cpp @@ -0,0 +1,1344 @@ +#include +/*------------------------------------------------------------------------------------------------ + Winamp 2.9/5 frontend class +------------------------------------------------------------------------------------------------*/ +#include +#include "main.h" +#include "wa2frontend.h" +#include "../winamp/wa_ipc.h" +#include "../winamp/ipc_pe.h" +#include "../gen_ml/ml.h" +#include "../gen_ml/main.h" +#include "../gen_hotkeys/wa_hotkeys.h" +#include "../nu/AutoChar.h" +#include "resource.h" +#include "../Agave/Language/api_language.h" + +#define WINAMP_EDIT_ID3 40188 + + +Winamp2FrontEnd wa2; + +void Winamp2FrontEnd::init(HWND hwndParent) +{ + // find Winamp's HWND so we can talk to it + hwnd_winamp = hwndParent; //FindWindow("Winamp v1.x",NULL); + + // check that hwnd_winamp isnt null and that we know about this specific ipc version + getVersion(); + + // more init + enabled = 1; + visible = 1; + + *(void **)&export_sa_get=(void*)SendMessageW(hwnd_winamp,WM_WA_IPC,2,IPC_GETSADATAFUNC); + *(void **)&export_sa_setreq=(void *)SendMessageW(hwnd_winamp,WM_WA_IPC,1,IPC_GETSADATAFUNC); + *(void **)&export_sa_get_deprecated=(void*)SendMessageW(hwnd_winamp,WM_WA_IPC,0,IPC_GETSADATAFUNC); + *(void **)&export_vu_get=(void*)SendMessageW(hwnd_winamp,WM_WA_IPC,0,IPC_GETVUDATAFUNC); + if (reinterpret_cast(export_vu_get) == -1) + export_vu_get=0; + + hwnd_playlist = getWnd(IPC_GETWND_PE); +} + +//----------------------------------------------------------------------------------------------- +Winamp2FrontEnd::Winamp2FrontEnd() +{ + m_version = (char *)malloc(64); + *m_version = 0; + enabled = visible = foundvis = + saved_video = +#ifdef MINIBROWSER_SUPPORT + saved_mb = +#endif + saved_pe = saved_eq = saved_main = 0; + + got_length_cache = 0; + cached_length = -1; + cached_length_time = 0; + got_pos_cache = 0; + cached_pos = -1; + cached_pos_time = 0; + + video_ideal_height = -1; + video_ideal_width = -1; + + hwnd_winamp = hwnd_playlist = NULL; + + export_sa_get = 0; + export_sa_setreq = 0; + export_sa_get_deprecated = 0; + export_vu_get = 0; +} + +//----------------------------------------------------------------------------------------------- +Winamp2FrontEnd::~Winamp2FrontEnd() +{ + setWindowsVisible(1); + free(m_version); +} + +//----------------------------------------------------------------------------------------------- +const char *Winamp2FrontEnd::getVersion() +{ + if (hwnd_winamp == NULL) + { + char err[16] = {0}; + MessageBoxA(NULL, WASABI_API_LNGSTRING(IDS_COULD_NOT_FIND_WINAMP), + WASABI_API_LNGSTRING_BUF(IDS_ERROR,err,16), 0); + m_version = "Winamp not found"; + return m_version; + } + + if (hwnd_winamp == NULL) + return NULL; + + if (*m_version == 0) + { + // get version number + int version = SendMessageW(hwnd_winamp,WM_WA_IPC,0,IPC_GETVERSION); + // check that we support this version of the ipc + //assert(((version & 0xFF) >> 8) > 0x20); + // format the version number into a string + wsprintfA(m_version, "%d.%d%d", (version & 0xF000) >> 12, version & (0xF0 >> 4), version & 0xF); + } + return m_version; +} + +//----------------------------------------------------------------------------------------------- +void Winamp2FrontEnd::userButton(int button, int modifier) +{ + if (!IsWindow(hwnd_winamp)) return; + int mod = 0; + switch (modifier) + { + case WA2_USERBUTTONMOD_NONE : break; + case WA2_USERBUTTONMOD_SHIFT: mod = 100; break; + case WA2_USERBUTTONMOD_CTRL : mod = 110; break; + } + switch (button) + { + case WA2_USERBUTTON_PREV: SendMessageW(hwnd_winamp, WM_COMMAND, WINAMP_BUTTON1 + mod,0); break; + case WA2_USERBUTTON_PLAY: SendMessageW(hwnd_winamp, WM_COMMAND, WINAMP_BUTTON2 + mod,0); break; + case WA2_USERBUTTON_PAUSE: SendMessageW(hwnd_winamp, WM_COMMAND, WINAMP_BUTTON3 + mod,0); break; + case WA2_USERBUTTON_STOP: SendMessageW(hwnd_winamp, WM_COMMAND, WINAMP_BUTTON4 + mod,0); break; + case WA2_USERBUTTON_NEXT: SendMessageW(hwnd_winamp, WM_COMMAND, WINAMP_BUTTON5 + mod,0); break; + } +} + +//----------------------------------------------------------------------------------------------- +void Winamp2FrontEnd::setOnTop(int ontop) +{ + if (!!ontop == !!isOnTop()) return; + toggleOnTop(); +} + +//----------------------------------------------------------------------------------------------- +void Winamp2FrontEnd::toggleOnTop() +{ + SendMessageW(hwnd_winamp, WM_COMMAND, WINAMP_OPTIONS_AOT, 0); +} + + +//----------------------------------------------------------------------------------------------- +void Winamp2FrontEnd::enqueueFile(const wchar_t *file) +{ + if (!IsWindow(hwnd_winamp)) return; + COPYDATASTRUCT cds; + cds.dwData = IPC_PLAYFILEW; + cds.lpData = (void *)file; + cds.cbData = sizeof(wchar_t) * (wcslen(file)+1); + SendMessageW(hwnd_winamp,WM_COPYDATA,(WPARAM)NULL,(LPARAM)&cds); +} + +//----------------------------------------------------------------------------------------------- +int Winamp2FrontEnd::isPlaying() +{ + if (!IsWindow(hwnd_winamp)) return 0; + return SendMessageW(hwnd_winamp,WM_WA_IPC,0,IPC_ISPLAYING) == 1; +} + +//----------------------------------------------------------------------------------------------- +int Winamp2FrontEnd::isPaused() +{ + if (!IsWindow(hwnd_winamp)) return 0; + return SendMessageW(hwnd_winamp,WM_WA_IPC,0,IPC_ISPLAYING) == 3; +} + +//----------------------------------------------------------------------------------------------- +int Winamp2FrontEnd::isStopped() +{ + if (!IsWindow(hwnd_winamp)) return 1; + return SendMessageW(hwnd_winamp,WM_WA_IPC,0,IPC_ISPLAYING) == 0; +} + +//----------------------------------------------------------------------------------------------- +int Winamp2FrontEnd::getPosition() +{ + if ( !IsWindow( hwnd_winamp ) ) + return 0; + + if ( got_pos_cache && GetTickCount() < cached_pos_time + 20 ) + return cached_pos; + + got_pos_cache = 1; + cached_pos_time = GetTickCount(); + + return cached_pos = SendMessageW( hwnd_winamp, WM_WA_IPC, 0, IPC_GETOUTPUTTIME ); +} + +//----------------------------------------------------------------------------------------------- +int Winamp2FrontEnd::getLength() +{ + if ( got_length_cache && GetTickCount() < cached_length_time + 3000 ) + return cached_length; + + if ( !IsWindow( hwnd_winamp ) ) + return 0; + + int l = SendMessageW( hwnd_winamp, WM_WA_IPC, 1, IPC_GETOUTPUTTIME ); + + if ( l == -1 ) + cached_length = -1; + else + cached_length = l * 1000; + + got_length_cache = 1; + cached_length_time = GetTickCount(); + + return cached_length; +} + +//----------------------------------------------------------------------------------------------- +int Winamp2FrontEnd::seekTo(int ms) +{ + got_pos_cache = 0; + if (!IsWindow(hwnd_winamp)) return 0; + return SendMessageW(hwnd_winamp,WM_WA_IPC,ms,IPC_JUMPTOTIME); +} + +//----------------------------------------------------------------------------------------------- +void Winamp2FrontEnd::setVolume(int v) +{ + if (!IsWindow(hwnd_winamp)) return; + SendMessageW(hwnd_winamp,WM_WA_IPC,v,IPC_SETVOLUME); +} + +//----------------------------------------------------------------------------------------------- +int Winamp2FrontEnd::getVolume() +{ + if (!IsWindow(hwnd_winamp)) return 255; + return SendMessageW(hwnd_winamp,WM_WA_IPC,(WPARAM)-666,IPC_SETVOLUME); +} + +//----------------------------------------------------------------------------------------------- +void Winamp2FrontEnd::setPanning(int p) +{ + if (!IsWindow(hwnd_winamp)) return; + SendMessageW(hwnd_winamp,WM_WA_IPC,(WPARAM)p,IPC_SETPANNING); +} + +//----------------------------------------------------------------------------------------------- +int Winamp2FrontEnd::getPanning() +{ + if (!IsWindow(hwnd_winamp)) return 0; + return SendMessageW(hwnd_winamp,WM_WA_IPC,(WPARAM)-666,IPC_SETPANNING); +} + +//----------------------------------------------------------------------------------------------- +int Winamp2FrontEnd::getInfo( int what ) +{ + if ( !IsWindow( hwnd_winamp ) ) + return 0; + + return SendMessageW( hwnd_winamp, WM_WA_IPC, (WPARAM)what, IPC_GETINFO ); +} + +//----------------------------------------------------------------------------------------------- +int Winamp2FrontEnd::getEqData(int what) +{ + if (!IsWindow(hwnd_winamp)) return 0; + return SendMessageW(hwnd_winamp,WM_WA_IPC,(WPARAM)what,IPC_GETEQDATA); +} + +//----------------------------------------------------------------------------------------------- +void Winamp2FrontEnd::setEqData(int what, int val) +{ + if (!IsWindow(hwnd_winamp)) return; + //SendMessageW(hwnd_winamp,WM_WA_IPC,what,IPC_GETEQDATA); // f this :) + SendMessageW(hwnd_winamp,WM_WA_IPC,0xDB000000 | ((what&0xFF) << 16) | (val&0xFFFF),IPC_SETEQDATA); +} + +//----------------------------------------------------------------------------------------------- +int Winamp2FrontEnd::getShuffle() +{ + if (!IsWindow(hwnd_winamp)) return 0; + return SendMessageW(hwnd_winamp,WM_WA_IPC,(WPARAM)0,IPC_GET_SHUFFLE); +} + +//----------------------------------------------------------------------------------------------- +int Winamp2FrontEnd::getRepeat() +{ + if (!IsWindow(hwnd_winamp)) return 0; + return SendMessageW(hwnd_winamp,WM_WA_IPC,(WPARAM)0,IPC_GET_REPEAT); +} + +//----------------------------------------------------------------------------------------------- +void Winamp2FrontEnd::setShuffle(int shuffle) +{ + if (!IsWindow(hwnd_winamp)) return; + SendMessageW(hwnd_winamp,WM_WA_IPC,(WPARAM)shuffle,IPC_SET_SHUFFLE); +} + +//----------------------------------------------------------------------------------------------- +void Winamp2FrontEnd::setRepeat(int repeat) +{ + if (!IsWindow(hwnd_winamp)) return; + SendMessageW(hwnd_winamp,WM_WA_IPC,(WPARAM)repeat,IPC_SET_REPEAT); +} + +//----------------------------------------------------------------------------------------------- +void Winamp2FrontEnd::setManualPlaylistAdvance(int manual) +{ + if (!IsWindow(hwnd_winamp)) return; + SendMessageW(hwnd_winamp,WM_WA_IPC,(WPARAM)manual,IPC_SET_MANUALPLADVANCE); +} + +//----------------------------------------------------------------------------------------------- +int Winamp2FrontEnd::getManualPlaylistAdvance() +{ + if (!IsWindow(hwnd_winamp)) return 0; + return SendMessageW(hwnd_winamp,WM_WA_IPC,(WPARAM)0,IPC_GET_MANUALPLADVANCE); +} + +//----------------------------------------------------------------------------------------------- +void Winamp2FrontEnd::enableWindows( int enabled ) +{ + if ( !IsWindow( hwnd_winamp ) ) + return; + + SendMessageW( hwnd_winamp, WM_WA_IPC, enabled ? 0 : 0xdeadbeef, IPC_ENABLEDISABLE_ALL_WINDOWS ); +} + +//----------------------------------------------------------------------------------------------- +int Winamp2FrontEnd::areWindowsEnabled() +{ + if ( !IsWindow( hwnd_winamp ) ) + return 1; + + return 1; // todo !! +} + +//----------------------------------------------------------------------------------------------- +int _IsWindowVisible( HWND w ) +{ + if ( !IsWindowVisible( w ) ) + return 0; + + RECT r; + GetWindowRect( w, &r ); + + return !( r.left >= 3000 && r.top >= 3000 ); +} + +int Winamp2FrontEnd::isMainWindowVisible() +{ + return SendMessageW(hwnd_winamp, WM_WA_IPC, (WPARAM)0, IPC_ISMAINWNDVISIBLE); +} + +//----------------------------------------------------------------------------------------------- +void Winamp2FrontEnd::setWindowsVisible(int v) +{ + if (visible == v) + return; + + if (!IsWindow(hwnd_winamp)) + return; + + if (v) + { + if (saved_main && ! isMainWindowVisible()) + SendMessageW(hwnd_winamp, WM_COMMAND, WINAMP_MAIN_WINDOW, 0); + if (saved_eq && !isWindowVisible(IPC_GETWND_EQ)) + SendMessageW(hwnd_winamp, WM_COMMAND, WINAMP_OPTIONS_EQ, 0); +#ifdef MINIBROWSER_SUPPORT + if (saved_mb && !isWindowVisible(IPC_GETWND_MB)) + SendMessageW(hwnd_winamp, WM_COMMAND, WINAMP_OPTIONS_MINIBROWSER, 0); +#endif + if (saved_pe && !isWindowVisible(IPC_GETWND_PE)) + SendMessageW(hwnd_winamp, WM_COMMAND, WINAMP_OPTIONS_PLEDIT, 0); + if (saved_video && !isWindowVisible(IPC_GETWND_VIDEO)) + SendMessageW(hwnd_winamp, WM_COMMAND, WINAMP_OPTIONS_VIDEO, 0); + } + else + { + if (isMainWindowVisible()) + { + saved_main = 1; + SendMessageW(hwnd_winamp, WM_COMMAND, WINAMP_MAIN_WINDOW, 0); + } + else + saved_main = 0; + + if (isWindowVisible(IPC_GETWND_EQ)) + { + saved_eq = 1; + SendMessageW(hwnd_winamp, WM_COMMAND, WINAMP_OPTIONS_EQ, 0); + } + else + saved_eq = 0; + +#ifdef MINIBROWSER_SUPPORT + if (isWindowVisible(IPC_GETWND_MB)) + { + saved_mb = 1; + SendMessageW(hwnd_winamp, WM_COMMAND, WINAMP_OPTIONS_MINIBROWSER, 0); + } + else saved_mb = 0; +#endif + if (isWindowVisible(IPC_GETWND_PE)) + { + saved_pe = 1; + SendMessageW(hwnd_winamp, WM_COMMAND, WINAMP_OPTIONS_PLEDIT, 0); + } + else + saved_pe = 0; + + if (isWindowVisible(IPC_GETWND_VIDEO)) + { + saved_video = 1; + SendMessageW(hwnd_winamp, WM_COMMAND, WINAMP_OPTIONS_VIDEO, 0); + } + else saved_video = 0; + } + visible = v; +} + +//----------------------------------------------------------------------------------------------- +int Winamp2FrontEnd::areWindowsVisible() +{ + if (!IsWindow(hwnd_winamp)) + return 1; + + return visible; +} + +//----------------------------------------------------------------------------------------------- +HWND Winamp2FrontEnd::getWnd(int wnd) +{ + if (!IsWindow(hwnd_winamp)) + return NULL; + + return (HWND)SendMessageW(hwnd_winamp,WM_WA_IPC,(WPARAM)wnd,IPC_GETWND); +} + +//----------------------------------------------------------------------------------------------- +int Winamp2FrontEnd::getPlaylistLength() +{ + if (!IsWindow(hwnd_winamp)) return 0; + return SendMessageW(hwnd_winamp,WM_WA_IPC,(WPARAM)0,IPC_GETLISTLENGTH); +} + +//----------------------------------------------------------------------------------------------- +int Winamp2FrontEnd::getCurPlaylistEntry() +{ + if (!IsWindow(hwnd_winamp)) return 0; + return SendMessageW(hwnd_winamp,WM_WA_IPC,(WPARAM)0,IPC_GETLISTPOS); +} + +//----------------------------------------------------------------------------------------------- +const wchar_t *Winamp2FrontEnd::getTitle(int plentry) +{ + if (!IsWindow(hwnd_winamp)) return NULL; + return (const wchar_t *)SendMessageW(hwnd_winamp,WM_WA_IPC,(WPARAM)plentry,IPC_GETPLAYLISTTITLEW); +} + +//----------------------------------------------------------------------------------------------- +const char *Winamp2FrontEnd::getFile(int plentry) +{ + if (!IsWindow(hwnd_winamp)) return NULL; + return (const char *)SendMessageW(hwnd_winamp,WM_WA_IPC,(WPARAM)plentry,IPC_GETPLAYLISTFILE); +} + +const wchar_t *Winamp2FrontEnd::getFileW(int plentry) +{ + if (!IsWindow(hwnd_winamp)) return NULL; + return (const wchar_t *)SendMessageW(hwnd_winamp,WM_WA_IPC,(WPARAM)plentry,IPC_GETPLAYLISTFILEW); +} + +//----------------------------------------------------------------------------------------------- +void Winamp2FrontEnd::playAudioCD(int cd) +{ + if (!IsWindow(hwnd_winamp)) return; + SendMessageW(hwnd_winamp,WM_COMMAND,ID_MAIN_PLAY_AUDIOCD1+cd,0); +} + +//----------------------------------------------------------------------------------------------- +void Winamp2FrontEnd::openFileDialog(HWND w) +{ + if (!IsWindow(hwnd_winamp)) return; + SendMessageW(hwnd_winamp, WM_WA_IPC, (WPARAM)w, IPC_OPENFILEBOX); +} + +//----------------------------------------------------------------------------------------------- +void Winamp2FrontEnd::openUrlDialog(HWND w) +{ + if (!IsWindow(hwnd_winamp)) return; + SendMessageW(hwnd_winamp, WM_COMMAND, WINAMP_BUTTON2_CTRL, 0); +} + +//----------------------------------------------------------------------------------------------- +void Winamp2FrontEnd::openDirectoryDialog(HWND w) +{ + if (!IsWindow(hwnd_winamp)) return; + SendMessageW(hwnd_winamp, WM_WA_IPC, (WPARAM)w, IPC_OPENDIRBOX); +} + +//----------------------------------------------------------------------------------------------- +void Winamp2FrontEnd::ejectPopupMenu() +{ + if (!IsWindow(hwnd_winamp)) return; + SendMessageW(hwnd_winamp, WM_WA_IPC, (WPARAM)0, IPC_SPAWNBUTTONPOPUP); +} + +//----------------------------------------------------------------------------------------------- +void Winamp2FrontEnd::previousPopupMenu() +{ + if (!IsWindow(hwnd_winamp)) return; + SendMessageW(hwnd_winamp, WM_WA_IPC, (WPARAM)1, IPC_SPAWNBUTTONPOPUP); +} + +//----------------------------------------------------------------------------------------------- +void Winamp2FrontEnd::nextPopupMenu() +{ + if (!IsWindow(hwnd_winamp)) return; + SendMessageW(hwnd_winamp, WM_WA_IPC, (WPARAM)2, IPC_SPAWNBUTTONPOPUP); +} + +//----------------------------------------------------------------------------------------------- +void Winamp2FrontEnd::pausePopupMenu() +{ + if (!IsWindow(hwnd_winamp)) return; + SendMessageW(hwnd_winamp, WM_WA_IPC, (WPARAM)3, IPC_SPAWNBUTTONPOPUP); +} + +//----------------------------------------------------------------------------------------------- +void Winamp2FrontEnd::playPopupMenu() +{ + if (!IsWindow(hwnd_winamp)) return; + SendMessageW(hwnd_winamp, WM_WA_IPC, (WPARAM)4, IPC_SPAWNBUTTONPOPUP); +} + +//----------------------------------------------------------------------------------------------- +void Winamp2FrontEnd::stopPopupMenu() +{ + if (!IsWindow(hwnd_winamp)) return; + SendMessageW(hwnd_winamp, WM_WA_IPC, (WPARAM)5, IPC_SPAWNBUTTONPOPUP); +} + +//----------------------------------------------------------------------------------------------- +void Winamp2FrontEnd::setDialogBoxParent(HWND w) +{ + if (!IsWindow(hwnd_winamp)) return; + SendMessageW(hwnd_winamp, WM_WA_IPC, (WPARAM)w, IPC_SETDIALOGBOXPARENT); +} + +void Winamp2FrontEnd::updateDialogBoxParent(HWND w) +{ + if (!IsWindow(hwnd_winamp)) return; + SendMessageW(hwnd_winamp, WM_WA_IPC, (WPARAM)w, IPC_UPDATEDIALOGBOXPARENT); +} + +//----------------------------------------------------------------------------------------------- +int Winamp2FrontEnd::isOnTop() +{ + if (!IsWindow(hwnd_winamp)) return 0; + return SendMessageW(hwnd_winamp, WM_WA_IPC, 0, IPC_IS_AOT); +} + +//----------------------------------------------------------------------------------------------- +void Winamp2FrontEnd::triggerPopupMenu(int x, int y) +{ + if (!IsWindow(hwnd_winamp)) return; + HMENU hMenu = (HMENU)SendMessageW(hwnd_winamp,WM_WA_IPC,(WPARAM)0,IPC_GET_HMENU); + DoTrackPopup(hMenu, TPM_RIGHTBUTTON, x, y, hwnd_winamp); +} + +//----------------------------------------------------------------------------------------------- +void Winamp2FrontEnd::triggerEQPresetMenu(int x, int y) +{ + waSpawnMenuParms p = {hwnd_winamp, x, y}; + SendMessageW(hwnd_winamp, WM_WA_IPC, (WPARAM)&p, IPC_SPAWNEQPRESETMENU); +} + +//----------------------------------------------------------------------------------------------- +int Winamp2FrontEnd::triggerFileMenu(int x, int y, int width, int height) +{ + waSpawnMenuParms2 p = {hwnd_winamp, x, y, width, height}; + return SendMessageW(hwnd_winamp, WM_WA_IPC, (WPARAM)&p, IPC_SPAWNFILEMENU); +} + +//----------------------------------------------------------------------------------------------- +int Winamp2FrontEnd::triggerPlayMenu(int x, int y, int width, int height) +{ + waSpawnMenuParms2 p = {hwnd_winamp, x, y, width, height}; + return SendMessageW(hwnd_winamp, WM_WA_IPC, (WPARAM)&p, IPC_SPAWNPLAYMENU); +} + +//----------------------------------------------------------------------------------------------- +int Winamp2FrontEnd::triggerOptionsMenu(int x, int y, int width, int height) +{ + waSpawnMenuParms2 p = {hwnd_winamp, x, y, width, height}; + return SendMessageW(hwnd_winamp, WM_WA_IPC, (WPARAM)&p, IPC_SPAWNOPTIONSMENU); +} + +//----------------------------------------------------------------------------------------------- +int Winamp2FrontEnd::triggerWindowsMenu(int x, int y, int width, int height) +{ + waSpawnMenuParms2 p = {hwnd_winamp, x, y, width, height}; + return SendMessageW(hwnd_winamp, WM_WA_IPC, (WPARAM)&p, IPC_SPAWNWINDOWSMENU); +} + +//----------------------------------------------------------------------------------------------- +int Winamp2FrontEnd::triggerHelpMenu(int x, int y, int width, int height) +{ + waSpawnMenuParms2 p = {hwnd_winamp, x, y, width, height}; + return SendMessageW(hwnd_winamp, WM_WA_IPC, (WPARAM)&p, IPC_SPAWNHELPMENU); +} + +//----------------------------------------------------------------------------------------------- +int Winamp2FrontEnd::triggerPEFileMenu(int x, int y, int width, int height) +{ + int IPC_GETMLWINDOW=SendMessageW(hwnd_winamp,WM_WA_IPC,(WPARAM)&"LibraryGetWnd",IPC_REGISTER_WINAMP_IPCMESSAGE); + if (IPC_GETMLWINDOW > 65536) SendMessageW(hwnd_winamp, WM_WA_IPC, (WPARAM)-1, IPC_GETMLWINDOW); + waSpawnMenuParms2 p = {hwnd_winamp, x, y, width, height}; + p.wnd = getWnd(IPC_GETWND_PE); + return SendMessageW(hwnd_winamp, WM_WA_IPC, (WPARAM)&p, IPC_SPAWNPEFILEMENU); +} + +//----------------------------------------------------------------------------------------------- +int Winamp2FrontEnd::triggerPEPlaylistMenu(int x, int y, int width, int height) +{ + waSpawnMenuParms2 p = {hwnd_winamp, x, y, width, height}; + p.wnd = getWnd(IPC_GETWND_PE); + return SendMessageW(hwnd_winamp, WM_WA_IPC, (WPARAM)&p, IPC_SPAWNPEPLAYLISTMENU); +} + +//----------------------------------------------------------------------------------------------- +int Winamp2FrontEnd::triggerPESortMenu(int x, int y, int width, int height) +{ + waSpawnMenuParms2 p = {hwnd_winamp, x, y, width, height}; + p.wnd = getWnd(IPC_GETWND_PE); + return SendMessageW(hwnd_winamp, WM_WA_IPC, (WPARAM)&p, IPC_SPAWNPESORTMENU); +} + +//----------------------------------------------------------------------------------------------- +int Winamp2FrontEnd::triggerPEHelpMenu(int x, int y, int width, int height) +{ + waSpawnMenuParms2 p = {hwnd_winamp, x, y, width, height}; + return SendMessageW(hwnd_winamp, WM_WA_IPC, (WPARAM)&p, IPC_SPAWNPEHELPMENU); +} + +//----------------------------------------------------------------------------------------------- +int Winamp2FrontEnd::triggerPEListOfListsMenu(int x, int y) +{ + int IPC_GETMLWINDOW=SendMessageW(hwnd_winamp,WM_WA_IPC,(WPARAM)&"LibraryGetWnd",IPC_REGISTER_WINAMP_IPCMESSAGE); + if (IPC_GETMLWINDOW > 65536) SendMessageW(hwnd_winamp, WM_WA_IPC, (WPARAM)-1, IPC_GETMLWINDOW); + waSpawnMenuParms p = {hwnd_winamp, x, y}; + return SendMessageW(hwnd_winamp, WM_WA_IPC, (WPARAM)&p, IPC_SPAWNPELISTOFPLAYLISTS); +} + +//----------------------------------------------------------------------------------------------- +int Winamp2FrontEnd::triggerMLFileMenu(int x, int y, int width, int height) +{ + waSpawnMenuParms2 p = {hwnd_winamp, x, y, width, height}; + return SendMessageW(hwnd_winamp, WM_WA_IPC, (WPARAM)&p, IPC_SPAWNMLFILEMENU); +} + +//----------------------------------------------------------------------------------------------- +int Winamp2FrontEnd::triggerMLViewMenu(int x, int y, int width, int height) +{ + waSpawnMenuParms2 p = {hwnd_winamp, x, y, width, height}; + return SendMessageW(hwnd_winamp, WM_WA_IPC, (WPARAM)&p, IPC_SPAWNMLVIEWMENU); +} + +//----------------------------------------------------------------------------------------------- +int Winamp2FrontEnd::triggerMLHelpMenu(int x, int y, int width, int height) +{ + waSpawnMenuParms2 p = {hwnd_winamp, x, y, width, height}; + return SendMessageW(hwnd_winamp, WM_WA_IPC, (WPARAM)&p, IPC_SPAWNMLHELPMENU); +} + +//----------------------------------------------------------------------------------------------- +HMENU Winamp2FrontEnd::getPopupMenu() +{ + if (!IsWindow(hwnd_winamp)) return NULL; + return (HMENU)SendMessageW(hwnd_winamp,WM_WA_IPC,(WPARAM)0,IPC_GET_HMENU); +} + +//----------------------------------------------------------------------------------------------- +HMENU Winamp2FrontEnd::getTopMenu() +{ + if (!IsWindow(hwnd_winamp)) return NULL; + return (HMENU)SendMessageW(hwnd_winamp,WM_WA_IPC,(WPARAM)-1,IPC_GET_HMENU); +} + +//----------------------------------------------------------------------------------------------- +int Winamp2FrontEnd::adjustOptionsPopupMenu(int direction) +{ + if (!IsWindow(hwnd_winamp)) return 0; + return SendMessageW(hwnd_winamp,WM_WA_IPC,(WPARAM)direction,IPC_ADJUST_OPTIONSMENUPOS); +} + +//----------------------------------------------------------------------------------------------- +HMENU Winamp2FrontEnd::getMenuBarMenu(int which) +{ + if (!IsWindow(hwnd_winamp)) return NULL; + return (HMENU)SendMessageW(hwnd_winamp,WM_WA_IPC,(WPARAM)which+1,IPC_GET_HMENU); +} + +//----------------------------------------------------------------------------------------------- +int Winamp2FrontEnd::adjustFFWindowsMenu(int direction) +{ + if (!IsWindow(hwnd_winamp)) return 0; + return SendMessageW(hwnd_winamp,WM_WA_IPC,(WPARAM)direction,IPC_ADJUST_FFWINDOWSMENUPOS); +} + +//----------------------------------------------------------------------------------------------- +int Winamp2FrontEnd::adjustFFOptionsMenu(int direction) +{ + if (!IsWindow(hwnd_winamp)) return 0; + return SendMessageW(hwnd_winamp,WM_WA_IPC,(WPARAM)direction,IPC_ADJUST_FFOPTIONSMENUPOS); +} + +//----------------------------------------------------------------------------------------------- +HWND Winamp2FrontEnd::getMainWindow() +{ + return hwnd_winamp; +} + +//----------------------------------------------------------------------------------------------- +void Winamp2FrontEnd::quit() +{ + if (!IsWindow(hwnd_winamp)) return; + SendMessageW(hwnd_winamp,WM_CLOSE,0,0); +} + +//----------------------------------------------------------------------------------------------- +int Winamp2FrontEnd::isWindowVisible(intptr_t which) +{ + return SendMessageW(hwnd_winamp, WM_WA_IPC, which, IPC_ISWNDVISIBLE); +} + +//----------------------------------------------------------------------------------------------- +void Winamp2FrontEnd::setWindowVisible(intptr_t which, int visible) +{ + int state = isWindowVisible(which); + if (!state == !visible) return; + int id = 0; + switch (which) + { + case IPC_GETWND_EQ: + DebugStringW(L"Showing EQ!!\n"); + id = WINAMP_OPTIONS_EQ; + break; + case IPC_GETWND_PE: + id = WINAMP_OPTIONS_PLEDIT; + break; +#ifdef MINIBROWSER_SUPPORT + case IPC_GETWND_MB: + id = WINAMP_OPTIONS_MINIBROWSER; + break; +#endif + case IPC_GETWND_VIDEO: + id = WINAMP_OPTIONS_VIDEO; + break; + } + SendMessageW(hwnd_winamp, WM_COMMAND, id, 0); +} + + +//----------------------------------------------------------------------------------------------- +void Winamp2FrontEnd::sendPlCmd(int which, int x, int y, int menu_align_flag) +{ + windowCommand wc = {which, x, y, menu_align_flag}; + SendMessageW(hwnd_winamp, WM_WA_IPC, (intptr_t)&wc, IPC_PLCMD); +} + +#ifdef MINIBROWSER_SUPPORT +//----------------------------------------------------------------------------------------------- +void Winamp2FrontEnd::sendMbCmd(int which, int x, int y, int menu_align_flag) +{ + windowCommand wc = {which, x, y, menu_align_flag}; + SendMessageW(hwnd_winamp, WM_WA_IPC, (intptr_t)&wc, IPC_MBCMD); +} +#endif + +//----------------------------------------------------------------------------------------------- +void Winamp2FrontEnd::sendVidCmd(int which, int x, int y, int menu_align_flag) +{ + windowCommand wc = {which, x, y, menu_align_flag}; + SendMessageW(hwnd_winamp, WM_WA_IPC, (intptr_t)&wc, IPC_VIDCMD); +} + + +//----------------------------------------------------------------------------------------------- +#define WINAMP_VISPLUGIN 40192 +void Winamp2FrontEnd::toggleVis() +{ + SendMessageW(hwnd_winamp, WM_COMMAND, WINAMP_VISPLUGIN, 0); +} + +//----------------------------------------------------------------------------------------------- +int Winamp2FrontEnd::isVisRunning() +{ + return SendMessageW(hwnd_winamp, WM_WA_IPC, 0, IPC_ISVISRUNNING); +} + +//----------------------------------------------------------------------------------------------- +HWND Winamp2FrontEnd::getVisWnd() +{ + return (HWND)SendMessageW(hwnd_winamp, WM_WA_IPC, 0, IPC_GETVISWND); +} + +//----------------------------------------------------------------------------------------------- +IDropTarget *Winamp2FrontEnd::getDropTarget() +{ + return (IDropTarget *)SendMessageW(hwnd_winamp, WM_WA_IPC, 0, IPC_GETDROPTARGET); +} + +//----------------------------------------------------------------------------------------------- +int Winamp2FrontEnd::getSamplerate() +{ + int realRate = SendMessageW(hwnd_winamp,WM_WA_IPC,5,IPC_GETINFO); + if (realRate == 1) + return 1000*SendMessageW(hwnd_winamp,WM_WA_IPC,0,IPC_GETINFO); + else + return realRate; +} + +//----------------------------------------------------------------------------------------------- +int Winamp2FrontEnd::getBitrate() +{ + return SendMessageW(hwnd_winamp,WM_WA_IPC,1,IPC_GETINFO); +} + +//----------------------------------------------------------------------------------------------- +int Winamp2FrontEnd::getChannels() +{ + return SendMessageW(hwnd_winamp,WM_WA_IPC,2,IPC_GETINFO); +} + +//----------------------------------------------------------------------------------------------- +int Winamp2FrontEnd::isValidEmbedWndState(embedWindowState *ws) +{ + return SendMessageW(hwnd_winamp, WM_WA_IPC, (intptr_t)ws, IPC_EMBED_ISVALID); +} + +//----------------------------------------------------------------------------------------------- +int Winamp2FrontEnd::PE_getNumItems() +{ + return SendMessageW(hwnd_playlist, WM_USER, IPC_PE_GETINDEXTOTAL, 0); +} + +//----------------------------------------------------------------------------------------------- +fileinfo2 *Winamp2FrontEnd::PE_getFileTitle(int index) +{ + static fileinfo2 fi; + fi.fileindex = index; + *(fi.filetitle) = 0; + *(fi.filelength) = 0; + SendMessageW(hwnd_playlist, WM_USER, IPC_PE_GETINDEXTITLE, (LPARAM)&fi); + return &fi; +} + +//----------------------------------------------------------------------------------------------- +fileinfo2W *Winamp2FrontEnd::PE_getFileTitleW(int index) +{ + static fileinfo2W fi; + fi.fileindex = index; + *(fi.filetitle) = 0; + *(fi.filelength) = 0; + SendMessageW(hwnd_playlist, WM_USER, IPC_PE_GETINDEXTITLEW, (LPARAM)&fi); + return &fi; +} + +//----------------------------------------------------------------------------------------------- +int Winamp2FrontEnd::PE_getCurrentIndex() +{ + //return SendMessageW(hwnd_playlist, WM_USER, IPC_PE_GETCURINDEX, 0); + return SendMessageW(hwnd_winamp, WM_USER, 0, IPC_GETLISTPOS); +} + +//----------------------------------------------------------------------------------------------- +void Winamp2FrontEnd::PE_setCurrentIndex(int i) +{ + SendMessageW(hwnd_winamp, WM_WA_IPC, i, IPC_SETPLAYLISTPOS); +} + +//----------------------------------------------------------------------------------------------- +HWND Winamp2FrontEnd::getMediaLibrary() +{ + int IPC_GETMLWINDOW=SendMessageW(hwnd_winamp,WM_WA_IPC,(WPARAM)&"LibraryGetWnd",IPC_REGISTER_WINAMP_IPCMESSAGE); + return IPC_GETMLWINDOW > 65536 ? (HWND)SendMessageW(hwnd_winamp, WM_WA_IPC, 0, IPC_GETMLWINDOW) : NULL; +} + +//----------------------------------------------------------------------------------------------- +void Winamp2FrontEnd::ensureMediaLibraryLoaded() +{ + int IPC_GETMLWINDOW=SendMessageW(hwnd_winamp,WM_WA_IPC,(WPARAM)&"LibraryGetWnd",IPC_REGISTER_WINAMP_IPCMESSAGE); + if (IPC_GETMLWINDOW > 65536) SendMessageW(hwnd_winamp, WM_WA_IPC, (WPARAM)-1, IPC_GETMLWINDOW); +} + + +//----------------------------------------------------------------------------------------------- +int Winamp2FrontEnd::hasVideoSupport() +{ + return SendMessageW(hwnd_winamp, WM_WA_IPC, 0, IPC_HAS_VIDEO_SUPPORT); +} + +//----------------------------------------------------------------------------------------------- +int Winamp2FrontEnd::isPlayingVideo() +{ + return SendMessageW(hwnd_winamp, WM_WA_IPC, 0, IPC_IS_PLAYING_VIDEO) > 0; +} + +//----------------------------------------------------------------------------------------------- +int Winamp2FrontEnd::isPlayingVideoFullscreen() +{ + return SendMessageW(hwnd_winamp, WM_WA_IPC, 0, IPC_IS_FULLSCREEN); +} + +//----------------------------------------------------------------------------------------------- +int Winamp2FrontEnd::isDoubleSize() +{ + return SendMessageW(hwnd_winamp, WM_WA_IPC, 0, IPC_ISDOUBLESIZE); +} + +//----------------------------------------------------------------------------------------------- +int Winamp2FrontEnd::getTimeDisplayMode() +{ + return SendMessageW(hwnd_winamp, WM_WA_IPC, 0, IPC_GETTIMEDISPLAYMODE); +} + +//----------------------------------------------------------------------------------------------- +void Winamp2FrontEnd::switchSkin(const wchar_t *skinname) +{ + static StringW wideSkinName; + wideSkinName=skinname; + PostMessage(hwnd_winamp, WM_WA_IPC, (intptr_t)wideSkinName.getValue(), IPC_SETSKINW); +} + +//----------------------------------------------------------------------------------------------- +void Winamp2FrontEnd::visNext() +{ + PostMessage(hwnd_winamp, WM_COMMAND, ID_VIS_NEXT, 0); +} + +//----------------------------------------------------------------------------------------------- +void Winamp2FrontEnd::visFullscreen() +{ + PostMessage(hwnd_winamp, WM_COMMAND, ID_VIS_FS, 0); +} + +//----------------------------------------------------------------------------------------------- +void Winamp2FrontEnd::visPrev() +{ + PostMessage(hwnd_winamp, WM_COMMAND, ID_VIS_PREV, 0); +} + +//----------------------------------------------------------------------------------------------- +void Winamp2FrontEnd::visRandom(int set) +{ + PostMessage(hwnd_winamp, WM_COMMAND, ID_VIS_RANDOM | (set << 16), 0); +} + +//----------------------------------------------------------------------------------------------- +void Winamp2FrontEnd::pollVisRandom() +{ + PostMessage(hwnd_winamp, WM_COMMAND, ID_VIS_RANDOM | 0xFFFF0000, 0); +} + +//----------------------------------------------------------------------------------------------- +void Winamp2FrontEnd::visConfig() +{ + PostMessage(hwnd_winamp, WM_COMMAND, ID_VIS_CFG, 0); +} + +//----------------------------------------------------------------------------------------------- +void Winamp2FrontEnd::visMenu() +{ + PostMessage(hwnd_winamp, WM_COMMAND, ID_VIS_MENU, 0); +} + +//----------------------------------------------------------------------------------------------- +void Winamp2FrontEnd::getIdealVideoSize(int *w, int *h) +{ + if (w) *w = video_ideal_width; + if (h) *h = video_ideal_height; +} + +//----------------------------------------------------------------------------------------------- +int Winamp2FrontEnd::getStopOnVideoClose() +{ + return SendMessageW(hwnd_winamp, WM_WA_IPC, 0, IPC_GETSTOPONVIDEOCLOSE); +} + +//----------------------------------------------------------------------------------------------- +void Winamp2FrontEnd::setStopOnVideoClose(int stop) +{ + SendMessageW(hwnd_winamp, WM_WA_IPC, stop, IPC_SETSTOPONVIDEOCLOSE); +} + +//----------------------------------------------------------------------------------------------- +int Winamp2FrontEnd::GetVideoResize() +{ + return SendMessageW(hwnd_winamp, WM_WA_IPC, 0, IPC_GETVIDEORESIZE); +} + + +void Winamp2FrontEnd::SetVideoResize(int stop) +{ + SendMessageW(hwnd_winamp, WM_WA_IPC, stop, IPC_SETVIDEORESIZE); +} + + +//----------------------------------------------------------------------------------------------- +BOOL CALLBACK findVisWndProc(HWND hwnd, LPARAM lParam) +{ + Winamp2FrontEnd *fe = (Winamp2FrontEnd*)lParam; + if (hwnd == fe->getVisWnd()) + { + fe->setFoundVis(); return FALSE; + } + return TRUE; +} + +//----------------------------------------------------------------------------------------------- +int Winamp2FrontEnd::isVis(HWND hwnd) +{ + if (hwnd == wa2.getVisWnd()) return 1; + foundvis = 0; + EnumChildWindows(hwnd, findVisWndProc, (LPARAM)this); + return foundvis; +} + +//----------------------------------------------------------------------------------------------- +HWND Winamp2FrontEnd::getPreferencesWindow() +{ + return (HWND)SendMessageW(hwnd_winamp, WM_WA_IPC, 0, IPC_GETPREFSWND); +} + +//----------------------------------------------------------------------------------------------- +void Winamp2FrontEnd::setPlEditWidthHeight(int width, int height) +{ + POINT pt={width, height}; + SendMessageW(hwnd_winamp, WM_WA_IPC, (WPARAM)&pt, IPC_SET_PE_WIDTHHEIGHT); +} + +//----------------------------------------------------------------------------------------------- +HINSTANCE Winamp2FrontEnd::getLanguagePackInstance() +{ + return (HINSTANCE)SendMessageW(hwnd_winamp, WM_WA_IPC, 0, IPC_GETLANGUAGEPACKINSTANCE); +} + +//----------------------------------------------------------------------------------------------- +void Winamp2FrontEnd::openTrackInfo() +{ + SendMessageW(hwnd_winamp, WM_COMMAND, WINAMP_EDIT_ID3, 0); +} + +//----------------------------------------------------------------------------------------------- +const char *Winamp2FrontEnd::getOutputPlugin() +{ + return (const char *)SendMessageW(hwnd_winamp, WM_WA_IPC, 0, IPC_GETOUTPUTPLUGIN); +} + +//----------------------------------------------------------------------------------------------- +void Winamp2FrontEnd::setDrawBorders(int d) +{ + SendMessageW(hwnd_winamp, WM_WA_IPC, d, IPC_SETDRAWBORDERS); +} + +//----------------------------------------------------------------------------------------------- +void Winamp2FrontEnd::disableSkinnedCursors(int disable) +{ + SendMessageW(hwnd_winamp, WM_WA_IPC, disable, IPC_DISABLESKINCURSORS); +} + +//----------------------------------------------------------------------------------------------- +int Winamp2FrontEnd::getMetaData(const wchar_t *filename, const wchar_t *name, wchar_t *data, int data_len) +{ + + if (!_wcsnicmp(filename, L"file://", 7)) + filename+=7; + extendedFileInfoStructW efis= + { + filename, + name, + data, + data_len, + }; + return SendMessageW(hwnd_winamp,WM_WA_IPC,(WPARAM)&efis,IPC_GET_EXTENDED_FILE_INFOW_HOOKABLE); +} +//------------------------------------------------------------------------------------------------ +void Winamp2FrontEnd::invalidateCache() +{ + got_length_cache = 0; + got_pos_cache = 0; +} + +//------------------------------------------------------------------------------------------------ +void Winamp2FrontEnd::registerGlobalHotkey(const char *name, int msg, int wparam, int lparam, int flags, const char *id) +{ + int m_genhotkeys_add_ipc=SendMessageW(hwnd_winamp,WM_WA_IPC,(WPARAM)&"GenHotkeysAdd",IPC_REGISTER_WINAMP_IPCMESSAGE); + genHotkeysAddStruct hs={0,}; + hs.name = (char *)name; + hs.uMsg = msg; + hs.wParam = wparam; + hs.lParam = lparam; + hs.flags = flags; + hs.id = (char *)id; + if (m_genhotkeys_add_ipc > 65536) SendMessageW(hwnd_winamp, WM_WA_IPC, (WPARAM)&hs, m_genhotkeys_add_ipc); +} + +//------------------------------------------------------------------------------------------------ +const char *Winamp2FrontEnd::getVideoInfoString() +{ + return (const char *)SendMessageW(hwnd_winamp, WM_WA_IPC, 4, IPC_GETINFO); +} + +//------------------------------------------------------------------------------------------------ +void Winamp2FrontEnd::playFile(const wchar_t *file) +{ + SendMessageW(hwnd_winamp, WM_WA_IPC, 0, IPC_DELETE); + COPYDATASTRUCT cds; + cds.dwData = IPC_PLAYFILEW; + cds.lpData = (void *)file; + cds.cbData = sizeof(wchar_t) * (wcslen(file)+1); // +1 to get the NULL, missing forever + SendMessageW(hwnd_winamp, WM_COPYDATA, (WPARAM)hwnd_winamp, (LPARAM)&cds); + SendMessageW(hwnd_winamp, WM_WA_IPC, 0, IPC_STARTPLAY); +} + +void Winamp2FrontEnd::clearPlaylist() +{ + SendMessageW(hwnd_winamp, WM_WA_IPC, 0, IPC_DELETE); +} + +//------------------------------------------------------------------------------------------------ +void Winamp2FrontEnd::rewind5s() +{ + SendMessageW(hwnd_winamp, WM_COMMAND, WINAMP_REW5S, 0); +} + +//------------------------------------------------------------------------------------------------ +void Winamp2FrontEnd::forward5s() +{ + SendMessageW(hwnd_winamp, WM_COMMAND, WINAMP_FFWD5S, 0); +} + +//------------------------------------------------------------------------------------------------ +void Winamp2FrontEnd::endOfPlaylist() +{ + SendMessageW(hwnd_winamp, WM_COMMAND, WINAMP_BUTTON5_CTRL, 0); +} + +//------------------------------------------------------------------------------------------------ +void Winamp2FrontEnd::startOfPlaylist() +{ + SendMessageW(hwnd_winamp, WM_COMMAND, WINAMP_BUTTON1_CTRL, 0); +} + +//------------------------------------------------------------------------------------------------ +void Winamp2FrontEnd::stopWithFade() +{ + SendMessageW(hwnd_winamp, WM_COMMAND, WINAMP_BUTTON4_SHIFT, 0); +} + +//------------------------------------------------------------------------------------------------ +void Winamp2FrontEnd::stopAfterCurrent() +{ + SendMessageW(hwnd_winamp, WM_COMMAND, WINAMP_BUTTON4_CTRL, 0); +} + +//------------------------------------------------------------------------------------------------ +int Winamp2FrontEnd::isWindowShade(int whichwnd) +{ + return SendMessageW(hwnd_winamp, WM_WA_IPC, whichwnd, IPC_IS_WNDSHADE); +} + +//------------------------------------------------------------------------------------------------ +int Winamp2FrontEnd::getCurTrackRating() +{ + return SendMessageW(hwnd_winamp, WM_WA_IPC, 0, IPC_GETRATING); +} + +void Winamp2FrontEnd::setCurTrackRating(int rating) +{ + SendMessageW(hwnd_winamp, WM_WA_IPC, rating, IPC_SETRATING); +} + +//------------------------------------------------------------------------------------------------ +int Winamp2FrontEnd::isExitEnabled() +{ + return SendMessageW(hwnd_winamp, WM_WA_IPC, 0, IPC_IS_EXIT_ENABLED); +} + +//------------------------------------------------------------------------------------------------ +int Winamp2FrontEnd::pushExitDisabled() +{ + return SendMessageW(hwnd_winamp, WM_WA_IPC, 0, IPC_PUSH_DISABLE_EXIT); +} + +//------------------------------------------------------------------------------------------------ +int Winamp2FrontEnd::popExitDisabled() +{ + return SendMessageW(hwnd_winamp, WM_WA_IPC, 0, IPC_POP_DISABLE_EXIT); +} + +void Winamp2FrontEnd::GetFileInfo(const wchar_t *filename, wchar_t *title, int titleCch, int *length) +{ + basicFileInfoStructW infoStruct = {0}; + infoStruct.filename = filename; + infoStruct.title = title; + infoStruct.titlelen = titleCch; + SendMessageW(hwnd_winamp, WM_WA_IPC, (WPARAM)&infoStruct, IPC_GET_BASIC_FILE_INFOW); + *length = infoStruct.length; +} + +const wchar_t *Winamp2FrontEnd::GetCurrentTitle() +{ + return (const wchar_t *)SendMessageW(hwnd_winamp, WM_WA_IPC, 0, IPC_GET_PLAYING_TITLE); +} + +const wchar_t *Winamp2FrontEnd::GetCurrentFile() +{ + return (const wchar_t *)SendMessageW(hwnd_winamp, WM_WA_IPC, 0, IPC_GET_PLAYING_FILENAME); +} + +void *Winamp2FrontEnd::CanPlay(const wchar_t *fn) +{ + return (void *)SendMessageW(hwnd_winamp, WM_WA_IPC, (WPARAM)fn, IPC_CANPLAY); +} + +bool Winamp2FrontEnd::IsPlaylist(const wchar_t *fn) +{ + return AGAVE_API_PLAYLISTMANAGER->CanLoad(fn); +} + + + +HWND GetMainContainerHWND(); +bool Winamp2FrontEnd::GetAlbumArt(const wchar_t *filename) +{ + // disabled 30 May 2012 as per email from Tejas w.r.t. to Rovi deal ending + #if 0 + if (!_wcsnicmp(filename, L"file://", 7)) + filename+=7; + + artFetchData fetch = {sizeof(artFetchData), GetMainContainerHWND(), }; + + + + wchar_t artist[512]=L"", album[512]=L"", gracenoteFileId[512]=L"", year[5]=L""; + getMetaData(filename, L"albumartist", artist, 512); + if (!WCSICMP(artist, L"")) //Add artist if albumartist not available + getMetaData(filename, L"artist", artist, 512); + getMetaData(filename, L"album", album, 512); + getMetaData(filename, L"GracenoteFileID", gracenoteFileId, 512); + getMetaData(filename, L"year", year, 5); + + fetch.artist=artist; + fetch.album=album; + fetch.gracenoteFileId=gracenoteFileId; + fetch.year=_wtoi(year); + + int r = SendMessageW(hwnd_winamp, WM_WA_IPC, (WPARAM)&fetch, IPC_FETCH_ALBUMART); + if(r == 0 && fetch.imgData && fetch.imgDataLen) // success, save art in correct location + { + AGAVE_API_ALBUMART->SetAlbumArt(filename,L"cover",0,0,fetch.imgData,fetch.imgDataLen,fetch.type); + WASABI_API_MEMMGR->sysFree(fetch.imgData); + return true; + } + #endif + return false; +} + +static void code(long* v, long* k) +{ + unsigned long y = v[0], z = v[1], sum = 0, /* set up */ + delta = 0x9e3779b9, n = 32 ; /* key schedule constant*/ + + while (n-- > 0) + { /* basic cycle start */ + sum += delta; + y += ((z << 4) + k[0]) ^(z + sum) ^((z >> 5) + k[1]); + z += ((y << 4) + k[2]) ^(y + sum) ^((y >> 5) + k[3]); /* end cycle */ + } + v[0] = y; v[1] = z; + +} + +static int getRegVer() +{ + int *x = (int*)malloc(32); + long s[3]; + long ss[2] = {GetTickCount(), (int)x + (int)s}; // fucko: better challenge, too + long tealike_key[4] = { 31337, 0xf00d, 0xdead, 0xbeef}; //fucko: hide this a bit + free(x); + s[0] = ss[0]; + s[1] = ss[1]; + s[2] = 0; + + SendMessageW(plugin.hwndParent, WM_WA_IPC, (WPARAM)s, IPC_GETREGISTEREDVERSION); + + code(ss, tealike_key); + + if (memcmp(s, ss, 8)) return 0; + + return s[2]; +} + +bool Winamp2FrontEnd::IsWinampPro() +{ + return !!getRegVer(); +} + +void Winamp2FrontEnd::openUrl(const wchar_t *url) +{ + SendMessageW(plugin.hwndParent, WM_WA_IPC, (WPARAM)url, IPC_OPEN_URL); +} + +#include "../gen_ml/menu.h" + +LRESULT sendMlIpc(int msg, WPARAM param) +{ + static LRESULT IPC_GETMLWINDOW; + if (!IPC_GETMLWINDOW) IPC_GETMLWINDOW = SendMessageW(plugin.hwndParent, WM_WA_IPC, (WPARAM)&"LibraryGetWnd", IPC_REGISTER_WINAMP_IPCMESSAGE); + HWND mlwnd = (HWND)SendMessageW(plugin.hwndParent, WM_WA_IPC, 0, IPC_GETMLWINDOW); + + if (param == 0 && msg == 0) return (LRESULT)mlwnd; + + if (IsWindow(mlwnd)) + return SendMessageW(mlwnd, WM_ML_IPC, param, msg); + + return 0; +} + +BOOL DoTrackPopup(HMENU hMenu, UINT fuFlags, int x, int y, HWND hwnd) +{ + if(hMenu == NULL) + { + return NULL; + } + + HWND ml_wnd = (HWND)sendMlIpc(0, 0); + if (IsWindow(ml_wnd)) + { + return Menu_TrackPopup(ml_wnd, hMenu, fuFlags, x, y, hwnd, NULL); + } + else + { + return TrackPopupMenu(hMenu, fuFlags, x, y, 0, hwnd, NULL); + } +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_ff/wa2frontend.h b/Src/Plugins/General/gen_ff/wa2frontend.h new file mode 100644 index 00000000..38eb819b --- /dev/null +++ b/Src/Plugins/General/gen_ff/wa2frontend.h @@ -0,0 +1,359 @@ +#ifndef __WINAMP2FRONTEND_H +#define __WINAMP2FRONTEND_H + +#define WA2_GETINFO_SAMPLERATE 0 +#define WA2_GETINFO_BITRATE 1 +#define WA2_GETINFO_CHANNELS 2 + +#define WA2_NUMBANDS 10 + +#define WA2_EQDATA_FIRSTBAND 0 +#define WA2_EQDATA_LASTBAND 9 +#define WA2_EQDATA_PREAMP 10 +#define WA2_EQDATA_ENABLED 11 +#define WA2_EQDATA_AUTO 12 + +#define IPC_GETWND_EQ 0 +#define IPC_GETWND_PE 1 +#define IPC_GETWND_MB 2 +#define IPC_GETWND_VIDEO 3 + +#define WA2_USERBUTTON_PREV 0 +#define WA2_USERBUTTON_PLAY 1 +#define WA2_USERBUTTON_PAUSE 2 +#define WA2_USERBUTTON_STOP 3 +#define WA2_USERBUTTON_NEXT 4 + +#define WA2_USERBUTTONMOD_NONE 0 +#define WA2_USERBUTTONMOD_SHIFT 1 +#define WA2_USERBUTTONMOD_CTRL 2 + +#define WINAMP_MAIN_WINDOW 40258 +#define WINAMP_OPTIONS_MINIBROWSER 40298 +#define WINAMP_OPTIONS_VIDEO 40328 +#define WINAMP_OPTIONS_PLEDIT 40040 +#define WINAMP_OPTIONS_EQ 40036 + +#define WINAMP_FILE_LOC 40185 +#define WINAMP_FILE_PLAY 40029 +#define WINAMP_FILE_DIR 40187 + +//----------------------------------------------------------------------------------------------- + +#include +#include "../gen_ml/ml.h" +#include "../winamp/wa_ipc.h" +#include "../winamp/ipc_pe.h" + +//----------------------------------------------------------------------------------------------- + +class Winamp2FrontEnd { + public: + friend BOOL CALLBACK findVisWndProc(HWND hwnd, LPARAM lParam); + Winamp2FrontEnd(); + virtual ~Winamp2FrontEnd(); + + void init(HWND hwndParent); + + const char *getVersion(); + + void enqueueFile(const wchar_t *file); + + /* + WA2_USERBUTTON_PLAY + WA2_USERBUTTON_PAUSE + WA2_USERBUTTON_STOP + WA2_USERBUTTON_NEXT + WA2_USERBUTTON_PREV + + WA2_USERBUTTONMOD_SHIFT + WA2_USERBUTTONMOD_CTRL + */ + void userButton(int button, int modifier); + + int isPlaying(); // 0 (false) or 1 (true) + int isPaused(); // 0 (false) or 1 (true) + int isStopped(); // if !isPlaying() and !isStopped() and !isPaused(), we're between tracks + + int getPosition(); // in ms + int getLength(); // in ms + int seekTo(int ms); // in millisecond + + void setVolume(int v); // 0 (silence) to 255 (loud) + int getVolume(); // 0 (silence) to 255 (loud) + + void setPanning(int p); // 0 (left) to 255 (right) + int getPanning(); // 0 (left) to 255 (right) + + void setShuffle(int shuffle); + int getShuffle(); + + void setRepeat(int repeat); + int getRepeat(); + + void setManualPlaylistAdvance(int manual); + int getManualPlaylistAdvance(); + + /* + WA2_GETINFO_SAMPLERATE : Samplerate (i.e. 44100) + WA2_GETINFO_BITRATE : Bitrate (i.e. 128) + WA2_GETINFO_CHANNELS : Channels (i.e. 2) + */ + int getInfo(int wa2_getinfo); + + /* + WA2_EQDATA_FIRSTBAND/LASTBAND (0 to 9) : The 10 bands of EQ data. 0-63 (+20db - -20db) + WA2_EQDATA_PREAMP : The preamp value. 0-63 (+20db - -20db) + WA2_EQDATA_ENABLED : Enabled. zero if disabled, nonzero if enabled. + WA2_EQDATA_AUTO : Autoload. zero if disabled, nonzero if enabled. + */ + int getEqData(int wa2_eqdata); + void setEqData(int wa2_eqdata, int val); + + void enableWindows(int enabled); + int areWindowsEnabled(); + void setWindowsVisible(int visible); + int areWindowsVisible(); + int isMainWindowVisible(); + + void *CanPlay(const wchar_t *); + bool IsPlaylist(const wchar_t *fn); + int getCurPlaylistEntry(); + int getPlaylistLength(); + + const wchar_t *GetCurrentTitle(); + const wchar_t *GetCurrentFile(); + const wchar_t *getTitle(int plentry); + const char *getFile(int plentry); + const wchar_t *getFileW(int plentry); + + void setCurTitle(const wchar_t* new_title); + + void playAudioCD(int cd); // id of cd (0 to 4) + + void openFileDialog(HWND w); + void openUrlDialog(HWND w); + void openUrl(const wchar_t *url); + void openDirectoryDialog(HWND w); + void ejectPopupMenu(); + void previousPopupMenu(); + void nextPopupMenu(); + void playPopupMenu(); + void pausePopupMenu(); + void stopPopupMenu(); + void setDialogBoxParent(HWND w); + void updateDialogBoxParent(HWND w); + + /* + IPC_GETWND_EQ + IPC_GETWND_PE + IPC_GETWND_MB + IPC_GETWND_VIDEO + */ + HWND getWnd(int wnd); + int isWindowVisible(intptr_t which); + void setWindowVisible(intptr_t which, int visible); + HWND getMediaLibrary(); + void ensureMediaLibraryLoaded(); + + int isOnTop(); + void setOnTop(int ontop); + void toggleOnTop(); + + // screen coordinates + void triggerPopupMenu(int x, int y); + void triggerEQPresetMenu(int x, int y); + int triggerFileMenu(int x, int y, int width, int height); + int triggerPlayMenu(int x, int y, int width, int height); + int triggerOptionsMenu(int x, int y, int width, int height); + int triggerWindowsMenu(int x, int y, int width, int height); + int triggerHelpMenu(int x, int y, int width, int height); + int triggerPEFileMenu(int x, int y, int width, int height); + int triggerPEPlaylistMenu(int x, int y, int width, int height); + int triggerPESortMenu(int x, int y, int width, int height); + int triggerPEHelpMenu(int x, int y, int width, int height); + int triggerMLFileMenu(int x, int y, int width, int height); + int triggerMLViewMenu(int x, int y, int width, int height); + int triggerMLHelpMenu(int x, int y, int width, int height); + int triggerPEListOfListsMenu(int x, int y); + + HMENU getTopMenu(); + HMENU getPopupMenu(); + int adjustOptionsPopupMenu(int direction); + + enum { + WA2_MAINMENUBAR_FILE = 0, + WA2_MAINMENUBAR_PLAY = 1, + WA2_MAINMENUBAR_OPTIONS = 2, + WA2_MAINMENUBAR_WINDOWS = 3, + WA2_MAINUMENUBAR_HELP = 4, + }; + + HMENU getMenuBarMenu(int which); + int adjustFFWindowsMenu(int direction); + int adjustFFOptionsMenu(int direction); + + HWND getMainWindow(); + + void quit(); + + char * (*export_sa_get_deprecated)(); + char * (*export_sa_get)(char data[75*2+8]); + void (*export_sa_setreq)(int); + int (*export_vu_get)(int channel); + + enum { + WA2_PLEDITPOPUP_ADD = 0, + WA2_PLEDITPOPUP_REM = 1, + WA2_PLEDITPOPUP_SEL = 2, + WA2_PLEDITPOPUP_MISC = 3, + WA2_PLEDITPOPUP_LIST = 4, + }; + + void sendPlCmd(int which, int x=0, int y=0, int menu_align_flag=0); + + enum { + WA2_MBCMD_BACK = 0, + WA2_MBCMD_FORWARD = 1, + WA2_MBCMD_STOP = 2, + WA2_MBCMD_RELOAD = 3, + WA2_MBPOPUP_MISC = 4, + }; + + void registerGlobalHotkey(const char *name, int msg, int wparam, int lparam, int flags, const char *id); + +#ifdef MINIBROWSER_SUPPORT + void sendMbCmd(int which, int x=0, int y=0, int menu_align_flag=0); +#endif + + enum { + WA2_VIDCMD_FULLSCREEN = 0, + WA2_VIDCMD_1X = 1, + WA2_VIDCMD_2X = 2, + WA2_VIDCMD_LIB = 3, + WA2_VIDPOPUP_MISC = 4, + WA2_VIDCMD_EXIT_FS = 5, + }; + + void sendVidCmd(int which, int x=0, int y=0, int menu_align_flag=0); + int hasVideoSupport(); + int isPlayingVideo(); + int isPlayingVideoFullscreen(); + int isDoubleSize(); + int getTimeDisplayMode(); + + void toggleVis(); + int isVisRunning(); + HWND getVisWnd(); + + IDropTarget *getDropTarget(); + + int getBitrate(); // in kbps + int getSamplerate(); // in khz + int getChannels(); // 1 mono, 2 stereo ... + + int isValidEmbedWndState(embedWindowState *ws); + + int PE_getNumItems(); + fileinfo2 *PE_getFileTitle(int index); + fileinfo2W *PE_getFileTitleW(int index); + int PE_getCurrentIndex(); + void PE_setCurrentIndex(int i); + + void switchSkin(const wchar_t *skinname); + void visNext(); + void visPrev(); + void visRandom(int set); + void pollVisRandom(); + void visFullscreen(); + void visConfig(); + void visMenu(); + + void setIdealVideoSize(int w, int h) { video_ideal_width = w; video_ideal_height = h; } + void getIdealVideoSize(int *w, int *h); + + int getStopOnVideoClose(); + void setStopOnVideoClose(int stop); + + int GetVideoResize(); + void SetVideoResize(int stop); + + virtual int isVis(HWND hwnd); // checks children too + + HWND getPreferencesWindow(); + void setPlEditWidthHeight(int width, int height); + + HINSTANCE getLanguagePackInstance(); + + void openTrackInfo(); + const char *getOutputPlugin(); + + void setDrawBorders(int d); + void disableSkinnedCursors(int disable); + + int getMetaData(const wchar_t *filename, const wchar_t *name, wchar_t *data, int data_len); + void GetFileInfo(const wchar_t *filename, wchar_t *title, int titleCch, int *length); + + void invalidateCache(); + + const char *getVideoInfoString(); + + void playFile(const wchar_t *file); + void rewind5s(); + void forward5s(); + void endOfPlaylist(); + void startOfPlaylist(); + void stopWithFade(); + void stopAfterCurrent(); + + void clearPlaylist(); + + int isWindowShade(int wnd); + + int getCurTrackRating(); + void setCurTrackRating(int rating); + + int isExitEnabled(); + int pushExitDisabled(); + int popExitDisabled(); + + + int DownloadFile(const char *url, const wchar_t *destfilepath = L"", bool addToMl = true, bool notifyDownloadsList = true); + void getDownloadPath(wchar_t path2store[MAX_PATH]); + void setDownloadPath(const wchar_t * path2store); + + bool GetAlbumArt(const wchar_t *filename); + bool IsWinampPro(); + +private: + void setFoundVis() { foundvis = 1; } + char *m_version; + HWND hwnd_winamp; + HWND hwnd_playlist; + int foundvis; + int enabled; + int visible; + int video_ideal_width; + int video_ideal_height; + DWORD cached_length_time; + int got_length_cache; + int cached_length; + + DWORD cached_pos_time; + int got_pos_cache; + int cached_pos; + + int saved_video, +#ifdef MINIBROWSER_SUPPORT + saved_mb, +#endif + saved_pe, saved_eq, saved_main; +}; + +//----------------------------------------------------------------------------------------------- + +extern Winamp2FrontEnd wa2; + +BOOL DoTrackPopup(HMENU hMenu, UINT fuFlags, int x, int y, HWND hwnd); + +#endif \ No newline at end of file diff --git a/Src/Plugins/General/gen_ff/wa2groupdefs.cpp b/Src/Plugins/General/gen_ff/wa2groupdefs.cpp new file mode 100644 index 00000000..4075aaa4 --- /dev/null +++ b/Src/Plugins/General/gen_ff/wa2groupdefs.cpp @@ -0,0 +1,36 @@ +#include +#include "wa2groupdefs.h" +#include + +//----------------------------------------------------------------------------------------------- +Wa2Groupdefs::Wa2Groupdefs() { + WASABI_API_SYSCB->syscb_registerCallback(this); +} + +//----------------------------------------------------------------------------------------------- +Wa2Groupdefs::~Wa2Groupdefs() { + WASABI_API_SYSCB->syscb_deregisterCallback(this); +} + +//----------------------------------------------------------------------------------------------- +int Wa2Groupdefs::skincb_onBeforeLoadingElements() { + StringW s; + + // header + + s = L"buf:" + L"" + L"" + + L"" + L" " + L""; + + // footer + + s += L""; + + WASABI_API_SKIN->loadSkinFile(s); + return 1; +} + diff --git a/Src/Plugins/General/gen_ff/wa2groupdefs.h b/Src/Plugins/General/gen_ff/wa2groupdefs.h new file mode 100644 index 00000000..bcc61741 --- /dev/null +++ b/Src/Plugins/General/gen_ff/wa2groupdefs.h @@ -0,0 +1,18 @@ +#ifndef __WA2GROUPDEFS_H +#define __WA2GROUPDEFS_H + +#include + +//----------------------------------------------------------------------------------------------- + +class Wa2Groupdefs : public SkinCallbackI { + public: + Wa2Groupdefs(); + virtual ~Wa2Groupdefs(); + + int skincb_onBeforeLoadingElements(); + + private: +}; + +#endif diff --git a/Src/Plugins/General/gen_ff/wa2playlist.cpp b/Src/Plugins/General/gen_ff/wa2playlist.cpp new file mode 100644 index 00000000..696590d7 --- /dev/null +++ b/Src/Plugins/General/gen_ff/wa2playlist.cpp @@ -0,0 +1,2 @@ +#include +#include "wa2playlist.h" diff --git a/Src/Plugins/General/gen_ff/wa2playlist.h b/Src/Plugins/General/gen_ff/wa2playlist.h new file mode 100644 index 00000000..d86f8167 --- /dev/null +++ b/Src/Plugins/General/gen_ff/wa2playlist.h @@ -0,0 +1,11 @@ +#ifndef _WA2PLAYLIST_H +#define _WA2PLAYLIST_H + +class Wa2Playlist +{ +public: + Wa2Playlist() {} + virtual ~Wa2Playlist() {} +}; + +#endif \ No newline at end of file diff --git a/Src/Plugins/General/gen_ff/wa2pldirobj.cpp b/Src/Plugins/General/gen_ff/wa2pldirobj.cpp new file mode 100644 index 00000000..66389851 --- /dev/null +++ b/Src/Plugins/General/gen_ff/wa2pldirobj.cpp @@ -0,0 +1,366 @@ +#include +#include "main.h" +#include "wa2pldirobj.h" +#include "wa2frontend.h" + +#include +#include "wa2pledit.h" + +/* +TODO: +register for playlists callbacks so we can keep up to date +drag&drop re-ordering +in-place renaming +widget for editing playlists + +*/ +const wchar_t plDirXuiObjectStr[] = L"PlaylistDirectory"; // This is the xml tag +char plDirXuiSvcName[] = "Playlist Directory XUI object"; // this is the name of the xuiservice + +static PlDirScriptObjectController _pldirController; +ScriptObjectController *pldirController = &_pldirController; + +BEGIN_SERVICES(wa2PlDirObj_Svcs); +DECLARE_SERVICETSINGLE(svc_scriptObject, PlDirScriptObjectSvc); +DECLARE_SERVICE(XuiObjectCreator); +END_SERVICES(wa2PlDirObj_Svcs, _wa2PlDirObj_Svcs); + +#define CB_POPULATE 0x6273 +#ifdef _X86_ +extern "C" +{ + int _link_wa2PlDirObj_Svcs; +} +#else +extern "C" +{ + int __link_wa2PlDirObj_Svcs; +} +#endif + +// ----------------------------------------------------------------------------------------------------- +// Service +ScriptObjectController *PlDirScriptObjectSvc::getController(int n) +{ + switch (n) + { + case 0: + return pldirController; + } + return NULL; +} + +// ----------------------------------------------------------------------------------------------------- +// PlDirObject +PlDirObject::PlDirObject() +{ + getScriptObject()->vcpu_setInterface(PLDIR_SCRIPTOBJECT_GUID, (void *)static_cast(this)); + getScriptObject()->vcpu_setClassName(L"PlDir"); + getScriptObject()->vcpu_setController(pldirController); + + _pldirController.mylist.addItem(this); +} + +PlDirObject::~PlDirObject() +{ + int numItems = getItemCount(); + + for (int i=0;isyscb_deregisterCallback(static_cast(this)); + _pldirController.mylist.removeItem(this); +} + +int PlDirObject::playlistcb_added(size_t index) +{ + postDeferredCallback(CB_POPULATE); + return 1; +} + +int PlDirObject::playlistcb_saved(size_t index) +{ + postDeferredCallback(CB_POPULATE); + return 1; +} + +int PlDirObject::onDeferredCallback(intptr_t p1, intptr_t p2) +{ + if (p1 == CB_POPULATE) + { + Populate(); + return 1; + } + return ListWnd::onDeferredCallback(p1, p2); +} + +void PlDirObject::Populate() +{ + int numItems = getItemCount(); + + for (int i=0;iLock(); + size_t count = AGAVE_API_PLAYLISTS->GetCount(); + for (size_t i=0;i!=count;i++) + { + const wchar_t *playlistName = AGAVE_API_PLAYLISTS->GetName(i); + GUID *playlist_guid = new GUID(AGAVE_API_PLAYLISTS->GetGUID(i)); + addItem(playlistName, (LPARAM)playlist_guid); // TODO: malloc pointer to GUID and store + wchar_t temp[256] = {0}; + size_t numItems=0; + AGAVE_API_PLAYLISTS->GetInfo(i, api_playlists_itemCount, &numItems, sizeof(numItems)); + WCSNPRINTF(temp, 256, L"%u", numItems); + this->setSubItem(i, 1, temp); + AGAVE_API_PLAYLISTS->GetInfo(i, api_playlists_totalTime, &numItems, sizeof(numItems)); //numitems holds now time + wchar_t buf[1024] = {0}; + TimeFmt::printHourMinSec(numItems, buf, 1024, 1); + this->setSubItem(i, 2, buf); + } + AGAVE_API_PLAYLISTS->Unlock(); + } +} + +int PlDirObject::onInit() +{ + /*colresize = 1; + resizing_col = true; + colresizeo = 1;*/ + + ListWnd::onInit(); + setName(L"Playlists"); + //setItemIcon( + //setShowColumnsHeaders(FALSE); + ListColumn *nameCol = new ListColumn(L"Playlist Title", 1); + //nameCol->setWidth(100); + insertColumn(nameCol); + /*ListColumn *countCol= new ListColumn(L"Items", 0); + countCol->setWidth(40); + countCol->setAlignment(COL_RIGHTALIGN); + insertColumn(countCol,1,COL_RIGHTALIGN); + ListColumn *lenCol= new ListColumn(L"Time", 0); + lenCol->setWidth(50); + lenCol->setAlignment(COL_CENTERALIGN); + insertColumn(lenCol, 1, COL_CENTERALIGN); + insertColumn(lenCol);*/ + addColumn(L"Items", 40, 0, COL_RIGHTALIGN); + addColumn(L"Time", 55, 0, COL_RIGHTALIGN); + + //addColumn(L"Time", 100); + Populate(); + + WASABI_API_SYSCB->syscb_registerCallback(static_cast(this)); + + return 1; +} +/* +int PlDirObject::onResize() +{ + ListWnd::onResize(); + RECT r; + getClientRect(&r); + ListColumn *lc = getColumn(0); + lc->setWidth(r.right - r.left); + return 1; +}*/ + +void PlDirObject::onDoubleClick(int itemnum) +{ + ListWnd::onDoubleClick(itemnum); + // find a playlisteditor object near us + Wa2PlaylistEditor *editor = static_cast(findWindowByInterface(Wa2PlaylistEditor::getInterfaceGuid())); + if (editor != NULL) + { + Wa2Playlist *playlist = getPlaylist(itemnum); + editor->setPlaylist(playlist); + } + GUID *playlist_guid = (GUID *)this->getItemData(itemnum); + if (playlist_guid) + { + size_t index; + AGAVE_API_PLAYLISTS->Lock(); + if (AGAVE_API_PLAYLISTS->GetPosition(*playlist_guid, &index) == API_PLAYLISTS_SUCCESS) + { + // TODO: benski> try to retrieve setting from ml_playlists for play vs enqueue on double click + const wchar_t *playlist_filename = AGAVE_API_PLAYLISTS->GetFilename(index); + wa2.playFile(playlist_filename); + } + AGAVE_API_PLAYLISTS->Unlock(); + } +} + +Wa2Playlist *PlDirObject::getPlaylist(int itemnum) +{ + return (Wa2Playlist *) - 1; +} + +// ----------------------------------------------------------------------------------------------------- +// PlDirScriptObjectController + +function_descriptor_struct PlDirScriptObjectController::exportedFunction[] = { + {L"showCurrentlyPlayingEntry", 0, (void*)PlDirScriptObjectController::pldir_showCurrentlyPlayingEntry}, + {L"getNumItems", 0, (void*)PlDirScriptObjectController::pldir_getNumItems}, + {L"renameItem", 2, (void*)PlDirScriptObjectController::pldir_renameItem}, + {L"getItemName", 1, (void*)PlDirScriptObjectController::pldir_getItemName}, + {L"playItem", 1, (void*)PlDirScriptObjectController::pldir_playItem}, + {L"enqueueItem", 1, (void*)PlDirScriptObjectController::pldir_enqueueItem}, + {L"refresh", 0, (void*)PlDirScriptObjectController::pldir_refresh}, +}; + +int PlDirScriptObjectController::getNumFunctions() +{ + return sizeof(exportedFunction) / sizeof(function_descriptor_struct); +} + +ScriptObject *PlDirScriptObjectController::instantiate() +{ + PlDirObject *c = new PlDirObject; + if (!c) return NULL; + return c->getScriptObject(); +} + +void PlDirScriptObjectController::destroy(ScriptObject *o) +{ + PlDirObject *obj = static_cast(o->vcpu_getInterface(PLDIR_SCRIPTOBJECT_GUID)); + ASSERT(obj != NULL); + delete obj; +} + +PlDirScriptObjectController::~PlDirScriptObjectController() +{ + // Destroy our list, otherwise we get a big bang on wasabi shutdown + if (_pldirController.mylist.getNumItems() > 0) + _pldirController.mylist.deleteAll(); +} + +void *PlDirScriptObjectController::encapsulate(ScriptObject *o) +{ + return NULL; // nobody can inherits from me yet (see rootobj or guiobj in studio if you want to allow that) +} + +void PlDirScriptObjectController::deencapsulate(void *) +{} + +// Script calls +scriptVar PlDirScriptObjectController::pldir_showCurrentlyPlayingEntry(SCRIPT_FUNCTION_PARAMS, ScriptObject *o) +{ + SCRIPT_FUNCTION_INIT + + HWND hPeWindow = wa2.getWnd(IPC_GETWND_PE); + SendMessageW(hPeWindow, WM_USER, 666, wa2.PE_getCurrentIndex()); + + RETURN_SCRIPT_VOID; +} + +scriptVar PlDirScriptObjectController::pldir_getNumItems(SCRIPT_FUNCTION_PARAMS, ScriptObject *o) +{ + SCRIPT_FUNCTION_INIT + + AGAVE_API_PLAYLISTS->Lock(); + size_t n = AGAVE_API_PLAYLISTS->GetCount(); + AGAVE_API_PLAYLISTS->Unlock(); + + return MAKE_SCRIPT_INT(n); +} + +scriptVar PlDirScriptObjectController::pldir_refresh(SCRIPT_FUNCTION_PARAMS, ScriptObject *o) +{ + SCRIPT_FUNCTION_INIT + + for (int i = 0; i < _pldirController.mylist.getNumItems(); i++) + { + PlDirObject *p = _pldirController.mylist.enumItem(i); + if (p) p->Populate(); + } + + // TODO: add refresh for ml pl view! + //HWND hMlWindow = wa2.getMediaLibrary(); + //SendMessageW(hMlWindow, ???); + + //AGAVE_API_PLAYLISTS->MoveBefore(2,1); + + RETURN_SCRIPT_VOID; +} + +scriptVar PlDirScriptObjectController::pldir_renameItem(SCRIPT_FUNCTION_PARAMS, ScriptObject *o, scriptVar item, scriptVar name) +{ + SCRIPT_FUNCTION_INIT + + AGAVE_API_PLAYLISTS->Lock(); + AGAVE_API_PLAYLISTS->RenamePlaylist(GET_SCRIPT_INT(item), GET_SCRIPT_STRING(name)); + AGAVE_API_PLAYLISTS->Unlock(); + + for (int i = 0; i < _pldirController.mylist.getNumItems(); i++) + { + PlDirObject* p = _pldirController.mylist.enumItem(i); + if (p) p->Populate(); + } + + //AGAVE_API_PLAYLISTS->MoveBefore(2,1); + + RETURN_SCRIPT_VOID; +} + +scriptVar PlDirScriptObjectController::pldir_getItemName(SCRIPT_FUNCTION_PARAMS, ScriptObject *o, scriptVar item) +{ + SCRIPT_FUNCTION_INIT + + AGAVE_API_PLAYLISTS->Lock(); + const wchar_t *k = AGAVE_API_PLAYLISTS->GetName(GET_SCRIPT_INT(item)); + AGAVE_API_PLAYLISTS->Unlock(); + + return MAKE_SCRIPT_STRING(k); +} + +scriptVar PlDirScriptObjectController::pldir_playItem(SCRIPT_FUNCTION_PARAMS, ScriptObject *o, scriptVar _itemnum) +{ + SCRIPT_FUNCTION_INIT + int itemnum = GET_SCRIPT_INT(_itemnum); + + GUID playlist_guid = AGAVE_API_PLAYLISTS->GetGUID(itemnum); + + size_t index; + AGAVE_API_PLAYLISTS->Lock(); + if (AGAVE_API_PLAYLISTS->GetPosition(playlist_guid, &index) == API_PLAYLISTS_SUCCESS) + { + // TODO: benski> try to retrieve setting from ml_playlists for play vs enqueue on double click + const wchar_t *playlist_filename = AGAVE_API_PLAYLISTS->GetFilename(index); + wa2.playFile(playlist_filename); + } + AGAVE_API_PLAYLISTS->Unlock(); + + RETURN_SCRIPT_VOID +} + +scriptVar PlDirScriptObjectController::pldir_enqueueItem(SCRIPT_FUNCTION_PARAMS, ScriptObject *o, scriptVar _itemnum) +{ + SCRIPT_FUNCTION_INIT + + int itemnum = GET_SCRIPT_INT(_itemnum); + + GUID playlist_guid = AGAVE_API_PLAYLISTS->GetGUID(itemnum); + + size_t index; + AGAVE_API_PLAYLISTS->Lock(); + if (AGAVE_API_PLAYLISTS->GetPosition(playlist_guid, &index) == API_PLAYLISTS_SUCCESS) + { + // TODO: benski> try to retrieve setting from ml_playlists for play vs enqueue on double click + const wchar_t *playlist_filename = AGAVE_API_PLAYLISTS->GetFilename(index); + wa2.enqueueFile(playlist_filename); + } + AGAVE_API_PLAYLISTS->Unlock(); + + RETURN_SCRIPT_VOID +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_ff/wa2pldirobj.h b/Src/Plugins/General/gen_ff/wa2pldirobj.h new file mode 100644 index 00000000..59a1d9b1 --- /dev/null +++ b/Src/Plugins/General/gen_ff/wa2pldirobj.h @@ -0,0 +1,105 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include "wa2playlist.h" +#include +#include + +class PlDirObject; + +extern ScriptObjectController *pldirController; + +// ----------------------------------------------------------------------------------------------------- +// ScriptObject Service +class PlDirScriptObjectSvc : public svc_scriptObjectI { + +public: + PlDirScriptObjectSvc() {}; + virtual ~PlDirScriptObjectSvc() {}; + + static const char *getServiceName() { return "PlDir script object"; } + virtual ScriptObjectController *getController(int n); +}; + +// ----------------------------------------------------------------------------------------------------- +// Script classe GUIDS + +// {61A7ABAD-7D79-41f6-B1D0-E1808603A4F4} +static const GUID PLDIR_SCRIPTOBJECT_GUID = +{ 0x61a7abad, 0x7d79, 0x41f6, { 0xb1, 0xd0, 0xe1, 0x80, 0x86, 0x3, 0xa4, 0xf4 } }; + +// ----------------------------------------------------------------------------------------------------- +// ScriptObject Interface + +// PlDir +class PlDirObject : public ListWnd, public PlaylistCallbackI +{ + public: + + PlDirObject(); + virtual ~PlDirObject(); + + virtual int onInit(); + //virtual int onResize(); + virtual int wantResizeCols() { return 0; } + virtual int wantHScroll() { return 0; } + + virtual void onDoubleClick(int itemnum); + virtual Wa2Playlist *getPlaylist(int itemnum); + + /* PlaylistCallbackI method overrides */ + int playlistcb_added(size_t index); + int playlistcb_saved(size_t index); + + + int onDeferredCallback(intptr_t p1, intptr_t p2); + + void Populate(); + private: + PtrList playlists; + +}; + +// ----------------------------------------------------------------------------------------------------- +// ScriptObjectControllers for our script classes + +// PlDir +class PlDirScriptObjectController : public ScriptObjectControllerI { + public: + virtual const wchar_t *getClassName() { return L"PlDir"; } + virtual const wchar_t *getAncestorClassName() { return L"Object"; } + virtual ScriptObjectController *getAncestorController() { return NULL; } + virtual int getNumFunctions(); + virtual const function_descriptor_struct *getExportedFunctions() { return exportedFunction; } + virtual GUID getClassGuid() { return PLDIR_SCRIPTOBJECT_GUID; } + virtual int getInstantiable() { return 0; } + virtual int getReferenceable() { return 0; } + virtual ScriptObject *instantiate(); + virtual void destroy(ScriptObject *o); + virtual void *encapsulate(ScriptObject *o); + virtual void deencapsulate(void *o); + + virtual ~PlDirScriptObjectController(); + + // Maki functions table + static scriptVar pldir_showCurrentlyPlayingEntry(SCRIPT_FUNCTION_PARAMS, ScriptObject *o); + static scriptVar pldir_getNumItems(SCRIPT_FUNCTION_PARAMS, ScriptObject *o); + static scriptVar pldir_renameItem(SCRIPT_FUNCTION_PARAMS, ScriptObject *o, scriptVar item, scriptVar name); + static scriptVar pldir_getItemName(SCRIPT_FUNCTION_PARAMS, ScriptObject *o, scriptVar item); + static scriptVar pldir_playItem(SCRIPT_FUNCTION_PARAMS, ScriptObject *o, scriptVar item); + static scriptVar pldir_enqueueItem(SCRIPT_FUNCTION_PARAMS, ScriptObject *o, scriptVar item); + static scriptVar pldir_refresh(SCRIPT_FUNCTION_PARAMS, ScriptObject *o); + PtrList mylist; + + private: + static function_descriptor_struct exportedFunction[]; +}; + +extern const wchar_t plDirXuiObjectStr[]; +extern char plDirXuiSvcName[]; +class PlDirXuiSvc : public XuiObjectSvc {}; + diff --git a/Src/Plugins/General/gen_ff/wa2pledit.cpp b/Src/Plugins/General/gen_ff/wa2pledit.cpp new file mode 100644 index 00000000..99b021fb --- /dev/null +++ b/Src/Plugins/General/gen_ff/wa2pledit.cpp @@ -0,0 +1,241 @@ +#include +#include "wa2pledit.h" +#include +#include +#include "wa2frontend.h" +#include +#include "../nu/AutoWide.h" +#ifndef _WASABIRUNTIME + +BEGIN_SERVICES(Wa2Pledit_Svc); +DECLARE_SERVICE(XuiObjectCreator); +END_SERVICES(Wa2Pledit_Svc, _Wa2Pledit_Svc); + +#ifdef _X86_ +extern "C" { int _link_Wa2PleditXuiSvc; } +#else +extern "C" { int __link_Wa2PleditXuiSvc; } +#endif + +#endif + +// ----------------------------------------------------------------------- +const wchar_t Wa2PleditXuiObjectStr[] = L"PlaylistEditor"; // This is the xml tag +char Wa2PleditXuiSvcName[] = "Playlist Editor xui object"; + +// ----------------------------------------------------------------------- + +#define TRACKLENGTH_WIDTH 40 +#define DC_CURRENTIDX 10 +#define DC_PLAY 11 +#define TIMER_REFRESHLIST 12 + +static SkinColor text(L"pledit.text"), rotext(L"pledit.text.readonly"); +static SkinColor text_disabled(L"pledit.text.disabled"); +static SkinColor bgcolor(L"pledit.bgcolor"); +static SkinColor currenttext(L"wasabi.itemlist.outline.focus"); +static SkinColor currentoutline(L"wasabi.itemlist.outline.current"); + +PtrList Wa2PlaylistEditor::editors; + +COLORREF Wa2PlaylistEditor::getTextColor(LPARAM lParam) { +// if (playlist == NULL) return text; +// PlaylistEntry *entry = playlist->enumEntry(lParam); +// if (entry && entry->getCurrent()) +// return currenttext; +// else + if (lParam == cur_index) + return currenttext; + return text; +} + +COLORREF Wa2PlaylistEditor::getBgColor(LPARAM lParam) { + return bgcolor; +} + +COLORREF Wa2PlaylistEditor::getFocusRectColor(LPARAM lParam) { + return currentoutline; +} + +int Wa2PlaylistEditor::needFocusRect(LPARAM lParam) +{ + return lParam == cur_index; +} + +Wa2PlaylistEditor::Wa2PlaylistEditor() +: curplaylist(0) +{ + curplaylist = NULL; + cur_index = wa2.PE_getCurrentIndex()+1; +} + +Wa2PlaylistEditor::~Wa2PlaylistEditor() { + killTimer(TIMER_REFRESHLIST); + editors.removeItem(this); +} + +int Wa2PlaylistEditor::onInit() +{ + WA2PLAYLISTEDITOR_PARENT::onInit(); + setShowColumnsHeaders(FALSE); + addColumn(L"Track #", calcTrackNumWidth()); + addColumn(L"Track Name", 200); + addColumn(L"Length", TRACKLENGTH_WIDTH, 1, COL_RIGHTALIGN); + editors.addItem(this); + return 1; +} + +void Wa2PlaylistEditor::onVScrollToggle(int set) { + resizeCols(); +} + +int Wa2PlaylistEditor::onResize() { + WA2PLAYLISTEDITOR_PARENT::onResize(); + resizeCols(); + return 1; +} + +void Wa2PlaylistEditor::resizeCols() { + RECT r; + getClientRect(&r); + ListColumn *c0 = getColumn(0); + int tw = calcTrackNumWidth(); + c0->setWidth(tw); + ListColumn *c1 = getColumn(1); + c1->setWidth(r.right-r.left - tw - TRACKLENGTH_WIDTH); +} + +int Wa2PlaylistEditor::calcTrackNumWidth() +{ + WndCanvas bc(this); + Wasabi::FontInfo fontInfo; + fontInfo.pointSize = getFontSize(); + int cw = getFontSize(); + int dw = 8; + bc.getTextExtent(L"W", &cw, NULL, &fontInfo); + bc.getTextExtent(L".", &dw, NULL, &fontInfo); + int n = getNumItems(); + if (n < 0) return dw+cw; + float f = log10((float)n); + int l = (int)ceil(f); + return (l * cw) + dw + 5; +} + +void *Wa2PlaylistEditor::getInterface(GUID interface_guid) { + if (interface_guid == Wa2PlaylistEditor::getInterfaceGuid()) { + return this; + } + return WA2PLAYLISTEDITOR_PARENT::getInterface(interface_guid); +} + +void Wa2PlaylistEditor::setPlaylist(Wa2Playlist *playlist) { + if (curplaylist == playlist) + return; + curplaylist = playlist; + loadList(); +} + +void Wa2PlaylistEditor::_loadList() { + int y = getScrollY(); + + setRedraw(FALSE); + + deleteAllItems(); + + if (curplaylist == (Wa2Playlist *)-1) { + + // load the current playlist (the one in the real playlist editor) + int n = wa2.PE_getNumItems(); + + for (int i=0;ifiletitle)); + setSubItem(i, 2, AutoWide(fi->filelength)); + } + } else { + // load an available playlist + } + + RECT r; + getClientRect(&r); + int ch = getContentsHeight(); + if (ch - y >= r.bottom-r.top) scrollToY(y); + else if (ch - r.bottom-r.top >= 0) scrollToY(ch - r.bottom-r.top); + else scrollToY(0); + + resizeCols(); + + postDeferredCallback(DC_CURRENTIDX); + setRedraw(TRUE); +} + +void Wa2PlaylistEditor::_onNewCurrentIndex(int idx) +{ + foreach(editors) + editors.getfor()->onNewCurrentIndex(idx); + endfor +} + +void Wa2PlaylistEditor::onNewCurrentIndex(int idx) +{ + if (idx!=cur_index) + { + int oldIdx = cur_index; + cur_index = idx; + invalidateItem(oldIdx); + invalidateItem(idx); + } +} + +void Wa2PlaylistEditor::onSetVisible(int show) { + WA2PLAYLISTEDITOR_PARENT::onSetVisible(show); + if (!show) getParent()->setFocus(); +} + +void Wa2PlaylistEditor::onPlaylistModified() { + if (curplaylist == (Wa2Playlist *)-1) + loadList(); +} + +void Wa2PlaylistEditor::_onPlaylistModified() { + // fist call the Pledit script object + SPlaylist::onPleditModified(); + // than all our old pledit xml objects + foreach(editors) + editors.getfor()->onPlaylistModified(); + endfor +} + +int Wa2PlaylistEditor::onDeferredCallback(intptr_t p1, intptr_t p2) +{ + if (p1 == DC_CURRENTIDX) + { + onNewCurrentIndex(wa2.PE_getCurrentIndex()+1); + return 1; + } + return WA2PLAYLISTEDITOR_PARENT::onDeferredCallback(p1, p2); +} + +void Wa2PlaylistEditor::onDoubleClick(int itemnum) +{ + WA2PLAYLISTEDITOR_PARENT::onDoubleClick(itemnum); + wa2.PE_setCurrentIndex(itemnum); + wa2.userButton(WA2_USERBUTTON_PLAY, 0); + _onNewCurrentIndex(itemnum+1); +} + +void Wa2PlaylistEditor::timerCallback(int id) +{ + if (id == TIMER_REFRESHLIST) { + killTimer(TIMER_REFRESHLIST); + _loadList(); + } +} + +void Wa2PlaylistEditor::loadList() +{ + killTimer(TIMER_REFRESHLIST); + setTimer(TIMER_REFRESHLIST, 500); +} diff --git a/Src/Plugins/General/gen_ff/wa2pledit.h b/Src/Plugins/General/gen_ff/wa2pledit.h new file mode 100644 index 00000000..9c08f1d8 --- /dev/null +++ b/Src/Plugins/General/gen_ff/wa2pledit.h @@ -0,0 +1,68 @@ +#ifndef _WA2PLEDIT_H +#define _WA2PLEDIT_H + +#include +#include "wa2playlist.h" + +#define WA2PLAYLISTEDITOR_PARENT ListWnd + +class Wa2PlaylistEditor; + +class Wa2PlaylistEditor : public WA2PLAYLISTEDITOR_PARENT +{ +public: + static GUID getInterfaceGuid() + { + // {265947B2-3EDB-453e-B748-EC17890F4FE4} + const GUID guid = + { 0x265947b2, 0x3edb, 0x453e, { 0xb7, 0x48, 0xec, 0x17, 0x89, 0xf, 0x4f, 0xe4 } }; + return guid; + } + + Wa2PlaylistEditor(); + virtual ~Wa2PlaylistEditor(); + + virtual int onInit(); + virtual int onResize(); + virtual int wantHScroll() { return 0; } + virtual void onVScrollToggle( int set ); + virtual COLORREF getTextColor( LPARAM lParam ); + virtual COLORREF getBgColor( LPARAM lParam ); + virtual void *getInterface( GUID interface_guid ); + virtual void setPlaylist( Wa2Playlist *playlist ); // -1 for working playlist + virtual int needFocusRect( LPARAM lParam ); + virtual COLORREF getFocusRectColor( LPARAM lParam ); + virtual void onSetVisible( int show ); + virtual int onDeferredCallback( intptr_t p1, intptr_t p2 ); + virtual void timerCallback( int id ); + virtual void onDoubleClick( int itemnum ); + + // object + virtual void onNewCurrentIndex( int idx ); + virtual void onPlaylistModified(); + + // class + static void _onNewCurrentIndex( int idx ); + static void _onPlaylistModified(); + virtual void loadList(); + +private: + void _loadList(); + void resizeCols(); + int calcTrackNumWidth(); + + Wa2Playlist *curplaylist; + static PtrList editors; + int cur_index; +}; + +// ----------------------------------------------------------------------- +extern const wchar_t Wa2PleditXuiObjectStr[]; +extern char Wa2PleditXuiSvcName[]; + +class Wa2PleditXuiSvc : public XuiObjectSvc {}; + + +#endif + + diff --git a/Src/Plugins/General/gen_ff/wa2songticker.cpp b/Src/Plugins/General/gen_ff/wa2songticker.cpp new file mode 100644 index 00000000..8067b056 --- /dev/null +++ b/Src/Plugins/General/gen_ff/wa2songticker.cpp @@ -0,0 +1,483 @@ +#include +#include "wa2songticker.h" +#include +#include +#include +#include +#include +#include "wa2frontend.h" +#include +#include +#include +#include + +// {9149C445-3C30-4e04-8433-5A518ED0FDDE} +static const GUID uioptions_guid = + { 0x9149c445, 0x3c30, 0x4e04, { 0x84, 0x33, 0x5a, 0x51, 0x8e, 0xd0, 0xfd, 0xde } }; + +// {280876CF-48C0-40bc-8E86-73CE6BB462E5} +static const GUID options_guid = + { 0x280876cf, 0x48c0, 0x40bc, { 0x8e, 0x86, 0x73, 0xce, 0x6b, 0xb4, 0x62, 0xe5 } }; + +const wchar_t songtickerXuiObjectStr[] = L"SongTicker"; // This is the xml tag +char songtickerXuiSvcName[] = "Song Ticker XUI object"; // this is the name of the xuiservice + +BEGIN_SERVICES(wa2SongTicker_Svcs); +DECLARE_SERVICE(XuiObjectCreator); +END_SERVICES(wa2SongTicker_Svcs, _wa2SongTicker_Svcs); + +#ifdef _X86_ +extern "C" +{ + int _link_wa2SongTicker_Svcs; +} +#else +extern "C" +{ + int __link_wa2SongTicker_Svcs; +} +#endif + +extern _float cfg_uioptions_textspeed; +extern _int cfg_uioptions_textincrement; + +#define TIMER_SONGTICKER_SCROLL 0x777 +//#define SONGTICKER_SCROLL_MS 200 // TODO: make adjustable (api_config, xml param, maki script object) +#define SONGTICKER_INCREMENT_PIXELS (cfg_uioptions_textincrement) // TODO: make adjustable? +#define SONGTICKER_SCROLL_MS ((13.0f*(float)cfg_uioptions_textincrement)/cfg_uioptions_textspeed) +#define SONGTICKER_SCROLL_ONE_PIXEL_MS (13.0f/cfg_uioptions_textspeed) +#define SONGTICKER_SKIP_MS 2000 // TODO: make adjustable + +XMLParamPair SongTicker::params[] = +{ + {SONGTICKER_TICKER, L"TICKER"}, +}; + +SongTicker::SongTicker() +{ + WASABI_API_MEDIACORE->core_addCallback(0, this); + song_title[0]=0; + song_length=-1; + position=0; + tickerMode=TICKER_SCROLL; + ticker_direction=1; + skipTimers=0; + grab_x = 0; + last_tick = Wasabi::Std::getTickCount(); + buffer_hw_valid=false; + textW=0; + + /* register XML parameters */ + xuihandle = newXuiHandle(); + CreateXMLParameters(xuihandle); + + CfgItem *ci=WASABI_API_CONFIG->config_getCfgItemByGuid(uioptions_guid); + if (ci) + { + viewer_addViewItem(ci->getDependencyPtr()); + } + + ci=WASABI_API_CONFIG->config_getCfgItemByGuid(options_guid); + if (ci) + { + viewer_addViewItem(ci->getDependencyPtr()); + } +} + +void SongTicker::CreateXMLParameters(int master_handle) +{ + //SONGTICKER_PARENT::CreateXMLParameters(master_handle); + int numParams = sizeof(params) / sizeof(params[0]); + hintNumberOfParams(xuihandle, numParams); + for (int i = 0;i < numParams;i++) + addParam(xuihandle, params[i], XUI_ATTRIBUTE_IMPLIED); +} + +SongTicker::~SongTicker() +{ + WASABI_API_MEDIACORE->core_delCallback(0, this); +// TODO: benski> is this the right place for this? + killTimer(TIMER_SONGTICKER_SCROLL); +} + +int SongTicker::onResize() +{ + killTimer(TIMER_SONGTICKER_SCROLL); + buffer_hw_valid=false; + invalidateBuffer(); + return SONGTICKER_PARENT::onResize(); +} + +int SongTicker::onInit() +{ + int r = SONGTICKER_PARENT::onInit(); + BuildTitle(); + return r; +} + +int SongTicker::corecb_onTitleChange(const wchar_t *title) +{ + BuildTitle(); + return 1; +} + +int SongTicker::corecb_onLengthChange(int newlength) +{ + song_length = newlength; + return 1; +} + +void SongTicker::BuildTitle() +{ + killTimer(TIMER_SONGTICKER_SCROLL); + const wchar_t *curFilename = wa2.GetCurrentFile(); + if (curFilename && *curFilename) + { + wa2.GetFileInfo(curFilename, song_title, 1024, &song_length); + WASABI_API_MEDIACORE->core_setTitle(song_title); + } + else + { + song_title[0]=0; + song_length=-1; + } + + if (!song_title[0] || ((unsigned long)song_title < 65536)) + { + display = WASABI_API_APP->main_getVersionString(); + } + else + { + // TODO: use TimeFmt:: to format the time + if (song_length >= 0) + display.printf(L"%s (%d:%02d)",song_title,song_length/60,song_length%60); + else + display.printf(L"%s",song_title); + } + + rotatingDisplay.printf(L"%s *** %s", display, display); + TextInfoCanvas c(this); + Wasabi::FontInfo fontInfo; + GetFontInfo(&fontInfo); + width_of_str_padded = c.getTextWidth(display, &fontInfo) + lpadding - rpadding; + width_of_str = c.getTextWidth(rotatingDisplay, &fontInfo) - c.getTextWidth(display, &fontInfo); + buffer_hw_valid=false; + invalidateBuffer(); +} + +int SongTicker::onBufferPaint(BltCanvas *canvas, int w, int h) +{ + SONGTICKER_PARENT::onBufferPaint(canvas, w, h); + + // TODO: benski> need to optimize this :) + RECT r = {0,0,w,h}; + canvas->fillRect(&r, RGB(0, 0, 0)); + + getClientRect(&r); + + Wasabi::FontInfo fontInfo; + GetFontInfo(&fontInfo); + /* + if (tickerMode == TICKER_OFF) + { + lpadding=min(lpadding, w); + w=max(w-lpadding+rpadding, 0); + canvas->textOut(lpadding, 0, w, h, display, &fontInfo); + return 1; + } + */ + const int textAreaWidth = r.right - r.left; + StringW *whichString = &display; + if (width_of_str_padded > textAreaWidth) // too big to fit? + { + switch (tickerMode) + { + case TICKER_OFF: + { + int extra = width_of_str_padded - textAreaWidth; + if (position>extra) + position=extra; + if (position<0) + position=0; + } + break; + + case TICKER_SCROLL: + { + // make sure our position isn't out of bounds + while (position < 0) + position += width_of_str; + + position %= (width_of_str); + + whichString = &rotatingDisplay; + skipTimers=0; + setTimer(TIMER_SONGTICKER_SCROLL, (int)SONGTICKER_SCROLL_MS); + } + break; + + case TICKER_BOUNCE: + { + int extra = width_of_str_padded- textAreaWidth; + if (position < 0) + { + position=0; + ticker_direction=1; + skipTimers=(int)(SONGTICKER_SKIP_MS/SONGTICKER_SCROLL_MS); + } + + if (position>=extra) + { + position=extra-1; + ticker_direction=-1; + skipTimers=(int)(SONGTICKER_SKIP_MS/SONGTICKER_SCROLL_MS); + } + if (position < 0) + position=0; + setTimer(TIMER_SONGTICKER_SCROLL, (int)SONGTICKER_SCROLL_MS); + } + break; + } + } + else // if there's enough room, just draw the string as-is + { + position=0; + } + int x =min(lpadding, w); + w=max(w-x+rpadding, 0); + canvas->textOut(lpadding, 0, w, h, whichString->getValueSafe(), &fontInfo); + + return 1; +} + +void SongTicker::timerCallback(int id) +{ + if (id == TIMER_SONGTICKER_SCROLL && !grab) + { + uint32_t this_tick = Wasabi::Std::getTickCount(); + if (TICKER_OFF == tickerMode) + { + killTimer(TIMER_SONGTICKER_SCROLL); + return; + } + if (skipTimers) + { + last_tick=this_tick; + skipTimers--; + return; + } + int numTicks=SONGTICKER_INCREMENT_PIXELS; + if (this_tick > last_tick) // make sure we havn't wrapped around + { + numTicks = (int)((this_tick - last_tick) / SONGTICKER_SCROLL_ONE_PIXEL_MS); + last_tick += (uint32_t)(numTicks * SONGTICKER_SCROLL_ONE_PIXEL_MS); // we do this instead of last_tick = this_tick, so we get some error shaping + } + else + last_tick=this_tick; + + // move the ticker + position+=(ticker_direction*numTicks); + // ask to be redrawn + if (numTicks) + invalidate(); + } + else + SONGTICKER_PARENT::timerCallback(id); +} + +void SongTicker::getBufferPaintSource(RECT *r) +{ + if (r) + { + RECT cr; + getClientRect(&cr); + const int textAreaWidth = cr.right - cr.left; + if (width_of_str_padded > textAreaWidth) // too big to fit? + { + switch (tickerMode) + { + case TICKER_OFF: + { + int extra = width_of_str_padded- textAreaWidth; + if (position>extra) + position=extra; + if (position<0) + position=0; + } + break; + + case TICKER_SCROLL: + { + // make sure our position isn't out of bounds + while (position < 0) + position += width_of_str; + + position %= (width_of_str); + } + break; + + case TICKER_BOUNCE: + { + int extra = width_of_str_padded - textAreaWidth; + + if (position>=extra) + { + position=extra-1; + ticker_direction=-1; + skipTimers=(int)(SONGTICKER_SKIP_MS/SONGTICKER_SCROLL_MS); + } + + if (position < 0) + { + position=0; + ticker_direction=1; + skipTimers=(int)(SONGTICKER_SKIP_MS/SONGTICKER_SCROLL_MS); + } + } + break; + } + } + else + { + position=0; + } + r->left = position; + r->right = cr.right - cr.left + position; + r->top = 0; + r->bottom = cr.bottom - cr.top; + } +} + +void SongTicker::getBufferPaintSize(int *w, int *h) +{ + RECT r; + getClientRect(&r); + int _w = r.right - r.left; + int _h = r.bottom - r.top; + + if (!buffer_hw_valid) + { + const int textAreaWidth = r.right - r.left; + StringW *whichString = &display; + if (width_of_str_padded > textAreaWidth && tickerMode == TICKER_SCROLL) // too big to fit? + { + whichString = &rotatingDisplay; + buffer_hw_valid=false; + } + + TextInfoCanvas canvas(this); + Wasabi::FontInfo fontInfo; + GetFontInfo(&fontInfo); + textW = canvas.getTextWidth(whichString->getValueSafe(), &fontInfo); + //int textH = canvas.getTextHeight(whichString->getValueSafe(), &fontInfo); + textW = textW + lpadding - rpadding; + buffer_hw_valid=true; + } + + *w = max(_w, textW); + *h = _h;//max(_h, textH); +} + +int SongTicker::setXuiParam(int _xuihandle, int attrid, const wchar_t *name, const wchar_t *strval) +{ + if (xuihandle != _xuihandle) return SONGTICKER_PARENT::setXuiParam(_xuihandle, attrid, name, strval); + switch (attrid) + { + case SONGTICKER_TICKER: + if (!WCSICMP(strval, L"bounce")) + tickerMode=TICKER_BOUNCE; + else if (!WCSICMP(strval, L"scroll")) + { + tickerMode=TICKER_SCROLL; + ticker_direction=1; + } + else if (!WCSICMP(strval, L"off")) + { + tickerMode=TICKER_OFF; + position=0; + killTimer(TIMER_SONGTICKER_SCROLL); + } + buffer_hw_valid=false; + invalidateBuffer(); + break; + default: + return 0; + } + return 1; +} + +int SongTicker::onAction(const wchar_t *action, const wchar_t *param, int x, int y, intptr_t p1, intptr_t p2, void *data, size_t datalen, ifc_window *source) +{ + int r = SONGTICKER_PARENT::onAction(action, param, x, y, p1, p2, data, datalen, source); + if(!WCSICMP(action, L"rebuildtitle")) + BuildTitle(); + + return r; +} + +int SongTicker::viewer_onEvent(api_dependent *item, const GUID *classguid, int event, intptr_t param, void *ptr, size_t ptrlen) +{ + if (*classguid == *CfgItem::depend_getClassGuid()) + { + if (event==CfgItem::Event_ATTRIBUTE_CHANGED) + { + CfgItem *ci = (CfgItem *)item->dependent_getInterface(CfgItem::depend_getClassGuid()); + if (ci->getGuid() == uioptions_guid + && ptr && (WCSCASEEQLSAFE((const wchar_t *)ptr, L"Text Ticker Speed") || WCSCASEEQLSAFE((const wchar_t *)ptr, L"Text Ticker Increment"))) + { + BuildTitle(); + } + } + } + + return 1; +} + +int SongTicker::onLeftButtonDown(int x, int y) +{ + if (!SONGTICKER_PARENT::onLeftButtonDown(x, y)) + { + grab = 0; + return 0; + } + + grab = 1; + grab_x = x + position; + // onMouseMove(x,y); + return 1; +} + +int SongTicker::onMouseMove(int x, int y) +{ + + if (!SONGTICKER_PARENT::onMouseMove(x, y)) + { + grab = 0; + } + + //POINT pos = {x, y}; + //clientToScreen(&pos); + + if (!grab) return 1; + + position = grab_x - x; + + // this forces us to calculate wraparound for position + RECT dummy; + getBufferPaintSource(&dummy); + + invalidate(); + return 1; +} + +int SongTicker::onLeftButtonUp(int x, int y) +{ + if (!SONGTICKER_PARENT::onLeftButtonUp(x, y)) + { + grab = 0; + return 0; + } + + grab = 0; + return 1; +} diff --git a/Src/Plugins/General/gen_ff/wa2songticker.h b/Src/Plugins/General/gen_ff/wa2songticker.h new file mode 100644 index 00000000..2d5f53be --- /dev/null +++ b/Src/Plugins/General/gen_ff/wa2songticker.h @@ -0,0 +1,108 @@ +#ifndef NULLSOFT_GEN_FF_WA2SONGTICKER_H +#define NULLSOFT_GEN_FF_WA2SONGTICKER_H + +//#include +#include +#include +#include +#include + +#define SONGTICKER_PARENT TextBase + +class SongTicker + : public SONGTICKER_PARENT, /* provides basic wasabi windowing functinoality */ + public CoreCallbackI, /* to get title change updates */ + public DependentViewerI /* for config callbacks*/ +{ + +public: + SongTicker(); + ~SongTicker(); + + enum TickerMode + { + TICKER_OFF, + TICKER_SCROLL, + TICKER_BOUNCE, + }; + + int onAction(const wchar_t *action, const wchar_t *param, int x, int y, intptr_t p1, intptr_t p2, void *data, size_t datalen, ifc_window *source); + +protected: +/* BaseWnd */ + virtual int onInit(); + virtual int onBufferPaint(BltCanvas *canvas, int w, int h); // draw the song ticker here + + /* TimerClient */ + virtual void timerCallback(int id); // scroll the song ticker every # seconds + + /* media core callbacks */ + /* TODO: benski> some thoughts... this differs from current behaviour but might be an interesting idea + virtual int corecb_onStarted(); // start the ticker + virtual int corecb_onStopped(); // go back to showing Winamp version string + virtual int corecb_onPaused(); // pause the ticker + virtual int corecb_onUnpaused(); // unpause the ticker + + // benski> currently unused in Winamp 5... might be worth implementing so we can draw text here + virtual int corecb_onStatusMsg(const wchar_t *text); + virtual int corecb_onWarningMsg(const wchar_t *text); + virtual int corecb_onErrorMsg(const wchar_t *text); + */ + + virtual int corecb_onTitleChange(const wchar_t *title); + // benski> not sure what the difference here is - virtual int corecb_onTitle2Change(const wchar_t *title2); + virtual int corecb_onLengthChange(int newlength); + void getBufferPaintSource(RECT *r); + virtual void getBufferPaintSize(int *w, int *h); + + /* TextBase */ + void invalidateTextBuffer() + { + invalidateBuffer(); + } + + int viewer_onEvent(api_dependent *item, const GUID *classguid, int event, intptr_t param, void *ptr, size_t ptrlen); + int onLeftButtonDown(int x, int y); + int onMouseMove(int x, int y); + int onLeftButtonUp(int x, int y); + + /*static */void CreateXMLParameters(int master_handle); + virtual int onResize(); +private: + int grab_x; + + /* Title */ + void BuildTitle(); + wchar_t song_title[1024]; + int song_length; + int position; + StringW display, rotatingDisplay; + int width_of_str, width_of_str_padded; + + /* */ + int textW; + bool buffer_hw_valid; + + /* scroll timer */ + TimerToken scroll_timer_id; + int ticker_direction; + TickerMode tickerMode; + int skipTimers; + uint32_t last_tick; + + /* XML Parameters */ + enum + { + SONGTICKER_TICKER, + }; + static XMLParamPair params[]; + int xuihandle; + int setXuiParam(int _xuihandle, int attrid, const wchar_t *name, const wchar_t *strval); + +}; + +extern const wchar_t songtickerXuiObjectStr[]; +extern char songtickerXuiSvcName[]; +class SongTickerXuiSvc : public XuiObjectSvc {}; + +#endif diff --git a/Src/Plugins/General/gen_ff/wa2wndembed.cpp b/Src/Plugins/General/gen_ff/wa2wndembed.cpp new file mode 100644 index 00000000..bcd2aaac --- /dev/null +++ b/Src/Plugins/General/gen_ff/wa2wndembed.cpp @@ -0,0 +1,946 @@ +#include +#include "wa2wndembed.h" +#include "wa2frontend.h" +#include "wa2buckitems.h" +#include "embedwndguid.h" +#include "main.h" +#include +#include "resource.h" +#include +#include +#include "wa2cfgitems.h" +#include "gen.h" +#include "../Agave/Language/api_language.h" + +extern TList forcedoffwnds; + +#define BUCKETITEM_WNDTYPE L"buck" +#define WINAMP_OPTIONS_WINDOWSHADE_PL 40266 +ReentryFilterObject wndMsgFilter; + +int embedTable[] = { + IPC_GETWND_PE, +#ifdef MINIBROWSER_SUPPORT + IPC_GETWND_MB, +#endif + + IPC_GETWND_VIDEO}; + +extern int switching_skin; +extern int going_fixedform; +extern int going_freeform; +extern HINSTANCE hInstance; +//----------------------------------------------------------------------------------------------- +void WaOsWndHost::onBeforeReparent(int host) +{ +#if defined(_WIN64) + embedWindowState *ws = (embedWindowState *)GetWindowLong(getHWND(), GWLP_USERDATA); +#else + embedWindowState* ws = (embedWindowState*)GetWindowLong(getHWND(), GWL_USERDATA); +#endif + // 0x49474541 is related to keeping windows shown on litestep desktops + if (ws == NULL || (int)ws == 0x49474541) + { + HWND w = getHWND(); + if (w == wa2.getWnd(IPC_GETWND_VIDEO)) + { + // this tells the video to not trigger its callback on windowposchanged, otherwise it will generate a new IPC_ONSHOW + SendMessageW(w, WM_USER + 0x2, 0, 1); + } + return ; + } + ws->extra_data[EMBED_STATE_EXTRA_REPARENTING] = 1; // tell winamp to ignore show/hide events + if (!host) + { + ShowWindow(getHWND(), SW_HIDE); + if (!transfer && ((switching_skin && !Wa2WndEmbed::hadRememberedWndVisible(getHWND())) || !switching_skin)) + { + PostMessage(getHWND(), WM_USER + 101, 0, 0); + } + } +} + +//----------------------------------------------------------------------------------------------- +void WaOsWndHost::onAfterReparent(int host) +{ +#if defined(_WIN64) + embedWindowState *ws = (embedWindowState *)GetWindowLong(getHWND(), GWLP_USERDATA); +#else + embedWindowState* ws = (embedWindowState*)GetWindowLong(getHWND(), GWL_USERDATA); +#endif + // 0x49474541 is related to keeping windows shown on litestep desktops + if (ws == NULL || (int)ws == 0x49474541) + { + HWND w = getHWND(); + if (w == wa2.getWnd(IPC_GETWND_VIDEO)) + { + // stop preventing handling of video windowposchanged + SendMessageW(w, WM_USER + 0x2, 0, 0); + } + return ; + } + ws->extra_data[EMBED_STATE_EXTRA_REPARENTING] = 0; // tell winamp NOT to ignore show/hide events anymore +} + +//----------------------------------------------------------------------------------------------- +int WaOsWndHost::onGetFocus() +{ + XuiOSWndHost::onGetFocus(); + ifc_window *z = findWindowByInterface(windowHolderGuid); + if (z) + { + WindowHolder *wh = static_cast(z->getInterface(windowHolderGuid)); + if (wh && wh->wndholder_wantAutoFocus()) + { + HWND w = getHWND(); + if (IsWindow(w)) SetFocus(w); + } + } + return 1; +} + +//----------------------------------------------------------------------------------------------- +int WaOsWndHost::wantFocus() +{ + ifc_window *w = findWindowByInterface(windowHolderGuid); + if (w) + { + WindowHolder *wh = static_cast(w->getInterface(windowHolderGuid)); + if (wh) + { + return wh->wndholder_wantAutoFocus(); + } + } + return 0; +} + +//----------------------------------------------------------------------------------------------- +int WaOsWndHost::onMouseWheelUp(int click, int lines) +{ + return 1; +} + +//----------------------------------------------------------------------------------------------- +int WaOsWndHost::onMouseWheelDown(int click, int lines) +{ + return 1; +} + +//----------------------------------------------------------------------------------------------- +void VideoLayoutMonitor::hook_onResize(int x, int y, int w, int h) +{ + SendMessageW(wa2.getWnd(IPC_GETWND_VIDEO), WM_TIMER, 12345, 0); +} + +//----------------------------------------------------------------------------------------------- +void VideoLayoutMonitor::hook_onMove() +{ + SendMessageW(wa2.getWnd(IPC_GETWND_VIDEO), WM_TIMER, 12345, 0); +} + +//----------------------------------------------------------------------------------------------- +Wa2WndEmbed::Wa2WndEmbed() +{ + WASABI_API_SYSCB->syscb_registerCallback(static_cast(this)); +} + +//----------------------------------------------------------------------------------------------- +Wa2WndEmbed::~Wa2WndEmbed() +{ + WASABI_API_SYSCB->syscb_deregisterCallback(static_cast(this)); + wa2wndstatus.deleteAll(); +} + +extern int we_have_ml; + +//----------------------------------------------------------------------------------------------- +int Wa2WndEmbed::testGuid(GUID g) +{ + if (embedWndGuidMgr.testGuid(g)) return 1; + + /* if (embedWndGuids.Data2 == g.Data2 && // embed wnd check :) + embedWndGuids.Data3 == g.Data3 && + !memcmp(embedWndGuids.Data4,g.Data4,8)) return 1;*/ + + return (g == pleditWndGuid || g == videoWndGuid +#ifdef MINIBROWSER_SUPPORT + || g == minibrowserWndGuid +#endif + || (we_have_ml && g == library_guid) + ); +} + +int make_sure_library_is_here_at_startup = 0; +extern int m_loading_at_startup; + +//----------------------------------------------------------------------------------------------- +ifc_window *Wa2WndEmbed::createWindowByGuid(GUID g, ifc_window *parent) +{ + if (m_loading_at_startup) + if (g == library_guid) + make_sure_library_is_here_at_startup = 1; + WaOsWndHost *oldhost = NULL; + if (embedWndGuidMgr.testGuid(g) && !embedWndGuidMgr.getEmbedWindowState(g)) + return NULL; + // first check if this window is already open in a host, and if so, remove it from the wndholder + foreach(wndhosts) + if (wndhosts.getfor()->g == g) + { + WaOsWndHost *host = wndhosts.getfor()->host; + oldhost = host; + host->setTransfering(1); + host->oswndhost_unhost(); + Layout *l = static_cast(host->getDesktopParent()); + if (l) + { + Container *c = l->getParentContainer(); + if (c) + { + if (!WCSCASEEQLSAFE(c->getId(), L"main")) + { + c->close(); // deferred if needed + } + else + { + softclose: + ifc_window *wnd = host->findWindowByInterface(windowHolderGuid); + if (wnd != NULL) + { + WindowHolder *wh = static_cast(wnd->getInterface(windowHolderGuid)); + if (wh != NULL) + { + wh->onRemoveWindow(1); + } + } + } + } + else goto softclose; + } + } + endfor; + // now host the wnd in a new host + WaOsWndHost *host = new WaOsWndHost(); + viewer_addViewItem(host); + EmbedEntry *ee = new EmbedEntry(); + wndhosts.addItem(ee); + ee->g = g; + ee->host = host; + ee->monitor = NULL; + ee->dep = host->getDependencyPtr(); + ee->cmds = NULL; + if (g == pleditWndGuid) + { + RECT r = {10, 20, 5, 38}; + host->oswndhost_setRegionOffsets(&r); + host->oswndhost_host(wa2.getWnd(IPC_GETWND_PE)); + ee->whichwnd = IPC_GETWND_PE; + host->setName((L"Playlist Editor")/*(WASABI_API_LNGSTRINGW(IDS_PLAYLIST_EDITOR)*/); + GuiObject *go = parent->getGuiObject(); + PlaylistAppCmds *plEditAppCmds = new PlaylistAppCmds(); + ee->cmds = plEditAppCmds; + go->guiobject_addAppCmds(plEditAppCmds); + plWnd = parent; //parent->getDesktopParent(); + //ShowWindow(host->getHWND(), SW_NORMAL); + } + else if (g == videoWndGuid) + { + RECT r = {11, 20, 8, 38}; + host->oswndhost_setRegionOffsets(&r); +#ifdef VIDDEBUG + DebugString("Video : Window service creates the host\n"); +#endif + HWND vid = wa2.getWnd(IPC_GETWND_VIDEO); + host->oswndhost_host(vid); + ((WaOsWndHost *)host)->setNoTransparency(); + ee->whichwnd = IPC_GETWND_VIDEO; + host->setName(WASABI_API_LNGSTRINGW(IDS_VIDEO)); + ifc_window *lw = parent->getDesktopParent(); + if (lw) + { + GuiObject *o = lw->getGuiObject(); + if (o) + { + ee->monitor = new VideoLayoutMonitor(o->guiobject_getScriptObject()); + } + } + SetTimer(vid, 12345, 250, NULL); + GuiObject *go = parent->getGuiObject(); + VideoAppCmds *videoAppCmds = new VideoAppCmds(); + ee->cmds = videoAppCmds; + go->guiobject_addAppCmds(videoAppCmds); + vidWnd = parent; //parent->getDesktopParent(); + //ShowWindow(host->getHWND(), SW_NORMAL); +#ifdef MINIBROWSER_SUPPORT + + } + else if (g == minibrowserWndGuid) + { + RECT r = {10, 20, 5, 38}; + host->oswndhost_setRegionOffsets(&r); + host->oswndhost_host(wa2.getWnd(IPC_GETWND_MB)); + ee->whichwnd = IPC_GETWND_MB; + host->setName("Minibrowser"); + GuiObject *go = parent->getGuiObject(); + mbWnd = parent; //parent->getDesktopParent(); + MinibrowserAppCmds *mbAppCmds = new MinibrowserAppCmds(); + ee->cmds = mbAppCmds; + go->guiobject_addAppCmds(mbAppCmds); + //ShowWindow(host->getHWND(), SW_NORMAL); +#endif + + } + else if (embedWndGuidMgr.testGuid(g)) /*(embedWndGuids.Data2 == g.Data2 && + embedWndGuids.Data3 == g.Data3 && + !memcmp(embedWndGuids.Data4,g.Data4,8))*/ + { + embedWindowState *ws = embedWndGuidMgr.getEmbedWindowState(g); + ASSERT(ws != NULL); + + if (0 == (WS_BORDER & GetWindowLongPtrW(ws->me, GWL_STYLE))) + { + RECT r = {11, 20, 8, 14}; + host->oswndhost_setRegionOffsets(&r); + } + else + host->oswndhost_setRegionOffsets(NULL); + + ws->extra_data[EMBED_STATE_EXTRA_HOSTCOUNT]++; + ws->extra_data[EMBED_STATE_EXTRA_FFROOTWND] = (intptr_t)parent; //parent->getDesktopParent(); + ee->whichwnd = (intptr_t)ws; + if (ws->flags & EMBED_FLAGS_NOTRANSPARENCY) host->setNoTransparency(); + host->oswndhost_host(ws->me); + wchar_t buf[512] = {0}; + GetWindowTextW(ws->me, buf, 512); + host->setName(buf); + } + else + { + wndhosts.removeItem(ee); + delete host; + delete ee; + return NULL; + } + wa2.setOnTop(cfg_options_alwaysontop.getValueAsInt()); + return host; +} + +//----------------------------------------------------------------------------------------------- +int Wa2WndEmbed::testType(const wchar_t *windowtype) +{ + return !_wcsicmp(windowtype, BUCKETITEM_WNDTYPE) || !_wcsicmp(windowtype, L"plsc"); +} + +//----------------------------------------------------------------------------------------------- +ifc_window *Wa2WndEmbed::createWindowOfType(const wchar_t *windowtype, ifc_window *parent, int n) +{ + if (!_wcsicmp(windowtype, BUCKETITEM_WNDTYPE)) + { + switch (n) + { + case 0: + { + PlBucketItem *bi = new PlBucketItem(); + bi->setBitmaps(L"winamp.thinger.pledit", NULL, L"winamp.thinger.pledit.hilited", L"winamp.thinger.pledit.selected"); + bucketitems.addItem(bi); + return bi; + } + case 1: + { + MlBucketItem *bi = new MlBucketItem(); + bi->setBitmaps(L"winamp.thinger.library", NULL, L"winamp.thinger.library.hilited", L"winamp.thinger.library.selected"); + bucketitems.addItem(bi); + return bi; + } + case 2: + { + VidBucketItem *bi = new VidBucketItem(); + bi->setBitmaps(L"winamp.thinger.video", NULL, L"winamp.thinger.video.hilited", L"winamp.thinger.video.selected"); + bucketitems.addItem(bi); + return bi; + } + case 3: + { + VisBucketItem *bi = new VisBucketItem(); + bi->setBitmaps(L"winamp.thinger.vis", NULL, L"winamp.thinger.vis.hilited", L"winamp.thinger.vis.selected"); + bucketitems.addItem(bi); + return bi; + } + // cases must be contiguous, enumerator stops at first NULL +#ifdef MINIBROWSER_SUPPORT + case 4: + { + MbBucketItem *bi = new MbBucketItem(); + bi->setBitmaps(hInstance, IDB_MB_TAB_NORMAL, NULL, IDB_MB_TAB_HILITED, IDB_MB_TAB_SELECTED); + bucketitems.addItem(bi); + return bi; + } +#endif + // n is enumertor, not whichwnd + // we also need some way for the embeddedwnd to expose at least one bitmap (ideally 3) so we can make a nice bucketitem here (this code uses a pledit icon) + /* default: + if (n > 1024) + { + EmbedBucketItem *bi = new EmbedBucketItem(); + bi->setBitmaps(hInstance, IDB_PLEDIT_TAB_NORMAL, NULL, IDB_PLEDIT_TAB_HILITED, IDB_PLEDIT_TAB_SELECTED); + bucketitems.addItem(bi); + return bi; + } + break;*/ + } + } + else if (!_wcsicmp(windowtype, L"plsc")) + { + switch (n) + { + case 0: + pldirs.addItem(new PlDirObject); + return pldirs.getLast(); + } + } + return NULL; +} + +//----------------------------------------------------------------------------------------------- +int Wa2WndEmbed::destroyWindow(ifc_window *w) +{ + foreach(bucketitems) + Wa2BucketItem *i = bucketitems.getfor(); + ifc_window *rw = i; + if (rw == w) + { + delete i; + return 1; + } + endfor; + foreach(wndhosts) + EmbedEntry *ee = wndhosts.getfor(); + WaOsWndHost *x = ee->host; + if (WASABI_API_WND->rootwndIsValid(x)) + { + ifc_window *rw = x; + if (rw == w) + { + ReentryFilter f(&wndMsgFilter, ee->whichwnd); + if (!f.mustLeave()) + { + // this would hide the winamp window, which is probably not what we want to do (it should remain visible if it + // was visible, no? + + // well, no, because we don't only run this in skin unloading, but also when a window gets destroyed (this is the wndcreation + // service being called to free what it created) -- this won't happen for mmd3/pledit because mmd3 has a static window for + // everything, which means that when you click close on it, it doesn't destroy it but hides it, so this code isn't called. but + // if you load another skin (ie: NonStep), and you close the pledit, it immediately reappears with the wa2 look since oswndhost_unhost + // reset the flags, region and parent to what they were before the window was embedded + + // i think that what we need is to save which windows were visible (and their location) before switching to freeform + // and to restore them when we go back to wa2 mode. this will also be more consistant with the freeform behavior of + // remembering visible status and coordinates on a per skin basis (since otherwise freeform dockings get screwed) + // it also makes sense when we consider that we are going to need to remove all windowshade modes from the embedded + // windows when going freeform. + + // see new functions: rememberVisibleWindows() and restoreVisibleWindows() + + // in any case, we need to hide the window here, at least temporarily in the case of skin unloading + { + if (ee->whichwnd > 1024) + { + embedWindowState *ws = NULL; + //embedWindowState *ws = (embedWindowState *)ee->whichwnd; + HWND hHost, hContent; + hHost = (NULL != x) ? x->getHWND() : NULL; + hContent = (NULL != hHost) ? (HWND)SendMessageW(plugin.hwndParent, WM_WA_IPC, (WPARAM)hHost, IPC_FF_GETCONTENTWND) : NULL; + + if (NULL != hContent) + { + ws = (embedWindowState *)GetWindowLongPtrW(hContent, GWLP_USERDATA); + } + else + { + embedWndGuidMgr.retireEmbedWindowState((embedWindowState *)ee->whichwnd); + } + + if (NULL != ws && + !(wa2.isValidEmbedWndState(ws) && --ws->hostcount != 0)) + { + if (0 != (EMBED_FLAGS_FFCALLBACK & ws->flags) && + NULL != ws->callback) + { + ws->callback(ws, FFC_DESTROYEMBED, (LPARAM)w); + } + + x->oswndhost_unhost(); + if (wa2.isValidEmbedWndState(ws)) + ws->wasabi_window = NULL; + + if (!x->isTransfering() && wa2.isValidEmbedWndState(ws)) + { + if (IsWindow(x->getHWND())) + { + SendMessageW(ws->me, WM_USER + 101, 0, 0); + } + embedWndGuidMgr.retireEmbedWindowState(ws); + } + + } + } + else + { + if (ee->whichwnd == IPC_GETWND_VIDEO) KillTimer(wa2.getWnd(ee->whichwnd), 12345); + x->oswndhost_unhost(); + if (!x->isTransfering()) + wa2.setWindowVisible(ee->whichwnd, 0); +#ifdef VIDDEBUG + if (ee->whichwnd == IPC_GETWND_VIDEO) DebugString("Video : Window service asks WA2 to close the window\n"); +#endif + + } + } + } + wndhosts.removeItem(ee); + + embedWindowState *ws = NULL; + HWND thiswnd = NULL; + if (ee->whichwnd > 1024) + { + if (IsWindow(x->getHWND())) + thiswnd = x->getHWND(); + //ws=(embedWindowState *)ee->whichwnd; + //thiswnd=ws->me; + } + else thiswnd = wa2.getWnd(ee->whichwnd); + //moved to xuioswndhost + //SetWindowLong(thiswnd,GWL_STYLE,GetWindowLong(thiswnd,GWL_STYLE)&~(WS_CHILDWINDOW)); + switch (ee->whichwnd) + { + case IPC_GETWND_PE: plWnd = NULL; break; +#ifdef MINIBROWSER_SUPPORT + case IPC_GETWND_MB: mbWnd = NULL; break; +#endif + case IPC_GETWND_VIDEO: +#ifdef VIDDEBUG + DebugString("Video : Window service destroys host\n"); +#endif + vidWnd = NULL; + break; + default: + if (ee->whichwnd > 1024 && ws && thiswnd != NULL) + { + ws->extra_data[EMBED_STATE_EXTRA_FFROOTWND] = 0; + } + break; + } + if (ee->cmds) + { + GuiObject *o = w->getParent()->getGuiObject(); + o->guiobject_removeAppCmds(ee->cmds); + } + x->oswndhost_unhost(); // ignored if already done by reentryfiltered code + delete ee->monitor; + delete ee->cmds; + delete x; + + + if (ee->whichwnd > 1024 && ws) + { + if (forcedoffwnds.haveItem(ws->me)) + { + RECT r; + GetWindowRect(ws->me, &r); + SetWindowPos(ws->me, NULL, r.left + 20000, r.top + 20000, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOREDRAW | SWP_NOSENDCHANGING | SWP_NOZORDER); + forcedoffwnds.delItem(ws->me); + } + } + delete ee; + SetFocus(wa2.getMainWindow()); + + return 1; + } + endfor; + foreach (pldirs) + PlDirObject *pldir = pldirs.getfor(); + if (pldir == w) + { + delete pldir; + return 1; + } + endfor; + } + return 0; +} + +//----------------------------------------------------------------------------------------------- +int Wa2WndEmbed::viewer_onEvent(ifc_window *item, int event, intptr_t param, void *ptr, size_t ptrlen) +{ + if (event == ifc_window::Event_SETVISIBLE) + { + /* if (!param) { + // the wnd may be going away, but then again, it might just be hidden to show an alternate layout of the same + // container, so before continuing, we need to check if it's actually going away. There is of course an exception + // in that if the window is hosted by a wndholder with autoclose="1", we should mirror the hiding state regardless + // of the container state + + api_window *whr = item->getParent(); + int except = 0; + if (whr) { + GuiObject *go = whr->getGuiObject(); + if (go) { + const char *par = go->guiobject_getXmlParam("autoclose"); + if (!par || (par && ATOI(par) == 1)) except = 1; + } + } + if (!except) { + api_window *lr = item->getDesktopParent(); + if (lr) { + Layout *l = static_cast(lr->getInterface(layoutGuid)); + if (l) { + Container *c = l->getParentContainer(); + if (c) { + if (c->isVisible()) return 1; + } + } + } + } + }*/ + foreach(wndhosts) + EmbedEntry *ee = wndhosts.getfor(); + XuiOSWndHost *x = ee->host; + ifc_window *rw = x; + if (rw == item) + { + { + ReentryFilter f(&wndMsgFilter, ee->whichwnd); + if (f.mustLeave()) continue; + } + if (ee->whichwnd > 1024) + { + embedWindowState *ws = (embedWindowState *)ee->whichwnd; + if (!param && wa2.isValidEmbedWndState(ws)) + { + if (IsWindow(ws->me)) + SendMessageW(ws->me, WM_USER + 101, 0, 0); + ifc_window *rwh = x->findWindowByInterface(windowHolderGuid); + if (rwh != NULL) + { + WindowHolder *wh = static_cast(rwh->getInterface(windowHolderGuid)); + if (wh != NULL) + { + wh->onRemoveWindow(1); + } + } + if (wa2.isValidEmbedWndState(ws)) ws->extra_data[EMBED_STATE_EXTRA_FFROOTWND] = NULL; + } + else if (wa2.isValidEmbedWndState(ws)) + { + ws->extra_data[EMBED_STATE_EXTRA_FFROOTWND] = (intptr_t)item->getParent(); + ShowWindow(ws->me, SW_NORMAL); + } + } + else + { + ReentryFilter f(&wndMsgFilter, ee->whichwnd); +#ifdef VIDDEBUG + if (ee->whichwnd == IPC_GETWND_VIDEO && param != wa2.isWindowVisible(ee->whichwnd)) DebugString("Video : Detected that the host is %s, syncing\n", param ? "shown" : "hidden"); +#endif + wa2.setWindowVisible(ee->whichwnd, param); + } + } + endfor; + } + return 1; +} + +int Wa2WndEmbed::onShowWindow(Container *c, GUID guid, const wchar_t *groupid) +{ + foreach(wndhosts) + EmbedEntry *ee = wndhosts.getfor(); + if (ee->g == guid) + { + ReentryFilter f(&wndMsgFilter, ee->whichwnd); + if (f.mustLeave()) return 1; + if (guid == videoWndGuid) wa2.setWindowVisible(IPC_GETWND_VIDEO, 1); +#ifdef MINIBROWSER_SUPPORT + else if (guid == minibrowserWndGuid) wa2.setWindowVisible(IPC_GETWND_MB, 1); +#endif + else if (guid == pleditWndGuid) wa2.setWindowVisible(IPC_GETWND_PE, 1); + } + endfor; + return 1; +} + +int Wa2WndEmbed::onHideWindow(Container *c, GUID guid, const wchar_t *groupid) +{ + /* if (guid == INVALID_GUID) return 1; + embedWindowState *ws = embedWndGuidMgr.getEmbedWindowState(guid); + if (ws != NULL && wa2.isValidEmbedWndState(ws)) { + if (IsWindow(ws->me)) + SendMessageW(ws->me,WM_USER+101,0,0); + api_window *x = (api_window*)ws->extra_data[EMBED_STATE_EXTRA_FFROOTWND]; + if (x && WASABI_API_WND->rootwndIsValid(x)) { + api_window *rwh = x->findWindowByInterface(windowHolderGuid); + if (rwh != NULL) { + WindowHolder *wh = static_cast(rwh->getInterface(windowHolderGuid)); + if (wh != NULL) { + wh->onRemoveWindow(1); + } + } + } + ws->extra_data[EMBED_STATE_EXTRA_FFROOTWND] = NULL; + } + */ + foreach(wndhosts) + EmbedEntry *ee = wndhosts.getfor(); + if (ee->g == guid) + { + ReentryFilter f(&wndMsgFilter, ee->whichwnd); + if (f.mustLeave()) return 1; + if (ee->host->isTransfering()) return 1; + ifc_window *dp = ee->host->getDesktopParent(); + if (dp) + { + Layout *l = static_cast(dp->getInterface(layoutGuid)); + if (l) + { + if (l->getParentContainer() != c) return 1; + } + } + if (guid == videoWndGuid) wa2.setWindowVisible(IPC_GETWND_VIDEO, 0); +#ifdef MINIBROWSER_SUPPORT + else if (guid == minibrowserWndGuid) wa2.setWindowVisible(IPC_GETWND_MB, 0); +#endif + else if (guid == pleditWndGuid) wa2.setWindowVisible(IPC_GETWND_PE, 0); + } + endfor; + + return 1; +} + +extern wchar_t *INI_FILE; + +int Wa2WndEmbed::embedRememberProc(embedWindowState *p, embedEnumStruct *parms) +{ + WndStatus *ws = new WndStatus; + ws->wndcode = -1; // if you insert a wnd that is not in embedTable, put -1 as wndcode + ws->wnd = p->me; + ws->visible = IsWindowVisible(p->me); + GetWindowRect(p->me, &ws->position); + // ws->position=p->r; + wa2wndstatus.addItem(ws); + + // only store the ml window position if not loading on startup + if(going_freeform && !m_loading_at_startup) + { + HWND mlwnd = wa2.getMediaLibrary(); + if(GetWindow(p->me, GW_CHILD) == mlwnd) + { + WritePrivateProfileStringW(L"gen_ff", L"classicmlwidth", StringPrintfW(L"%d", ws->position.right - ws->position.left), INI_FILE); + WritePrivateProfileStringW(L"gen_ff", L"classicmlheight", StringPrintfW(L"%d", ws->position.bottom - ws->position.top), INI_FILE); + } + } + return 0; +} + +extern int m_loading_at_startup; + +//----------------------------------------------------------------------------------------------- +// todo: remember and restore windowshade modes +void Wa2WndEmbed::rememberVisibleWindows() +{ + wa2wndstatus.deleteAll(); + for (int i = 0;i < sizeof(embedTable) / sizeof(embedTable[0]);i++) + { + HWND w = wa2.getWnd(embedTable[i]); + WndStatus *ws = new WndStatus; + ws->wndcode = embedTable[i]; // if you insert a wnd that is not in embedTable, put -1 as wndcode + ws->wnd = w; + ws->visible = wa2.isWindowVisible(embedTable[i]); + GetWindowRect(w, &ws->position); + if (going_freeform) + { + if (embedTable[i] == IPC_GETWND_PE) + { + int peheight = ws->position.bottom - ws->position.top; + int pewidth = ws->position.right - ws->position.left; + if (!m_loading_at_startup) + { + WritePrivateProfileStringW(L"gen_ff", L"classicplwidth", StringPrintfW(L"%d", pewidth), INI_FILE); + WritePrivateProfileStringW(L"gen_ff", L"classicplheight", StringPrintfW(L"%d", peheight), INI_FILE); + } + int classicpews = wa2.isWindowShade(IPC_GETWND_PE); + if (!m_loading_at_startup || GetPrivateProfileIntW(L"gen_ff", L"classicplws", -1, INI_FILE) == -1) + WritePrivateProfileStringW(L"gen_ff", L"classicplws", classicpews ? L"1" : L"0", INI_FILE); + if (classicpews) + SendMessageW(wa2.getMainWindow(), WM_COMMAND, WINAMP_OPTIONS_WINDOWSHADE_PL, 0); + GetWindowRect(w, &ws->position); + } + } + wa2wndstatus.addItem(ws); + } + embedEnumStruct cs = { embedRememberProc, 0}; + SendMessageW(wa2.getMainWindow(), WM_WA_IPC, (WPARAM)&cs, IPC_EMBED_ENUM); +} + +int Wa2WndEmbed::hadRememberedWndVisible(HWND w) +{ + int n = wa2wndstatus.getNumItems(); + for (int i = 0;i < n;i++) + { + WndStatus *ws = wa2wndstatus.enumItem(i); + if (ws->wnd == w && ws->visible) + return 1; + } + return 0; +} + +void Wa2WndEmbed::restoreVisibleWindows() +{ + int n = wa2wndstatus.getNumItems(); + HWND mlwnd = wa2.getMediaLibrary(); + for (int i = 0;i < n;i++) + { + WndStatus *ws = wa2wndstatus.enumItem(i); + if (going_fixedform && !m_loading_at_startup) + { + if (embedTable[i] == IPC_GETWND_PE) + { + int classicpews = GetPrivateProfileIntW(L"gen_ff", L"classicplws", 0, INI_FILE); + if (classicpews) + { + SendMessageW(wa2.getMainWindow(), WM_COMMAND, WINAMP_OPTIONS_WINDOWSHADE_PL, 0); + } + int classicwidth = GetPrivateProfileIntW(L"gen_ff", L"classicplwidth", 275, INI_FILE); + int classicheight = GetPrivateProfileIntW(L"gen_ff", L"classicplheight", 145, INI_FILE); + wa2.setPlEditWidthHeight(classicwidth, classicheight); + } + + if(GetWindow(ws->wnd, GW_CHILD) == mlwnd) + { + // only restore the ml window size if we were able to read in saved values + int mlwidth = GetPrivateProfileIntW(L"gen_ff", L"classicmlwidth", -1, INI_FILE); + int mlheight = GetPrivateProfileIntW(L"gen_ff", L"classicmlheight", -1, INI_FILE); + if(mlwidth != -1 && mlheight != -1) + SetWindowPos(ws->wnd, 0, 0, 0, mlwidth, mlheight, SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOMOVE); + } + } + // FG> as of oct19, this function only restores state for windows that WERE visible + // because there is no reason to hide one, since this function is designed to bring + // back those windows that were here in one mode, but aren't so anymore in another + if (ws->visible) + { + if (ws->wndcode != -1) + { + wa2.setWindowVisible(ws->wndcode, ws->visible); + } + else + { + ShowWindow(ws->wnd, ws->visible ? SW_SHOWNA : SW_HIDE); + } + } + } +} + +PtrList Wa2WndEmbed::wa2wndstatus; + +//----------------------------------------------------------------------------------------------- +PlaylistAppCmds::PlaylistAppCmds() +: addCmd(L"Add", PL_ADD, AppCmds::SIDE_LEFT, 0), + remCmd(L"Rem", PL_REM, AppCmds::SIDE_LEFT, 0), + selCmd(L"Sel", PL_SEL, AppCmds::SIDE_LEFT, 0), + miscCmd(L"Misc", PL_MISC, AppCmds::SIDE_LEFT, 0), + listCmd(L"List", PL_LIST, AppCmds::SIDE_RIGHT, 0) +{ + appcmds_addCmd(&addCmd); + appcmds_addCmd(&remCmd); + appcmds_addCmd(&selCmd); + appcmds_addCmd(&miscCmd); + appcmds_addCmd(&listCmd); +} + +void PlaylistAppCmds::appcmds_onCommand(int id, const RECT *buttonRect, int which_button) +{ + switch (id) + { + case PL_ADD: + wa2.sendPlCmd(Winamp2FrontEnd::WA2_PLEDITPOPUP_ADD, buttonRect->left, buttonRect->top, TPM_BOTTOMALIGN | TPM_LEFTALIGN); + break; + case PL_REM: + wa2.sendPlCmd(Winamp2FrontEnd::WA2_PLEDITPOPUP_REM, buttonRect->left, buttonRect->top, TPM_BOTTOMALIGN | TPM_LEFTALIGN); + break; + case PL_SEL: + wa2.sendPlCmd(Winamp2FrontEnd::WA2_PLEDITPOPUP_SEL, buttonRect->left, buttonRect->top, TPM_BOTTOMALIGN | TPM_LEFTALIGN); + break; + case PL_MISC: + wa2.sendPlCmd(Winamp2FrontEnd::WA2_PLEDITPOPUP_MISC, buttonRect->left, buttonRect->top, TPM_BOTTOMALIGN | TPM_LEFTALIGN); + break; + case PL_LIST: + wa2.sendPlCmd(Winamp2FrontEnd::WA2_PLEDITPOPUP_LIST, buttonRect->right, buttonRect->top, TPM_BOTTOMALIGN | TPM_RIGHTALIGN); + break; + } +} + + +#ifdef MINIBROWSER_SUPPORT +//----------------------------------------------------------------------------------------------- +MinibrowserAppCmds::MinibrowserAppCmds() +{ + appcmds_addCmd(new CmdRec("Back", MB_BACK, AppCmds::SIDE_LEFT, 1)); + appcmds_addCmd(new CmdRec("Forward", MB_FORWARD, AppCmds::SIDE_LEFT, 1)); + appcmds_addCmd(new CmdRec("Stop", MB_STOP, AppCmds::SIDE_LEFT, 1)); + appcmds_addCmd(new CmdRec("Reload", MB_RELOAD, AppCmds::SIDE_LEFT, 1)); + appcmds_addCmd(new CmdRec("Misc", MB_MISC, AppCmds::SIDE_RIGHT, 1)); +} + +void MinibrowserAppCmds::appcmds_onCommand(int id, const RECT *buttonRect, int which_button) +{ + switch (id) + { + case MB_BACK: + wa2.sendMbCmd(Winamp2FrontEnd::WA2_MBCMD_BACK); + break; + case MB_FORWARD: + wa2.sendMbCmd(Winamp2FrontEnd::WA2_MBCMD_FORWARD); + break; + case MB_STOP: + wa2.sendMbCmd(Winamp2FrontEnd::WA2_MBCMD_STOP); + break; + case MB_RELOAD: + wa2.sendMbCmd(Winamp2FrontEnd::WA2_MBCMD_RELOAD); + break; + case MB_MISC: + wa2.sendMbCmd(Winamp2FrontEnd::WA2_MBPOPUP_MISC, buttonRect->right, buttonRect->top, TPM_BOTTOMALIGN | TPM_RIGHTALIGN); + break; + } +} +#endif + +//----------------------------------------------------------------------------------------------- +VideoAppCmds::VideoAppCmds() +{ + appcmds_addCmd(new CmdRec(L"Fullscreen", VID_FULLSCREEN, AppCmds::SIDE_LEFT, 1)); + appcmds_addCmd(new CmdRec(L"1x", VID_1X, AppCmds::SIDE_LEFT, 1)); + appcmds_addCmd(new CmdRec(L"2x", VID_2X, AppCmds::SIDE_LEFT, 1)); + appcmds_addCmd(new CmdRec(L"TV", VID_LIB, AppCmds::SIDE_LEFT, 1)); + appcmds_addCmd(new CmdRec(L"Misc", VID_MISC, AppCmds::SIDE_RIGHT, 1)); +} + +void VideoAppCmds::appcmds_onCommand(int id, const RECT *buttonRect, int which_button) +{ + switch (id) + { + case VID_FULLSCREEN: + wa2.sendVidCmd(Winamp2FrontEnd::WA2_VIDCMD_FULLSCREEN); + break; + case VID_1X: + wa2.sendVidCmd(Winamp2FrontEnd::WA2_VIDCMD_1X); + break; + case VID_2X: + wa2.sendVidCmd(Winamp2FrontEnd::WA2_VIDCMD_2X); + break; + case VID_LIB: + wa2.sendVidCmd(Winamp2FrontEnd::WA2_VIDCMD_LIB); + break; + case VID_MISC: + wa2.sendVidCmd(Winamp2FrontEnd::WA2_VIDPOPUP_MISC, buttonRect->right, buttonRect->top, TPM_BOTTOMALIGN | TPM_RIGHTALIGN); + break; + } +} + diff --git a/Src/Plugins/General/gen_ff/wa2wndembed.h b/Src/Plugins/General/gen_ff/wa2wndembed.h new file mode 100644 index 00000000..f50501c7 --- /dev/null +++ b/Src/Plugins/General/gen_ff/wa2wndembed.h @@ -0,0 +1,223 @@ +#ifndef __WA2WNDEMBED_H +#define __WA2WNDEMBED_H + +#include "../winamp/wa_ipc.h" +#include +#include +#include +#include +#include +#include +#include +#include "wa2pldirobj.h" + +class BucketItem; +class XuiOSWndHost; +class Wa2BucketItem; + +#define EMBED_STATE_EXTRA_LINK 0 +#define EMBED_STATE_EXTRA_ATTACHED 1 +#define EMBED_STATE_EXTRA_HOSTCOUNT 61 +#define EMBED_STATE_EXTRA_REPARENTING 62 +#define EMBED_STATE_EXTRA_FFROOTWND 63 + +//----------------------------------------------------------------------------------------------- +// {E6323F86-1724-4cd3-9D87-70591FC16E5E} +static const GUID playerWndGuid = +{ 0xe6323f86, 0x1724, 0x4cd3, { 0x9d, 0x87, 0x70, 0x59, 0x1f, 0xc1, 0x6e, 0x5e } }; +// benski> don't use this in a windowholder, this is for + + +//----------------------------------------------------------------------------------------------- +// {45F3F7C1-A6F3-4ee6-A15E-125E92FC3F8D} +static const GUID pleditWndGuid = +{ 0x45f3f7c1, 0xa6f3, 0x4ee6, { 0xa1, 0x5e, 0x12, 0x5e, 0x92, 0xfc, 0x3f, 0x8d } }; + +//----------------------------------------------------------------------------------------------- +// {F0816D7B-FFFC-4343-80F2-E8199AA15CC3} +static const GUID videoWndGuid = +{ 0xf0816d7b, 0xfffc, 0x4343, { 0x80, 0xf2, 0xe8, 0x19, 0x9a, 0xa1, 0x5c, 0xc3 } }; + +#ifdef MINIBROWSER_SUPPORT + +//----------------------------------------------------------------------------------------------- +// {CF477C3E-FDC8-44a2-9066-58D0184D47A8} +static const GUID minibrowserWndGuid = +{ 0xcf477c3e, 0xfdc8, 0x44a2, { 0x90, 0x66, 0x58, 0xd0, 0x18, 0x4d, 0x47, 0xa8 } }; + +#endif +// scan +static const GUID embedWndGuids = +{ 0x00000000, 0xf000, 0x44a2, { 0x90, 0x66, 0x58, 0xd0, 0x18, 0x4d, 0x47, 0xa8 } }; + +// {A8533CEC-D05D-45b8-A617-E2B7F2C2CF82} +static const GUID embeddedWndStateGuid = +{ 0xa8533cec, 0xd05d, 0x45b8, { 0xa6, 0x17, 0xe2, 0xb7, 0xf2, 0xc2, 0xcf, 0x82 } }; + +// {6B0EDF80-C9A5-11d3-9F26-00C04F39FFC6} +static const GUID library_guid = +{ 0x6b0edf80, 0xc9a5, 0x11d3, { 0x9f, 0x26, 0x0, 0xc0, 0x4f, 0x39, 0xff, 0xc6 } }; + +// {B397A4CE-455A-4d62-8BF6-D0F91ACB70E6} +static const GUID preferences_guid = +{ 0xb397a4ce, 0x455a, 0x4d62, { 0x8b, 0xf6, 0xd0, 0xf9, 0x1a, 0xcb, 0x70, 0xe6 } }; + +// {0000000A-000C-0010-FF7B-01014263450C} +static const GUID avs_guid = +{ 10, 12, 16, { 255, 123, 1, 1, 66, 99, 69, 12 } }; + +// {8DDA9D48-B915-4320-A888-831A1D837516} +static const GUID about_guid = +{ 0x8dda9d48, 0xb915, 0x4320, { 0xa8, 0x88, 0x83, 0x1a, 0x1d, 0x83, 0x75, 0x16 } }; + +// {D6201408-476A-4308-BF1B-7BACA1124B12} +static const GUID lightning_bolt_guid = +{ 0xd6201408, 0x476a, 0x4308, { 0xbf, 0x1b, 0x7b, 0xac, 0xa1, 0x12, 0x4b, 0x12 } }; + +// {53DE6284-7E88-4c62-9F93-22ED68E6A024} +static const GUID colorthemes_guid = +{ 0x53de6284, 0x7e88, 0x4c62, { 0x9f, 0x93, 0x22, 0xed, 0x68, 0xe6, 0xa0, 0x24 } }; + + +//----------------------------------------------------------------------------------------------- +class WaOsWndHost : public XuiOSWndHost +{ + public: + WaOsWndHost() : transparencysafe(1), transfer(0) {} + virtual void onBeforeReparent(int i); + virtual void onAfterReparent(int i); + virtual int wantHideOnUnhost() { return 1; } + virtual int wantFocus(); + virtual int onGetFocus(); + virtual int handleTransparency() { return transparencysafe; } + virtual void setNoTransparency() { transparencysafe = 0; } + void setTransfering(int i) { transfer = i; } + int isTransfering() { return transfer; } + virtual int onMouseWheelUp(int click, int lines); + virtual int onMouseWheelDown(int click, int lines); + private: + int transparencysafe; + int transfer; +}; + +//----------------------------------------------------------------------------------------------- +class VideoLayoutMonitor : public H_Layout +{ + public: + VideoLayoutMonitor(ScriptObject *o) : H_Layout(o) { } + VideoLayoutMonitor() {} + virtual void hook_onResize(int x, int y, int w, int h); + virtual void hook_onMove(); +}; + +//----------------------------------------------------------------------------------------------- +class EmbedEntry +{ + public: + WaOsWndHost *host; + ifc_dependent *dep; + intptr_t whichwnd; + AppCmds *cmds; + VideoLayoutMonitor *monitor; + GUID g; +}; + +class WndStatus +{ + public: + int wndcode; // or -1 + HWND wnd; + int visible; + RECT position; +}; + +//----------------------------------------------------------------------------------------------- +class Wa2WndEmbed : public svc_windowCreateI, DependentViewerTPtr, public WndCallbackI +{ + public: + Wa2WndEmbed(); + virtual ~Wa2WndEmbed(); + + static const char *getServiceName() { return "Playlist Editor window creator"; } + + virtual int testGuid(GUID g); + virtual ifc_window *createWindowByGuid(GUID g, ifc_window *parent); + virtual int testType(const wchar_t *windowtype); + virtual ifc_window *createWindowOfType(const wchar_t *windowtype, ifc_window *parent, int n); + virtual int destroyWindow(ifc_window *w); + virtual int viewer_onEvent(ifc_window *item, int event, intptr_t param, void *ptr, size_t ptrlen); + + static void rememberVisibleWindows(); + static void restoreVisibleWindows(); + static int hadRememberedWndVisible(HWND wnd); + static int embedRememberProc(embedWindowState *p, embedEnumStruct *parms); + + virtual int onShowWindow(Container *c, GUID guid, const wchar_t *groupid); + virtual int onHideWindow(Container *c, GUID guid, const wchar_t *groupid); + + PtrList bucketitems; + PtrList wndhosts; + PtrList pldirs; + static PtrList wa2wndstatus; + static int switching_holder; + +}; + +extern ReentryFilterObject wndMsgFilter; + +//----------------------------------------------------------------------------------------------- +class PlaylistAppCmds : public AppCmdsI +{ + public: + PlaylistAppCmds(); + virtual ~PlaylistAppCmds() {} + virtual void appcmds_onCommand(int id, const RECT *buttonRect, int which_button); + + enum { + PL_ADD=0, + PL_REM, + PL_SEL, + PL_MISC, + PL_LIST, + }; + +protected: + CmdRec addCmd, remCmd, selCmd, miscCmd, listCmd; +}; + +#ifdef MINIBROWSER_SUPPORT + +//----------------------------------------------------------------------------------------------- +class MinibrowserAppCmds : public AppCmdsI { + public: + MinibrowserAppCmds(); + virtual ~MinibrowserAppCmds() {} + virtual void appcmds_onCommand(int id, const RECT *buttonRect, int which_button); + + enum { + MB_BACK=0, + MB_FORWARD, + MB_STOP, + MB_RELOAD, + MB_MISC, + }; +}; +#endif + +//----------------------------------------------------------------------------------------------- +class VideoAppCmds : public AppCmdsI { + public: + VideoAppCmds(); + virtual ~VideoAppCmds() {} + virtual void appcmds_onCommand(int id, const RECT *buttonRect, int which_button); + + enum { + VID_FULLSCREEN=0, + VID_1X, + VID_2X, + VID_LIB, + VID_MISC, + }; +}; + +#endif \ No newline at end of file diff --git a/Src/Plugins/General/gen_ff/wasabi.dsp b/Src/Plugins/General/gen_ff/wasabi.dsp new file mode 100644 index 00000000..e2db5c47 --- /dev/null +++ b/Src/Plugins/General/gen_ff/wasabi.dsp @@ -0,0 +1,1931 @@ +# Microsoft Developer Studio Project File - Name="wasabi" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Static Library" 0x0104 + +CFG=wasabi - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "wasabi.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "wasabi.mak" CFG="wasabi - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "wasabi - Win32 Release" (based on "Win32 (x86) Static Library") +!MESSAGE "wasabi - Win32 Debug" (based on "Win32 (x86) Static Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "wasabi - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "_build/Release" +# PROP Intermediate_Dir "_build/Release" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /Yu"stdafx.h" /FD /c +# ADD CPP /nologo /MD /W3 /GX /Zd /O2 /I "../gen_ff" /I "../wasabi" /I "../wasabi/api/font/freetype/freetype2/include" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /Yu"precomp.h" /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ELSEIF "$(CFG)" == "wasabi - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "_build/Debug" +# PROP Intermediate_Dir "_build/Debug" +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /Yu"stdafx.h" /FD /GZ /c +# ADD CPP /nologo /W3 /Gm /GX /ZI /Od /I "../gen_ff" /I "../wasabi" /I "../wasabi/api/font/freetype/freetype2/include" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /Yu"precomp.h" /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LIB32=link.exe -lib +# ADD BASE LIB32 /nologo +# ADD LIB32 /nologo + +!ENDIF + +# Begin Target + +# Name "wasabi - Win32 Release" +# Name "wasabi - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Group "system" + +# PROP Default_Filter "" +# Begin Group "file" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\wasabi\bfc\file\filename.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\bfc\file\readdir.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\bfc\file\recursedir.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\bfc\file\wildcharsenum.cpp +# End Source File +# End Group +# Begin Group "memory" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\wasabi\bfc\foreach.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\bfc\freelist.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\bfc\loadlib.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\bfc\memblock.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\bfc\ptrlist.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\bfc\stack.cpp +# End Source File +# End Group +# Begin Group "utils" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\wasabi\bfc\util\profiler.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\bfc\util\timefmt.cpp +# End Source File +# End Group +# Begin Group "string" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\wasabi\bfc\string\bigstring.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\bfc\string\encodedstr.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\bfc\string\playstring.cpp +# End Source File +# Begin Source File + +SOURCE=..\Wasabi\bfc\string\string.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\bfc\string\url.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\bfc\string\utf8.cpp +# End Source File +# End Group +# Begin Group "parse" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\wasabi\bfc\parse\hierarchyparser.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\bfc\parse\paramparser.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\bfc\parse\pathparse.cpp +# End Source File +# End Group +# Begin Source File + +SOURCE=..\wasabi\bfc\assert.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\bfc\critsec.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\bfc\depend.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\bfc\node.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\bfc\nsGUID.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\bfc\std.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\bfc\std_file.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\bfc\std_math.cpp +# End Source File +# End Group +# Begin Group "apis" + +# PROP Default_Filter "" +# Begin Group "svc" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\wasabi\api\service\api_service.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\service\api_servicei.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\service\api_servicex.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\syscb\callbacks\consolecb.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\service\servicei.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\service\svc_enum.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\service\svccache.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\service\svcenum.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\service\svcenumbyguid.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\service\svcenumt.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\service\svcmgr.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\service\waservicefactory.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\service\waservicefactorybase.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\service\waservicefactoryi.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\service\waservicefactoryt.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\service\waservicefactorytsingle.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\service\waservicefactoryx.cpp +# End Source File +# End Group +# Begin Group "wnd" + +# PROP Default_Filter "" +# Begin Group "wndclass" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\wasabi\api\wnd\wndclass\abstractwndhold.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\wnd\wndclass\appbarwnd.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\wnd\wndclass\backbufferwnd.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\wnd\basewnd.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\wnd\wndclass\blankwnd.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\wnd\wndclass\bufferpaintwnd.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\wnd\wndclass\buttbar.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\wnd\wndclass\buttwnd.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\wnd\wndclass\clickwnd.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\wnd\wndclass\editwnd.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\wnd\wndclass\editwndstring.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\wnd\wndclass\embeddedxui.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\wnd\wndclass\foreignwnd.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\wnd\wndclass\framewnd.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\wnd\wndclass\gradientwnd.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\wnd\wndclass\guiobjwnd.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\wnd\wndclass\labelwnd.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\wnd\wndclass\listwnd.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\wnd\wndclass\oswnd.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\wnd\wndclass\oswndhost.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\wnd\paintset.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\wnd\wndclass\qpaintwnd.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\wnd\rootwnd.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\wnd\wndclass\rootwndholder.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\wnd\wndclass\scbkgwnd.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\wnd\wndclass\scrollbar.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\wnd\wndclass\sepwnd.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\wnd\wndclass\slider.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\wnd\wndclass\status.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\wnd\wndclass\svcwndhold.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\wnd\wndclass\tooltip.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\wnd\wndclass\treewnd.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\wnd\wndclass\typesheet.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\wnd\virtualwnd.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\wnd\wndclass\wndholder.cpp +# End Source File +# End Group +# Begin Source File + +SOURCE=..\wasabi\api\wnd\api_wnd.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\autobitmap.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\wnd\bitmap.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\wnd\blending.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\wnd\contextmenu.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\wnd\cursor.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\wnd\deactivatemgr.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\wnd\dragitemi.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\wnd\filteredcolor.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\wnd\findobjectcb.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\bfc\util\findopenrect.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\wnd\keyboard.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\wnd\paintcb.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\wnd\popexitcb.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\wnd\popexitchecker.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\wnd\popup.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\bfc\std_wnd.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\service\svcs\svc_wndcreate.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\timer\timerclient.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\wnd\platform\win32\win32_canvas.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\wnd\platform\win32\win32_region.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\wnd\wndapi.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\syscb\callbacks\wndcb.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\wnd\wndtrack.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\xmlobject.cpp +# End Source File +# End Group +# Begin Group "timer" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\wasabi\api\timer\api_timer.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\timer\genwnd.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\timer\timerapi.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\timer\timermul.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\timer\tmultiplex.cpp +# End Source File +# End Group +# Begin Group "app" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\wasabi\api\application\api_application.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\application\api_applicationi.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\application\api_applicationx.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\wac\\compon.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\bfc\util\inifile.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\application\pathmgr.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\application\version.cpp +# End Source File +# End Group +# Begin Group "font" + +# PROP Default_Filter "" +# Begin Group "freetype" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\wasabi\api\font\freetype\freetype.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\font\freetype\freetypefont.cpp +# End Source File +# End Group +# Begin Group "win32" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\wasabi\api\font\win32\truetypefont_win32.cpp +# End Source File +# End Group +# Begin Source File + +SOURCE=..\wasabi\api\font\api_font.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\font\bitmapfont.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\font\font.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\font\fontapi.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\service\svcs\svc_font.cpp +# End Source File +# End Group +# Begin Group "syscb" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\wasabi\api\syscb\api_syscb.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\syscb\api_syscbi.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\syscb\api_syscbx.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\syscb\cbmgr.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\syscb\callbacks\syscb.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\syscb\callbacks\syscbi.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\syscb\callbacks\syscbx.cpp +# End Source File +# End Group +# Begin Group "imgldr" + +# PROP Default_Filter "" +# Begin Group "pngload" + +# PROP Default_Filter "" +# Begin Group "pnglib" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\wasabi\api\imgldr\pngload\pnglib\PNG.C +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\imgldr\pngload\pnglib\PNGERROR.C +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\imgldr\pngload\pnglib\PNGGET.C +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\imgldr\pngload\pnglib\PNGMEM.C +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\imgldr\pngload\pnglib\PNGREAD.C +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\imgldr\pngload\pnglib\PNGRIO.C +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\imgldr\pngload\pnglib\PNGRTRAN.C +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\imgldr\pngload\pnglib\PNGRUTIL.C +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\imgldr\pngload\pnglib\PNGSET.C +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\imgldr\pngload\pnglib\PNGTRANS.C +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# End Group +# Begin Source File + +SOURCE=..\wasabi\api\imgldr\pngload\loader.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\imgldr\pngload\pngload.cpp +# End Source File +# End Group +# Begin Group "jpgload" + +# PROP Default_Filter "" +# Begin Group "jpgdlib" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\wasabi\api\imgldr\jpgload\jpgdlib\H2v2.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\imgldr\jpgload\jpgdlib\idct.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\imgldr\jpgload\jpgdlib\jidctfst.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\imgldr\jpgload\jpgdlib\jpegdecoder.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\imgldr\jpgload\jpgdlib\jpegdecoder.h +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\imgldr\jpgload\jpgdlib\jpegdecoder.inl +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\imgldr\jpgload\jpgdlib\main.h +# End Source File +# End Group +# Begin Source File + +SOURCE=..\wasabi\api\imgldr\jpgload\jpgload.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\imgldr\jpgload\loader_jpg.cpp +# End Source File +# End Group +# Begin Group "imggen" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\wasabi\bfc\draw\drawpoly.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\imgldr\imggen\grad.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\imgldr\imggen\osedge.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\imgldr\imggen\poly.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\imgldr\imggen\solid.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\service\svcs\svc_imggen.cpp +# End Source File +# End Group +# Begin Source File + +SOURCE=..\wasabi\api\imgldr\api_imgldr.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\imgldr\imgldr.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\imgldr\imgldrapi.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\service\svcs\svc_imgload.cpp +# End Source File +# End Group +# Begin Group "ffskin" + +# PROP Default_Filter "" +# Begin Group "widgets" + +# PROP Default_Filter "" +# Begin Group "tooltips" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\grouptips.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\service\svcs\svc_tooltips.cpp +# End Source File +# End Group +# Begin Group "mb" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\mb\Atl.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\mb\iebrowser.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\mb\mbsvc.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\service\svcs\svc_minibrowser.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\mb\xuibrowser.cpp +# End Source File +# End Group +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\animlayer.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\button.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\checkbox.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\combobox.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\compbuck2.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\customobject.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\dropdownlist.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\edit.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\feeds\feedwatch.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\feeds\feedwatcherso.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\fx_dmove.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\bfc\draw\gradient.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\group.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\groupclickwnd.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\grouplist.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\grouptgbutton.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\guiradiogroup.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\wndmgr\guistatuscb.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\historyeditbox.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\layer.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\mb\mainminibrowser.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\mb\minibrowser.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\mb\minibrowserwnd.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\mouseredir.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\nakedobject.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\objdirwnd.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\objectactuator.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\pathpicker.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\pslider.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\sa.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\seqband.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\seqpreamp.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\seqvis.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\spanbar.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\sseeker.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\sstatus.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\service\svcs\svc_xuiobject.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\svolbar.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\wnd\wndclass\tabsheet.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\wnd\wndclass\tabsheetbar.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\text.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\tgbutton.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\title.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\titlebox.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\xuiaddparams.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\xuibookmarklist.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\xuicheckbox.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\xuicombobox.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\xuicustomobject.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\xuidropdownlist.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\xuieditbox.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\xuiframe.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\xuigradientwnd.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\xuigrid.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\xuigroupxfade.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\xuihideobject.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\xuihistoryedit.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\xuilist.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\xuimenu.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\xuimenuso.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\xuiobjdirwnd.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\xuioswndhost.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\xuipathpicker.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\xuiprogressgrid.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\xuiradiogroup.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\xuirect.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\xuisendparams.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\xuistatus.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\xuitabsheet.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\xuithemeslist.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\xuititlebox.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\xuitree.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\xuiwndholder.cpp +# End Source File +# End Group +# Begin Source File + +SOURCE=..\wasabi\api\skin\api_skin.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\cursormgr.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\gammamgr.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\groupmgr.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\groupwndcreate.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\guitree.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\regioncache.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\skin.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\skinapi.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\syscb\callbacks\skincb.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\skinclr.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\skinelem.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\skinfilter.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\skinfont.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\skinitem.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\skinparse.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\service\svcs\svc_skinfilter.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\service\svcs\svc_textfeed.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\feeds\textfeed.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets.cpp +# End Source File +# End Group +# Begin Group "xml" + +# PROP Default_Filter "" +# Begin Group "parser" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\wasabi\api\xml\parser\hashtable.c +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\xml\parser\simap.cpp +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\xml\parser\xmlparser.c +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\xml\parser\xmlrole.c +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\xml\parser\xmltok.c +# SUBTRACT CPP /YX /Yc /Yu +# End Source File +# End Group +# Begin Source File + +SOURCE=..\wasabi\api\xml\api_xml.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\xml\xmlapi.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\xml\xmlparams.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\xml\xmlparamsi.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\xml\xmlparamsx.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\xml\xmlreader.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\xml\xmlwrite.cpp +# End Source File +# End Group +# Begin Group "script" + +# PROP Default_Filter "" +# Begin Group "maki" + +# PROP Default_Filter "" +# Begin Group "maki classes" + +# PROP Default_Filter "" +# Begin Group "encapsulators" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\wasabi\api\script\objects\rootobj.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\objects\rootobjcb.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\objects\rootobjcbi.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\objects\rootobjcbx.cpp +# End Source File +# End Group +# Begin Group "c_script" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\wasabi\api\script\objects\c_script\c_browser.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\objects\c_script\c_button.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\objects\c_script\c_checkbox.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\objects\c_script\c_container.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\objects\c_script\c_dropdownlist.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\objects\c_script\c_edit.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\objects\c_script\c_group.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\objects\c_script\c_guilist.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\objects\c_script\c_guiobject.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\objects\c_script\c_guitree.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\objects\c_script\c_layout.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\objects\c_script\c_menubutton.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\objects\c_script\c_querylist.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\objects\c_script\c_rootobj.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\objects\c_script\c_slider.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\objects\c_script\c_text.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\objects\c_script\c_togglebutton.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\objects\c_script\c_treeitem.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\objects\c_script\h_browser.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\objects\c_script\h_button.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\objects\c_script\h_checkbox.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\objects\c_script\h_container.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\objects\c_script\h_dropdownlist.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\objects\c_script\h_edit.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\objects\c_script\h_group.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\objects\c_script\h_guilist.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\objects\c_script\h_guiobject.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\objects\c_script\h_guitree.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\objects\c_script\h_layout.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\objects\c_script\h_menubutton.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\objects\c_script\h_querylist.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\objects\c_script\h_rootobj.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\objects\c_script\h_slider.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\objects\c_script\h_text.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\objects\c_script\h_togglebutton.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\objects\c_script\h_treeitem.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\objects\c_script\scripthook.cpp +# End Source File +# End Group +# Begin Group "scriptcore" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\wasabi\api\script\objects\core\coreadminobj.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\objects\core\coreobj.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\objects\core\svc_scriptcore.cpp +# End Source File +# End Group +# Begin Source File + +SOURCE=..\wasabi\api\script\objects\guiobj.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\objcontrollert.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\objects\rootobjcontroller.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\objects\rootobject.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\objects\rootobjecti.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\objects\rootobjectx.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\objects\sbitlist.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\objects\slist.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\objects\smap.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\objects\spopup.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\objects\sregion.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\objects\systemobj.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\objects\timer.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\objects\wacobj.cpp +# End Source File +# End Group +# Begin Source File + +SOURCE=..\wasabi\api\script\guru.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\objcontroller.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\objecttable.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\script.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\scriptmgr.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\scriptobj.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\scriptobji.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\scriptobjx.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\vcpu.cpp +# End Source File +# End Group +# Begin Group "debugger" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\wasabi\api\script\debugger\api_makidebug.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\debugger\debugapi.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\debugger\debuggerui.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\debugger\debugsymbols.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\debugger\disasm.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\debugger\jitd.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\debugger\jitdbreak.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\debugger\sdebuggerui.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\debugger\sourcecodeline.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\debugger\vcpudebug.cpp +# End Source File +# End Group +# Begin Source File + +SOURCE=..\wasabi\api\script\api_maki.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\api_makii.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\script\api_makix.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\service\svcs\svc_scriptobj.cpp +# End Source File +# End Group +# Begin Group "wndmgr" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\wasabi\api\wndmgr\alphamgr.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\wndmgr\animate.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\wndmgr\api_wndmgr.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\wndmgr\appcmds.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\wndmgr\autopopup.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\wndmgr\container.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\wndmgr\gc.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\wndmgr\layout.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\wndmgr\msgbox.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\wndmgr\resize.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\wndmgr\skinembed.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\wndmgr\skinwnd.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\wndmgr\snappnt.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\wndmgr\wndmgrapi.cpp +# End Source File +# End Group +# Begin Group "core" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\wasabi\api\core\api_core.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\syscb\callbacks\corecb.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\service\svcs\svc_action.cpp +# End Source File +# End Group +# Begin Group "filereaders" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\wasabi\api\filereader\api_filereader.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\filereader\local\fileread.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\filereader\\filereaderapi.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\filereader\res\resread.h +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\service\svcs\svc_fileread.cpp +# End Source File +# End Group +# Begin Group "cfg" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\wasabi\api\config\api_config.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\config\api_configi.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\config\api_configx.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\config\items\attribute.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\config\items\attrstr.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\config\items\cfgitemi.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api/config\cfglist.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\config\cfgscriptobj.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\config\config.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\config\items\intarray.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\config\options.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\config\uioptions.cpp +# End Source File +# End Group +# Begin Group "locales" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\wasabi\api\locales\api_locales.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\locales\api_localesi.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\locales\api_localesx.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\locales\localesmgr.cpp +# End Source File +# End Group +# Begin Group "memmgr" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\wasabi\api\memmgr\api_memmgr.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\memmgr\memmgrapi.cpp +# End Source File +# End Group +# Begin Group "util" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\wasabi\api\util\selectfile.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\util\systray.cpp +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\util\varmgr.cpp +# End Source File +# End Group +# End Group +# Begin Group "debug" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\stats\statswnd.cpp + +!IF "$(CFG)" == "wasabi - Win32 Release" + +# PROP Exclude_From_Build 1 + +!ELSEIF "$(CFG)" == "wasabi - Win32 Debug" + +!ENDIF + +# End Source File +# Begin Source File + +SOURCE=..\wasabi\api\skin\widgets\stats\xuistats.cpp + +!IF "$(CFG)" == "wasabi - Win32 Release" + +# PROP Exclude_From_Build 1 + +!ELSEIF "$(CFG)" == "wasabi - Win32 Debug" + +!ENDIF + +# End Source File +# End Group +# Begin Source File + +SOURCE=..\wasabi\api\apiinit.cpp +# End Source File +# Begin Source File + +SOURCE=.\precomp.cpp +# ADD CPP /Yc"precomp.h" +# End Source File +# Begin Source File + +SOURCE=.\wasabicfg.h +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=.\StdAfx.h +# End Source File +# End Group +# Begin Source File + +SOURCE=.\Readme.txt +# End Source File +# End Target +# End Project diff --git a/Src/Plugins/General/gen_ff/wasabi.vcproj b/Src/Plugins/General/gen_ff/wasabi.vcproj new file mode 100644 index 00000000..dcee2cf7 --- /dev/null +++ b/Src/Plugins/General/gen_ff/wasabi.vcproj @@ -0,0 +1,286 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Src/Plugins/General/gen_ff/wasabicfg.h b/Src/Plugins/General/gen_ff/wasabicfg.h new file mode 100644 index 00000000..1140295f --- /dev/null +++ b/Src/Plugins/General/gen_ff/wasabicfg.h @@ -0,0 +1,491 @@ +#ifndef __WASABI_CFG_H +#define __WASABI_CFG_H + +#define GEN_FF +#define WA5 + +//#define _WASABIRUNTIME + +// Uncomment this to have an old-style global api pointer +//#define WA3COMPATIBILITY + + +#ifndef _WASABIRUNTIME + +#ifndef WA3COMPATIBILITY +#define WASABINOMAINAPI +#endif + +/* + +Comment or uncomment the following directives according to the needs of your application : + +*/ + +/* note to the team: + + the WANT_WASABI_API_* directives will go away once we're done splitting the api, their only purpose + is to split the api one bit at a time while the rest remains working. when it's done, all that will remain + will be the WASABI_COMPILE_* directives + +*/ + + +// This allows component (external plugins) +//#define WASABI_COMPILE_COMPONENTS + +// This enables the layered UI +#define WASABI_COMPILE_WND + +// This enables multiplexed timers +#define WASABI_COMPILE_TIMERS + +// This enables xml group loading within the window api +#define WASABI_COMPILE_SKIN +#define WASABI_SCRIPT_SYSTEMOBJECT_WA3COMPATIBLE + +// This enables internationalizaiton +// #define WASABI_COMPILE_UTF + +// This enables action handling in UI objects (clicks and keypresses) +//#define WASABI_COMPILE_ACTIONS // CUT!!! + +// This enables UI scripting +#define WASABI_COMPILE_SCRIPT + +// This enables keyboard locales in UI +#define WASABI_COMPILE_LOCALES + +// This enables bitmap and truetype font rendering +#define WASABI_COMPILE_FONTS + +//#define WASABI_FONT_RENDERER_USE_WIN32 +#define WASABI_FONT_RENDERER_USE_FREETYPE + +// This sets the static font renderer. If you are compiling with api_config, the attribute to set is { 0x280876cf, 0x48c0, 0x40bc, { 0x8e, 0x86, 0x73, 0xce, 0x6b, 0xb4, 0x62, 0xe5 } }, "Font Renderer" +#if defined(WASABI_FONT_RENDERER_USE_WIN32) +#define WASABI_FONT_RENDERER L"" // "" is Win32 +#elif defined(WASABI_FONT_RENDERER_USE_FREETYPE) +#define WASABI_FONT_RENDERER L"Freetype" // Freetype lib +#else +#define WASABI_FONT_RENDERER L"" // "" default for OS +#endif + +// This lets you override all bitmapfonts using TTF fonts (for internationalization). define to a function call or a global value to change this value dynamically. +// If you are compiling with api_config, the attribute to set is { 0x280876cf, 0x48c0, 0x40bc, { 0x8e, 0x86, 0x73, 0xce, 0x6b, 0xb4, 0x62, 0xe5 } }, "Use bitmap fonts (no international support)" +#define WASABI_FONT_TTFOVERRIDE 0 // 1 does all rendering with TTF + +#define WASABI_WNDMGR_ANIMATEDRECTS 0 // if api_config is compiled, the item controlling this is {280876CF-48C0-40bc-8E86-73CE6BB462E5};"Animated rects" +#define WASABI_WNDMGR_FINDOPENRECT 0 // if api_config is compiled, the item controlling this is {280876CF-48C0-40bc-8E86-73CE6BB462E5};"Find open rect" +#define WASABI_WNDMGR_LINKLAYOUTSCALES 0 // if api_config is compiled, the item controlling this is {9149C445-3C30-4e04-8433-5A518ED0FDDE};"Link layouts scale" +#define WASABI_WNDMGR_LINKLAYOUTSALPHA 0 // if api_config is compiled, the item controlling this is {9149C445-3C30-4e04-8433-5A518ED0FDDE};"Link layouts alpha" +#define WASABI_WNDMGR_DESKTOPALPHA 1 // if api_config is compiled, the item controlling this is {9149C445-3C30-4e04-8433-5A518ED0FDDE};"Enable desktop alpha" +// This enables loading for pngs, jpgs (you need to add the necessary image loader services) +#define WASABI_COMPILE_IMGLDR +#define WASABI_COMPILE_IMGLDR_PNGREAD +#define WASABI_COMPILE_IMGLDR_JPGREAD + +// This enables metadb support +//#define WASABI_COMPILE_METADB + +// This enables config file support +#define WASABI_COMPILE_CONFIG + +// This enables the filereader pipeline +#define WASABI_COMPILE_FILEREADER + +// This enables the zip filereader +//#define WASABI_COMPILE_ZIPREADER + +// This enables the xml parser for config and group loading +#define WASABI_COMPILE_XMLPARSER + +// This enables system callback management +#define WASABI_COMPILE_SYSCB + +// This enables centralized memory allocation/deallocation +#define WASABI_COMPILE_MEMMGR + +#define WASABI_DIRS_FROMEXEPATH // otherwise, if the lib is running from a dll in another path, undefining that means the path are relative to the DLL path +#define WASABI_SKINS_SUBDIRECTORY L"skins" +#define WASABI_RESOURCES_SUBDIRECTORY L"plugins\\freeform" + +#define WASABI_COMPILE_MEDIACORE + +#define WASABI_COMPILE_WNDMGR +#define WASABI_COMPILE_PAINTSETS +#define WASABI_COMPILE_MAKIDEBUG + +#define WASABI_CUSTOMIMPL_MEDIACORE +#define WASABI_WIDGETS_MEDIASLIDERS + +#define WASABI_CUSTOM_CONTEXTMENUS + +#define WASABI_CUSTOM_QUIT +#define WASABI_CUSTOM_QUITFN { extern void appQuit(); appQuit(); } + +#define WASABI_CUSTOM_ONTOP + +#else // not _WASABIRUNTIME + +// this section should define the entire set of wasabi parts since this is a full runtime build + +#define WASABI_COMPILE_COMPONENTS +#define WASABI_COMPILE_SKIN +#define WASABI_COMPILE_ACTIONS +#define WASABI_COMPILE_SCRIPT +#define WASABI_COMPILE_FONTS +#define WASABI_COMPILE_LOCALES +#define WASABI_COMPILE_IMGLDR +#define WASABI_COMPILE_IMGLDR_PNGREAD +#define WASABI_COMPILE_IMGLDR_JPGREAD +#define WASABI_COMPILE_METADB +#define WASABI_COMPILE_CONFIG +#define WASABI_COMPILE_FILEREADER +#define WASABI_COMPILE_XMLPARSER +#define WASABI_COMPILE_SYSCB +#define WASABI_COMPILE_MEMMGR +#define WASABI_COMPILE_SKIN_WA2 +#define WASABI_COMPILE_PAINTSETS +#define WASABI_COMPILE_WNDMGR +#define WASABI_COMPILE_MEDIACORE +#define WASABI_COMPILE_TIMERS +#define WASABI_COMPILE_WND +#define WASABI_COMPILE_UTF +#define WASABI_SKINS_SUBDIRECTORY "skins" +#define WASABI_FONT_RENDERER "" // "" is Win32 +#define WASABI_WNDMGR_ANIMATEDRECTS 0 // if api_config is compiled, the item controlling this is {280876CF-48C0-40bc-8E86-73CE6BB462E5};"Animated rects" +#define WASABI_WNDMGR_FINDOPENRECT 0 // if api_config is compiled, the item controlling this is {280876CF-48C0-40bc-8E86-73CE6BB462E5};"Find open rect" +#define WASABI_COMPILE_MAKIDEBUG + +#endif // not _WASABIRUNTIME + +#ifdef _WASABIRUNTIME + #define WASABI_API_SYSTEM api + #define WASABI_API_APP api + #define WASABI_API_COMPONENT api + #define WASABI_API_SVC api + #define WASABI_API_SYSCB api + #define WASABI_API_MAKI api + #define WASABI_API_UTF api + #define WASABI_API_WND api + #define WASABI_API_IMGLDR api + #define WASABI_API_FILE api + #define WASABI_API_TIMER api + #define WASABI_API_WNDMGR api + #define WASABI_API_SKIN api + #define WASABI_API_METADB api + #define WASABI_API_LOCALE api + #define WASABI_API_CONFIG api + #define WASABI_API_FONT api + #define WASABI_API_MEMMGR api + #define WASABI_API_XML api + #define WASABI_API_MEDIACORE api + #define WASABI_API_MAKIDEBUG debugApi + +#else // _WASABIRUNTIME + + #define WASABI_API_SYSTEM systemApi + + #define WASABI_API_APP applicationApi + #define WASABI_API_SVC serviceApi + #define WASABI_API_SYSCB sysCallbackApi + + #ifdef WASABI_COMPILE_COMPONENTS + #define WASABI_API_COMPONENT componentApi + #endif + + #ifdef WASABI_COMPILE_SCRIPT + #define WASABI_API_MAKI makiApi + #endif + + #ifdef WASABI_COMPILE_UTF + #define WASABI_API_UTF utfApi + #endif + + #ifdef WASABI_COMPILE_WND + #define WASABI_API_WND wndApi + #endif + + #ifdef WASABI_COMPILE_IMGLDR + #define WASABI_API_IMGLDR imgLoaderApi + #endif + + #ifdef WASABI_COMPILE_FILEREADER + #define WASABI_API_FILE fileApi + #endif + + #ifdef WASABI_COMPILE_TIMERS + #define WASABI_API_TIMER timerApi + #endif + + #ifdef WASABI_COMPILE_WNDMGR + #define WASABI_API_WNDMGR wndManagerApi + #endif + + #ifdef WASABI_COMPILE_SKIN + #define WASABI_API_SKIN skinApi + #endif + + #ifdef WASABI_COMPILE_METADB + #define WASABI_API_METADB metadbApi + #endif + + #ifdef WASABI_COMPILE_LOCALES + #define WASABI_API_LOCALE localesApi + #endif + + #ifdef WASABI_COMPILE_CONFIG + #define WASABI_API_CONFIG configApi + #endif + + #ifdef WASABI_COMPILE_FONTS + #define WASABI_API_FONT fontApi + #endif + + #ifdef WASABI_COMPILE_MEMMGR + #define WASABI_API_MEMMGR memmgrApi + #endif + + #ifdef WASABI_COMPILE_XMLPARSER + #define WASABI_API_XML xmlApi + #endif + + #ifdef WASABI_COMPILE_MEDIACORE + #define WASABI_API_MEDIACORE coreApi + #endif + + + #ifdef WASABI_COMPILE_MAKIDEBUG + #define WASABI_API_MAKIDEBUG debugApi + #endif + +#endif // _WASABIRUNTIME + +// #define WASABI_EXTERNAL_GUIOBJECTS + +#define WASABI_WIDGETS_GUIOBJECT +#define WASABI_WIDGETS_LAYER +#define WASABI_WIDGETS_TEXT +#define WASABI_WIDGETS_BUTTON +#define WASABI_WIDGETS_TGBUTTON +#define WASABI_WIDGETS_ANIMLAYER +#define WASABI_WIDGETS_GROUPLIST +#define WASABI_WIDGETS_MOUSEREDIR +#define WASABI_WIDGETS_SLIDER +#define WASABI_WIDGETS_MEDIAVIS +#define WASABI_WIDGETS_MEDIAEQCURVE +#define WASABI_WIDGETS_MEDIASTATUS +//#define WASABI_WIDGETS_SVCWND +#define WASABI_WIDGETS_EDIT +#define WASABI_WIDGETS_TITLEBAR +#define WASABI_WIDGETS_COMPBUCK +#define WASABI_WIDGETS_BROWSER +#define WASABI_WIDGETS_FRAME +#define WASABI_WIDGETS_GRID +//#define WASABI_WIDGETS_QUERYDRAG +//#define WASABI_WIDGETS_QUERYLIST +//#define WASABI_WIDGETS_FILTERLIST +//#define WASABI_WIDGETS_QUERYLINE +#define WASABI_WIDGETS_TABSHEET +#define WASABI_WIDGETS_CHECKBOX +#define WASABI_WIDGETS_TITLEBOX +#define WASABI_WIDGETS_CUSTOMOBJECT +#define WASABI_WIDGETS_OSWNDHOST +#define WASABI_WIDGETS_RADIOGROUP +#define WASABI_WIDGETS_LIST +#define WASABI_WIDGETS_TREE +#define WASABI_WIDGETS_DROPDOWNLIST +#define WASABI_WIDGETS_COMBOBOX +#define WASABI_WIDGETS_HISTORYEDITBOX +#define WASABI_WIDGETS_OBJECTDIRECTORY +#define WASABI_WIDGETS_RECTANGLE +#define WASABI_WIDGETS_PATHPICKER +#define WASABI_WIDGETS_GRADIENT +#define WASABI_WIDGETS_MENUBUTTON +#define WASABI_WIDGETS_MENU +#define WASABI_WIDGETS_WNDHOLDER +#define WASABI_WIDGETS_LAYOUTSTATUS +#define WASABI_WIDGETS_TOOLTIPS + +#include "../Winamp/buildType.h" +#if defined(_DEBUG) || defined(WASABI_DEBUG)// || defined(INTERNAL) || defined(BETA) || defined(NIGHT) +#define WASABI_COMPILE_STATSWND +#endif + +#if defined(_DEBUG) +#ifndef WASABI_DEBUG +#define WASABI_DEBUG +#endif +#endif + +#define WASABI_TOOLOBJECT_HIDEOBJECT +#define WASABI_TOOLOBJECT_SENDPARAMS +#define WASABI_TOOLOBJECT_ADDPARAMS + +// #endif // WASABI_EXTERNAL_GUIOBJECTS +#define WASABI_COMPILE_COLORTHEMES + +#define WASABI_SCRIPTOBJECTS_POPUP +#define WASABI_SCRIPTOBJECTS_LIST +#define WASABI_SCRIPTOBJECTS_BITLIST +#define WASABI_SCRIPTOBJECTS_REGION +#define WASABI_SCRIPTOBJECTS_TIMER +#define WASABI_SCRIPTOBJECTS_MAP +#define WASABI_SCRIPTOBJECTS_EMBEDDEDXUI // needed by 3rd+ level objects + +#ifndef WASABI_EXTERNAL_GUIOBJECTS +#define WASABI_SCRIPTOBJECTS_WAC +#endif // WASABI_EXTERNAL_GUIOBJECTS + +// lone is super dirty but wants to get stuff working. we need to clean that up +#ifdef WASABI_CUSTOMIMPL_MEDIACORE +class api_core; +extern api_core *createCustomCoreApi(); +extern void destroyCustomCoreApi(api_core *core); +#endif + +#define WASABI_WNDMGR_ALLCONTAINERSDYNAMIC 0 +#define WASABI_WNDMGR_NORESPAWN +#define WASABI_WNDMGR_OSMSGBOX + +#define WASABI_STATICVARMGR +#define WASABI_CUSTOM_MODULE_SVCMGR + +#define ON_LAYOUT_CHANGED { extern void onLayoutChanged(); onLayoutChanged(); } + +#define ON_FATAL_SKIN_ERROR { extern void onFatalSkinError(); onFatalSkinError(); } + +#define ON_CREATE_EXTERNAL_WINDOW_GUID(x, y) { extern int onCreateExternalWindowGuid(GUID g); y = onCreateExternalWindowGuid(x); } + +#define ON_TOGGLE_DESKTOPALPHA(v) { extern void onToggleDesktopAlpha(int v); onToggleDesktopAlpha(v); } +#define ON_TWEAK_CONTAINER_NAMEW(x) { extern const wchar_t *onTweakContainerNameW(const wchar_t *name); x = onTweakContainerNameW(x); } +#define ON_TWEAK_RENDER_RATIO(x) { extern double onTweakRenderRatio(double v); x = onTweakRenderRatio(x); } +#define ON_CUSTOM_ALTF4 { extern void onCustomAltF4(); onCustomAltF4(); } + +#define WASABI_DEFAULT_STDCONTAINER L"resizable_status" + +#define SWITCH_SKIN(x) { extern void switchSkin(const wchar_t *name); switchSkin(x); } +#define IS_SKIN_STILL_LOADING(x) { extern int isSkinStillLoading(); x = isSkinStillLoading(); } + +#define ON_LOAD_EXTRA_COLORTHEMES() { extern void loadExtraColorThemes(); loadExtraColorThemes(); } + +#define LOCALES_CUSTOM_LOAD(x) { extern const wchar_t *localesCustomGetFile(); x = localesCustomGetFile(); } + +#define DEFAULT_CROSSFADE_ENABLED FALSE + +#define CUSTOM_VARS(x, y) { extern const wchar_t *getCustomVar(const wchar_t *var); y = getCustomVar(x); } + +#define WASABI_NO_RELEASEMODE_DEBUGSTRINGS + +#define WASABI_CUSTOM_MINIDB(field, buf, len) { extern void getCustomMetaData(const wchar_t *f, wchar_t *b, int l); getCustomMetaData(field, buf, len); } + +#define GET_SONG_INFO_TEXT(ret) { extern const wchar_t *getSongInfoText(); ret = getSongInfoText(); } + +#define GET_SONG_INFO_TEXT_TRANSLATED(ret) { extern const wchar_t *getSongInfoTextTranslated(); ret = getSongInfoTextTranslated(); } + +#define GET_KBDFORWARD_WND(g, wnd) { extern OSWINDOWHANDLE getKeyboardForwardWnd(GUID g); wnd = getKeyboardForwardWnd(g); } + +#define WASABI_EDITWND_LISTCOLORS // editwnds use list foreground & background rather than their own colors + +#define WASABI_APPBAR_ONDOCKCHANGED(wnd) { extern void onAppBarDockChanged(ifc_window *w); onAppBarDockChanged(wnd); } + +#define WASABI_GET_VERSION(cs, n) { extern const char *getVersion(); STRNCPY(cs, getVersion(), n); cs[n] = 0; } + +#define WASABI_ON_MAIN_MOVE(hwnd) { extern void onMainLayoutMove(HWND w); onMainLayoutMove(hwnd); } + +#define WASABI_ON_REPARENT(hwnd) { extern void onReParent(HWND w); onReParent(hwnd); } + +#define WASABI_ON_REINIT(hwnd) { extern void onReInit(HWND w); onReInit(hwnd); } + +#define WASABI_GET_TEMPDISABLE_AOT(x) { extern int getAOTTempDisable(); x = getAOTTempDisable(); } + +#define WASABI_CHECK_CAN_EXIT(x) { extern int canExitWinamp(); x = canExitWinamp(); } + +#define WASABI_CHECK_OFFSCREENCHECK_DISABLE(x) { extern int fsMonitorIsFS(); x = fsMonitorIsFS(); } + +#define WASABI_MODAL_PUSH { extern void modalPush(); modalPush(); } +#define WASABI_MODAL_POP { extern void modalPop(); modalPop(); } + +// config defaults + +#define DEFAULT_DESKTOPALPHA TRUE +#define DEFAULT_LINKLAYOUTSCALE TRUE +#define DEFAULT_LINKLAYOUTSALPHA FALSE +#define DEFAULT_LINKALLALPHA TRUE +#define DEFAULT_LINKALLRATIO FALSE +#define DEFAULT_LINKEDALPHA 255 +#define DEFAULT_AUTOOPACITYTIME 2000 +#define DEFAULT_AUTOOPACITYFADEIN 250 +#define DEFAULT_AUTOOPACITYFADEOUT 1000 +#define DEFAULT_AUTOOPACITYTYPE 0 +#define DEFAULT_EXTENDAUTOOPACITY 25 +#define DEFAULT_USERATIOLOCKS FALSE +#define DEFAULT_TIMERRESOLUTION 33 +#define DEFAULT_TOOLTIPS TRUE +#define DEFAULT_TEXTSPEED 1.0f/3.0f +#define DEFAULT_TEXTINCREMENT 1 +#define DEFAULT_APPBARHIDETIME 500 +#define DEFAULT_APPBARSHOWTIME 500 + +#define UTF8 0 +#define WANT_UTF8_WARNINGS + +//#define DROP_BITMAP_ON_IDLE + +//#include "../../bfc/api/api_system.h" +#include +#include +#include + +#ifdef WASABI_COMPILE_MEMMGR +# include +#endif + +#ifdef WASABI_COMPILE_SCRIPT +# include +#endif + +#ifdef WASABI_COMPILE_FONTS +# include +#endif + +#ifdef WASABI_COMPILE_WND +# include +#endif + +#ifdef WASABI_COMPILE_IMGLDR +# include +#endif + +#ifdef WASABI_COMPILE_FILEREADER +# include +#endif + +#ifdef WASABI_COMPILE_TIMERS +# include +#endif + +#ifdef WASABI_COMPILE_WNDMGR +# include +#endif + +#ifdef WASABI_COMPILE_LOCALES +# include +#endif + +#ifdef WASABI_COMPILE_CONFIG +# include +#endif + +# include "../xml/obj_xml.h" + +#ifdef WASABI_COMPILE_SKIN +# include +#endif + +#ifdef WASABI_COMPILE_MAKIDEBUG +# include +#endif + +#endif diff --git a/Src/Plugins/General/gen_hotkeys/Configdlg.cpp b/Src/Plugins/General/gen_hotkeys/Configdlg.cpp new file mode 100644 index 00000000..d0572efe --- /dev/null +++ b/Src/Plugins/General/gen_hotkeys/Configdlg.cpp @@ -0,0 +1,471 @@ +#include "ConfigDlg.h" +#include "gen_hotkeys.h" +#include "accelBlock.h" + +#define ListView_InsertColumnW(hwnd, iCol, pcol) \ + (int)SNDMSG((hwnd), LVM_INSERTCOLUMNW, (WPARAM)(int)(iCol), (LPARAM)(const LV_COLUMNW *)(pcol)) + +#define ListView_InsertItemW(hwnd, pitem) \ + (int)SNDMSG((hwnd), LVM_INSERTITEMW, 0, (LPARAM)(const LV_ITEMW *)(pitem)) + +#define ListView_SetItemTextW(hwndLV, i, iSubItem_, pszText_) \ +{ LV_ITEMW _macro_lvi;\ + _macro_lvi.iSubItem = (iSubItem_);\ + _macro_lvi.pszText = (pszText_);\ + SNDMSG((hwndLV), LVM_SETITEMTEXTW, (WPARAM)(i), (LPARAM)(LV_ITEMW *)&_macro_lvi);\ +} + +#define ListView_SetItemW(hwnd, pitem) \ + (BOOL)SNDMSG((hwnd), LVM_SETITEMW, 0, (LPARAM)(LV_ITEMW *)(pitem)) + +void SetListItem(HWND hwHKList, HWND hwHK, HOTKEY_DATA *hk_data, int idx = -1, int failed = 0); + +static AcceleratorBlocker *pAccelBlock = NULL; +static bool changed = false; + +static int setCheckedStuff(HWND hwndDlg) +{ + int checked = SendMessage(GetDlgItem(hwndDlg, IDC_ENABLED), BM_GETCHECK, 0, 0) == BST_CHECKED; + EnableWindow(GetDlgItem(hwndDlg,IDC_HKLIST),checked); + EnableWindow(GetDlgItem(hwndDlg,IDC_COMBO),checked); + EnableWindow(GetDlgItem(hwndDlg,IDC_HOTKEY),checked); + EnableWindow(GetDlgItem(hwndDlg,IDC_ADD),checked); + if (!checked) + { + EnableWindow(GetDlgItem(hwndDlg,IDC_SAVE),0); + EnableWindow(GetDlgItem(hwndDlg,IDC_REMOVE),0); + } + EnableWindow(GetDlgItem(hwndDlg,IDC_DEFAULT),checked); + return checked; +} + +int ResizeComboBoxDropDown(HWND hwndDlg, UINT id, const char* str, int width){ + SIZE size = {0}; + HWND control = GetDlgItem(hwndDlg, id); + HDC hdc = GetDC(control); + // get and select parent dialog's font so that it'll calculate things correctly + HFONT font = (HFONT)SendMessage(hwndDlg,WM_GETFONT,0,0), oldfont = (HFONT)SelectObject(hdc,font); + GetTextExtentPoint32(hdc, str, lstrlen(str)+1, &size); + + int ret = width; + if(size.cx > width) + { + SendDlgItemMessageW(hwndDlg, id, CB_SETDROPPEDWIDTH, size.cx, 0); + ret = size.cx; + } + + SelectObject(hdc, oldfont); + ReleaseDC(control, hdc); + return ret; +} + +int ResizeComboBoxDropDownW(HWND hwndDlg, UINT id, const wchar_t *str, int width){ + SIZE size = {0}; + HWND control = GetDlgItem(hwndDlg, id); + HDC hdc = GetDC(control); + // get and select parent dialog's font so that it'll calculate things correctly + HFONT font = (HFONT)SendMessage(hwndDlg,WM_GETFONT,0,0), oldfont = (HFONT)SelectObject(hdc,font); + GetTextExtentPoint32W(hdc, str, (int)wcslen(str)+1, &size); + + int ret = width; + if(size.cx > width) + { + SendDlgItemMessageW(hwndDlg, id, CB_SETDROPPEDWIDTH, size.cx, 0); + ret = size.cx; + } + + SelectObject(hdc, oldfont); + ReleaseDC(control, hdc); + return ret; +} + +BOOL CALLBACK ConfigProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + HWND hwHKList = GetDlgItem(hwndDlg, IDC_HKLIST); + HWND hwCombo = GetDlgItem(hwndDlg, IDC_COMBO); + HWND hwHK = GetDlgItem(hwndDlg, IDC_HOTKEY); + + switch (uMsg) + { + case WM_INITDIALOG: + { + int colwidth1 = -1, colwidth2 = -1; + if (hwHKList && hwHK) + { + RECT r; + + SendMessage(hwHKList, WM_SETREDRAW, FALSE, 0); + ListView_SetExtendedListViewStyle(hwHKList, LVS_EX_FULLROWSELECT | LVS_EX_INFOTIP); + + GetClientRect(hwHKList, &r); + + colwidth2 = GetPrivateProfileIntW(L"gen_hotkeys", L"col2", -1, g_iniFile); + LVCOLUMNW lc = {LVCF_TEXT | LVCF_WIDTH, 0, + (colwidth2==-1?((r.right / 2) - GetSystemMetrics(SM_CYHSCROLL)):colwidth2), + WASABI_API_LNGSTRINGW(IDS_GHK_HOTKEY_COLUMN), 0, 0 + }; + + ListView_InsertColumnW(hwHKList, 0, &lc); + + lc.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM; + colwidth1 = GetPrivateProfileIntW(L"gen_hotkeys", L"col1", -1, g_iniFile); + lc.cx = (colwidth1==-1?(r.right - lc.cx - GetSystemMetrics(SM_CYHSCROLL)):colwidth1); + lc.pszText = WASABI_API_LNGSTRINGW(IDS_GHK_ACTION_COLUMN); + lc.iSubItem = 1; + + ListView_InsertColumnW(hwHKList, 0, &lc); + } + + changed = false; + SubclassEditBox(hwHK); + if (NULL != hwHK && NULL == pAccelBlock) + pAccelBlock = new AcceleratorBlocker(hwHK); + + HBITMAP hBitmap = LoadBitmap(psPlugin.hDllInstance, MAKEINTRESOURCE(IDB_LVSTATE)); + + HIMAGELIST hImageList = ImageList_Create(14, 14, ILC_COLOR8|ILC_MASK, 1, 0); + ImageList_AddMasked(hImageList, hBitmap, RGB(252, 254, 252)); + ListView_SetImageList(hwHKList, hImageList, LVSIL_STATE); + DeleteObject(hBitmap); + + ListView_SetItemCount(hwHKList, g_dwHotkeys); + + // We don't want our hot keys while in the config + for (size_t i = 0; i < g_dwHotkeys; i++) + { + SetLastError(0); + UnregisterHotkey(g_hotkeys + i); + SetListItem(hwHKList, hwHK, &g_hotkeys[i].hkd, -1, g_hotkeys[i].failed); + } + hotkeysClear(); + + if(colwidth1==-1) + ListView_SetColumnWidth(hwHKList, 0, LVSCW_AUTOSIZE); + if(colwidth2==-1) + ListView_SetColumnWidth(hwHKList, 1, LVSCW_AUTOSIZE); + + // clear + SendMessage(hwHK, wmHKCtlSet, 0, 0); + + ListView_SetItemState(hwHKList, -1, 0, LVIS_SELECTED); + + SendMessage(hwHKList, WM_SETREDRAW, TRUE, 0); + + int width = 0; + for (size_t i = 0; i < GetCommandsNum(); i++) + { + bool unicode = 0; + char *cmdname = GetCommandName((unsigned int)i, &unicode); + if (*cmdname) + { + LRESULT pos; + if (unicode) + { + pos = SendMessageW(hwCombo, CB_ADDSTRING, 0, (LPARAM) cmdname); + width = ResizeComboBoxDropDownW(hwndDlg, IDC_COMBO, (wchar_t*)cmdname, width); + } + else + { + pos = SendMessageA(hwCombo, CB_ADDSTRING, 0, (LPARAM) cmdname); + width = ResizeComboBoxDropDown(hwndDlg, IDC_COMBO, cmdname, width); + } + SendMessage(hwCombo, CB_SETITEMDATA, pos, i); + } + } + + // Set the enabled checkbox + CheckDlgButton(hwndDlg,IDC_ENABLED,GetPrivateProfileIntW(L"gen_hotkeys", L"enabled", 0, g_iniFile)); + CheckDlgButton(hwndDlg,IDC_ENABLED_WM_APPCOMMAND,GetPrivateProfileIntW(L"gen_hotkeys", L"appcommand", 0, g_iniFile)); + setCheckedStuff(hwndDlg); + + if (NULL != WASABI_API_APP) + WASABI_API_APP->DirectMouseWheel_EnableConvertToMouseWheel(hwHKList, TRUE); + + break; + } + + case WM_COMMAND: + { + // we don't want accelerator keys working when the hotkey window is focused + // that could be a problem if someone wants to bind Alt+D for example... + if (GetFocus() == hwHK) + break; + + switch (LOWORD(wParam)) + { + case IDC_ADD: + { + HOTKEY_DATA hkd; + DWORD dwHotKey = (unsigned long)SendMessage(hwHK, wmHKCtlGet, 0, 0); + UINT iCommand = (unsigned int)SendMessage(hwCombo, CB_GETITEMDATA, SendMessage(hwCombo, CB_GETCURSEL, 0, 0), 0); + + if (!dwHotKey || iCommand == CB_ERR) + break; + + hkd.dwHotKey = dwHotKey; + hkd.iCommand = iCommand; + + changed = true; + SetListItem(hwHKList, hwHK, &hkd); + break; + } + + case IDC_REMOVE: + { + int i = ListView_GetNextItem(hwHKList, -1, LVNI_SELECTED); + while (i != -1) + { + LVITEM lvi = {LVIF_PARAM, i}; + ListView_GetItem(hwHKList, &lvi); + HOTKEY_DATA *hkd = (HOTKEY_DATA *) lvi.lParam; + delete hkd; + + changed = true; + ListView_DeleteItem(hwHKList, i); + i = ListView_GetNextItem(hwHKList, -1, LVNI_SELECTED); + } + EnableWindow(GetDlgItem(hwndDlg, IDC_REMOVE), 0); + break; + } + + case IDC_SAVE: + { + if (ListView_GetSelectedCount(hwHKList) != 1) + break; + + int iSel = ListView_GetNextItem(hwHKList, -1, LVNI_SELECTED); + + LVITEMW lvi = {LVIF_PARAM, iSel}; + ListView_GetItem(hwHKList, &lvi); + HOTKEY_DATA *hkd = (HOTKEY_DATA *) lvi.lParam; + if (hkd) + { + DWORD dwHotKey = (unsigned long)SendMessage(hwHK, wmHKCtlGet, 0, 0); + UINT iCommand = (unsigned int)SendMessage(hwCombo, CB_GETITEMDATA, SendMessage(hwCombo, CB_GETCURSEL, 0, 0), 0); + + if (!dwHotKey || iCommand == CB_ERR) + break; + + hkd->dwHotKey = dwHotKey; + hkd->iCommand = iCommand; + + changed = true; + SetListItem(hwHKList, hwHK, hkd, iSel); + } + break; + } + + case IDC_ENABLED: + if (setCheckedStuff(hwndDlg)) + { + DWORD dwSelected = ListView_GetSelectedCount(hwHKList); + EnableWindow(GetDlgItem(hwndDlg, IDC_SAVE), dwSelected == 1); + EnableWindow(GetDlgItem(hwndDlg, IDC_REMOVE), dwSelected); + changed = true; + } + break; + + case IDC_DEFAULT: + { + DWORD i; + SendMessage(hwndDlg, WM_SETREDRAW, FALSE, 0); + + DWORD dwCount = (DWORD)ListView_GetItemCount(hwHKList); + if (dwCount) + { + for (i = 0; i < dwCount; i++) + { + LVITEM lvi = {LVIF_PARAM,(int)i}; + ListView_GetItem(hwHKList, &lvi); + HOTKEY_DATA *hkd = (HOTKEY_DATA *) lvi.lParam; + delete hkd; + } + } + + changed = true; + ListView_DeleteAllItems(hwHKList); + + for (i = 0; i < DEFHKDS_NUM; i++) + { + SetListItem(hwHKList, hwHK, g_defhkds + i); + } + + // restore the size of the columns on a reset as well + ListView_SetColumnWidth(hwHKList, 0, LVSCW_AUTOSIZE); + ListView_SetColumnWidth(hwHKList, 1, LVSCW_AUTOSIZE); + + ListView_SetItemState(hwHKList, -1, 0, LVIS_SELECTED); + SendMessage(hwndDlg, WM_SETREDRAW, TRUE, 0); + InvalidateRect(hwndDlg, 0, 0); + break; + } + } + break; + } + + case WM_NOTIFY: + { + LPNMHDR nmhdr = (LPNMHDR) lParam; + if (nmhdr && nmhdr->idFrom == IDC_HKLIST) + { + if (nmhdr->code == LVN_ITEMCHANGED) + { + LPNMLISTVIEW pnmv = (LPNMLISTVIEW) lParam; + DWORD dwSelected = ListView_GetSelectedCount(hwHKList); + EnableWindow(GetDlgItem(hwndDlg, IDC_SAVE), dwSelected == 1); + EnableWindow(GetDlgItem(hwndDlg, IDC_REMOVE), dwSelected); + + SendMessage(hwHK, wmHKCtlSet, 0, 0); + SendMessage(hwCombo, CB_SETCURSEL, -1, 0); + + if (dwSelected == 1 && (pnmv->uNewState & LVIS_SELECTED)) + { + HOTKEY_DATA *hkd = (HOTKEY_DATA *) pnmv->lParam; + if (hkd) + { + SendMessage(hwHK, wmHKCtlSet, hkd->dwHotKey, 0); + bool unicode = 0; + char *cmd = GetCommandName(hkd->iCommand, &unicode); + if (*cmd) + { + if(unicode) SendMessageW(hwCombo, CB_SELECTSTRING, -1, (LPARAM) cmd); + else SendMessageA(hwCombo, CB_SELECTSTRING, -1, (LPARAM) cmd); + } + } + } + } + else if(nmhdr->code == LVN_KEYDOWN) + { + LPNMLVKEYDOWN pnmv = (LPNMLVKEYDOWN) lParam; + if(pnmv->wVKey == VK_DELETE) + { + changed = true; + SendMessage(hwndDlg,WM_COMMAND,MAKEWPARAM(IDC_REMOVE,0),0); + } + else if(pnmv->wVKey == 'A' && GetAsyncKeyState(VK_CONTROL)) + { + for(int i = 0; i < ListView_GetItemCount(hwHKList); i++) + { + ListView_SetItemState(hwHKList,i,LVIS_SELECTED,LVIS_SELECTED); + } + } + } + } + } + break; + + case WM_DESTROY: + { + int checked = (SendMessage(GetDlgItem(hwndDlg, IDC_ENABLED), BM_GETCHECK, 0, 0) == BST_CHECKED); + writePrivateProfileInt(L"enabled", checked); + writePrivateProfileInt(L"appcommand", + (SendMessage(GetDlgItem(hwndDlg, IDC_ENABLED_WM_APPCOMMAND), BM_GETCHECK, 0, 0) == BST_CHECKED)); + hotkeysClear(); + + int iCount = ListView_GetItemCount(hwHKList); + HOTKEY_DATA *hkds = NULL; + if (iCount) + { + hkds = new HOTKEY_DATA[iCount]; // TODO: could alloca this + memset(hkds, 0, iCount * sizeof(HOTKEY_DATA)); + } + if (hkds || !iCount) + { + for (size_t i = 0; i < (unsigned int)iCount; i++) + { + LVITEM lvi = {LVIF_PARAM, (int)i}; + ListView_GetItem(hwHKList, &lvi); + HOTKEY_DATA *hkd = (HOTKEY_DATA *) lvi.lParam; + if (hkd) + { + hkds[i] = *hkd; + delete hkd; + } + } + + hotkeysLoad(hkds, iCount, checked); + if (changed) hotkeysSave(hkds, iCount); + delete [] hkds; + } + writePrivateProfileInt(L"col1", ListView_GetColumnWidth(hwHKList,0)); + writePrivateProfileInt(L"col2", ListView_GetColumnWidth(hwHKList,1)); + RegisterShellHookWindow(psPlugin.hwndParent); + if (NULL != pAccelBlock && hwHK == pAccelBlock->GetHwnd()) + { + delete(pAccelBlock); + pAccelBlock = NULL; + } + + if (NULL != WASABI_API_APP) + WASABI_API_APP->DirectMouseWheel_EnableConvertToMouseWheel(hwHKList, FALSE); + } + break; + } + return FALSE; +} + +// idx = -1 for insertion +void SetListItem(HWND hwHKList, HWND hwHK, HOTKEY_DATA *hk_data, int idx, int failed) +{ + wchar_t szHK[1024] = {L""}; + + SendMessage(hwHK, wmHKCtlSet, hk_data->dwHotKey, 0); + GetWindowTextW(hwHK, szHK, sizeof(szHK)); + + HOTKEY_DATA *hk_data_allocated = 0; + + if (idx < 0) + hk_data_allocated = new HOTKEY_DATA; + + if (idx >= 0 || hk_data_allocated) + { + if (idx < 0) + *hk_data_allocated = *hk_data; + + int pos = ListView_GetItemCount(hwHKList); + + if (idx >= 0) + pos = idx; + + // Add item to the list + bool unicode = 0; + LVITEM lvi = { + LVIF_PARAM | LVIF_TEXT | LVIF_STATE, + pos, + 0, + LVIS_SELECTED | INDEXTOSTATEIMAGEMASK(failed ? (UINT)1 : 0) | LVIS_FOCUSED, + LVIS_SELECTED | LVIS_STATEIMAGEMASK | LVIS_FOCUSED, + GetCommandName(hk_data->iCommand, &unicode), + 0, + 0, + (LPARAM) hk_data_allocated + }; + + char tmp[1024] = {0}; + + if (!lvi.pszText || !*lvi.pszText) + { + if (hk_data->szCommand) + { + StringCchPrintf(tmp, 1024, "[%s]", hk_data->szCommand); + lvi.pszText = tmp; + } + } + if (!lvi.pszText || !*lvi.pszText) + lvi.pszText = WASABI_API_LNGSTRING(IDS_GHK_ACTION_NOT_FOUND); + + if (idx >= 0) + { + lvi.mask ^= LVIF_PARAM; + if(unicode) ListView_SetItemW(hwHKList, &lvi); + else ListView_SetItem(hwHKList, &lvi); + } + else + { + ListView_SetItemState(hwHKList, -1, 0, LVIS_SELECTED | LVIS_FOCUSED); + if(unicode) ListView_InsertItemW(hwHKList, &lvi); + else ListView_InsertItem(hwHKList, &lvi); + } + ListView_SetItemTextW(hwHKList, pos, 1, szHK); + } +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_hotkeys/Configdlg.h b/Src/Plugins/General/gen_hotkeys/Configdlg.h new file mode 100644 index 00000000..a6fc772a --- /dev/null +++ b/Src/Plugins/General/gen_hotkeys/Configdlg.h @@ -0,0 +1,9 @@ +#ifndef __CONFIG_DIALOG_H__ +#define __CONFIG_DIALOG_H__ + +#include +#include "resource.h" + +BOOL CALLBACK ConfigProc(HWND hwndDlg,UINT uMsg,WPARAM wParam,LPARAM lParam); + +#endif \ No newline at end of file diff --git a/Src/Plugins/General/gen_hotkeys/HOTKEY.CPP b/Src/Plugins/General/gen_hotkeys/HOTKEY.CPP new file mode 100644 index 00000000..ad6144dc --- /dev/null +++ b/Src/Plugins/General/gen_hotkeys/HOTKEY.CPP @@ -0,0 +1,56 @@ +// HotKey.cpp: implementation of the CHotKey class. +// +////////////////////////////////////////////////////////////////////// + +#include "gen_hotkeys.h" +#include "HotKey.h" + +////////////////////////////////////////////////////////////////////// +// Registration / Unregstration +////////////////////////////////////////////////////////////////////// + +int RegisterHotkey(HOTKEY *hk) +{ + if (!hk) return 1; + + wchar_t atomName[1024] = {0}; + bool unicode = 0; + char* name = GetCommandName(hk->hkd.iCommand, &unicode); + StringCchPrintfW(atomName, 1024, (unicode?L"%s %X %X %X":L"%hs %X %X %X"), name, hk->hkd.dwHotKey, hk->hkd.iCommand, GetTickCount()); + hk->atom = GlobalAddAtomW(atomName); + if (!hk->atom) + return 1; + + return !RegisterHotKey(psPlugin.hwndParent, hk->atom, GetModKeys(hk->hkd.dwHotKey), LOBYTE(hk->hkd.dwHotKey)); +} + +void UnregisterHotkey(HOTKEY *hk) +{ + if (!hk || !hk->atom) + return; + + UnregisterHotKey(psPlugin.hwndParent, hk->atom); + GlobalDeleteAtom(hk->atom); + hk->atom = NULL; +} + +////////////////////////////////////////////////////////////////////// +// Set / Get +////////////////////////////////////////////////////////////////////// + +UINT GetModKeys(DWORD dwHotKey) +{ + UINT result = 0; + WORD wHotKey = HIBYTE(dwHotKey); + + if (wHotKey & HOTKEYF_ALT) + result |= MOD_ALT; + if (wHotKey & HOTKEYF_CONTROL) + result |= MOD_CONTROL; + if (wHotKey & HOTKEYF_WIN) + result |= MOD_WIN; + if (wHotKey & HOTKEYF_SHIFT) + result |= MOD_SHIFT; + + return result; +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_hotkeys/HOTKEY.H b/Src/Plugins/General/gen_hotkeys/HOTKEY.H new file mode 100644 index 00000000..88919dae --- /dev/null +++ b/Src/Plugins/General/gen_hotkeys/HOTKEY.H @@ -0,0 +1,32 @@ +// HotKey.h: interface for the CHotKey class. +// +////////////////////////////////////////////////////////////////////// + +#if !defined(AFX_HOTKEY_H__82523B98_0E49_4B2A_8206_A4262D29C3C8__INCLUDED_) +#define AFX_HOTKEY_H__82523B98_0E49_4B2A_8206_A4262D29C3C8__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + +#include "gen_hotkeys.h" + +struct HOTKEY_DATA +{ + DWORD dwHotKey; + int iCommand; + wchar_t *szCommand; +}; + +struct HOTKEY +{ + HOTKEY_DATA hkd; + ATOM atom; + BOOL failed; +}; + +int RegisterHotkey(HOTKEY *hk); +void UnregisterHotkey(HOTKEY *hk); +UINT GetModKeys(DWORD dwHotKey); + +#endif // !defined(AFX_HOTKEY_H__82523B98_0E49_4B2A_8206_A4262D29C3C8__INCLUDED_) \ No newline at end of file diff --git a/Src/Plugins/General/gen_hotkeys/HotKeyCtl.cpp b/Src/Plugins/General/gen_hotkeys/HotKeyCtl.cpp new file mode 100644 index 00000000..61e1ea28 --- /dev/null +++ b/Src/Plugins/General/gen_hotkeys/HotKeyCtl.cpp @@ -0,0 +1,293 @@ +#include "HotKeyCtl.h" +#include "gen_hotkeys.h" + +struct HKWND_DATA +{ + WNDPROC lpfnEditWndProc; + DWORD dwScanCode; + WORD wMod; + DWORD dwHotKey; +}; + +LRESULT CALLBACK HotKeyWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + +#if(_WIN32_WINNT < 0x0500) +#define VK_BROWSER_BACK 0xA6 +#define VK_BROWSER_FORWARD 0xA7 +#define VK_BROWSER_REFRESH 0xA8 +#define VK_BROWSER_STOP 0xA9 +#define VK_BROWSER_SEARCH 0xAA +#define VK_BROWSER_FAVORITES 0xAB +#define VK_BROWSER_HOME 0xAC + +#define VK_VOLUME_MUTE 0xAD +#define VK_VOLUME_DOWN 0xAE +#define VK_VOLUME_UP 0xAF +#define VK_MEDIA_NEXT_TRACK 0xB0 +#define VK_MEDIA_PREV_TRACK 0xB1 +#define VK_MEDIA_STOP 0xB2 +#define VK_MEDIA_PLAY_PAUSE 0xB3 +#define VK_LAUNCH_MAIL 0xB4 +#define VK_LAUNCH_MEDIA_SELECT 0xB5 +#define VK_LAUNCH_APP1 0xB6 +#define VK_LAUNCH_APP2 0xB7 +#endif + +#ifndef VK_SLEEP +#define VK_SLEEP 0x5F +#endif + +UINT wmHKCtlSet; +UINT wmHKCtlGet; + +int SubclassEditBox(HWND hwEdit) +{ + if (!IsWindow(hwEdit)) + return 0; + + if (!wmHKCtlSet) + wmHKCtlSet = RegisterWindowMessage("gen_hotkeys HotKeyCTL set"); + + if (!wmHKCtlGet) + wmHKCtlGet = RegisterWindowMessage("gen_hotkeys HotKeyCTL get"); + + HKWND_DATA *hkwnd_data = new HKWND_DATA; + if (!hkwnd_data) + return 0; + + memset(hkwnd_data, 0, sizeof(HKWND_DATA)); + + hkwnd_data->lpfnEditWndProc = (WNDPROC)(LONG_PTR)SetWindowLongPtrW(hwEdit, GWLP_WNDPROC, (LONGX86)(LONG_PTR)HotKeyWndProc); + + SetWindowLongPtr(hwEdit, GWLP_USERDATA, (LONGX86)(LONG_PTR) hkwnd_data); + + return 1; +} + +void HotKeySetText(HWND hwHK, HKWND_DATA *hkwnd_data) +{ + if (!IsWindow(hwHK) || !hkwnd_data) + return; + + wchar_t szKeyName[1024] = L""; + wchar_t *p = szKeyName; + DWORD dwSize = sizeof(szKeyName); + DWORD dwLen = 0; + WORD wMod; + + if (hkwnd_data->dwHotKey) + wMod = HIBYTE(hkwnd_data->dwHotKey); + else + wMod = hkwnd_data->wMod; + + if(wMod & HOTKEYF_WIN) { + // GetKeyNameText gives us Left/Right Windows but RegisterHotKey doesn't seperate the two + //GetKeyNameText(MAKELPARAM(0, MapVirtualKey(VK_LWIN, 0)) | (1 << 24), p, dwSize); + // so we use just "Winkey" to avoid confusion + WASABI_API_LNGSTRINGW_BUF(IDS_GHK_WINKEY_STR,p,dwSize); + dwLen = lstrlenW(szKeyName); + StringCchCatW(p, dwSize, L" + "); + dwSize -= dwLen + 3; + p = szKeyName + dwLen + 3; + } + if(wMod & HOTKEYF_CONTROL) { + GetKeyNameTextW(MAKELONG(0, MapVirtualKey(VK_CONTROL, 0)), p, dwSize); + dwLen = lstrlenW(szKeyName); + StringCchCatW(p, dwSize, L" + "); + dwSize -= dwLen + 3; + p = szKeyName + dwLen + 3; + } + if(wMod & HOTKEYF_SHIFT) { + GetKeyNameTextW(MAKELONG(0, MapVirtualKey(VK_SHIFT, 0)), p, dwSize); + dwLen = lstrlenW(szKeyName); + StringCchCatW(p, dwSize, L" + "); + dwSize -= dwLen + 3; + p = szKeyName + dwLen + 3; + } + if(wMod & HOTKEYF_ALT) { + GetKeyNameTextW(MAKELONG(0, MapVirtualKey(VK_MENU, 0)), p, dwSize); + dwLen = lstrlenW(szKeyName); + StringCchCatW(p, dwSize, L" + "); + dwSize -= dwLen + 3; + p = szKeyName + dwLen + 3; + } + + if(hkwnd_data->dwHotKey) + { + switch (LOBYTE(hkwnd_data->dwHotKey)) + { + case VK_BROWSER_BACK: + WASABI_API_LNGSTRINGW_BUF(IDS_BROWSER_BACK,p,dwSize); + break; + case VK_BROWSER_FORWARD: + WASABI_API_LNGSTRINGW_BUF(IDS_BROWSER_FORWARD,p,dwSize); + break; + case VK_BROWSER_REFRESH: + WASABI_API_LNGSTRINGW_BUF(IDS_BROWSER_REFRESH,p,dwSize); + break; + case VK_BROWSER_STOP: + WASABI_API_LNGSTRINGW_BUF(IDS_BROWSER_STOP,p,dwSize); + break; + case VK_BROWSER_SEARCH: + WASABI_API_LNGSTRINGW_BUF(IDS_BROWSER_SEARCH,p,dwSize); + break; + case VK_BROWSER_FAVORITES: + WASABI_API_LNGSTRINGW_BUF(IDS_BROWSER_FAVOURITES,p,dwSize); + break; + case VK_BROWSER_HOME: + WASABI_API_LNGSTRINGW_BUF(IDS_BROWSER_HOME,p,dwSize); + break; + case VK_VOLUME_MUTE: + WASABI_API_LNGSTRINGW_BUF(IDS_VOLUME_MUTE,p,dwSize); + break; + case VK_VOLUME_DOWN: + WASABI_API_LNGSTRINGW_BUF(IDS_VOLUME_DOWN,p,dwSize); + break; + case VK_VOLUME_UP: + WASABI_API_LNGSTRINGW_BUF(IDS_VOLUME_UP,p,dwSize); + break; + case VK_MEDIA_NEXT_TRACK: + WASABI_API_LNGSTRINGW_BUF(IDS_NEXT_TRACK,p,dwSize); + break; + case VK_MEDIA_PREV_TRACK: + WASABI_API_LNGSTRINGW_BUF(IDS_PREVIOUS_TRACK,p,dwSize); + break; + case VK_MEDIA_STOP: + WASABI_API_LNGSTRINGW_BUF(IDS_STOP,p,dwSize); + break; + case VK_MEDIA_PLAY_PAUSE: + WASABI_API_LNGSTRINGW_BUF(IDS_PLAY_PAUSE,p,dwSize); + break; + case VK_LAUNCH_MAIL: + WASABI_API_LNGSTRINGW_BUF(IDS_LAUNCH_MAIL,p,dwSize); + break; + case VK_LAUNCH_MEDIA_SELECT: + WASABI_API_LNGSTRINGW_BUF(IDS_LAUNCH_MEDIA_SELECT,p,dwSize); + break; + case VK_LAUNCH_APP1: + WASABI_API_LNGSTRINGW_BUF(IDS_LAUCH_APP1,p,dwSize); + break; + case VK_LAUNCH_APP2: + WASABI_API_LNGSTRINGW_BUF(IDS_LAUCH_APP2,p,dwSize); + break; + case VK_SLEEP: + WASABI_API_LNGSTRINGW_BUF(IDS_SLEEP,p,dwSize); + break; + case VK_PAUSE: + WASABI_API_LNGSTRINGW_BUF(IDS_PAUSE,p,dwSize); + break; + default: + GetKeyNameTextW(hkwnd_data->dwScanCode, p, dwSize); + if (!*p) + StringCchPrintfW(p, 1024, L"0x%X", hkwnd_data->dwScanCode); + break; + } + } + else + szKeyName[dwLen] = 0; + + SetWindowTextW(hwHK, szKeyName); + + SendMessageW(hwHK, EM_SETSEL, lstrlenW(szKeyName), lstrlenW(szKeyName)); +} + +LRESULT CALLBACK HotKeyWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + HKWND_DATA *hkwnd_data = (HKWND_DATA *)(LONG_PTR)GetWindowLongPtr(hwnd, GWLP_USERDATA); + HKWND_DATA hkwnd_data_local; + + if (uMsg == wmHKCtlSet) + { + int extflag = 0; + if (HIBYTE(wParam) & HOTKEYF_EXT) + extflag = (1 << 24); + + hkwnd_data->dwHotKey = (unsigned long)wParam; + hkwnd_data->dwScanCode = MAKELPARAM(0, MapVirtualKey(LOBYTE(wParam), 0)) | extflag; + //hkwnd_data->wMod = HIBYTE(wParam); + HotKeySetText(hwnd, hkwnd_data); + return 0; + } + else if (uMsg == wmHKCtlGet) + { + return hkwnd_data->dwHotKey; + } + + switch (uMsg) + { + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + { + hkwnd_data->dwHotKey = 0; + + switch (wParam) + { + case VK_SHIFT: + hkwnd_data->wMod |= HOTKEYF_SHIFT; + break; + case VK_CONTROL: + hkwnd_data->wMod |= HOTKEYF_CONTROL; + break; + case VK_MENU: + hkwnd_data->wMod |= HOTKEYF_ALT; + break; + case VK_LWIN: + case VK_RWIN: + hkwnd_data->wMod |= HOTKEYF_WIN; + break; + default: + hkwnd_data->dwScanCode = (unsigned long)lParam; + if (lParam & (1 << 24)) // extended bit + hkwnd_data->wMod |= HOTKEYF_EXT; + else + hkwnd_data->wMod &= ~HOTKEYF_EXT; + hkwnd_data->dwHotKey = MAKEWORD(wParam, hkwnd_data->wMod); + break; + } + + HotKeySetText(hwnd, hkwnd_data); + return 1; + } + + case WM_KEYUP: + case WM_SYSKEYUP: + { + switch (wParam) + { + case VK_SHIFT: + hkwnd_data->wMod &= ~HOTKEYF_SHIFT; + break; + case VK_CONTROL: + hkwnd_data->wMod &= ~HOTKEYF_CONTROL; + break; + case VK_MENU: + hkwnd_data->wMod &= ~HOTKEYF_ALT; + break; + case VK_LWIN: + case VK_RWIN: + hkwnd_data->wMod &= ~HOTKEYF_WIN; + break; + } + + HotKeySetText(hwnd, hkwnd_data); + return 1; + } + + case WM_CHAR: + case WM_PASTE: + return 0; + + case WM_DESTROY: + if (hkwnd_data) + { + SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONGX86)(LONG_PTR) hkwnd_data->lpfnEditWndProc); + SetWindowLongPtr(hwnd, GWLP_USERDATA, 0); + hkwnd_data_local = *hkwnd_data; + delete hkwnd_data; + hkwnd_data = &hkwnd_data_local; + } + break; + } + + return CallWindowProc(hkwnd_data->lpfnEditWndProc, hwnd, uMsg, wParam, lParam); +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_hotkeys/HotKeyCtl.h b/Src/Plugins/General/gen_hotkeys/HotKeyCtl.h new file mode 100644 index 00000000..237aabba --- /dev/null +++ b/Src/Plugins/General/gen_hotkeys/HotKeyCtl.h @@ -0,0 +1,13 @@ +#ifndef ____HOTKEY_CTL___H____ +#define ____HOTKEY_CTL___H____ + +#include + +#define HOTKEYF_WIN 0x10 + +int SubclassEditBox(HWND hwEdit); + +extern UINT wmHKCtlSet; +extern UINT wmHKCtlGet; + +#endif//____HOTKEY_CTL___H____ \ No newline at end of file diff --git a/Src/Plugins/General/gen_hotkeys/RESOURCE.H b/Src/Plugins/General/gen_hotkeys/RESOURCE.H new file mode 100644 index 00000000..0cfe5975 --- /dev/null +++ b/Src/Plugins/General/gen_hotkeys/RESOURCE.H @@ -0,0 +1,116 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by gen_hotkeys.rc +// +#define IDS_GHK_TITLE_STR 0 +#define IDS_GHK_HOTKEY_REG_FAILED 1 +#define IDC_CANCEL 2 +#define IDS_GHK_HOTKEY_REG_FAILED_MORE 2 +#define IDS_GHK_ABOUT_TEXT 3 +#define IDS_GHK_HOTKEY_COLUMN 4 +#define IDS_GHK_ACTION_COLUMN 5 +#define IDS_GHK_ACTION_NOT_FOUND 6 +#define IDS_GHK_WINKEY_STR 7 +#define IDS_BROWSER_BACK 8 +#define IDS_BROWSER_FORWARD 9 +#define IDS_BROWSER_REFRESH 10 +#define IDS_BROWSER_STOP 11 +#define IDS_BROWSER_SEARCH 12 +#define IDS_BROWSER_FAVOURITES 13 +#define IDS_BROWSER_HOME 14 +#define IDS_VOLUME_MUTE 15 +#define IDS_VOLUME_DOWN 16 +#define IDS_VOLUME_UP 17 +#define IDS_NEXT_TRACK 18 +#define IDS_PREVIOUS_TRACK 19 +#define IDS_STOP 20 +#define IDS_PLAY_PAUSE 21 +#define IDS_LAUNCH_MAIL 22 +#define IDS_LAUNCH_MEDIA_SELECT 23 +#define IDS_LAUCH_APP1 24 +#define IDS_LAUCH_APP2 25 +#define IDS_SLEEP 26 +#define IDS_PLAYBACK__PLAY 32 +#define IDS_PLAYBACK__PAUSE 33 +#define IDS_PLAYBACK__STOP 34 +#define IDS_PLAYBACK__PREVIOUS_IN_PLAYLIST 35 +#define IDS_PLAYBACK__NEXT_IN_PLAYLIST 36 +#define IDS_PLAYBACK__VOLUME_UP 37 +#define IDS_PLAYBACK__VOLUME_DOWN 38 +#define IDS_PLAYBACK__FORWARD 39 +#define IDS_PLAYBACK__REWIND 40 +#define IDS_PLAYBACK__REPEAT_ON 41 +#define IDS_PLAYBACK__REPEAT_OFF 42 +#define IDS_UI__TOGGLE_EQ 43 +#define IDS_UI__TOGGLE_PLAYLIST 44 +#define IDS_UI__TOGGLE_AOT 45 +#define IDS_UI__ABOUT 46 +#define IDS_PLAYBACK__JUMP_TO_BOX 47 +#define IDS_PLAYBACK__OPEN_FILE_DIALOG 48 +#define IDS_PLAYBACK__OPEN_LOC_DIALOG 49 +#define IDS_PLAYBACK__OPEN_FOLDER_DIALOG 50 +#define IDS_GENERAL__QUIT 51 +#define IDS_UI__PREFERENCES 52 +#define IDS_GENERAL__COPY_TITLE 53 +#define IDS_GENERAL__COPY_FILE_PATH 54 +#define IDS_PLAYBACK__PLAY_PAUSE 55 +#define IDS_PLAYBACK__TOGGLE_REPEAT 56 +#define IDS_PLAYBACK__TOGGLE_SHUFFLE 57 +#define IDS_UI__TOGGLE_WINSHADE 58 +#define IDS_UI__TOGGLE_PLAYLIST_WINSHADE 59 +#define IDS_UI__TOGGLE_DOUBLESIZE 60 +#define IDS_UI__TOGGLE_MAIN_WINDOW 61 +#define IDS_UI__TOGGLE_MINIBROWSER 62 +#define IDS_VIS__PREFERENCES 63 +#define IDS_VIS__PLUGIN_PREFERENCES 64 +#define IDS_VIS_TOGGLE_VIS_ON_OFF 65 +#define IDS_UI__SELECT_SKIN 66 +#define IDS_PLAYBACK__STOP_WITH_FADEOUT 67 +#define IDS_PLAYBACK__STOP_AFTER_CURRENT 68 +#define IDS_PLAYBACK__START_OF_LIST 69 +#define IDS_PLAYBACK__END_OF_LIST 70 +#define IDS_UI__BRING_TO_FRONT_OR_HIDE 71 +#define IDS_RATING__5_STARS 72 +#define IDS_RATING__4_STARS 73 +#define IDS_RATING__3_STARS 74 +#define IDS_RATING__2_STARS 75 +#define IDS_RATING__1_STAR 76 +#define IDS_RATING__NO_RATING 77 +#define IDS_GENERAL__COPY_TITLEW 78 +#define IDS_GENERAL__COPY_FILE_PATHW 79 +#define IDS_RATING__INC 80 +#define IDS_RATING__DEC 81 +#define IDS_PAUSE 82 +#define IDS_VIS_NEXT_PRESET 83 +#define IDS_VIS_PREV_PRESET 84 +#define IDS_VIS_RAND_PRESET 85 +#define IDS_VIS_FULL_SCREEN 86 +#define IDS_VIS_CONFIG 87 +#define IDS_PLAYBACK__MANUAL_ADVANCE 87 +#define IDS_VIS_MENU 88 +#define IDS_GENERAL_FFOD 89 +#define IDS_PLAYBACK_EDIT_ID3 90 +#define IDD_CONFIG 101 +#define IDB_LOGO 103 +#define IDB_LVSTATE 104 +#define IDC_ENABLED 1006 +#define IDC_ENABLED_WM_APPCOMMAND 1007 +#define IDC_HOTKEY 1012 +#define IDC_HKLIST 1017 +#define IDC_COMBO 1018 +#define IDC_SAVE 1019 +#define IDC_ADD 1020 +#define IDC_REMOVE 1021 +#define IDC_DEFAULT 1022 +#define IDS_NULLSOFT_GLOBAL_HOTKEYS 65534 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 91 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1024 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/Src/Plugins/General/gen_hotkeys/WINAMP.H b/Src/Plugins/General/gen_hotkeys/WINAMP.H new file mode 100644 index 00000000..2ebafaf6 --- /dev/null +++ b/Src/Plugins/General/gen_hotkeys/WINAMP.H @@ -0,0 +1,38 @@ +#ifndef _WAFE_H_ +#define _WAFE_H_ + +#define IPC_FF_FIRST 2000 +#define IPC_FF_NOTIFYHOTKEY IPC_FF_FIRST + 6 // data = const char * to hotkey description + +#define WINAMP_OPTIONS_EQ 40036 // toggles the EQ window +#define WINAMP_OPTIONS_PLEDIT 40040 // toggles the playlist window +#define WINAMP_VOLUMEUP 40058 // turns the volume up a little +#define WINAMP_VOLUMEDOWN 40059 // turns the volume down a little +#define WINAMP_FFWD5S 40060 // fast forwards 5 seconds +#define WINAMP_REW5S 40061 // rewinds 5 seconds + +// the following are the five main control buttons, with optionally shift +// or control pressed +// (for the exact functions of each, just try it out) +#define WINAMP_BUTTON1 40044 +#define WINAMP_BUTTON2 40045 +#define WINAMP_BUTTON3 40046 +#define WINAMP_BUTTON4 40047 +#define WINAMP_BUTTON5 40048 +#define WINAMP_BUTTON1_SHIFT 40144 +#define WINAMP_BUTTON2_SHIFT 40145 +#define WINAMP_BUTTON3_SHIFT 40146 +#define WINAMP_BUTTON4_SHIFT 40147 +#define WINAMP_BUTTON5_SHIFT 40148 +#define WINAMP_BUTTON1_CTRL 40154 +#define WINAMP_BUTTON2_CTRL 40155 +#define WINAMP_BUTTON3_CTRL 40156 +#define WINAMP_BUTTON4_CTRL 40157 +#define WINAMP_BUTTON5_CTRL 40158 + +#define WINAMP_FILE_PLAY 40029 // pops up the load file(s) box +#define WINAMP_OPTIONS_PREFS 40012 // pops up the preferences +#define WINAMP_OPTIONS_AOT 40019 // toggles always on top +#define WINAMP_HELP_ABOUT 40041 // pops up the about box :) + +#endif \ No newline at end of file diff --git a/Src/Plugins/General/gen_hotkeys/Wacommands.cpp b/Src/Plugins/General/gen_hotkeys/Wacommands.cpp new file mode 100644 index 00000000..7acc71fc --- /dev/null +++ b/Src/Plugins/General/gen_hotkeys/Wacommands.cpp @@ -0,0 +1,366 @@ +#include "WACommands.h" +#include "gen_hotkeys.h" +#include "../nu/AutoWide.h" + +typedef struct { + UINT strId; + genHotkeysAddStruct ghk; +} genHotkeysAddStructLocalised; + +genHotkeysAddStructLocalised WADefCommands[] = { + {IDS_PLAYBACK__PLAY,{0, 0, WM_COMMAND, WINAMP_BUTTON2, 0, "ghkdc play"}}, + {IDS_PLAYBACK__PAUSE,{0, 0, WM_COMMAND, WINAMP_BUTTON3, 0, "ghkdc pause"}}, + {IDS_PLAYBACK__STOP,{0, 0, WM_COMMAND, WINAMP_BUTTON4, 0, "ghkdc stop"}}, + {IDS_PLAYBACK__PREVIOUS_IN_PLAYLIST,{0, 0, WM_COMMAND, WINAMP_BUTTON1, 0, "ghkdc prev"}}, + {IDS_PLAYBACK__NEXT_IN_PLAYLIST,{0, 0, WM_COMMAND, WINAMP_BUTTON5, 0, "ghkdc next"}}, + {IDS_PLAYBACK__VOLUME_UP,{0, 0, WM_COMMAND, WINAMP_VOLUMEUP, 0, "ghkdc vup"}}, + {IDS_PLAYBACK__VOLUME_DOWN,{0, 0, WM_COMMAND, WINAMP_VOLUMEDOWN, 0, "ghkdc vdown"}}, + {IDS_PLAYBACK__FORWARD,{0, 0, WM_COMMAND, WINAMP_FFWD5S, 0, "ghkdc forward"}}, + {IDS_PLAYBACK__REWIND,{0, 0, WM_COMMAND, WINAMP_REW5S, 0, "ghkdc rewind"}}, + {IDS_PLAYBACK__REPEAT_ON,{0, 0, WM_WA_IPC, 1, IPC_SET_REPEAT, "ghkdc repeat on"}}, + {IDS_PLAYBACK__REPEAT_OFF,{0, 0, WM_WA_IPC, 0, IPC_SET_REPEAT, "ghkdc repeat off"}}, + {IDS_UI__TOGGLE_EQ,{0, 0, WM_COMMAND, WINAMP_OPTIONS_EQ, 0, "ghkdc t eq"}}, + {IDS_UI__TOGGLE_PLAYLIST,{0, 0, WM_COMMAND, WINAMP_OPTIONS_PLEDIT, 0, "ghkdc t pl"}}, + {IDS_UI__TOGGLE_AOT,{0, 0, WM_COMMAND, WINAMP_OPTIONS_AOT, 0, "ghkdc t aot"}}, + {IDS_UI__ABOUT,{0, HKF_BRING_TO_FRONT, WM_COMMAND, WINAMP_HELP_ABOUT, 0, "ghkdc about"}}, + {IDS_PLAYBACK__JUMP_TO_BOX,{0, HKF_BRING_TO_FRONT, WM_COMMAND, 40194, 0, "ghkdc jump"}}, + {IDS_PLAYBACK__OPEN_FILE_DIALOG,{0, HKF_BRING_TO_FRONT|HKF_WPARAM_HWND, WM_WA_IPC, 0, IPC_OPENFILEBOX, "ghkdc file"}}, + {IDS_PLAYBACK__OPEN_LOC_DIALOG,{0, HKF_BRING_TO_FRONT, WM_COMMAND, WINAMP_BUTTON2_CTRL, 0, "ghkdc url"}}, + {IDS_PLAYBACK__OPEN_FOLDER_DIALOG,{0, HKF_BRING_TO_FRONT|HKF_WPARAM_HWND, WM_WA_IPC, 0, IPC_OPENDIRBOX, "ghkdc dir"}}, + {IDS_GENERAL__QUIT,{0, 0, WM_CLOSE, 0, 0, "ghkdc quit"}}, + {IDS_UI__PREFERENCES,{0, HKF_BRING_TO_FRONT, WM_WA_IPC, (WPARAM)(-1), IPC_OPENPREFSTOPAGE, "ghkdc prefs"}}, + {IDS_GENERAL__COPY_TITLE,{0, HKF_COPY_RET|HKF_PLPOS_WPARAM, WM_WA_IPC, 0, IPC_GETPLAYLISTTITLE, "ghkdc copy"}}, + {IDS_GENERAL__COPY_FILE_PATH,{0, HKF_COPY_RET|HKF_WPARAM_PLPOS, WM_WA_IPC, 0, IPC_GETPLAYLISTFILE, "ghkdc copy path"}}, + {IDS_PLAYBACK__PLAY_PAUSE,{0, HKF_ISPLAYING_WL, WM_COMMAND, WINAMP_BUTTON3, WINAMP_BUTTON2, "ghkdc play/pause"}}, + {IDS_PLAYBACK__TOGGLE_REPEAT,{0, 0, WM_COMMAND, 40022, 0, "ghkdc t repeaat"}}, + {IDS_PLAYBACK__TOGGLE_SHUFFLE,{0, 0, WM_COMMAND, 40023, 0, "ghkdc t shuffle"}}, + {IDS_UI__TOGGLE_WINSHADE,{0, 0, WM_COMMAND, 40064, 0, "ghkdc ws"}}, + {IDS_UI__TOGGLE_PLAYLIST_WINSHADE,{0, 0, WM_COMMAND, 40266, 0, "ghkdc pl ws"}}, + {IDS_UI__TOGGLE_DOUBLESIZE,{0, 0, WM_COMMAND, 40165, 0, "ghkdc ds"}}, + {IDS_UI__TOGGLE_MAIN_WINDOW,{0, 0, WM_COMMAND, 40258, 0, "ghkdc t main"}}, + // removed due to it not being in current winamp builds now {IDS_UI__TOGGLE_MINIBROWSER,{0, 0, WM_COMMAND, 40298, 0, "ghkdc t mb"}}, + {IDS_VIS__PREFERENCES,{0, HKF_BRING_TO_FRONT, WM_COMMAND, 40191, 0, "ghkdc vis prefs"}}, + {IDS_VIS_TOGGLE_VIS_ON_OFF,{0, 0, WM_COMMAND, 40192, 0, "ghkdc t vis"}}, + // added in 1.91 based on user feedback (ID_VIS_* options) and also from finding the correct menu id to use + {IDS_VIS__PLUGIN_PREFERENCES,{0, HKF_BRING_TO_FRONT, WM_COMMAND, 40221, 0, "ghkdc prefs vis"}}, + {IDS_VIS_NEXT_PRESET,{0, 0, WM_COMMAND, ID_VIS_NEXT, 0, "ghkdc vis next"}}, + {IDS_VIS_PREV_PRESET,{0, 0, WM_COMMAND, ID_VIS_PREV, 0, "ghkdc vis prev"}}, + {IDS_VIS_RAND_PRESET,{0, 0, WM_COMMAND, ID_VIS_RANDOM, 0, "ghkdc vis rand"}}, + {IDS_VIS_FULL_SCREEN,{0, 0, WM_COMMAND, ID_VIS_FS, 0, "ghkdc vis fs"}}, + {IDS_UI__SELECT_SKIN,{0, HKF_BRING_TO_FRONT, WM_COMMAND, 40219, 0, "ghkdc skin"}}, + {IDS_PLAYBACK__STOP_WITH_FADEOUT,{0, 0, WM_COMMAND, WINAMP_BUTTON4_SHIFT, 0, "ghkdc stop fade"}}, + {IDS_PLAYBACK__STOP_AFTER_CURRENT,{0, 0, WM_COMMAND, WINAMP_BUTTON4_CTRL, 0, "ghkdc stop ac"}}, + {IDS_PLAYBACK__START_OF_LIST,{0, 0, WM_COMMAND, WINAMP_BUTTON1_CTRL, 0, "ghkdc sol"}}, + {IDS_PLAYBACK__END_OF_LIST,{0, 0, WM_COMMAND, WINAMP_BUTTON5_CTRL, 0, "ghkdc eol"}}, + {IDS_UI__BRING_TO_FRONT_OR_HIDE,{0, HKF_SHOWHIDE, WM_SHOWWINDOW, 0, 0, "ghkdc show/hide"}}, + {IDS_RATING__5_STARS,{0, 0, WM_WA_IPC, 5, IPC_SETRATING, "ghkdc rating 5"}}, + {IDS_RATING__4_STARS,{0, 0, WM_WA_IPC, 4, IPC_SETRATING, "ghkdc rating 4"}}, + {IDS_RATING__3_STARS,{0, 0, WM_WA_IPC, 3, IPC_SETRATING, "ghkdc rating 3"}}, + {IDS_RATING__2_STARS,{0, 0, WM_WA_IPC, 2, IPC_SETRATING, "ghkdc rating 2"}}, + {IDS_RATING__1_STAR,{0, 0, WM_WA_IPC, 1, IPC_SETRATING, "ghkdc rating 1"}}, + {IDS_RATING__NO_RATING,{0, 0, WM_WA_IPC, 0, IPC_SETRATING, "ghkdc rating 0"}}, + // added in 1.4+ so that we can deal with unicode strings on 5.3x+ Winamp versions + {IDS_GENERAL__COPY_TITLEW,{0, HKF_COPYW_RET|HKF_PLPOS_WPARAM, WM_WA_IPC, 0, IPC_GETPLAYLISTTITLEW, "ghkdc copyw"}}, + {IDS_GENERAL__COPY_FILE_PATHW,{0, HKF_COPYW_RET|HKF_WPARAM_PLPOS, WM_WA_IPC, 0, IPC_GETPLAYLISTFILEW, "ghkdc copy pathw"}}, + // added in 1.6+ for neorth + {IDS_RATING__INC,{0, 0, WM_WA_IPC, 6, IPC_SETRATING, "ghkdc inc rating"}}, + {IDS_RATING__DEC,{0, 0, WM_WA_IPC, (WPARAM)(-1), IPC_SETRATING, "ghkdc dec rating"}}, + // added in 1.91 for kzuse + {IDS_PLAYBACK__MANUAL_ADVANCE,{0, 0, WM_COMMAND, 40395, 0, "ghkdc man adv"}}, + // added in 1.92 as killing of separate gen_find_on_disk.dll to be native + {IDS_GENERAL_FFOD,{0, 0, WM_COMMAND, 40468, 0, "FFOD_Cur"}}, + {IDS_PLAYBACK_EDIT_ID3,{0, 0, WM_COMMAND, 40188, 0, "VCFI"}}, +}; + +static unsigned int WACommandsNum; +static unsigned int WACommandsAllocated; +WACommand *WACommands=0; + +inline unsigned int GetCommandsNum() +{ + return WACommandsNum; +} + +static bool ReallocateCommands() +{ + if (WACommandsNum < WACommandsAllocated) + return true; + + WACommand *newCommands = new WACommand[ WACommandsNum * 2 + 16]; + if (newCommands) + memcpy(newCommands, WACommands, WACommandsAllocated*sizeof(WACommand)); + + WACommandsAllocated = WACommandsNum * 2 + 16; + delete [] WACommands; + WACommands = newCommands; + if (!WACommands) + { + WACommandsAllocated = 0; + return false; + } + return true; +} + +void InitCommands() +{ + int l = sizeof(WADefCommands) / sizeof(WADefCommands[0]); + while (l--) + { + wchar_t rateBuf[1024] = {0}; + wchar_t* rateStr = rateBuf; + WADefCommands[l].ghk.name = (char*)WASABI_API_LNGSTRINGW_BUF(WADefCommands[l].strId, rateBuf, 1024); + switch(WADefCommands[l].strId) + { + // for the rating entries force any asterisks to a unicode star + case IDS_RATING__1_STAR: + case IDS_RATING__2_STARS: + case IDS_RATING__3_STARS: + case IDS_RATING__4_STARS: + case IDS_RATING__5_STARS: + while(rateStr && *rateStr) + { + if(*rateStr == L'*') *rateStr = L'\u2605'; + rateStr=CharNextW(rateStr); + } + break; + } + WADefCommands[l].ghk.flags |= HKF_UNICODE_NAME; + AddCommand(&WADefCommands[l].ghk); + } +} + +int AddCommand(genHotkeysAddStruct *ghas) +{ + unsigned int idx = WACommandsNum; + if (!ghas) + return -1; + + // legacy support + if (!ghas->id) + ghas->id = ghas->name; + + if (WACommands) + { + unsigned int l = WACommandsNum; + while (l--) + { + if (!lstrcmpiW(WACommands[l].id, AutoWide(ghas->id))) + { + WACommands[l].bEnabled = !(ghas->flags & HKF_DISABLED); + char *name = 0; + if(!(ghas->flags & HKF_UNICODE_NAME)) + name = (char *) malloc(lstrlen(ghas->name) + 1); + else + name = (char *) malloc((lstrlenW((wchar_t*)ghas->name) + 1) * sizeof(wchar_t)); + if (name) + { + free(WACommands[l].name); + WACommands[l].name = name; + } + WACommands[l].dwFlags = (ghas->flags & 0x7FFFFFFF); + WACommands[l].uMsg = ghas->uMsg; + WACommands[l].wParam = ghas->wParam; + WACommands[l].lParam = ghas->lParam; + WACommands[l].wnd = ghas->wnd; + return l; + } + } + } + WACommandsNum++; + if (!ReallocateCommands()) + { + WACommandsNum--; + return -2; + } + + if(!(ghas->flags & HKF_UNICODE_NAME)) + WACommands[idx].name = _strdup(ghas->name); + else + WACommands[idx].name = (char*)_wcsdup((wchar_t*)ghas->name); + + if (!WACommands[idx].name) + { + WACommandsNum--; + return -1; + } + + WACommands[idx].id = AutoWideDup(ghas->id); + if (!WACommands[idx].id) + { + free(WACommands[idx].name); + WACommandsNum--; + return -1; + } + WACommands[idx].dwFlags = (ghas->flags & 0x7FFFFFFF); + WACommands[idx].uMsg = ghas->uMsg; + WACommands[idx].wParam = ghas->wParam; + WACommands[idx].lParam = ghas->lParam; + WACommands[idx].bEnabled = !(ghas->flags & HKF_DISABLED); + WACommands[idx].wnd = ghas->wnd; + return idx; +} + +inline char *GetCommandName(unsigned int i, bool *unicode) +{ + if (i < WACommandsNum) + { + if(unicode) *unicode = !!(WACommands[i].dwFlags & HKF_UNICODE_NAME); + return WACommands[i].name; + } + return ""; +} + +inline wchar_t *GetCommandId(unsigned int i) +{ + if (i < WACommandsNum) + return WACommands[i].id; + return L""; +} + +int GetCommandIdx(wchar_t *id) +{ + if (WACommands) + { + unsigned int l = WACommandsNum; + while (l--) + { + if (!lstrcmpiW(WACommands[l].id, id)) + { + return l; + } + } + } + return -1; +} + +int HandleByScript(unsigned int i) +{ + if (i >= WACommandsNum) return 0; + if (SendMessage(psPlugin.hwndParent, WM_WA_IPC, (WPARAM)WACommands[i].name, IPC_FF_NOTIFYHOTKEY)) return 0; + return 1; +} + +int DoCommand(unsigned int i) +{ + if (i >= WACommandsNum) + return 0; + + if (!WACommands[i].bEnabled) + return 0; + + if (HandleByScript(i)) return 1; + + WPARAM wParam = WACommands[i].wParam; + LPARAM lParam = WACommands[i].lParam; + + /*if (WACommands[i].dwFlags & HKF_CUSTOM_FUNC) + { + if (WACommands[i].uMsg) + { + pfnWAC pfn = (pfnWAC) WACommands[i].uMsg; + pfn(); + } + + return 0; + }*/ + + if (WACommands[i].dwFlags & HKF_SHOWHIDE) + { + DWORD dwThisProcId, dwProcId; + GetWindowThreadProcessId(psPlugin.hwndParent, &dwThisProcId); + GetWindowThreadProcessId(GetForegroundWindow(), &dwProcId); + if (IsWindowVisible(psPlugin.hwndParent) && dwProcId == dwThisProcId) + ShowWindow(psPlugin.hwndParent, SW_MINIMIZE); + else + { + ShowWindow(psPlugin.hwndParent, SW_SHOWNORMAL); + SetForegroundWindow(psPlugin.hwndParent); + } + } + + HWND hwnddest=WACommands[i].wnd ? WACommands[i].wnd : psPlugin.hwndParent; + + if (WACommands[i].dwFlags & HKF_BRING_TO_FRONT) + { + SetForegroundWindow(psPlugin.hwndParent); + } + if (WACommands[i].dwFlags & HKF_WPARAM_HWND) + { + wParam = (WPARAM) psPlugin.hwndParent; + } + if (WACommands[i].dwFlags & HKF_WPARAM_PLPOS) + { + wParam = (WPARAM) SendMessage(psPlugin.hwndParent, WM_WA_IPC, 0, IPC_GETLISTPOS); + } + if (WACommands[i].dwFlags & HKF_WPARAM_ISPLAYING_WL) + { + if (SendMessage(psPlugin.hwndParent, WM_WA_IPC, 0, IPC_ISPLAYING)) + wParam = WACommands[i].wParam; // Pause + else + wParam = WACommands[i].lParam; // Play + + lParam = 0; + } + + LRESULT lResult = SendMessage(hwnddest, WACommands[i].uMsg, wParam, lParam); + + if (WACommands[i].dwFlags & HKF_COPY && lResult) + { + char *szTitle = (char *) lResult; + + if (!OpenClipboard(psPlugin.hwndParent)) + return 0; + EmptyClipboard(); + + int bufLen = lstrlen(szTitle) + 1; + HGLOBAL hglbCopy = GlobalAlloc(GMEM_MOVEABLE, bufLen); + if (!hglbCopy) + { + CloseClipboard(); + return 0; + } + + char *szClipboardTitle = (char *) GlobalLock(hglbCopy); + if (!szClipboardTitle) + { + CloseClipboard(); + return 0; + } + lstrcpyn(szClipboardTitle, szTitle, bufLen); + GlobalUnlock(hglbCopy); + + SetClipboardData(CF_TEXT, hglbCopy); + + CloseClipboard(); + } + + if (WACommands[i].dwFlags & HKF_COPYW && lResult) + { + wchar_t *szTitle = (wchar_t *) lResult; + + if (!OpenClipboard(psPlugin.hwndParent)) + return 0; + EmptyClipboard(); + + int bufLen = (lstrlenW(szTitle) + 1) * sizeof(wchar_t); + HGLOBAL hglbCopy = GlobalAlloc(GMEM_MOVEABLE, bufLen); + if (!hglbCopy) + { + CloseClipboard(); + return 0; + } + + wchar_t *szClipboardTitle = (wchar_t *) GlobalLock(hglbCopy); + if (!szClipboardTitle) + { + CloseClipboard(); + return 0; + } + lstrcpynW(szClipboardTitle, szTitle, bufLen); + GlobalUnlock(hglbCopy); + + SetClipboardData(CF_UNICODETEXT, hglbCopy); + CloseClipboard(); + } + return (int)lResult; +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_hotkeys/Wacommands.h b/Src/Plugins/General/gen_hotkeys/Wacommands.h new file mode 100644 index 00000000..0ae832ff --- /dev/null +++ b/Src/Plugins/General/gen_hotkeys/Wacommands.h @@ -0,0 +1,50 @@ +#ifndef ___WINAMP_COMMANDS___H___ +#define ___WINAMP_COMMANDS___H___ + +#include "../winamp/wa_ipc.h" +#include "wa_hotkeys.h" + +// calls SetForegroundWindow before sending the message +#define HKF_BRING_TO_FRONT 0x1 +// sets wParam with Winamp's window handle +#define HKF_WPARAM_HWND 0x2 +// copies returned text to the clipboard (CF_TEXT) +#define HKF_COPY_RET 0x4 +// sets wParam with current pledit position +#define HKF_WPARAM_PLPOS 0x8 +// sets wParam to genHotkeysAddStruct's wParam if playing, lParam if not +// uses IPC_ISPLAYING to check if playing +#define HKF_WPARAM_ISPLAYING_WL 0x10 +// brings Winamp to front or minimizes Winamp if already at front +#define HKF_SHOWHIDE 0x20 +#define HKF_CUSTOM_FUNC 0x40 +// copies returned text to the clipboard (CF_UNICODETEXT) +#define HKF_COPYW_RET 0x80 +#define HKF_UNICODE_NAME 0x100 +// set this when the 'name' is passed as a unicode string + +typedef void (*pfnWAC)(); + +struct WACommand +{ + wchar_t *id; + char *name; + DWORD dwFlags; + UINT uMsg; + WPARAM wParam; + LPARAM lParam; + BOOL bEnabled; + HWND wnd; +}; + +extern WACommand *WACommands; + +extern inline unsigned int GetCommandsNum(); +void InitCommands(); +int AddCommand(genHotkeysAddStruct *ghas); +extern inline char *GetCommandName(unsigned int i, bool *unicode); +extern inline wchar_t *GetCommandId(unsigned int i); +int GetCommandIdx(wchar_t *id); +int DoCommand(unsigned int i); + +#endif \ No newline at end of file diff --git a/Src/Plugins/General/gen_hotkeys/accelBlock.cpp b/Src/Plugins/General/gen_hotkeys/accelBlock.cpp new file mode 100644 index 00000000..e98245d7 --- /dev/null +++ b/Src/Plugins/General/gen_hotkeys/accelBlock.cpp @@ -0,0 +1,47 @@ +#include "gen_hotkeys.h" +#include "api__gen_hotkeys.h" +#include "./accelBlock.h" + +static BOOL RegisterMessageProcessor(ifc_messageprocessor *processor, BOOL bRegister) +{ + if (NULL == WASABI_API_APP) + return FALSE; + + if (bRegister) + WASABI_API_APP->app_addMessageProcessor(processor); + else + WASABI_API_APP->app_removeMessageProcessor(processor); + + return TRUE; +} + +AcceleratorBlocker::AcceleratorBlocker(HWND hwndToBlock) : hwnd(hwndToBlock) +{ + RegisterMessageProcessor(this, TRUE); +} +AcceleratorBlocker::~AcceleratorBlocker() +{ + RegisterMessageProcessor(this, FALSE); +} + +bool AcceleratorBlocker::ProcessMessage(MSG *pMsg) +{ + if (pMsg->hwnd != hwnd) + return false; + + switch(pMsg->message) + { + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + TranslateMessage(pMsg); + DispatchMessageW(pMsg); + return true; + } + return false; +} + + +#define CBCLASS AcceleratorBlocker +START_DISPATCH; +CB(IFC_MESSAGEPROCESSOR_PROCESS_MESSAGE, ProcessMessage) +END_DISPATCH; \ No newline at end of file diff --git a/Src/Plugins/General/gen_hotkeys/accelBlock.h b/Src/Plugins/General/gen_hotkeys/accelBlock.h new file mode 100644 index 00000000..f0d5e792 --- /dev/null +++ b/Src/Plugins/General/gen_hotkeys/accelBlock.h @@ -0,0 +1,24 @@ +#ifndef NULLSOFT_WINAMP_ACCELERATOR_BLOCKER_HEADER +#define NULLSOFT_WINAMP_ACCELERATOR_BLOCKER_HEADER + +#include +#include + +class AcceleratorBlocker : public ifc_messageprocessor +{ +public: + AcceleratorBlocker(HWND hwndToBlock); + ~AcceleratorBlocker(); + +public: + bool ProcessMessage(MSG *pMsg); + HWND GetHwnd() { return hwnd; } +protected: + RECVS_DISPATCH; + +protected: + HWND hwnd; + +}; + +#endif// NULLSOFT_WINAMP_ACCELERATOR_BLOCKER_HEADER \ No newline at end of file diff --git a/Src/Plugins/General/gen_hotkeys/api__gen_hotkeys.h b/Src/Plugins/General/gen_hotkeys/api__gen_hotkeys.h new file mode 100644 index 00000000..823353fd --- /dev/null +++ b/Src/Plugins/General/gen_hotkeys/api__gen_hotkeys.h @@ -0,0 +1,10 @@ +#ifndef NULLSOFT_GEN_HOTKEYS_API_H +#define NULLSOFT_GEN_HOTKEYS_API_H + +#include "api/service/api_service.h" +#include "../Agave/Language/api_language.h" + +#include +#define WASABI_API_APP applicationApi + +#endif // !NULLSOFT_GEN_HOTKEYS_API_H \ No newline at end of file diff --git a/Src/Plugins/General/gen_hotkeys/gen_hotkeys.cpp b/Src/Plugins/General/gen_hotkeys/gen_hotkeys.cpp new file mode 100644 index 00000000..f5e496ad --- /dev/null +++ b/Src/Plugins/General/gen_hotkeys/gen_hotkeys.cpp @@ -0,0 +1,519 @@ +//#define PLUGIN_NAME //"Nullsoft Global Hotkeys" +#define PLUGIN_VERSION L"1.97" + +// do this so we can get WM_APPCOMMAND support (needed in 1.6+) +// (have to targetting xp sp1+ so we get the specific play & pause messages +// though it'll work fine with win2k+ as those messages will never appear) +//////#define _WIN32_WINNT 0x0501 +#include "gen_hotkeys.h" +#include "ConfigDlg.h" +#include "HotKey.h" +#include "WACommands.h" +#include + +/////////////////////////////////////////////////////////// +// Globals +/////////////////////////////////////////////////////////// + +// The global plugin instance +winampGeneralPurposePlugin psPlugin = +{ + GPPHDR_VER_U, + "nullsoft(gen_hotkeys.dll)", + pluginInit, + pluginConfig, + hotkeysClear +}; +// Winamp's window procdure +WNDPROC lpWndProcOld = NULL; +static int winampIsUnicode=false; +static int appcommand=false; +// hotkeys +HOTKEY *g_hotkeys = NULL; +DWORD g_dwHotkeys = 0; +// mutex to prevent two gen_hotkeys +HANDLE g_hMutex = NULL; +LPARAM uShellHook = NULL; + +// wasabi based services for localisation support +api_service *WASABI_API_SVC = 0; +api_application *WASABI_API_APP = NULL; +api_language *WASABI_API_LNG = 0; +HINSTANCE WASABI_API_LNG_HINST = 0, WASABI_API_ORIG_HINST = 0; + +static prefsDlgRecW g_prefsItem = {0}; +static wchar_t g_titlestr[128]; +wchar_t *g_iniFile = 0; + +// don't forget to set DEFHKDS_NUM if you change this +HOTKEY_DATA g_defhkds[] = { + {MAKEWORD(VK_INSERT, HOTKEYF_ALT | HOTKEYF_CONTROL | HOTKEYF_EXT), -1, L"ghkdc play"}, + {MAKEWORD(VK_HOME, HOTKEYF_ALT | HOTKEYF_CONTROL | HOTKEYF_EXT), -1, L"ghkdc pause"}, + {MAKEWORD(VK_END, HOTKEYF_ALT | HOTKEYF_CONTROL | HOTKEYF_EXT), -1, L"ghkdc stop"}, + {MAKEWORD(VK_PRIOR, HOTKEYF_ALT | HOTKEYF_CONTROL | HOTKEYF_EXT), -1, L"ghkdc prev"}, + {MAKEWORD(VK_NEXT, HOTKEYF_ALT | HOTKEYF_CONTROL | HOTKEYF_EXT), -1, L"ghkdc next"}, + {MAKEWORD(VK_UP, HOTKEYF_ALT | HOTKEYF_CONTROL | HOTKEYF_EXT), -1, L"ghkdc vup"}, + {MAKEWORD(VK_DOWN, HOTKEYF_ALT | HOTKEYF_CONTROL | HOTKEYF_EXT), -1, L"ghkdc vdown"}, + {MAKEWORD(VK_RIGHT, HOTKEYF_ALT | HOTKEYF_CONTROL | HOTKEYF_EXT), -1, L"ghkdc forward"}, + {MAKEWORD(VK_LEFT, HOTKEYF_ALT | HOTKEYF_CONTROL | HOTKEYF_EXT), -1, L"ghkdc rewind"}, + {MAKEWORD('J', HOTKEYF_ALT | HOTKEYF_CONTROL | HOTKEYF_EXT), -1, L"ghkdc jump"}, + {MAKEWORD('L', HOTKEYF_ALT | HOTKEYF_CONTROL | HOTKEYF_EXT), -1, L"ghkdc file"}, + //multimedia keyboard stuff + {2226, -1, L"ghkdc stop"}, + {2227, -1, L"ghkdc play/pause"}, + {2225, -1, L"ghkdc prev"}, + {2224, -1, L"ghkdc next"}, + }; + +#define TIMER_ID 0x8855 + +static int m_genhotkeys_add_ipc; + +/////////////////////////////////////////////////////////// +// DLL entry function +/////////////////////////////////////////////////////////// + +#ifndef _DEBUG +BOOL WINAPI _DllMainCRTStartup(HINSTANCE hInst, ULONG ul_reason_for_call, LPVOID lpReserved) +{ + DisableThreadLibraryCalls(hInst); + return TRUE; +} +#endif + +/////////////////////////////////////////////////////////// +// Plugin functions +/////////////////////////////////////////////////////////// + +int pluginInit() +{ + RegisterShellHookWindow(psPlugin.hwndParent); + uShellHook = RegisterWindowMessage(TEXT("SHELLHOOK")); + + g_iniFile = (wchar_t*)SendMessage(psPlugin.hwndParent, WM_WA_IPC, 0, IPC_GETINIFILEW); + + // loader so that we can get the localisation service api for use + WASABI_API_SVC = (api_service*)SendMessage(psPlugin.hwndParent, WM_WA_IPC, 0, IPC_GET_API_SERVICE); + if (WASABI_API_SVC == (api_service*)1 || + NULL == WASABI_API_SVC) + { + return 1; + } + + waServiceFactory *sf = WASABI_API_SVC->service_getServiceByGuid(applicationApiServiceGuid); + if (sf) WASABI_API_APP = reinterpret_cast(sf->getInterface()); + + sf = WASABI_API_SVC->service_getServiceByGuid(languageApiGUID); + if (sf) WASABI_API_LNG = reinterpret_cast(sf->getInterface()); + + // need to have this initialised before we try to do anything with localisation features + WASABI_API_START_LANG(psPlugin.hDllInstance,GenHotkeysLangGUID); + + m_genhotkeys_add_ipc = (int)SendMessage( psPlugin.hwndParent, WM_WA_IPC, (WPARAM) &"GenHotkeysAdd", IPC_REGISTER_WINAMP_IPCMESSAGE); + + static wchar_t szDescription[256]; + StringCchPrintfW(szDescription, ARRAYSIZE(szDescription), + WASABI_API_LNGSTRINGW(IDS_NULLSOFT_GLOBAL_HOTKEYS), PLUGIN_VERSION); + psPlugin.description = (char*)szDescription; + + appcommand = GetPrivateProfileIntW(L"gen_hotkeys", L"appcommand", 0, g_iniFile); + + // Save Winamp's window procedure + winampIsUnicode = IsWindowUnicode(psPlugin.hwndParent); + lpWndProcOld = (WNDPROC)(LONG_PTR)GetWindowLongPtr(psPlugin.hwndParent, GWLP_WNDPROC); + if (winampIsUnicode) + SetWindowLongPtrW(psPlugin.hwndParent, GWLP_WNDPROC, (LONGX86)(LONG_PTR)WndProc); + else + SetWindowLongPtrA(psPlugin.hwndParent, GWLP_WNDPROC, (LONGX86)(LONG_PTR)WndProc); + + InitCommands(); + + SetTimer(psPlugin.hwndParent, TIMER_ID, 10, NULL); //call hotkeysInit() when all plugins are loaded + + //register prefs screen + g_prefsItem.dlgID = IDD_CONFIG; + g_prefsItem.name = WASABI_API_LNGSTRINGW_BUF(IDS_GHK_TITLE_STR,g_titlestr,128); + g_prefsItem.proc = ConfigProc; + g_prefsItem.hInst = WASABI_API_LNG_HINST; + // delay the adding of this + // for some reason when changing a lang pack it can cause the WM_DESTROY of ConfigProc + // to be called which completely messes up the ghk list and so can wipe it :( + SendMessage(psPlugin.hwndParent, WM_WA_IPC, (WPARAM) &g_prefsItem, IPC_ADD_PREFS_DLGW); + + return 0; +} + +int hotkeysLoad(HOTKEY_DATA *hkds, DWORD num, int do_register, int verbose /*=1*/) +{ + unsigned int uFailed = 0; + + delete [] g_hotkeys; + g_hotkeys = NULL; + + if (num) + { + g_hotkeys = new HOTKEY[num]; + memset(g_hotkeys, 0, num * sizeof(HOTKEY)); + } + if (!g_hotkeys) + { + g_dwHotkeys = 0; + return 1; + } + + g_dwHotkeys = num; + + wchar_t moreStr[64] = {0}; + WASABI_API_LNGSTRINGW_BUF(IDS_GHK_HOTKEY_REG_FAILED_MORE,moreStr,64); + size_t bufLen = 256 + wcslen(moreStr) + 1; + wchar_t* szFailed = (wchar_t*)malloc(bufLen*sizeof(wchar_t)); + WASABI_API_LNGSTRINGW_BUF(IDS_GHK_HOTKEY_REG_FAILED,szFailed,bufLen); + DWORD dwLeft = 256 - lstrlenW(szFailed); + DWORD dwFailedWithNoMsg = 0; + + for (DWORD i = 0; i < num; i++) + { + g_hotkeys[i].hkd = hkds[i]; + + if (g_hotkeys[i].hkd.iCommand < 0) + // action not loaded yet for this hotkey + continue; + + if (do_register && RegisterHotkey(g_hotkeys + i)) + { + if (verbose && dwLeft) + { + wchar_t szTemp[1024] = {0}; + bool unicode = 0; + char* name = GetCommandName(g_hotkeys[i].hkd.iCommand, &unicode); + StringCchPrintfW(szTemp, 1024, (unicode?L"\n\t%s":L"\n\t%S"), name); + DWORD dwLen = lstrlenW(szTemp); + if (dwLen < dwLeft) + { + StringCchCatW(szFailed, bufLen, szTemp); + dwLeft -= dwLen; + } + else + dwFailedWithNoMsg++; + } + + g_hotkeys[i].failed = TRUE; + uFailed++; + } + } + + if (verbose && uFailed) + { + if (dwFailedWithNoMsg) + StringCchCatW(szFailed, bufLen, moreStr); + HWND parent = (HWND)SendMessage(psPlugin.hwndParent, WM_WA_IPC, 0, IPC_GETDIALOGBOXPARENT); + if (parent == 0 || parent == (HWND)1) + parent=psPlugin.hwndParent; + + MessageBoxW(parent, szFailed, g_titlestr, MB_OK | MB_ICONSTOP | MB_TOPMOST); + } + free(szFailed); + + return uFailed; +} + +static volatile LONG initted=0; +void hotkeysInit() +{ + if (initted) + return; + initted=1; + + int enabled = GetPrivateProfileIntW(L"gen_hotkeys", L"enabled", 0, g_iniFile); + + // base the mutex on the current winamp install + // (makes it work better with mutliple winamp installs otherwise the older + // "Winamp - gen_hotkeys.dll ^&*#@" mutex prevents different hotkeys from + // being initialised between the different installs without going to prefs) + char mutexStr[MAX_PATH] = {0}, ghkFilename[MAX_PATH] = {0}; + GetModuleFileName(psPlugin.hDllInstance, ghkFilename, MAX_PATH); + StringCchPrintf(mutexStr, MAX_PATH, "Winamp - %s ^&*#@", ghkFilename); + g_hMutex = CreateMutex(0, TRUE, mutexStr); + if (GetLastError() == ERROR_ALREADY_EXISTS) + enabled = 0; + + int i; + + for (i = 0; i < DEFHKDS_NUM; i++) + { + g_defhkds[i].iCommand = GetCommandIdx(g_defhkds[i].szCommand); + g_defhkds[i].szCommand = 0; + } + + int l = GetPrivateProfileIntW(L"gen_hotkeys", L"nbkeys", -1, g_iniFile); + int ver = GetPrivateProfileIntW(L"gen_hotkeys", L"version", 1, g_iniFile); + if (l != -1) + { + if (ver == 2) + { + HOTKEY_DATA *hkds = new HOTKEY_DATA[l]; // TODO: could alloca this + if (hkds) + { + for (i = 0; i < l; i++) + { + wchar_t tmp[1024] = {0}, action[1024] = {0}; + StringCchPrintfW(tmp, 1024, L"action%d", i); + GetPrivateProfileStringW(L"gen_hotkeys", tmp, L"", action, 1024, g_iniFile); + StringCchPrintfW(tmp, 1024, L"hotkey%d", i); + int hotkey = GetPrivateProfileIntW(L"gen_hotkeys", tmp, 0, g_iniFile); + + hkds[i].dwHotKey = hotkey; + + hkds[i].iCommand = GetCommandIdx(action); + if (hkds[i].iCommand >= 0) + hkds[i].szCommand = NULL; + else + hkds[i].szCommand = _wcsdup(action); + } + + hotkeysLoad(hkds, l, enabled, 0); + delete [] hkds; + } + } + // legacy support + else if (ver == 1) + { + HOTKEY_DATA *hkds = new HOTKEY_DATA[l]; // TODO: could alloca this + if (hkds) + { + int nb = 0; + for (i = 0;i < l;i++) + { + wchar_t tmp[512] = {0}; + StringCchPrintfW(tmp, 512, L"msg%d", i); + unsigned int msg = GetPrivateProfileIntW(L"gen_hotkeys", tmp, 0, g_iniFile); + if (msg) + { + StringCchPrintfW(tmp, 512, L"wparam%d", i); + WPARAM wparam = GetPrivateProfileIntW(L"gen_hotkeys", tmp, 0, g_iniFile); + StringCchPrintfW(tmp, 512, L"lparam%d", i); + LPARAM lparam = GetPrivateProfileIntW(L"gen_hotkeys", tmp, 0, g_iniFile); + for (int j = 0;WACommands[j].name;j++) + { + if (WACommands[j].uMsg == msg && WACommands[j].wParam == wparam && WACommands[j].lParam == lparam) + { + StringCchPrintfW(tmp, 512, L"hotkey%d", i); + int hotkey = GetPrivateProfileIntW(L"gen_hotkeys", tmp, 0, g_iniFile); + hkds[nb].dwHotKey = hotkey; + hkds[nb].iCommand = j; + hkds[nb].szCommand = NULL; + nb++; + break; + } + } + } + } + hotkeysLoad(hkds, nb, enabled, 0); + delete [] hkds; + } + } + } + else + { + // load defaults + hotkeysLoad(g_defhkds, DEFHKDS_NUM, enabled, 0); + } +} + +void writePrivateProfileInt(wchar_t *section, int val) +{ + wchar_t s[32] = {0}; + StringCchPrintfW(s, 32, L"%d", val); + WritePrivateProfileStringW(L"gen_hotkeys", section, s, g_iniFile); +} + +void hotkeysSave(HOTKEY_DATA *hkds, DWORD num) +{ + int enabled = GetPrivateProfileIntW(L"gen_hotkeys", L"enabled", 0, g_iniFile); + appcommand = GetPrivateProfileIntW(L"gen_hotkeys", L"appcommand", 0, g_iniFile); + WritePrivateProfileStringW(L"gen_hotkeys", NULL, NULL, g_iniFile); + writePrivateProfileInt(L"nbkeys", num); + writePrivateProfileInt(L"version", 2); + writePrivateProfileInt(L"enabled", enabled); + writePrivateProfileInt(L"appcommand", appcommand); + if (!hkds || !num) + { + return ; + } + for (size_t i = 0; i < num; i++) + { + wchar_t tmp[1024] = {0}; + StringCchPrintfW(tmp, 1024, L"action%d", i); + if (hkds[i].iCommand >= 0) + WritePrivateProfileStringW(L"gen_hotkeys", tmp, GetCommandId(hkds[i].iCommand), g_iniFile); + else if (hkds[i].szCommand) + WritePrivateProfileStringW(L"gen_hotkeys", tmp, hkds[i].szCommand, g_iniFile); + StringCchPrintfW(tmp, 1024, L"hotkey%d", i); + writePrivateProfileInt(tmp, hkds[i].dwHotKey); + } +} + +void pluginConfig() +{ + SendMessage(psPlugin.hwndParent, WM_WA_IPC, (WPARAM) &g_prefsItem, IPC_OPENPREFSTOPAGE); +} + +void hotkeysClear() +{ + DeregisterShellHookWindow(psPlugin.hwndParent); + + if (!g_hotkeys) + return; + + // Unregister all of the hot keys, and delete all of our unique hot key identifiers + for (DWORD i = 0; i < g_dwHotkeys; i++) + { + UnregisterHotkey(g_hotkeys + i); + } + delete [] g_hotkeys; + g_hotkeys = NULL; +} + +/////////////////////////////////////////////////////////// +// Plugin export function +/////////////////////////////////////////////////////////// + +extern "C" __declspec(dllexport) winampGeneralPurposePlugin * winampGetGeneralPurposePlugin() { return &psPlugin; } + +/////////////////////////////////////////////////////////// +// DLL Windows message handling procedure +/////////////////////////////////////////////////////////// + +LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + if(message == uShellHook && appcommand && wParam == HSHELL_APPCOMMAND) + { + // WM_APPCOMMAND info:: http://msdn2.microsoft.com/en-us/library/ms646275.aspx + int cmd = GET_APPCOMMAND_LPARAM(lParam); + switch (cmd) + { + case APPCOMMAND_MEDIA_PLAY_PAUSE: + { + int playing = (int)SendMessage(psPlugin.hwndParent, WM_WA_IPC, 0, IPC_ISPLAYING); + SendMessage(hwnd, WM_COMMAND, MAKEWPARAM((playing ? WINAMP_BUTTON3 : WINAMP_BUTTON2),0), 0); + } + break; + case APPCOMMAND_MEDIA_NEXTTRACK: + SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(WINAMP_BUTTON5,0), 0); + return TRUE; + case APPCOMMAND_MEDIA_PREVIOUSTRACK: + SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(WINAMP_BUTTON1,0), 0); + return TRUE; + case APPCOMMAND_MEDIA_STOP: + SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(WINAMP_BUTTON4,0), 0); + return TRUE; + case APPCOMMAND_VOLUME_DOWN: + SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(WINAMP_VOLUMEDOWN,0), 0); + return TRUE; + case APPCOMMAND_VOLUME_UP: + SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(WINAMP_VOLUMEUP,0), 0); + return TRUE; + /*case APPCOMMAND_VOLUME_MUTE: + return TRUE;*/ + + // info on play/pause (xp sp1+) commands:: http://msdn2.microsoft.com/en-us/library/bb417078.aspx + case APPCOMMAND_MEDIA_PLAY: + { + int playing = (int)SendMessage(psPlugin.hwndParent, WM_WA_IPC, 0, IPC_ISPLAYING); + // play if not currently playing/are stopped + if(!playing) SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(WINAMP_BUTTON2,0), 0); + // if paused then start playing again + else if(playing == 3) SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(WINAMP_BUTTON3,0), 0); + // otherwise do nothing if playing already (playing == 1) + } + return TRUE; + case APPCOMMAND_MEDIA_PAUSE: + { + int playing = (int)SendMessage(psPlugin.hwndParent, WM_WA_IPC, 0, IPC_ISPLAYING); + // if playing then we pause + if(playing == 1) SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(WINAMP_BUTTON3,0), 0); + // otherwise do nothing if already stopped or paused (playing == 0 || playing == 3) + } + return TRUE; + case APPCOMMAND_MEDIA_FAST_FORWARD: + SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(WINAMP_FFWD5S,0), 0); + return TRUE; + case APPCOMMAND_MEDIA_REWIND: + SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(WINAMP_REW5S,0), 0); + return TRUE; + } + } + + switch (message) + { + case WM_TIMER: + if (wParam == TIMER_ID) + { + KillTimer(hwnd, TIMER_ID); + hotkeysInit(); + return 0; + } + break; + + case WM_HOTKEY: + if (g_hotkeys) + { + for (unsigned int i = 0; i < g_dwHotkeys; i++) + { + if (g_hotkeys[i].atom == wParam) + { + DoCommand(g_hotkeys[i].hkd.iCommand); + return 0; + } + } + } + break; + + case WM_WA_IPC: + if (lParam == m_genhotkeys_add_ipc && m_genhotkeys_add_ipc > 65536) + { + int cmd = AddCommand((genHotkeysAddStruct *) wParam); + if (cmd > 0) + { + for (unsigned int i = 0; i < g_dwHotkeys; i++) + { + if (g_hotkeys[i].hkd.iCommand < 0 + && g_hotkeys[i].hkd.szCommand + && !lstrcmpiW(g_hotkeys[i].hkd.szCommand, GetCommandId(cmd))) + { + g_hotkeys[i].hkd.iCommand = cmd; + free(g_hotkeys[i].hkd.szCommand); + g_hotkeys[i].hkd.szCommand = NULL; + int enabled = GetPrivateProfileIntW(L"gen_hotkeys", L"enabled", 0, g_iniFile); + if (enabled) RegisterHotkey(&g_hotkeys[i]); + } + } + } + return cmd; + } + + // handles the weird inc/dec current rating (since it's locked to 0-5 + // we can just wrap around and correct the real value passed on to the api) + else if(lParam == IPC_SETRATING && (wParam == -1 || wParam == 6)) + { + LRESULT curRating = SendMessage(hwnd, WM_WA_IPC, + SendMessage(hwnd, WM_WA_IPC, 0, IPC_GETLISTPOS), + IPC_GETRATING); + if(wParam == 6) // increment + { + wParam = curRating+1; + } + else // decrement + { + wParam = curRating-1; + } + } + break; + } + + // If we don't know how to handle this message, let WinAMP do it for us + if (winampIsUnicode) + return CallWindowProcW(lpWndProcOld, hwnd, message, wParam, lParam); + else + return CallWindowProcA(lpWndProcOld, hwnd, message, wParam, lParam); +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_hotkeys/gen_hotkeys.h b/Src/Plugins/General/gen_hotkeys/gen_hotkeys.h new file mode 100644 index 00000000..4cfb390c --- /dev/null +++ b/Src/Plugins/General/gen_hotkeys/gen_hotkeys.h @@ -0,0 +1,56 @@ +#ifndef ___HOTAMP3_H___ +#define ___HOTAMP3_H___ + +#include +#include +#include "winamp.h" +#include "wa_hotkeys.h" +#include "resource.h" +#include "hotkey.h" +#include "WACommands.h" +#include "HotKeyCtl.h" +#include "../winamp/gen.h" +#include "api__gen_hotkeys.h" +#include + +/////////////////////////////////////////////////////////// +// Hot keys initializer function +/////////////////////////////////////////////////////////// +void hotkeysInit(); +int hotkeysLoad(HOTKEY_DATA *hkds, DWORD num, int do_register, int verbose=1); +void hotkeysSave(HOTKEY_DATA *hkds, DWORD num); +void hotkeysClear(); + +/////////////////////////////////////////////////////////// +// Plugin function headers +/////////////////////////////////////////////////////////// +int pluginInit(); +void pluginConfig(); +void pluginQuit(); + +/////////////////////////////////////////////////////////// +// DLL Windows message handling procedure header +/////////////////////////////////////////////////////////// +LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); + +/////////////////////////////////////////////////////////// +// Globals +/////////////////////////////////////////////////////////// +extern winampGeneralPurposePlugin psPlugin; +extern HOTKEY *g_hotkeys; +extern DWORD g_dwHotkeys; +extern wchar_t *g_iniFile; +extern HOTKEY_DATA g_defhkds[]; +#define DEFHKDS_NUM 15 + +void writePrivateProfileInt(wchar_t *section, int val); + +#ifndef LONGX86 +#ifdef _WIN64 + #define LONGX86 LONG_PTR +#else /*_WIN64*/ + #define LONGX86 LONG +#endif /*_WIN64*/ +#endif // LONGX86 + +#endif \ No newline at end of file diff --git a/Src/Plugins/General/gen_hotkeys/gen_hotkeys.rc b/Src/Plugins/General/gen_hotkeys/gen_hotkeys.rc new file mode 100644 index 00000000..6187bdd1 --- /dev/null +++ b/Src/Plugins/General/gen_hotkeys/gen_hotkeys.rc @@ -0,0 +1,217 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Bitmap +// + +IDB_LVSTATE BITMAP "x.bmp" + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_CONFIG DIALOGEX 0, 0, 273, 246 +STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD +FONT 8, "MS Shell Dlg", 0, 0, 0x0 +BEGIN + GROUPBOX "Global Hotkeys",IDC_STATIC,0,0,272,246 + LTEXT "Global Hotkeys are keyboard shortcuts that you can use from within any running\napplication.",IDC_STATIC,6,11,260,17 + CONTROL "Enable default &multimedia key support (you can customize them below)\n(Will process media related WM_APPCOMMAND messages - see MSDN)",IDC_ENABLED_WM_APPCOMMAND, + "Button",BS_AUTOCHECKBOX | BS_TOP | BS_MULTILINE | WS_TABSTOP,6,32,259,17 + CONTROL "&Enabled",IDC_ENABLED,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,6,54,42,10 + PUSHBUTTON "Restore &defaults",IDC_DEFAULT,189,53,76,13 + CONTROL "List1",IDC_HKLIST,"SysListView32",LVS_REPORT | LVS_SHOWSELALWAYS | WS_BORDER | WS_TABSTOP,6,70,259,137 + LTEXT "Ac&tion",IDC_STATIC,6,213,24,9,SS_CENTERIMAGE + COMBOBOX IDC_COMBO,34,211,231,154,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + LTEXT "&Hotkey",IDC_STATIC,6,229,24,9 + EDITTEXT IDC_HOTKEY,34,227,147,13,ES_AUTOHSCROLL + PUSHBUTTON "&Add",IDC_ADD,182,227,23,13 + PUSHBUTTON "&Set",IDC_SAVE,208,227,20,13 + PUSHBUTTON "&Remove",IDC_REMOVE,230,227,35,13 +END + + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "#include ""version.rc2""\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + IDS_NULLSOFT_GLOBAL_HOTKEYS "Nullsoft Global Hotkeys v%s" + 65535 "{250FAA3C-20CD-49db-A932-67B1C0191B0E}" +END + +STRINGTABLE +BEGIN + IDS_GHK_TITLE_STR "Global Hotkeys" + IDS_GHK_HOTKEY_REG_FAILED + "Hotkey registration failed as another program has already registered the following hotkey(s):\t\t\n" + IDS_GHK_HOTKEY_REG_FAILED_MORE "\n\tMore..." + IDS_GHK_HOTKEY_COLUMN "Hotkey" + IDS_GHK_ACTION_COLUMN "Action" + IDS_GHK_ACTION_NOT_FOUND "(action not found)" + IDS_GHK_WINKEY_STR "Winkey" + IDS_BROWSER_BACK "Browser Back" + IDS_BROWSER_FORWARD "Browser Forward" + IDS_BROWSER_REFRESH "Browser Refresh" + IDS_BROWSER_STOP "Browser Stop" + IDS_BROWSER_SEARCH "Browser Search" + IDS_BROWSER_FAVOURITES "Browser Favorites" + IDS_BROWSER_HOME "Browser Home" + IDS_VOLUME_MUTE "Volume Mute" +END + +STRINGTABLE +BEGIN + IDS_VOLUME_DOWN "Volume Down" + IDS_VOLUME_UP "Volume Up" + IDS_NEXT_TRACK "Next Track" + IDS_PREVIOUS_TRACK "Previous Track" + IDS_STOP "Stop" + IDS_PLAY_PAUSE "Play/Pause" + IDS_LAUNCH_MAIL "Launch Mail" + IDS_LAUNCH_MEDIA_SELECT "Launch Media Select" + IDS_LAUCH_APP1 "Launch App1" + IDS_LAUCH_APP2 "Launch App2" + IDS_SLEEP "Sleep" +END + +STRINGTABLE +BEGIN + IDS_PLAYBACK__PLAY "Playback: Play" + IDS_PLAYBACK__PAUSE "Playback: Pause" + IDS_PLAYBACK__STOP "Playback: Stop" + IDS_PLAYBACK__PREVIOUS_IN_PLAYLIST "Playback: Previous in playlist" + IDS_PLAYBACK__NEXT_IN_PLAYLIST "Playback: Next in playlist" + IDS_PLAYBACK__VOLUME_UP "Playback: Volume up" + IDS_PLAYBACK__VOLUME_DOWN "Playback: Volume down" + IDS_PLAYBACK__FORWARD "Playback: Forward" + IDS_PLAYBACK__REWIND "Playback: Rewind" + IDS_PLAYBACK__REPEAT_ON "Playback: Repeat on" + IDS_PLAYBACK__REPEAT_OFF "Playback: Repeat off" + IDS_UI__TOGGLE_EQ "UI: Toggle EQ" + IDS_UI__TOGGLE_PLAYLIST "UI: Toggle playlist" + IDS_UI__TOGGLE_AOT "UI: Toggle always on top" + IDS_UI__ABOUT "UI: About" + IDS_PLAYBACK__JUMP_TO_BOX "Playback: Jump to box" +END + +STRINGTABLE +BEGIN + IDS_PLAYBACK__OPEN_FILE_DIALOG "Playback: Open file dialog" + IDS_PLAYBACK__OPEN_LOC_DIALOG "Playback: Open location dialog" + IDS_PLAYBACK__OPEN_FOLDER_DIALOG "Playback: Open folder dialog" + IDS_GENERAL__QUIT "General: Quit" + IDS_UI__PREFERENCES "UI: Preferences" + IDS_GENERAL__COPY_TITLE "General: Copy title" + IDS_GENERAL__COPY_FILE_PATH "General: Copy file path" + IDS_PLAYBACK__PLAY_PAUSE "Playback: Play/Pause" + IDS_PLAYBACK__TOGGLE_REPEAT "Playback: Toggle repeat" + IDS_PLAYBACK__TOGGLE_SHUFFLE "Playback: Toggle shuffle" + IDS_UI__TOGGLE_WINSHADE "UI: Toggle windowshade" + IDS_UI__TOGGLE_PLAYLIST_WINSHADE "UI: Toggle playlist windowshade" + IDS_UI__TOGGLE_DOUBLESIZE "UI: Toggle doublesize" + IDS_UI__TOGGLE_MAIN_WINDOW "UI: Toggle main window" + IDS_UI__TOGGLE_MINIBROWSER "UI: Toggle main minibrowser" + IDS_VIS__PREFERENCES "Visualization: Preferences" +END + +STRINGTABLE +BEGIN + IDS_VIS__PLUGIN_PREFERENCES "Visualization: Plug-in preferences" + IDS_VIS_TOGGLE_VIS_ON_OFF "Visualization: Toggle on/off" + IDS_UI__SELECT_SKIN "UI: Select skin" + IDS_PLAYBACK__STOP_WITH_FADEOUT "Playback: Stop with fadeout" + IDS_PLAYBACK__STOP_AFTER_CURRENT "Playback: Stop after current" + IDS_PLAYBACK__START_OF_LIST "Playback: Start of list" + IDS_PLAYBACK__END_OF_LIST "Playback: End of list" + IDS_UI__BRING_TO_FRONT_OR_HIDE "UI: Bring to front/hide Winamp" + IDS_RATING__5_STARS "Rate current item: *****" + IDS_RATING__4_STARS "Rate current item: ****" + IDS_RATING__3_STARS "Rate current item: ***" + IDS_RATING__2_STARS "Rate current item: **" + IDS_RATING__1_STAR "Rate current item: *" + IDS_RATING__NO_RATING "Rate current item: No rating" + IDS_GENERAL__COPY_TITLEW "General: Copy title (Unicode)" + IDS_GENERAL__COPY_FILE_PATHW "General: Copy file path (Unicode)" +END + +STRINGTABLE +BEGIN + IDS_RATING__INC "Rating: Increase rating of current item" + IDS_RATING__DEC "Rating: Decrease rating of current item" + IDS_PAUSE "Pause" + IDS_VIS_NEXT_PRESET "Visualization: Next preset" + IDS_VIS_PREV_PRESET "Visualization: Previous preset" + IDS_VIS_RAND_PRESET "Visualization: Random preset" + IDS_VIS_FULL_SCREEN "Visualization: Toggle fullscreen" + IDS_PLAYBACK__MANUAL_ADVANCE "Playback: Toggle manual advance" + IDS_GENERAL_FFOD "General: Explore current item folder" + IDS_PLAYBACK_EDIT_ID3 "Playback: View current file information" +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// +#include "version.rc2" + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/Src/Plugins/General/gen_hotkeys/gen_hotkeys.sln b/Src/Plugins/General/gen_hotkeys/gen_hotkeys.sln new file mode 100644 index 00000000..25d787a7 --- /dev/null +++ b/Src/Plugins/General/gen_hotkeys/gen_hotkeys.sln @@ -0,0 +1,29 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29424.173 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gen_hotkeys", "gen_hotkeys.vcxproj", "{A0E56406-B1C9-4FC8-9589-A640D71A7364}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A0E56406-B1C9-4FC8-9589-A640D71A7364}.Debug|Win32.ActiveCfg = Debug|Win32 + {A0E56406-B1C9-4FC8-9589-A640D71A7364}.Debug|Win32.Build.0 = Debug|Win32 + {A0E56406-B1C9-4FC8-9589-A640D71A7364}.Debug|x64.ActiveCfg = Debug|x64 + {A0E56406-B1C9-4FC8-9589-A640D71A7364}.Release|Win32.ActiveCfg = Release|Win32 + {A0E56406-B1C9-4FC8-9589-A640D71A7364}.Release|Win32.Build.0 = Release|Win32 + {A0E56406-B1C9-4FC8-9589-A640D71A7364}.Release|x64.ActiveCfg = Release|x64 + {A0E56406-B1C9-4FC8-9589-A640D71A7364}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {AF3A8D23-4DD8-4A09-BD21-7A59EDA47D7C} + EndGlobalSection +EndGlobal diff --git a/Src/Plugins/General/gen_hotkeys/gen_hotkeys.vcxproj b/Src/Plugins/General/gen_hotkeys/gen_hotkeys.vcxproj new file mode 100644 index 00000000..f8d7bc1c --- /dev/null +++ b/Src/Plugins/General/gen_hotkeys/gen_hotkeys.vcxproj @@ -0,0 +1,316 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {A0E56406-B1C9-4FC8-9589-A640D71A7364} + gen_hotkeys + 10.0.19041.0 + + + + DynamicLibrary + v142 + Unicode + + + DynamicLibrary + v142 + Unicode + + + DynamicLibrary + v142 + Unicode + + + DynamicLibrary + v142 + Unicode + + + + + + + + + + + + + + + false + $(PlatformShortName)_$(Configuration)\ + $(PlatformShortName)_$(Configuration)\ + $(IncludePath) + $(LibraryPath) + + + false + $(PlatformShortName)_$(Configuration)\ + $(PlatformShortName)_$(Configuration)\ + + + false + $(PlatformShortName)_$(Configuration)\ + $(PlatformShortName)_$(Configuration)\ + $(IncludePath) + $(LibraryPath) + true + + + false + $(PlatformShortName)_$(Configuration)\ + $(PlatformShortName)_$(Configuration)\ + + + false + + + Debug + + + + + Debug + + + + Disabled + .;..\..\..\Wasabi;%(AdditionalIncludeDirectories) + _WIN32_WINNT=0x0601;WINVER=0x0601;WIN32;_DEBUG;_WINDOWS;_USRDLL;HOTAMP3_EXPORTS;%(PreprocessorDefinitions) + false + true + EnableFastChecks + MultiThreadedDebugDLL + $(IntDir) + $(IntDir) + $(IntDir)$(TargetName).pdb + Level3 + ProgramDatabase + true + true + + + _WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions) + 0x0409 + + + odbc32.lib;odbccp32.lib;comctl32.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)$(TargetExt) + true + $(IntDir)$(TargetName).pdb + false + $(IntDir)$(TargetName).lib + MachineX86 + false + %(AdditionalLibraryDirectories) + Windows + $(IntDir)$(TargetName).map + + + xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ +xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ + Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\' + + + + + Disabled + .;..\..\..\Wasabi;%(AdditionalIncludeDirectories) + _WIN32_WINNT=0x0601;WINVER=0x0601;WIN64;_DEBUG;_WINDOWS;_USRDLL;HOTAMP3_EXPORTS;%(PreprocessorDefinitions) + false + true + EnableFastChecks + MultiThreadedDebugDLL + $(IntDir) + $(IntDir) + $(IntDir)$(TargetName).pdb + Level3 + ProgramDatabase + true + true + + + _WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions) + 0x0409 + + + odbc32.lib;odbccp32.lib;comctl32.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)$(TargetExt) + true + $(IntDir)$(TargetName).pdb + false + $(IntDir)$(TargetName).lib + false + %(AdditionalLibraryDirectories) + Windows + $(IntDir)$(TargetName).map + + + xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ +xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ + Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\' + + + + + MinSpace + OnlyExplicitInline + Size + .;..\..\..\Wasabi;%(AdditionalIncludeDirectories) + _WIN32_WINNT=0x0601;WINVER=0x0601;WIN32;NDEBUG;_WINDOWS;_USRDLL;HOTAMP3_EXPORTS;%(PreprocessorDefinitions) + true + true + MultiThreadedDLL + true + $(IntDir) + $(IntDir) + $(IntDir)$(TargetName).pdb + Level3 + None + true + + + _WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions) + 0x0409 + + + odbc32.lib;odbccp32.lib;comctl32.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)$(TargetExt) + false + $(IntDir)$(TargetName).pdb + $(IntDir)$(TargetName).map + true + true + false + $(IntDir)$(TargetName).lib + MachineX86 + false + %(AdditionalLibraryDirectories) + Windows + + + xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ + Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\' + + + + + MinSpace + OnlyExplicitInline + Size + .;..\..\..\Wasabi;%(AdditionalIncludeDirectories) + _WIN32_WINNT=0x0601;WINVER=0x0601;WIN64;NDEBUG;_WINDOWS;_USRDLL;HOTAMP3_EXPORTS;%(PreprocessorDefinitions) + true + true + MultiThreadedDLL + true + $(IntDir) + $(IntDir) + $(IntDir)$(TargetName).pdb + Level3 + None + true + + + _WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions) + 0x0409 + + + odbc32.lib;odbccp32.lib;comctl32.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)$(TargetExt) + false + $(IntDir)$(TargetName).pdb + $(IntDir)$(TargetName).map + true + true + false + $(IntDir)$(TargetName).lib + false + %(AdditionalLibraryDirectories) + Windows + + + xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ + Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\' + + + + + + WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;HOTAMP3_EXPORTS + WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;HOTAMP3_EXPORTS + WIN64;_DEBUG;_WINDOWS;_MBCS;_USRDLL;HOTAMP3_EXPORTS + WIN64;NDEBUG;_WINDOWS;_MBCS;_USRDLL;HOTAMP3_EXPORTS + + + WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;HOTAMP3_EXPORTS + WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;HOTAMP3_EXPORTS + WIN64;_DEBUG;_WINDOWS;_MBCS;_USRDLL;HOTAMP3_EXPORTS + WIN64;NDEBUG;_WINDOWS;_MBCS;_USRDLL;HOTAMP3_EXPORTS + + + WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;HOTAMP3_EXPORTS + WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;HOTAMP3_EXPORTS + WIN64;_DEBUG;_WINDOWS;_MBCS;_USRDLL;HOTAMP3_EXPORTS + WIN64;NDEBUG;_WINDOWS;_MBCS;_USRDLL;HOTAMP3_EXPORTS + + + WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;HOTAMP3_EXPORTS + WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;HOTAMP3_EXPORTS + WIN64;_DEBUG;_WINDOWS;_MBCS;_USRDLL;HOTAMP3_EXPORTS + WIN64;NDEBUG;_WINDOWS;_MBCS;_USRDLL;HOTAMP3_EXPORTS + + + WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;HOTAMP3_EXPORTS + WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;HOTAMP3_EXPORTS + WIN64;_DEBUG;_WINDOWS;_MBCS;_USRDLL;HOTAMP3_EXPORTS + WIN64;NDEBUG;_WINDOWS;_MBCS;_USRDLL;HOTAMP3_EXPORTS + + + + + + + + + + + + + + + + + + + + + + + {3e0bfa8a-b86a-42e9-a33f-ec294f823f7f} + + + + + + \ No newline at end of file diff --git a/Src/Plugins/General/gen_hotkeys/gen_hotkeys.vcxproj.filters b/Src/Plugins/General/gen_hotkeys/gen_hotkeys.vcxproj.filters new file mode 100644 index 00000000..515b3109 --- /dev/null +++ b/Src/Plugins/General/gen_hotkeys/gen_hotkeys.vcxproj.filters @@ -0,0 +1,79 @@ + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + {b429b35f-cbbb-427f-b618-1e7c2ef4fa72} + + + {d06654b1-494e-468a-9e54-d70525ca2531} + + + {b03b7dae-2c85-4929-8e46-eb6b06b87635} + + + {36cdd73d-755e-40a7-945a-f6cad3b9379c} + + + + + Image Files + + + + + Ressource Files + + + \ No newline at end of file diff --git a/Src/Plugins/General/gen_hotkeys/version.rc2 b/Src/Plugins/General/gen_hotkeys/version.rc2 new file mode 100644 index 00000000..f151f4be --- /dev/null +++ b/Src/Plugins/General/gen_hotkeys/version.rc2 @@ -0,0 +1,39 @@ + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// +#include "..\..\..\Winamp/buildType.h" +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,97,0,0 + PRODUCTVERSION WINAMP_PRODUCTVER + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Winamp SA" + VALUE "FileDescription", "Winamp General Purpose Plug-in" + VALUE "FileVersion", "1,97,0,0" + VALUE "InternalName", "Nullsoft Global Hotkeys" + VALUE "LegalCopyright", "Copyright © 2003-2023 Winamp SA" + VALUE "LegalTrademarks", "Nullsoft and Winamp are trademarks of Winamp SA" + VALUE "OriginalFilename", "gen_hotkeys.dll" + VALUE "ProductName", "Winamp" + VALUE "ProductVersion", STR_WINAMP_PRODUCTVER + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/Src/Plugins/General/gen_hotkeys/wa_hotkeys.h b/Src/Plugins/General/gen_hotkeys/wa_hotkeys.h new file mode 100644 index 00000000..2877c01d --- /dev/null +++ b/Src/Plugins/General/gen_hotkeys/wa_hotkeys.h @@ -0,0 +1,91 @@ +#ifndef WA_HOTKEYS +#define WA_HOTKEYS + +/* +** #define IPC_GEN_HOTKEYS_ADD xxxx // pass a genHotkeysAddStruct * struct in data +** +** To get the IPC_GEN_HOTKEYS_ADD IPC number, you need to do this: +** +** genhotkeys_add_ipc=SendMessage(winampWindow,WM_WA_IPC,(WPARAM)&"GenHotkeysAdd",IPC_REGISTER_WINAMP_IPCMESSAGE); +** +** Then you can use: +** +** PostMessage(winampWindow,WM_WA_IPC,(WPARAM)&myGenHotkeysAddStruct,genhotkeys_add_ipc); +*/ + +//flags for the genHotkeysAddStruct struct +#define HKF_BRING_TO_FRONT 0x1 // calls SetForegroundWindow before sending the message +#define HKF_HWND_WPARAM 0x2 // sets wParam with Winamp's window handle +#define HKF_COPY 0x4 // copies returned text to the clipboard (using CF_TEXT) +#define HKF_PLPOS_WPARAM 0x8 // sets wParam with current pledit position +#define HKF_ISPLAYING_WL 0x10 // sets wParam to genHotkeysAddStruct's wParam if playing, lParam if not + // uses IPC_ISPLAYING to check if playing +#define HKF_SHOWHIDE 0x20 // brings Winamp to front or minimizes Winamp if already at front +#define HKF_NOSENDMSG 0x40 // don't send any message to the winamp window + +#define HKF_COPYW 0x80 // copies returned text to the clipboard (using CF_UNICODETEXT) +#define HKF_UNICODE_NAME 0x100 // set this when the 'name' is passed as a unicode string + +#define HKF_DISABLED 0x80000000 + +#include "WinDef.h" + +typedef struct { + char *name; //name that will appear in the Global Hotkeys preferences panel + DWORD flags; //one or more flags from above + UINT uMsg; //message that will be sent to winamp's main window (must always be !=NULL) + WPARAM wParam; //wParam that will be sent to winamp's main window + LPARAM lParam; //lParam that will be sent to winamp's main window + char *id; //unique string to identify this command - case insensitive + HWND wnd; //set the HWND to send message (or 0 for main winamp window) + + int extended[6]; //for future extension - always set to zero! +} genHotkeysAddStruct; + +/* +** Before calling this function you need to setup the genHotkeysAddStruct struct +** +** genHotkeysAddStruct test_key = {0}; // this will make the compiler zero the struct for you +** // though you can do it manually if you want to as well +** +** UINT test_ipc = 0; // this will hold the uMsg value to look for in the assigned window proc +** +** // then call the function to register the hotkey +** AddGlobalHotkey(&test_key,"Playback: Test key","Test_key",&test_ipc); +** +** void AddGlobalHotkey(genHotkeysAddStruct *hotkey, char* name, char* id, int* ipc){ +** UINT genhotkeys_add_ipc = 0; +** hotkey->wnd = 0; +** hotkey->flags = 0; +** hotkey->name = name; +** hotkey->id = id; +** *ipc = hotkey->uMsg = SendMessage(winampWindow,WM_WA_IPC,(WPARAM)&hotkey->id,IPC_REGISTER_WINAMP_IPCMESSAGE); +** +** genhotkeys_add_ipc=SendMessage(winampWindow,WM_WA_IPC,(WPARAM)&"GenHotkeysAdd",IPC_REGISTER_WINAMP_IPCMESSAGE); +** PostMessage(winampWindow,WM_WA_IPC,(WPARAM)hotkey,genhotkeys_add_ipc); +** } +*/ + +/* +** This shows how to detect the registered hotkey message +** then it's upto you what you do +** +** LRESULT CALLBACK WinampWnd(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam){ +** int ret = 0; +** +** // handles the hotkey for the option :o) +** if(uMsg == test_ipc){ +** // here you can do things needed for the hotkey you registered +** } +** +** // do stuff before the winamp proc has been called +** +** ret = CallWindowProc(WinampProc,hwnd,uMsg,wParam,lParam); +** +** // do other stuff after the winamp proc has been called +** // then return the value from CallWindowProc(..) +** return ret; +** } +*/ + +#endif \ No newline at end of file diff --git a/Src/Plugins/General/gen_hotkeys/x.bmp b/Src/Plugins/General/gen_hotkeys/x.bmp new file mode 100644 index 00000000..43ebcfc0 Binary files /dev/null and b/Src/Plugins/General/gen_hotkeys/x.bmp differ diff --git a/Src/Plugins/General/gen_ml/HeaderIconList.cpp b/Src/Plugins/General/gen_ml/HeaderIconList.cpp new file mode 100644 index 00000000..97b3e3f3 --- /dev/null +++ b/Src/Plugins/General/gen_ml/HeaderIconList.cpp @@ -0,0 +1,1115 @@ +#include "main.h" + +#include "../gen_ml/ml.h" +#include "../gen_ml/ml_ipc_0313.h" +#include "../Winamp/gen.h" + +#define WM_EX_GETREALLIST (WM_USER + 0x01) +#define WM_EX_UNLOCKREDRAW (WM_USER + 0x02) +#define WM_EX_UPDATESCROLLINFO (WM_USER + 0x03) +#define WM_EX_GETCOUNTPERPAGE (WM_USER + 0x04) + +#define LVN_EX_SIZECHANGED (LVN_LAST) + +#define IWF_NO 0x0000 +#define IWF_ALL 0x00FF +#define IWF_CONTAINER 0x0001 +#define IWF_LISTVIEW 0x0002 +#define IWF_HEADER 0x0004 +#define IWF_UPDATENOW 0x1000 + +BOOL +CopyListColumnToHeaderItem(const LVCOLUMNW *column, HDITEMW *item) +{ + if (NULL == column || NULL == item) + return FALSE; + + item->mask = 0; + if (0 != (LVCF_FMT & column->mask)) + { + item->mask |= HDI_FORMAT; + item->fmt = 0; + + switch((LVCFMT_JUSTIFYMASK & column->fmt)) + { + case LVCFMT_RIGHT: + item->fmt |= HDF_RIGHT; + break; + case LVCFMT_CENTER: + item->fmt |= HDF_CENTER; + break; + default: + item->fmt |= HDF_LEFT; + break; + } + + if (0 != (LVCFMT_IMAGE & column->fmt)) + item->fmt |= HDF_IMAGE; + + if (0 != (LVCFMT_BITMAP_ON_RIGHT & column->fmt)) + item->fmt |= HDF_BITMAP_ON_RIGHT; + } + + if (0 != (LVCF_WIDTH & column->mask)) + { + item->mask |= HDI_WIDTH; + item->cxy = column->cx; + } + + if (0 != (LVCF_TEXT & column->mask)) + { + item->mask |= HDI_TEXT; + item->pszText = column->pszText; + item->cchTextMax = column->cchTextMax; + } + + if (0 != (LVCF_IMAGE & column->mask)) + { + item->mask |= HDI_IMAGE; + item->iImage = column->iImage; + } + + if (0 != (LVCF_ORDER & column->mask)) + { + item->mask |= HDI_ORDER; + item->iOrder = column->iOrder; + } + + return TRUE; +} + +BOOL +CopyHeaderItemToListColumn(const HDITEMW *item, LVCOLUMNW *column) +{ + if (NULL == column || NULL == item) + return FALSE; + + column->mask = 0; + if (0 != (HDI_FORMAT& item->mask)) + { + column->mask |= LVCF_FMT ; + column->fmt = 0; + + switch((HDF_JUSTIFYMASK & item->fmt)) + { + case HDF_RIGHT: + column->fmt |= LVCFMT_RIGHT; + break; + case HDF_CENTER: + column->fmt |= LVCFMT_CENTER; + break; + default: + column->fmt |= LVCFMT_LEFT; + break; + } + + if (0 != (HDF_IMAGE & item->fmt)) + column->fmt |= LVCFMT_IMAGE; + + if (0 != (HDF_BITMAP_ON_RIGHT & item->fmt)) + column->fmt |= LVCFMT_BITMAP_ON_RIGHT; + } + + if (0 != (HDI_WIDTH & item->mask)) + { + column->mask |= LVCF_WIDTH; + column->cx = item->cxy; + } + + if (0 != (HDI_TEXT & item->mask)) + { + column->mask |= LVCF_TEXT; + column->pszText = item->pszText; + column->cchTextMax = item->cchTextMax; + } + + if (0 != (HDI_IMAGE & item->mask)) + { + column->mask |= LVCF_IMAGE; + column->iImage = item->iImage; + } + + if (0 != (HDI_ORDER & item->mask)) + { + column->mask |= LVCF_ORDER; + column->iOrder = item->iOrder; + } + + return TRUE; +} + +static BOOL UpdateScrollInfo(HWND hwnd, UINT fMask, BOOL bRedraw) +{ + HWND hwndList; + SCROLLINFO si; + BOOL bUpdateBars(FALSE); + + hwndList = GetDlgItem(hwnd,2); + + si.cbSize= sizeof(SCROLLINFO); + si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS; + + if (!hwndList || !GetScrollInfo(hwnd, SB_VERT, &si)) + return FALSE; + + if (SIF_RANGE & fMask) + { + RECT rc, rv; + + UINT nPage; + INT nMax; + BOOL bScrollVisible; + HDLAYOUT headerLayout; + WINDOWPOS headerPos; + + HWND hwndHeader = GetDlgItem(hwnd, 3); + if (!hwndHeader) + return FALSE; + + GetClientRect(hwnd, &rc); + ListView_GetViewRect(hwndList,&rv); + + headerLayout.prc = &rc; + headerLayout.pwpos = &headerPos; + + SendMessageW(hwndHeader, HDM_LAYOUT, 0, (LPARAM)&headerLayout); + + bScrollVisible = (si.nMax > 0 && si.nPage > 0 && (INT)si.nPage < si.nMax); + + nMax = rv.bottom - rv.top; + nPage = (rc.bottom - rc.top); + + if (si.nMax != nMax || si.nPage != nPage) + { + si.nMin = 0; + si.nMax = nMax; + si.nPage = nPage; + SetScrollInfo(hwnd, SB_VERT, &si, FALSE); + bUpdateBars = TRUE; + if (bScrollVisible != (si.nMax > 0 && si.nPage > 0 && (INT)si.nPage < si.nMax)) + { + MLSkinnedScrollWnd_UpdateBars(hwnd, bRedraw); + GetClientRect(hwnd, &rc); + + headerLayout.prc = &rc; + headerLayout.pwpos = &headerPos; + + SendMessageW(hwndHeader, HDM_LAYOUT, 0, (LPARAM)&headerLayout); + + headerPos.flags |= SWP_NOREDRAW; + SetWindowPos(hwndHeader, headerPos.hwndInsertAfter, headerPos.x, headerPos.y, + headerPos.cx, headerPos.cy, headerPos.flags); + + SetWindowPos(hwndList, NULL, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, + SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE | SWP_NOREDRAW); + + if (FALSE != bRedraw) + RedrawWindow(hwnd, NULL, NULL, RDW_INVALIDATE | RDW_ALLCHILDREN); + + bUpdateBars = FALSE; + } + } + + } + + if (SIF_POS & fMask) + { + POINT p; + if (FALSE != SendMessageW(hwndList, LVM_GETORIGIN, (WPARAM)0, (LPARAM)&p)) + { + if (p.y < si.nMin) + p.y = si.nMin; + + if (p.y > (si.nMax - (int)si.nPage)) + p.y = si.nMax - si.nPage; + + if (p.y != si.nPos) + { + si.nPos = p.y; + si.fMask = SIF_POS; + SetScrollInfo(hwnd, SB_VERT, &si, TRUE); + bUpdateBars = TRUE; + } + } + } + if (bUpdateBars) + MLSkinnedScrollWnd_UpdateBars(hwnd, bRedraw); + + return TRUE; +} + +static void +UpdateFontMetrics(HWND hwnd, BOOL redraw) +{ + HWND controlWindow; + RECT headerRect; + unsigned int windowStyle; + + + windowStyle = GetWindowLongPtr(hwnd, GWL_STYLE); + if(0 != (WS_VISIBLE & windowStyle)) + SetWindowLongPtrW(hwnd, GWL_STYLE, windowStyle & ~WS_VISIBLE); + + + controlWindow = GetDlgItem(hwnd, 3); + if (NULL != controlWindow) + { + MLSkinnedHeader_SetHeight(controlWindow, -1); + if (FALSE == GetWindowRect(controlWindow, &headerRect)) + SetRectEmpty(&headerRect); + } + else + SetRectEmpty(&headerRect); + + + controlWindow = GetDlgItem(hwnd, 2); + if (NULL != controlWindow) + { + + RECT rect; + if (FALSE != GetClientRect(hwnd, &rect)) + { + SetWindowPos(controlWindow, NULL, + rect.left, rect.top + headerRect.bottom - headerRect.top, + rect.right - rect.left, (rect.bottom - rect.top) - (headerRect.bottom - headerRect.top), + SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOREDRAW | SWP_NOCOPYBITS); + } + } + + + if (0 != (WS_VISIBLE & windowStyle)) + { + windowStyle = GetWindowLongPtr(hwnd, GWL_STYLE); + if (0 == (WS_VISIBLE & windowStyle)) + { + windowStyle |= WS_VISIBLE; + SetWindowLongPtrW(hwnd, GWL_STYLE, windowStyle); + } + + if (FALSE != redraw) + RedrawWindow(hwnd, NULL, NULL, RDW_INVALIDATE | RDW_ERASE | RDW_ALLCHILDREN); + } +} + +static HHOOK hook = NULL; +static HWND hwndToMonitor = NULL; + +typedef struct UpdateSelection +{ + BOOL active; + int iFrom; + int iTo; +}UpdateSelection; + + +typedef struct ListViewData +{ + WNDPROC prevWindowProc; + UpdateSelection updateSelection; +} ListViewData; + +#define LVCWP_NORMAL 0 +#define LVCWP_UPDATESCROLLINFO (1 << 0) + +static LRESULT CALLBACK HookProc(int nCode, WPARAM wParam, LPARAM lParam) +{ + MSG *pMsg = (MSG*)lParam; + if (pMsg->hwnd == hwndToMonitor) + { + switch(pMsg->message) + { + case WM_LBUTTONUP: + case WM_RBUTTONUP: + { + LRESULT result = CallNextHookEx(hook, nCode, wParam, lParam); + UnhookWindowsHookEx(hook); + hwndToMonitor = NULL; + hook = NULL; + return result; + } + case WM_MOUSEMOVE: + if ((MK_LBUTTON | MK_RBUTTON) & pMsg->wParam) + { + RECT rw; + POINTS pts(MAKEPOINTS(pMsg->lParam)); + POINT pt; + POINTSTOPOINT(pt, pts); + MapWindowPoints(pMsg->hwnd, HWND_DESKTOP, &pt, 1); + GetWindowRect(pMsg->hwnd, &rw); + if (pt.y < rw.top || pt.y > rw.bottom) + { + HWND hwndParent = GetParent(pMsg->hwnd); + if (hwndParent) + { + SCROLLINFO si; + si.cbSize = sizeof(SCROLLINFO); + si.fMask = SIF_RANGE | SIF_POS | SIF_PAGE; + if (GetScrollInfo(hwndParent, SB_VERT, &si)) + { + if ((si.nPos > si.nMin && pt.y < rw.top) || (si.nPos < (si.nMax - (INT)si.nPage) && pt.y > rw.bottom)) + { + SendMessageW(hwndParent, WM_SETREDRAW, FALSE, 0L); + LRESULT result = CallNextHookEx(hook, nCode, wParam, lParam); + PostMessageW(hwndParent, WM_EX_UPDATESCROLLINFO, SIF_POS, FALSE); + PostMessageW(hwndParent, WM_EX_UNLOCKREDRAW, IWF_CONTAINER | IWF_LISTVIEW | IWF_UPDATENOW, 0L); + return result; + } + } + } + } + SleepEx(1, TRUE); + } + break; + } + } + return CallNextHookEx(hook, nCode, wParam, lParam); +} +static void +ListView_ScheduleSelectionUpdate(HWND hwnd, int iFrom, int iTo) +{ + ListViewData *listView; + listView = (ListViewData*)(LONG_PTR)GetWindowLongPtrW(hwnd, GWLP_USERDATA); + if (NULL == listView) + return; + + listView->updateSelection.iFrom = iFrom; + listView->updateSelection.iTo = iTo; + listView->updateSelection.active = TRUE; +} + +static void +ListView_UpdateSelection(HWND hwnd, int iFrom, int iTo) +{ + HWND parentWindow; + LVITEM item; + int start, stop, i, lim; + + start = (int)SendMessageW(hwnd, LVM_GETSELECTIONMARK, 0, 0L); + stop = (int)SendMessageW(hwnd, LVM_GETNEXTITEM, iFrom - 1, LVNI_FOCUSED); + if (stop < start) + { + int tmp = start; + start = stop; + stop = tmp; + } + + item.state = 0; + item.stateMask = LVIS_SELECTED; + + if (start != iFrom) + { + if (start < iFrom) + { + i = start; + lim = iFrom; + } + else + { + i = iFrom; + lim = start; + } + + i--; + for(;;) + { + i = (int)SendMessageW(hwnd, LVM_GETNEXTITEM, (WPARAM)i, + (LPARAM)(LVNI_ALL | LVNI_SELECTED)); + if (-1 == i || i >= lim) + break; + + SendMessageW(hwnd, LVM_SETITEMSTATE, i, (LPARAM)&item); + } + } + if (stop < iTo) + { + i = stop + 1; + lim = iTo; + + i--; + for(;;) + { + i = (int)SendMessageW(hwnd, LVM_GETNEXTITEM, (WPARAM)i, + (LPARAM)(LVNI_ALL | LVNI_SELECTED)); + if (-1 == i || i > lim) + break; + + SendMessageW(hwnd, LVM_SETITEMSTATE, i, (LPARAM)&item); + } + } + + parentWindow = GetAncestor(hwnd, GA_PARENT); + if (NULL != parentWindow) + { + HWND notifyWindow; + + SendMessageW(parentWindow, WM_SETREDRAW, TRUE, 0L); + RedrawWindow(parentWindow, NULL, NULL, RDW_INVALIDATE | RDW_ALLCHILDREN); + + notifyWindow = GetAncestor(parentWindow, GA_PARENT); + if (NULL != notifyWindow) + { + NMLVODSTATECHANGE stateChange; + stateChange.hdr.idFrom = GetWindowLongPtrW(parentWindow, GWLP_ID); + stateChange.hdr.hwndFrom = parentWindow; + stateChange.hdr.code = LVN_ODSTATECHANGED; + stateChange.iFrom = start; + stateChange.iTo = stop; + stateChange.uNewState = LVIS_SELECTED; + stateChange.uOldState = 0; + SendMessageW(notifyWindow, WM_NOTIFY, stateChange.hdr.idFrom, (LPARAM)&stateChange); + } + } +} +static LRESULT +ListView_CallWindowProc(ListViewData *listView, HWND hwnd, unsigned int uMsg, WPARAM wParam, LPARAM lParam, + unsigned int flags) +{ + LRESULT result; + + result = CallWindowProcW(listView->prevWindowProc, hwnd, uMsg, wParam, lParam); + + if (FALSE != listView->updateSelection.active) + { + listView->updateSelection.active = FALSE; + ListView_UpdateSelection(hwnd, listView->updateSelection.iFrom, listView->updateSelection.iTo); + } + + if (0 != (LVCWP_UPDATESCROLLINFO & flags)) + { + HWND hwndParent; + hwndParent = GetAncestor(hwnd, GA_PARENT); + if (NULL != hwndParent) + UpdateScrollInfo(hwndParent, SIF_POS, TRUE); + } + + return result; +} + +static LRESULT WINAPI +ListView_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + ListViewData *listView; + listView = (ListViewData*)(LONG_PTR)GetWindowLongPtrW(hwnd, GWLP_USERDATA); + + if (NULL == listView || + NULL == listView->prevWindowProc) + { + return DefWindowProcW(hwnd, uMsg, wParam, lParam); + } + + switch(uMsg) + { + case WM_DESTROY: + SetWindowLongPtrW(hwnd, GWLP_USERDATA, NULL); + CallWindowProcW(listView->prevWindowProc, hwnd, uMsg, wParam, lParam); + free(listView); + return 0; + case WM_KEYDOWN: + switch(wParam) + { + case VK_PRIOR: + case VK_UP: + case VK_HOME: + case VK_NEXT: + case VK_END: + case VK_DOWN: + case VK_LEFT: + case VK_RIGHT: + return ListView_CallWindowProc(listView, hwnd, uMsg, wParam, lParam, LVCWP_UPDATESCROLLINFO); + } + break; + case WM_TIMER: + if (43 == wParam) + return ListView_CallWindowProc(listView, hwnd, uMsg, wParam, lParam, LVCWP_UPDATESCROLLINFO); + break; + case WM_HSCROLL: + case WM_VSCROLL: + case WM_MOUSEWHEEL: + case LVM_ENSUREVISIBLE: + return ListView_CallWindowProc(listView, hwnd, uMsg, wParam, lParam, LVCWP_UPDATESCROLLINFO); + case WM_LBUTTONDOWN: + case WM_RBUTTONDOWN: + { + LVHITTESTINFO ht; + ht.pt.x = GET_X_LPARAM(lParam); + ht.pt.y = GET_Y_LPARAM(lParam); + if (-1 == SendMessageW(hwnd, LVM_HITTEST, 0, (LPARAM)&ht) || 0 == (LVHT_ONITEM & ht.flags)) + { + hwndToMonitor = hwnd; + hook = SetWindowsHookEx(WH_MSGFILTER, HookProc, NULL, GetCurrentThreadId()); + } + } + break; + } + + return ListView_CallWindowProc(listView, hwnd, uMsg, wParam, lParam, LVCWP_NORMAL); +} + + +static LRESULT +HeaderIconList_OnCreate(HWND hwnd, CREATESTRUCT *createStruct) +{ + HWND hwndList, hwndHeader; + MLSKINWINDOW m; + RECT rc; + DWORD style; + + m.skinType = SKINNEDWND_TYPE_SCROLLWND; + m.style = SWS_USESKINFONT | SWS_USESKINCOLORS | SWS_USESKINCURSORS; + m.hwndToSkin = hwnd; + MLSkinWindow(g_hwnd, &m); + + SetScrollRange(hwnd, SB_VERT, 0, 0, FALSE); + MLSkinnedScrollWnd_UpdateBars(hwnd, FALSE); + + GetClientRect(hwnd, &rc); + + style = WS_CLIPSIBLINGS | WS_CHILD | WS_VISIBLE | HDS_BUTTONS | HDS_FULLDRAG; + hwndHeader = CreateWindowExW(WS_EX_NOPARENTNOTIFY, WC_HEADERW, L"", style, 0, 0, rc.right - rc.left, 20, hwnd, (HMENU)3,0,0); + if (NULL != hwndHeader) + { + m.hwndToSkin = hwndHeader; + m.skinType = SKINNEDWND_TYPE_HEADER; + m.style = SWS_USESKINFONT | SWS_USESKINCOLORS | SWS_USESKINCURSORS; + MLSkinWindow(g_hwnd, &m); + } + + style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_CHILD | WS_TABSTOP | WS_VISIBLE | + LVS_ICON | LVS_SHOWSELALWAYS | LVS_OWNERDATA | LVS_NOLABELWRAP; + + hwndList = CreateWindowExW(WS_EX_NOPARENTNOTIFY, WC_LISTVIEWW,L"",style,0, 20, rc.right - rc.left, rc.bottom - rc.top - 20, hwnd,(HMENU)2,0,0); + if (NULL != hwndList) + { + ListViewData *listView; + listView = (ListViewData*)calloc(1, sizeof(ListViewData)); + if (NULL != listView) + { + listView->prevWindowProc = (WNDPROC)(LONG_PTR)SetWindowLongPtrW(hwndList, GWLP_WNDPROC, (LONGX86)(LONG_PTR)ListView_WindowProc); + SetWindowLongPtrW(hwndList,GWLP_USERDATA, (LONGX86)(LONG_PTR)listView); + } + + SendMessageW(hwndList, LVM_SETEXTENDEDLISTVIEWSTYLE, LVS_EX_INFOTIP, LVS_EX_INFOTIP); + + if (NULL != hwndHeader) + SetWindowPos(hwndHeader, hwndList, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); + + m.skinType = SKINNEDWND_TYPE_LISTVIEW; + m.hwndToSkin = hwndList; + m.style = SWS_USESKINFONT | SWS_USESKINCOLORS | SWS_USESKINCURSORS | SWLVS_FULLROWSELECT | SWLVS_DOUBLEBUFFER; + MLSkinWindow(g_hwnd, &m); + MLSkinnedScrollWnd_SetMode(hwndList, SCROLLMODE_STANDARD); + MLSkinnedScrollWnd_ShowHorzBar(hwndList, FALSE); + MLSkinnedScrollWnd_ShowVertBar(hwndList, FALSE); + } + + UpdateFontMetrics(hwnd, FALSE); + + return 0; +} + +static void +HeaderIconList_OnDestroy(HWND hwnd) +{ +} + +// TODO need to finish off the butting up to edge handling +static void +HeaderIconList_OnWindowPosChanged(HWND hwnd, WINDOWPOS *windowPos) +{ + RECT rc; + HWND hwndHeader, hwndList; + hwndHeader = GetDlgItem(hwnd, 3); + hwndList = GetDlgItem(hwnd, 2); + BOOL bRedraw; + + bRedraw = (0 == (SWP_NOREDRAW & windowPos->flags)); + + if ((SWP_NOSIZE | SWP_NOMOVE) == ((SWP_NOSIZE | SWP_NOMOVE | SWP_FRAMECHANGED) & windowPos->flags)) + return; + + hwndHeader = GetDlgItem(hwnd, 3); + hwndList = GetDlgItem(hwnd, 2); + if (hwndHeader && hwndList) + { + HDLAYOUT headerLayout; + WINDOWPOS headerPos; + + GetClientRect(hwnd, &rc); + + /*SCROLLINFO si; + si.cbSize= sizeof(SCROLLINFO); + si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS; + BOOL got = GetScrollInfo(hwnd, SB_VERT, &si); + BOOL bScrollVisible = (si.nMax > 0 && si.nPage > 0 && (INT)si.nPage <= si.nMax);*/ + + headerLayout.prc = &rc; + headerLayout.pwpos = &headerPos; + + if (FALSE != SendMessageW(hwndHeader, HDM_LAYOUT, 0, (LPARAM)&headerLayout)) + { + headerPos.flags |= ((SWP_NOREDRAW | SWP_NOCOPYBITS) & windowPos->flags); + SetWindowPos(hwndHeader, headerPos.hwndInsertAfter, headerPos.x, headerPos.y, + headerPos.cx, headerPos.cy, headerPos.flags); + } + + SetWindowPos(hwndList, NULL, rc.left, rc.top, rc.right - rc.left/* + (bScrollVisible ? 16 : 0)*/, rc.bottom - rc.top, + SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW | SWP_NOCOPYBITS); + + UpdateScrollInfo(hwnd, SIF_RANGE | SIF_POS, bRedraw); + } + + NMHDR hdr; + hdr.code = LVN_EX_SIZECHANGED; + hdr.hwndFrom = hwnd; + hdr.idFrom = GetWindowLongPtrW(hwnd,GWLP_ID); + SendMessageW(GetParent(hwnd), WM_NOTIFY, hdr.idFrom, (LPARAM)&hdr); +} + +static void +HeaderIconList_OnVertScroll(HWND hwnd, INT actionLayout, INT trackPosition, HWND scrollBar) +{ + SCROLLINFO si={sizeof(si),SIF_TRACKPOS | SIF_POS | SIF_RANGE | SIF_PAGE,0}; + if (GetScrollInfo(hwnd,SB_VERT,&si)) + { + int pos(0), itemHeight; + BOOL bRedraw(TRUE); + HWND hwndList; + + hwndList = GetDlgItem(hwnd, 2); + if (!hwndList) + return; + + itemHeight = HIWORD(SendMessageW(hwndList, LVM_GETITEMSPACING, 0, 0L)); + + if (si.nPos > (si.nMax - (INT)si.nPage)) + si.nPos = si.nMax - si.nPage; + + switch(actionLayout) + { + case SB_TOP: pos = si.nMin; break; + case SB_BOTTOM: pos = si.nMax; break; + case SB_LINEDOWN: pos = si.nPos + itemHeight/2; break; + case SB_LINEUP: pos = si.nPos - itemHeight/2; break; + case SB_PAGEDOWN: pos = si.nPos + si.nPage - (((INT)si.nPage > itemHeight && 0 == ((INT)si.nPage%itemHeight)) ? itemHeight : 0);break; + case SB_PAGEUP: pos = si.nPos - si.nPage - (((INT)si.nPage > itemHeight && 0 == ((INT)si.nPage%itemHeight)) ? itemHeight : 0);break; + case SB_THUMBPOSITION: + case SB_THUMBTRACK: + { + POINT pt; + if (!SendMessageW(hwndList, LVM_GETORIGIN, 0, (LPARAM)&pt)) + return; + si.nPos = pt.y; + pos = si.nTrackPos; + bRedraw = FALSE; + } + break; + case SB_ENDSCROLL: MLSkinnedScrollWnd_UpdateBars(hwnd, TRUE); return; + default: pos = si.nPos; + } + + if (pos < si.nMin) pos = si.nMin; + if (pos > (si.nMax - (INT)si.nPage)) pos = si.nMax - si.nPage; + if (pos != si.nPos) + { + BOOL br; + if (!bRedraw) + { + UpdateWindow(GetDlgItem(hwnd, 3)); + UpdateWindow(hwnd); + SendMessageW(hwnd, WM_SETREDRAW, FALSE, 0L); + } + br = (BOOL)SendMessageW(hwndList, LVM_SCROLL, 0, pos - si.nPos); + if (!bRedraw) SendMessageW(hwnd, WM_SETREDRAW, TRUE, 0L); + if (br) + { + si.fMask = SIF_POS; + si.nPos = pos; + SetScrollInfo(hwnd, SB_VERT, &si, FALSE); + + if (!bRedraw) + InvalidateRect(hwndList, NULL, TRUE); + } + } + } +} + +static void +HeaderIconList_OnSetFont(HWND hwnd, HFONT font, BOOL redraw) +{ + if (0 == (SWS_USESKINFONT & MLSkinnedWnd_GetStyle(hwnd))) + { + HWND controlWindow; + + controlWindow = GetDlgItem(hwnd,3); + if (NULL != controlWindow) + SendMessageW(controlWindow, WM_SETFONT, (WPARAM)font, MAKELPARAM(0, 0L)); + + controlWindow = GetDlgItem(hwnd,2); + if (NULL != controlWindow) + SendMessageW(controlWindow, WM_SETFONT, (WPARAM)font, MAKELPARAM(0, 0L)); + + UpdateFontMetrics(hwnd, redraw); + } +} + +static LRESULT +HeaderIconList_OnGetFont(HWND hwnd) +{ + HWND listWindow; + + listWindow = GetDlgItem(hwnd, 2); + if (NULL != listWindow) + return SendMessageW(listWindow, WM_GETFONT, 0, 0L); + + return DefWindowProcW(hwnd, WM_GETFONT, 0, 0L); +} + +static void +HeaderIconList_OnSetRedraw(HWND hwnd, BOOL enableRedraw) +{ + HWND childWindow; + + DefWindowProcW(hwnd, WM_SETREDRAW, enableRedraw, 0L); + + childWindow = GetDlgItem(hwnd, 3); + if (NULL != childWindow) + { + SendMessage(childWindow, WM_SETREDRAW, enableRedraw, 0L); + if (FALSE != enableRedraw) + InvalidateRect(childWindow, NULL, TRUE); + } + + childWindow = GetDlgItem(hwnd, 2); + if (NULL != childWindow) + { + SendMessage(childWindow, WM_SETREDRAW, enableRedraw, 0L); + if (FALSE != enableRedraw) + InvalidateRect(childWindow, NULL, TRUE); + } +} + +static void +HeaderIconList_OnSkinUpdated(HWND hwnd, BOOL notifyChildren, BOOL redraw) +{ + UpdateFontMetrics(hwnd, redraw); +} + +static LRESULT +HeaderIconList_OnDisplaySort(HWND hwnd, int sortIndex, BOOL ascendingOrder) +{ + HWND headerWindow; + + headerWindow = GetDlgItem(hwnd, 3); + if (NULL == headerWindow) + return 0; + + return SENDMLIPC(headerWindow, ML_IPC_SKINNEDHEADER_DISPLAYSORT, MAKEWPARAM(sortIndex, ascendingOrder)); +} + +static LRESULT +HeaderIconList_OnGetSort(HWND hwnd) +{ + HWND headerWindow; + + headerWindow = GetDlgItem(hwnd, 3); + if (NULL == headerWindow) + return 0; + + return SENDMLIPC(headerWindow, ML_IPC_SKINNEDHEADER_GETSORT, 0); +} + + +static LRESULT +HeaderIconList_OnMediaLibraryIPC(HWND hwnd, INT msg, INT_PTR param) +{ + switch(msg) + { + case ML_IPC_SKINNEDWND_SKINUPDATED: HeaderIconList_OnSkinUpdated(hwnd, LOWORD(param), HIWORD(param)); break; + case ML_IPC_SKINNEDLISTVIEW_DISPLAYSORT: return HeaderIconList_OnDisplaySort(hwnd, LOWORD(param), HIWORD(param)); + case ML_IPC_SKINNEDLISTVIEW_GETSORT: return HeaderIconList_OnGetSort(hwnd); + } + return 0; +} + +static LRESULT CALLBACK HeaderIconList(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { + switch(uMsg) + { + case WM_CREATE: return HeaderIconList_OnCreate(hwnd, (CREATESTRUCT*)lParam); + case WM_DESTROY: HeaderIconList_OnDestroy(hwnd); return 0; + case WM_WINDOWPOSCHANGED: HeaderIconList_OnWindowPosChanged(hwnd, (WINDOWPOS*)lParam); return 0; + case WM_VSCROLL: HeaderIconList_OnVertScroll(hwnd, LOWORD(wParam), (short)HIWORD(wParam), (HWND)lParam); return 0; + case WM_ERASEBKGND: return 1; + case WM_SETFONT: HeaderIconList_OnSetFont(hwnd, (HFONT)wParam, LOWORD(lParam)); return 0; + case WM_GETFONT: return HeaderIconList_OnGetFont(hwnd); + case WM_SETREDRAW: HeaderIconList_OnSetRedraw(hwnd, (BOOL)wParam); return 0; + case WM_TIMER: + if (43 == wParam) + { + KillTimer(hwnd, 43); + PostMessageW(hwnd, WM_EX_UPDATESCROLLINFO, SIF_POS, FALSE); + PostMessageW(hwnd, WM_EX_UNLOCKREDRAW, IWF_CONTAINER | IWF_LISTVIEW | IWF_UPDATENOW, 0L); + } + break; + + case WM_EX_UPDATESCROLLINFO: + return UpdateScrollInfo(hwnd, (UINT)wParam, (BOOL)lParam); + + case WM_EX_UNLOCKREDRAW: + + SendMessageW(hwnd, WM_SETREDRAW, TRUE, 0L); + if (IWF_CONTAINER & wParam) { InvalidateRect(hwnd, NULL, FALSE); if (IWF_UPDATENOW & wParam) UpdateWindow(hwnd); } + if (IWF_LISTVIEW & wParam) + { + HWND hwndList = GetDlgItem(hwnd, 2); + if(hwndList) { InvalidateRect(hwndList, NULL, TRUE); if (IWF_UPDATENOW & wParam) UpdateWindow(hwndList); } + } + if (IWF_HEADER & wParam) + { + HWND hwndHeader = GetDlgItem(hwnd, 3); + if(hwndHeader) { InvalidateRect(hwndHeader, NULL, TRUE); if (IWF_UPDATENOW & wParam) UpdateWindow(hwndHeader); } + } + break; + case LVM_GETHEADER: + return (LRESULT)GetDlgItem(hwnd,3); + case LVM_DELETECOLUMN: + { + HWND headerWindow; + headerWindow = GetDlgItem(hwnd,3); + if (NULL != headerWindow) + return SendMessageW(headerWindow, HDM_DELETEITEM, wParam, 0L); + } + return FALSE; + case LVM_GETCOLUMNWIDTH: + { + HWND headerWindow; + headerWindow = GetDlgItem(hwnd,3); + if (NULL != headerWindow) + { + HDITEMW headerItem; + headerItem.mask = HDI_WIDTH; + if (FALSE != SendMessageW(headerWindow, HDM_GETITEM, wParam, (LPARAM)&headerItem)) + return headerItem.cxy; + } + } + return 0; + case LVM_INSERTCOLUMNA: + case LVM_INSERTCOLUMNW: + { + LVCOLUMNW *listColumn = (LVCOLUMNW*)lParam; + HDITEMW headerItem; + HWND headerWindow; + headerWindow = GetDlgItem(hwnd,3); + if (NULL == headerWindow) + return -1; + + if (FALSE == CopyListColumnToHeaderItem(listColumn, &headerItem)) + return -1; + + if (0 == (HDI_FORMAT & headerItem.mask)) + { + headerItem.mask |= HDI_FORMAT; + headerItem.fmt = HDF_LEFT; + } + + return SendMessageW(headerWindow, + (LVM_INSERTCOLUMNW == uMsg) ? HDM_INSERTITEMW : HDM_INSERTITEMA, + wParam, (LPARAM)&headerItem); + } + break; + case LVM_SETITEMCOUNT: + { + HWND hwndList = GetDlgItem(hwnd, 2); + if (hwndList) + { + LRESULT lr = ListView_WindowProc(hwndList, uMsg, wParam, lParam); + UpdateScrollInfo(hwnd, SIF_RANGE | SIF_POS, TRUE); + return lr; + } + } + break; + case LVM_GETCOLUMNW: + case LVM_GETCOLUMNA: + { + LVCOLUMNW *l = (LVCOLUMNW *)lParam; + HDITEMW h; + HWND headerWindow; + + headerWindow = GetDlgItem(hwnd, 3); + if (NULL == headerWindow) + return FALSE; + + if (FALSE == CopyListColumnToHeaderItem(l, &h)) + return FALSE; + + if(!SendMessageW(headerWindow, + (LVM_GETCOLUMNW == uMsg) ? HDM_GETITEMW : HDM_GETITEMA, + wParam, + (LPARAM)&h)) + { + return FALSE; + } + + if (FALSE == CopyHeaderItemToListColumn(&h, l)) + return FALSE; + } + return TRUE; + + case LVM_SETCOLUMNW: + case LVM_SETCOLUMNA: + { + LVCOLUMNW *l = (LVCOLUMNW *)lParam; + HDITEMW h; + HWND headerWindow; + + headerWindow = GetDlgItem(hwnd, 3); + if (NULL == headerWindow) + return FALSE; + + if (FALSE == CopyListColumnToHeaderItem(l, &h)) + return FALSE; + + if(!SendMessageW(headerWindow, + (LVM_SETCOLUMNW == uMsg) ? HDM_SETITEMW : HDM_SETITEMA, + wParam, (LPARAM)&h)) + { + return FALSE; + } + + if (FALSE == CopyHeaderItemToListColumn(&h, l)) + return FALSE; + } + return TRUE; + case LVM_SETCOLUMNWIDTH: + { + HWND headerWindow; + HDITEMW headerItem; + + headerWindow = GetDlgItem(hwnd, 3); + if (NULL == headerWindow) + return FALSE; + + if (LVSCW_AUTOSIZE == lParam) + return FALSE; + + if (LVSCW_AUTOSIZE_USEHEADER == lParam) + return FALSE; + + headerItem.mask = HDI_WIDTH; + headerItem.cxy = (int)lParam; + + return SendMessageW(headerWindow, HDM_SETITEMW, (WPARAM)wParam, (LPARAM)&headerItem); + } + break; + case WM_NOTIFY: + { + BOOL bPost(FALSE); + LPNMHDR l=(LPNMHDR)lParam; + if(l->idFrom == 2) + { + switch(l->code) + { + case LVN_ODFINDITEMA: + case LVN_ODFINDITEMW: + bPost = TRUE; + break; + case LVN_ODSTATECHANGED: + { + NMLVODSTATECHANGE *stateChange; + stateChange = (NMLVODSTATECHANGE*)lParam; + + if (0 != (LVIS_SELECTED & (stateChange->uNewState ^ stateChange->uOldState))) + { + + SendMessageW(hwnd, WM_SETREDRAW, FALSE, 0L); + ListView_ScheduleSelectionUpdate(l->hwndFrom, stateChange->iFrom,stateChange->iTo); + } + + return 0; + } + break; + } + l->idFrom = GetWindowLong(hwnd,GWL_ID); + l->hwndFrom = hwnd; + LRESULT lr = SendMessageW(GetParent(hwnd),uMsg,l->idFrom,lParam); + if (bPost) + { + UpdateWindow(GetDlgItem(hwnd, 3)); + SendMessageW(hwnd, WM_SETREDRAW, FALSE, 0L); + PostMessageW(hwnd, WM_EX_UPDATESCROLLINFO, SIF_POS, TRUE); + PostMessageW(hwnd, WM_EX_UNLOCKREDRAW, IWF_CONTAINER | IWF_LISTVIEW, 0L); + } + return lr; + } + else if(l->idFrom == 3) { + switch(l->code) { + case HDN_ITEMCLICKA: + case HDN_ITEMCLICKW: + { + NMHEADER *nm = (NMHEADER*)lParam; + HWND hwndParent; + hwndParent = GetParent(hwnd); + if (hwndParent) + { + wParam = GetWindowLongPtrW(hwnd,GWLP_ID); + if(nm->iButton == 0) { // left click + NMLISTVIEW p = {{hwnd, wParam, LVN_COLUMNCLICK},-1,nm->iItem,0}; + return SendMessageW(hwndParent,WM_NOTIFY,wParam,(LPARAM)&p); + } else if(nm->iButton == 1) { // right click + NMHDR p = {nm->hdr.hwndFrom,wParam,NM_RCLICK}; + return SendMessageW(hwndParent,WM_NOTIFY,wParam,(LPARAM)&p); + } + } + + } + break; + } + return SendMessageW(GetParent(hwnd),uMsg, wParam,lParam); + } + } + break; + case WM_EX_GETREALLIST: + return (LRESULT)GetDlgItem(hwnd, 2); + case WM_EX_GETCOUNTPERPAGE: + { + HWND hwndList; + RECT rc; + hwndList = GetDlgItem(hwnd, 2); + if (hwndList) + { + GetClientRect(hwndList, &rc); + OffsetRect(&rc, -rc.left, -rc.top); + DWORD spacing = (DWORD)SendMessageW(hwndList, LVM_GETITEMSPACING, 0, 0L); + if (LOWORD(spacing) && HIWORD(spacing)) + { + return (rc.right/LOWORD(spacing)) * (rc.bottom /HIWORD(spacing) + ((rc.bottom%HIWORD(spacing)) ? 1 : 0)); + } + + } + } + return 0; + + case WM_ML_IPC: + return HeaderIconList_OnMediaLibraryIPC(hwnd, (INT)lParam, (INT_PTR)wParam); + + default: + if(uMsg >= LVM_FIRST && uMsg < LVM_FIRST + 0x100) + { + HWND hwndList = GetDlgItem(hwnd,2); + if (hwndList) return ListView_WindowProc(hwndList, uMsg, wParam, lParam); + } + break; + } + return DefWindowProcW(hwnd,uMsg,wParam,lParam); +} + + +void InitHeaderIconList() { + WNDCLASSW wc = {0, }; + + if (GetClassInfoW(plugin.hDllInstance, L"HeaderIconList", &wc)) return; + wc.style = CS_DBLCLKS; + wc.lpfnWndProc = HeaderIconList; + wc.hInstance = plugin.hDllInstance; + wc.lpszClassName = L"HeaderIconList"; + RegisterClassW(&wc); +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/IPC.cpp b/Src/Plugins/General/gen_ml/IPC.cpp new file mode 100644 index 00000000..cc85ad2c --- /dev/null +++ b/Src/Plugins/General/gen_ml/IPC.cpp @@ -0,0 +1,1588 @@ +#include "main.h" +#include +#include +#include +#include "./ml.h" +#include "../winamp/wa_ipc.h" +#include "../winamp/ipc_pe.h" +#include "./resource.h" +#include "./comboskin.h" +#include "./childwnd.h" +#include "./sendto.h" +#include "./navigation.h" +#include "../nu/AutoWide.h" +#include "../nu/ns_wc.h" +#include "api__gen_ml.h" + +#include "./ml_ipc_0313.h" +#include "./ml_imageloader.h" +#include "./ml_imagefilter.h" +#include "./ml_imagelist.h" +#include "./ml_rating.h" +#include "./ml_ratingcolumn.h" +#include "./ml_cloud.h" +#include "./ml_cloudcolumn.h" +#include "./skinning.h" +#include "./fileview.h" + + +extern HWND m_curview_hwnd; +extern "C" HWND g_ownerwnd; +int queryEditOther(HWND hwnd, char *query, char *viewname, int mode); + +extern C_ItemList m_plugins; +extern HNAVCTRL hNavigation; +extern HMLIMGFLTRMNGR hmlifMngr; +extern HMLIMGLST hmlilRating; +extern HMLIMGLST hmlilCloud; + +static wchar_t playBuf[64], enqueueBuf[64]; + +#define WEBINFO_PREFFERED_HEIGHT 100 + +#define MAX_MENU_DEPTH 10 + +typedef struct _NAVMENUBUILDER +{ + HMENU hMenu[MAX_MENU_DEPTH]; + HNAVITEM hParent[MAX_MENU_DEPTH]; + INT level; + NAVITEM_I nis; + INT maxNum; + INT offset; + INT counter; +} NAVMENUBUILDER; + +static INT GetNavImageIndexFromTag(HMLIMGLST hmlil, INT_PTR tag) +{ + if (!hmlil) return -1; + + switch(tag) + { + case MLTREEIMAGE_NONE: + case MLTREEIMAGE_DEFAULT: + case MLTREEIMAGE_BRANCH: + return -1; + case MLTREEIMAGE_BRANCH_EXPANDED: + case MLTREEIMAGE_BRANCH_COLLAPSED: + case MLTREEIMAGE_BRANCH_NOCHILD: + break; + } + return MLImageListI_GetIndexFromTag(hmlil, tag); +} + +static BOOL CALLBACK EnumerateNavItemsCB(HNAVITEM hItem, LPARAM lParam) +{ + NAVMENUBUILDER *pnmb; + HNAVITEM hParent; + if (!lParam || !hItem) return FALSE; + + pnmb = (NAVMENUBUILDER*)lParam; + pnmb->counter++; + if (pnmb->maxNum > 0 && pnmb->counter == pnmb->maxNum) return FALSE; + if (!NavItemI_GetIndirect(hItem, &pnmb->nis)) return FALSE; + if (pnmb->nis.pszText) FixAmpsW(pnmb->nis.pszText, pnmb->nis.cchTextMax); + + hParent = NavItemI_GetParent(hItem); + while (hParent != pnmb->hParent[pnmb->level]) + { + pnmb->level--; + if (pnmb->level < 0) return FALSE; + } + + if (NIS_HASCHILDREN_I & pnmb->nis.style) + { + pnmb->level++; + if (pnmb->level == MAX_MENU_DEPTH) return FALSE; + + pnmb->hMenu[pnmb->level] = CreatePopupMenu(); + pnmb->hParent[pnmb->level] = hItem; + + // this will add a separator into the parent menu if we have a more than 1 child menu (ml_pmp can do this) + if(pnmb->level > 1 && GetMenuItemCount(pnmb->hMenu[pnmb->level-1]) == 1) AppendMenuW(pnmb->hMenu[pnmb->level-1], MF_SEPARATOR, 0, 0); + AppendMenuW(pnmb->hMenu[pnmb->level-1], MF_ENABLED | MF_STRING | MF_POPUP, (UINT_PTR)pnmb->hMenu[pnmb->level], pnmb->nis.pszText); + AppendMenuW(pnmb->hMenu[pnmb->level], MF_ENABLED | MF_STRING, pnmb->nis.id + pnmb->offset, pnmb->nis.pszText); + } + else + { + // if we've got a 'root' item for the current level then add a separator + if(pnmb->level && GetMenuItemCount(pnmb->hMenu[pnmb->level]) == 1) AppendMenuW(pnmb->hMenu[pnmb->level], MF_SEPARATOR, 0, 0); + AppendMenuW(pnmb->hMenu[pnmb->level], MF_ENABLED | MF_STRING, pnmb->nis.id + pnmb->offset, pnmb->nis.pszText); + } + + return TRUE; +} + +static INT_PTR getPlRating(HWND hwndML, INT_PTR plindex) +{ + const wchar_t *filename = (const wchar_t *)SendMessage(plugin.hwndParent, WM_WA_IPC, plindex, IPC_GETPLAYLISTFILEW); + if (!filename) return 0; + return SendMessage(hwndML, WM_ML_IPC, (WPARAM)filename, ML_IPC_GET_FILE_RATINGW); +} + +static INT_PTR SetPlRating(HWND hwndML, int plindex, int rating) +{ + const wchar_t *filename = (const wchar_t *)SendMessage(plugin.hwndParent, WM_WA_IPC, plindex, IPC_GETPLAYLISTFILEW); + if (!filename) return 0; + file_set_ratingW data; + data.fileName = filename; + data.newRating = rating; + return (INT_PTR)SendMessage(hwndML, WM_ML_IPC, (WPARAM)&data, ML_IPC_SET_FILE_RATINGW); +} + +INT_PTR IPC_PL_SetRating(HWND hwndML, INT_PTR param) +{ + pl_set_rating *psr = (pl_set_rating *)param; + if (psr) return SetPlRating(hwndML, psr->plentry, psr->rating); + return 0; +} + +static INT_PTR IPC_InsertView(UINT msg, INT_PTR param) +{ + INT insertAfterId = 0, parentId = 0; + BOOL converted = FALSE; + HNAVITEM hItem = 0, hParentItem = 0, hInsertAfter = 0; + NAVITEM_I nis = {0}; + + switch(msg) + { + case ML_IPC_TREEITEM_ADD: + case ML_IPC_TREEITEM_INSERT: + if (((MLTREEITEM*)param)->title) + { + nis.pszText = AutoWideDup(((MLTREEITEM*)param)->title); + converted = TRUE; + } + case ML_IPC_TREEITEM_INSERTW: + case ML_IPC_TREEITEM_ADDW: + if (ML_IPC_TREEITEM_ADD == msg || ML_IPC_TREEITEM_ADDW == msg) + nis.id = (INT)((MLTREEITEMW*)param)->id; + else + insertAfterId= (INT)((MLTREEITEMW*)param)->id; + + if (!converted) + nis.pszText = ((MLTREEITEMW*)param)->title; + + parentId = (INT)((MLTREEITEMW*)param)->parentId; + nis.style |= (((MLTREEITEMW*)param)->hasChildren) ? NIS_HASCHILDREN_I : 0; + nis.iImage = ((MLTREEITEMW*)param)->imageIndex; + break; + case ML_IPC_ADDTREEITEM_EX: + nis.iImage = ((mlAddTreeItemStructEx*)param)->imageIndex; + case ML_IPC_ADDTREEITEM: + if (((mlAddTreeItemStruct*)param)->title) + { + nis.pszText = AutoWideDup(((mlAddTreeItemStruct*)param)->title); + converted = TRUE; + } + parentId= (INT)((mlAddTreeItemStruct*)param)->parent_id; + nis.style |= (((mlAddTreeItemStruct*)param)->has_children) ? NIS_HASCHILDREN_I : 0; + break; + } + + nis.iImage = GetNavImageIndexFromTag(NavCtrlI_GetImageList(hNavigation), nis.iImage); + nis.iSelectedImage = nis.iImage; + nis.styleMask |= NIS_HASCHILDREN_I; + + if (nis.id) nis.mask |= NIMF_ITEMID_I; + if (nis.iImage) nis.mask |= NIMF_IMAGE_I; + if (nis.iSelectedImage) nis.mask |= NIMF_IMAGESEL_I; + if (nis.style) nis.mask |= NIMF_STYLE_I; + if (nis.pszText) nis.mask |= NIMF_TEXT_I; + if (nis.state) nis.mask |= NIMF_STATE_I; + + hInsertAfter = NavCtrlI_FindItem(hNavigation, insertAfterId); + hParentItem = NavCtrlI_FindItem(hNavigation, parentId); + + hItem = NavCtrlI_InsertItem(hNavigation, hInsertAfter, hParentItem, &nis); + + if (converted && nis.pszText) free(nis.pszText); + if (hItem) + { + if (ML_IPC_ADDTREEITEM ==msg || ML_IPC_ADDTREEITEM_EX == msg) + ((mlAddTreeItemStruct*)param)->this_id = NavItemI_GetId(hItem); + else + ((MLTREEITEMW*)param)->id = NavItemI_GetId(hItem); + } + + return (INT_PTR)hItem; +} + +static BOOL IPC_SetView(int msg, INT_PTR param) +{ + HNAVITEM hItem; + switch(msg) + { + case ML_IPC_SETTREEITEM: + case ML_IPC_SETTREEITEM_EX: + hItem = NavCtrlI_FindItem(hNavigation, (INT)((mlAddTreeItemStruct*)param)->this_id); + if (!hItem) return FALSE; + if (!NavItemI_SetText(hItem, AutoWide(((mlAddTreeItemStruct*)param)->title)) || + !NavItemI_SetStyle(hItem, (((mlAddTreeItemStruct*)param)->has_children) ? NIS_HASCHILDREN_I : 0, NIS_HASCHILDREN_I)) + return FALSE; + if (ML_IPC_SETTREEITEM_EX == msg) + { + INT mlilIndex = GetNavImageIndexFromTag(NavCtrlI_GetImageList(hNavigation), ((mlAddTreeItemStructEx*)param)->imageIndex); + if (!NavItemI_SetImageIndex(hItem, mlilIndex, IMAGE_NORMAL_I) || !NavItemI_SetImageIndex(hItem, mlilIndex, IMAGE_SELECTED_I)) return FALSE; + } + return TRUE; + case ML_IPC_TREEITEM_SETINFO: + case ML_IPC_TREEITEM_SETINFOW: + hItem = (((MLTREEITEMINFO*)param)->handle) ? + (HNAVITEM)((MLTREEITEMINFO*)param)->handle : + NavCtrlI_FindItem(hNavigation, (INT)((MLTREEITEMINFO*)param)->item.id); + if (!hItem) return FALSE; + if (MLTI_TEXT & ((MLTREEITEMINFO*)param)->mask) + { + if (!NavItemI_SetText(hItem, (ML_IPC_TREEITEM_SETINFOW == msg) ? ((MLTREEITEMINFOW*)param)->item.title : + AutoWide(((MLTREEITEMINFO*)param)->item.title))) return FALSE; + } + if (MLTI_ID & ((MLTREEITEMINFO*)param)->mask && + ! NavItemI_SetId(hItem, (INT)((MLTREEITEMINFO*)param)->item.id)) return FALSE; + if (MLTI_CHILDREN & ((MLTREEITEMINFO*)param)->mask && + !NavItemI_SetStyle(hItem, (((MLTREEITEMINFO*)param)->item.hasChildren) ? NIS_HASCHILDREN_I : 0, NIS_HASCHILDREN_I)) return FALSE; + if (MLTI_IMAGE & ((MLTREEITEMINFO*)param)->mask) + { + INT mlilIndex = GetNavImageIndexFromTag(NavCtrlI_GetImageList(hNavigation), ((MLTREEITEMINFO*)param)->item.imageIndex); + if (!NavItemI_SetImageIndex(hItem, mlilIndex, IMAGE_NORMAL_I) || !NavItemI_SetImageIndex(hItem, mlilIndex, IMAGE_SELECTED_I)) return FALSE; + } + } + return FALSE; +} + +static BOOL IPC_GetView(INT msg, INT_PTR param) +{ + HNAVITEM hItem; + + hItem = (((MLTREEITEMINFO*)param)->handle) ? + (HNAVITEM)((MLTREEITEMINFO*)param)->handle : + NavCtrlI_FindItem(hNavigation, (INT)((MLTREEITEMINFO*)param)->item.id); + if (!hItem) return FALSE; + + if (MLTI_ID & ((MLTREEITEMINFO*)param)->mask) ((MLTREEITEMINFO*)param)->item.id = NavItemI_GetId(hItem); + if (MLTI_CHILDREN & ((MLTREEITEMINFO*)param)->mask) ((MLTREEITEMINFO*)param)->item.hasChildren = NavItemI_HasChildren(hItem); + /// TODO: index <--> tag + if (MLTI_IMAGE & ((MLTREEITEMINFO*)param)->mask) + { + INT_PTR tag; + + ((MLTREEITEMINFO*)param)->item.id = (MLImageListI_GetTagFromIndex(NavCtrlI_GetImageList(hNavigation), + NavItemI_GetImageIndex(hItem, IMAGE_NORMAL_I), + &tag)) ? (INT)tag : -1; + } + if (MLTI_TEXT & ((MLTREEITEMINFO*)param)->mask) + { + if (ML_IPC_TREEITEM_GETINFO == msg) + { + wchar_t buffer[4096] = {0}; + if (!NavItemI_GetText(hItem, buffer, 4096)) return FALSE; + if (!WideCharToMultiByte(CP_ACP, 0, buffer, -1, + ((MLTREEITEMINFO*)param)->item.title, + (INT)((MLTREEITEMINFO*)param)->item.titleLen, NULL, NULL)) return FALSE; + } + else + { + if (!NavItemI_GetText(hItem, ((MLTREEITEMINFOW*)param)->item.title, (INT)((MLTREEITEMINFOW*)param)->item.titleLen)) return FALSE; + } + } + + return TRUE; +} + +static INT_PTR IPC_SendToWinamp(INT_PTR param) +{ + mlSendToWinampStruct *p = (mlSendToWinampStruct*)param; + if (p->data) + { + int enq = p->enqueue & 1; + if (!(p->enqueue & 2) && g_config->ReadInt(L"enqueuedef", 0)) enq ^= 1; + + switch (p->type) + { + case ML_TYPE_ITEMRECORDLIST: + case ML_TYPE_CDTRACKS: + main_playItemRecordList((itemRecordList*)p->data, enq); + break; + case ML_TYPE_ITEMRECORDLISTW: + main_playItemRecordListW((itemRecordListW *)p->data, enq); + break; + case ML_TYPE_STREAMNAMES: + case ML_TYPE_FILENAMES: + { + char *list = (char*)p->data; + int cnt = 0; + if (list) while (*list) + { + if (!cnt && !enq) SendMessage(plugin.hwndParent, WM_WA_IPC, 0, IPC_DELETE); + cnt++; + + COPYDATASTRUCT cds; + cds.dwData = IPC_PLAYFILE; + cds.lpData = (void *) list; + cds.cbData =(INT)strlen((char *) cds.lpData) + 1; // include space for null char + SendMessage(plugin.hwndParent, WM_COPYDATA, (WPARAM)NULL, (LPARAM)&cds); + + list += strlen(list) + 1; + } + if (cnt && !enq) SendMessage(plugin.hwndParent, WM_WA_IPC, 0, IPC_STARTPLAY); + } + break; + case ML_TYPE_STREAMNAMESW: + case ML_TYPE_FILENAMESW: + { + wchar_t *list = (wchar_t*)p->data; + int cnt = 0; + if ( list ) + { + while ( *list ) + { + if ( !cnt && !enq ) SendMessage( plugin.hwndParent, WM_WA_IPC, 0, IPC_DELETE ); + cnt++; + + COPYDATASTRUCT cds; + cds.dwData = IPC_PLAYFILEW; + cds.lpData = (void *)list; + cds.cbData = (INT)( sizeof( wchar_t ) * wcslen( (wchar_t *)cds.lpData ) + sizeof( wchar_t ) ); // include space for null char + SendMessage( plugin.hwndParent, WM_COPYDATA, (WPARAM)NULL, (LPARAM)&cds ); + + list += wcslen( list ) + 1; + } + } + + if (cnt && !enq) + SendMessage(plugin.hwndParent, WM_WA_IPC, 0, IPC_STARTPLAY); + } + break; + } + } + return 0; +} + +typedef struct __DRAGTARGET +{ + HWND hwnd; + UINT state; + INT testResult; +} DRAGTARGET; + +typedef enum +{ + DRAGSTATE_NOTSUPPORTED = -2, + DRAGSTATE_UNKNOWN = -1, + DRAGSTATE_REJECTED = 0, + DRAGSTATE_ACCEPTED = 1, +} DRAGSTATE; + +static DRAGTARGET dragTarget; +static UINT WAML_NOTIFY_DRAGDROP = 0; + +static INT_PTR IPC_HandleDrag(HWND hwndML, mlDropItemStruct *pdip) +{ + if (NULL == pdip) + { + SetCursor(LoadCursor(NULL, IDC_NO)); + return 0; + } + + HWND hTarget = (NULL != pdip) ? WindowFromPoint(pdip->p) : NULL; + + if (0 == WAML_NOTIFY_DRAGDROP) + { + WAML_NOTIFY_DRAGDROP = RegisterWindowMessageW(WAMLM_DRAGDROP); + if (0 == WAML_NOTIFY_DRAGDROP) + { + if (0 == (ML_HANDLEDRAG_FLAG_NOCURSOR & pdip->flags)) + SetCursor(LoadCursor(NULL, IDC_NO)); + return 0; + } + } + + if (NULL != dragTarget.hwnd && hTarget != dragTarget.hwnd) + { + if (DRAGSTATE_NOTSUPPORTED != dragTarget.state && + DRAGSTATE_UNKNOWN != dragTarget.state) + SendMessageW(dragTarget.hwnd, WAML_NOTIFY_DRAGDROP, DRAGDROP_DRAGLEAVE, 0L); + + dragTarget.hwnd = NULL; + dragTarget.state = DRAGSTATE_UNKNOWN; + dragTarget.testResult = 0; + } + + if (NULL == dragTarget.hwnd && NULL != hTarget) + { + DWORD targetPid; + GetWindowThreadProcessId(hTarget, &targetPid); + if (GetCurrentProcessId() != targetPid) + { + if (0 == (ML_HANDLEDRAG_FLAG_NOCURSOR & pdip->flags)) + SetCursor(LoadCursor(NULL, IDC_NO)); + return 0; + } + dragTarget.hwnd = hTarget; + } + + if (NULL != dragTarget.hwnd) + { + switch(dragTarget.state) + { + case DRAGSTATE_UNKNOWN: + pdip->result = 0; + if (SendMessageW(dragTarget.hwnd, WAML_NOTIFY_DRAGDROP, DRAGDROP_DRAGENTER, (LPARAM)pdip)) + { + dragTarget.testResult = pdip->result; + dragTarget.state = (dragTarget.testResult > 0) ? DRAGSTATE_ACCEPTED : DRAGSTATE_REJECTED; + if (0 == (ML_HANDLEDRAG_FLAG_NOCURSOR & pdip->flags)) + SetCursor((dragTarget.testResult > 0) ? hDragNDropCursor : LoadCursor(NULL, IDC_NO)); + return 0; + } + else + dragTarget.state = DRAGSTATE_NOTSUPPORTED; + break; + + case DRAGSTATE_REJECTED: + pdip->result = dragTarget.testResult; + return 0; + + case DRAGSTATE_ACCEPTED: + SendMessageW(dragTarget.hwnd, WAML_NOTIFY_DRAGDROP, DRAGDROP_DRAGOVER, (LPARAM)pdip); + dragTarget.testResult = pdip->result; + if (0 == (ML_HANDLEDRAG_FLAG_NOCURSOR & pdip->flags)) + SetCursor((dragTarget.testResult > 0) ? hDragNDropCursor : LoadCursor(NULL, IDC_NO)); + return 0; + } + } + pdip->result = handleDragDropMove(hwndML, pdip->type, pdip->p, !(pdip->flags & ML_HANDLEDRAG_FLAG_NOCURSOR)) > 0 ? 1 : -1; + return 0; +} + +static INT_PTR IPC_HandleDrop(INT_PTR param) +{ + if (param) + { + extern HNAVITEM g_treedrag_lastSel; + if (g_treedrag_lastSel) + { + NavItemI_SetState(g_treedrag_lastSel, 0, NIS_DROPHILITED_I); + g_treedrag_lastSel = 0; + } + + mlDropItemStruct *m = (mlDropItemStruct *)param; + if (!m->data) return 0; + + HWND h = WindowFromPoint(m->p); + if (!h) return 0;; + + if (NULL != dragTarget.hwnd) + { + BOOL bProcessed = FALSE; + if (DRAGSTATE_NOTSUPPORTED != dragTarget.state && + DRAGSTATE_UNKNOWN != dragTarget.state) + { + if (h != dragTarget.hwnd) + SendMessageW(dragTarget.hwnd, WAML_NOTIFY_DRAGDROP, DRAGDROP_DRAGLEAVE, 0L); + else + { + bProcessed = TRUE; + if (DRAGSTATE_ACCEPTED == dragTarget.state) + SendMessageW(dragTarget.hwnd, WAML_NOTIFY_DRAGDROP, DRAGDROP_DROP, (LPARAM)m); + } + } + + + dragTarget.hwnd = NULL; + dragTarget.state = DRAGSTATE_UNKNOWN; + dragTarget.testResult = 0; + + if (bProcessed) + return 0; + } + + extern HWND wa3wnd_fixwnd(HWND); + h = wa3wnd_fixwnd(h); + + itemRecordList *obj = (itemRecordList *)m->data; // may not be valid heh + + if (IsChild(plugin.hwndParent, h)) h = plugin.hwndParent; + else if (g_PEWindow && IsChild(g_PEWindow, h)) h = g_PEWindow; + else + { + HWND vid = (HWND)SendMessage(plugin.hwndParent, WM_WA_IPC, IPC_GETWND_VIDEO, IPC_GETWND); + if (vid) + { + if (h == vid || IsChild(vid, h)) h = plugin.hwndParent; + } + } + + if (h == plugin.hwndParent) + { + int enqueue = (!!g_config->ReadInt(L"enqueuedef", 0)) ^(!!(GetAsyncKeyState(VK_SHIFT) & 0x8000)); + if (m->type == ML_TYPE_ITEMRECORDLIST || m->type == ML_TYPE_CDTRACKS) + main_playItemRecordList((itemRecordList *)m->data, enqueue); + else if (m->type == ML_TYPE_ITEMRECORDLISTW) + main_playItemRecordListW((itemRecordListW *)m->data, enqueue); + else if (m->type == ML_TYPE_FILENAMES || m->type == ML_TYPE_STREAMNAMES) + { + if (!enqueue) SendMessage(plugin.hwndParent, WM_WA_IPC, 0, IPC_DELETE); + char *p = (char*)m->data; + + while (p && *p) + { + COPYDATASTRUCT cds; + cds.dwData = IPC_PLAYFILE; + cds.lpData = (void *) p; + cds.cbData =(INT) strlen((char *) cds.lpData) + 1; // include space for null char + SendMessage(plugin.hwndParent, WM_COPYDATA, (WPARAM)NULL, (LPARAM)&cds); + p += strlen(p) + 1; + } + if (!enqueue) SendMessage(plugin.hwndParent, WM_WA_IPC, 0, IPC_STARTPLAY); + } + else if (m->type == ML_TYPE_FILENAMESW || m->type == ML_TYPE_STREAMNAMESW) + { + if (!enqueue) SendMessage(plugin.hwndParent, WM_WA_IPC, 0, IPC_DELETE); + wchar_t *p = (wchar_t*)m->data; + + while (p && *p) + { + COPYDATASTRUCT cds; + cds.dwData = IPC_PLAYFILEW; + cds.lpData = (void *) p; + cds.cbData =(INT)sizeof(wchar_t) * wcslen((wchar_t *) cds.lpData) + sizeof(wchar_t); // include space for null char + SendMessage(plugin.hwndParent, WM_COPYDATA, (WPARAM)NULL, (LPARAM)&cds); + p += wcslen(p) + 1; + } + if (!enqueue) SendMessage(plugin.hwndParent, WM_WA_IPC, 0, IPC_STARTPLAY); + } + else if (m->type == ML_TYPE_PLAYLIST) + { + if (!enqueue) SendMessage(plugin.hwndParent, WM_WA_IPC, 0, IPC_DELETE); + mlPlaylist *p = (mlPlaylist *)m->data; + + COPYDATASTRUCT cds; + cds.dwData = IPC_PLAYFILEW; + cds.lpData = (void *) p->filename; + cds.cbData = sizeof(wchar_t)*(wcslen(p->filename) + 1);// include space for null char + SendMessage(plugin.hwndParent, WM_COPYDATA, (WPARAM)NULL, (LPARAM)&cds); + + if (!enqueue) SendMessage(plugin.hwndParent, WM_WA_IPC, 0, IPC_STARTPLAY); + } + else if (m->type == ML_TYPE_PLAYLISTS) + { + if (!enqueue) SendMessage(plugin.hwndParent, WM_WA_IPC, 0, IPC_DELETE); + mlPlaylist **playlists = (mlPlaylist **)m->data; + while (playlists && *playlists) + { + mlPlaylist *p = *playlists; + COPYDATASTRUCT cds; + cds.dwData = IPC_PLAYFILEW; + cds.lpData = (void *) p->filename; + cds.cbData = sizeof(wchar_t)*(wcslen(p->filename) + 1);// include space for null char + SendMessage(plugin.hwndParent, WM_COPYDATA, (WPARAM)NULL, (LPARAM)&cds); + playlists++; + } + + if (!enqueue) SendMessage(plugin.hwndParent, WM_WA_IPC, 0, IPC_STARTPLAY); + } + } + else if (h == g_PEWindow) + { + POINT p = m->p; + ScreenToClient(g_PEWindow, &p); + LRESULT idx = SendMessage(g_PEWindow, WM_USER, IPC_PE_GETIDXFROMPOINT, (LPARAM) & p); + SendMessage(g_PEWindow, WM_USER, IPC_PE_SAVEEND, (LPARAM)idx); + if (m->type == ML_TYPE_ITEMRECORDLIST || m->type == ML_TYPE_CDTRACKS) + main_playItemRecordList((itemRecordList *)m->data, 1); + else if (m->type == ML_TYPE_ITEMRECORDLISTW) + main_playItemRecordListW((itemRecordListW *)m->data, 1); + else if (m->type == ML_TYPE_FILENAMES || m->type == ML_TYPE_STREAMNAMES) + { + char *fileName = (char*)m->data; + while (fileName && *fileName) + { + COPYDATASTRUCT cds; + cds.dwData = IPC_PLAYFILE; + cds.lpData = (void *)fileName; + cds.cbData = (INT)strlen((char *) cds.lpData) + 1; // include space for null char + SendMessage(plugin.hwndParent, WM_COPYDATA, (WPARAM)NULL, (LPARAM)&cds); + fileName += strlen(fileName) + 1; + } + } + else if (m->type == ML_TYPE_FILENAMESW || m->type == ML_TYPE_STREAMNAMESW) + { + wchar_t *fileName = (wchar_t*)m->data; + while (fileName && *fileName) + { + COPYDATASTRUCT cds; + cds.dwData = IPC_PLAYFILEW; + cds.lpData = (void *)fileName; + cds.cbData = (INT)sizeof(wchar_t) * wcslen((wchar_t *) cds.lpData) + sizeof(wchar_t); // include space for null char + SendMessage(plugin.hwndParent, WM_COPYDATA, (WPARAM)NULL, (LPARAM)&cds); + fileName += wcslen(fileName) + 1; + } + } + else if (m->type == ML_TYPE_PLAYLIST) + { + mlPlaylist *pl = (mlPlaylist *)m->data; + + COPYDATASTRUCT cds; + cds.dwData = IPC_PLAYFILEW; + cds.lpData = (void *) pl->filename; + cds.cbData = sizeof(wchar_t)*(wcslen(pl->filename) + 1);// include space for null char + SendMessage(plugin.hwndParent, WM_COPYDATA, (WPARAM)NULL, (LPARAM)&cds); + } + SendMessage(g_PEWindow, WM_USER, IPC_PE_RESTOREEND, 0); + } + else if (h == NavCtrlI_GetHWND(hNavigation)) + { + HNAVITEM hItemHit, hItemSel; + POINT pt = m->p; + MapWindowPoints(HWND_DESKTOP, h, &pt, 1); + + hItemHit = NavCtrlI_HitTest(hNavigation, &pt, NULL); + hItemSel = NavCtrlI_GetSelection(hNavigation); + + if (hItemHit == hItemSel) m->result = -1; + else + { + INT itemId = NavItemI_GetId(hItemHit); + if (itemId && plugin_SendMessage(ML_MSG_TREE_ONDROPTARGET, itemId, m->type, NULL) > 0) + plugin_SendMessage(ML_MSG_TREE_ONDROPTARGET, itemId, m->type, (INT_PTR)m->data); + } + } + else{ + // TODO: verify this is correct against NAV api usage + // try to send to the current view if all else failed + // e.g. dragging a playlist view onto bookmarks page + HNAVITEM hItemSel = NavCtrlI_GetSelection(hNavigation); + INT itemId = NavItemI_GetId(hItemSel); + if(itemId && plugin_SendMessage(ML_MSG_TREE_ONDROPTARGET, itemId, m->type, NULL) > 0) + plugin_SendMessage(ML_MSG_TREE_ONDROPTARGET, itemId, m->type, (INT_PTR)m->data); + } + } + return 0; +} + +static INT_PTR IPC_GetTree(INT_PTR param) +{ + mlGetTreeStruct *mgts; + HNAVITEM hStartItem = NULL, hStartItem2 = NULL; + NAVMENUBUILDER nmb; + wchar_t buffer[1024] = {0}; + + if (!param) return NULL; + + mgts = (mlGetTreeStruct *)param; + + hStartItem = NavCtrlI_FindItem(hNavigation, mgts->item_start); + + if (hStartItem) hStartItem2 = NavItemI_GetChild(hStartItem); + + nmb.level = 0; + nmb.hMenu[0] = CreatePopupMenu(); + nmb.hParent[0] = NavItemI_GetParent(hStartItem2); + nmb.maxNum = mgts->max_numitems; + nmb.offset = mgts->cmd_offset; + nmb.counter = 0; + nmb.nis.mask = NIMF_TEXT_I | NIMF_STYLE_I | NIMF_ITEMID_I; + nmb.nis.styleMask = NIS_HASCHILDREN_I; + nmb.nis.cchTextMax = sizeof(buffer)/sizeof(wchar_t); + nmb.nis.pszText = buffer; + + if(hStartItem2 || !mgts->item_start) NavCtrlI_EnumItems(hNavigation, EnumerateNavItemsCB, hStartItem2, (LPARAM)&nmb); + + return (INT_PTR)nmb.hMenu[0]; +} + +static INT_PTR IPC_GetRating(HWND hwndML, INT_PTR param) +{ + LRESULT plindex = SendMessage(plugin.hwndParent, WM_WA_IPC, 0, IPC_GETLISTPOS); + return getPlRating(hwndML, plindex); +} + +static INT_PTR IPC_SetRating(HWND hwndML, INT_PTR param) +{ + INT_PTR rating = min(5, max(0, param)); + LRESULT plindex = SendMessage(plugin.hwndParent, WM_WA_IPC, 0, IPC_GETLISTPOS); + SetPlRating(hwndML, (INT)plindex, (INT)rating); + SendMessage(plugin.hwndParent, WM_WA_IPC, 0, IPC_UPDTITLE); + return 1; +} + +static INT IPC_RegisterNavigationImage(INT_PTR param) +{ + HMLIMGLST hmlilNavigation; + MLIMAGESOURCE_I is; + INT_PTR tag; + INT mlilIndex; + + if (!param) return -1; + + hmlilNavigation = NavCtrlI_GetImageList(hNavigation); + if (!hmlilNavigation) return -1; + + ZeroMemory(&is, sizeof(MLIMAGESOURCE_I)); + is.hInst = ((MLTREEIMAGE*)param)->hinst; + is.bpp = 24; + is.lpszName = MAKEINTRESOURCEW(((MLTREEIMAGE*)param)->resourceId); + is.type = SRC_TYPE_BMP_I; + is.flags = ISF_FORCE_BPP_I; + + tag = ((MLTREEIMAGE*)param)->imageIndex; + if (-1 != tag) + { + mlilIndex = MLImageListI_GetIndexFromTag(hmlilNavigation, tag); + if (-1 != mlilIndex) + { + if (!MLImageListI_Replace(hmlilNavigation, mlilIndex, &is, + GUID_NULL, tag)) tag = -1; + } + } + else mlilIndex = -1; + + if (-1 == mlilIndex) + { + if ( -1 == tag) + { + static INT_PTR tagCounter = 8000; + tag = tagCounter++; + } + mlilIndex = MLImageListI_Add(hmlilNavigation, &is, MLIF_FILTER1_UID, tag); + if (-1 == mlilIndex) tag = -1; + } + + return (INT)tag; +} + +INT_PTR OnGetCurrentView(void) +{ + return (INT_PTR)m_curview_hwnd; +} + +static BOOL OnFlickerFixIPC(FLICKERFIX * pff) +{ + return (pff) ? FlickerFixWindow(pff->hwnd, pff->mode) : FALSE; +} + +static HBITMAP OnIPC_MLImageLoader_LoadDib(MLIMAGESOURCE *pmlis) +{ + return (pmlis && pmlis->cbSize == sizeof(MLIMAGESOURCE)) ? + MLImageLoaderI_LoadDib((MLIMAGESOURCE_I*)&pmlis->hInst) : + NULL; +} + +static BOOL OnIPC_MLImageLoader_CopyStruct(MLIMAGESOURCECOPY* pmlisCopy) +{ + return (pmlisCopy && pmlisCopy->dest && pmlisCopy->src && + pmlisCopy->dest->cbSize == sizeof(MLIMAGESOURCE) && + pmlisCopy->dest->cbSize == sizeof(MLIMAGESOURCE)) ? + MLImageLoaderI_CopyData((MLIMAGESOURCE_I*)&pmlisCopy->dest->hInst, (MLIMAGESOURCE_I*)&pmlisCopy->src->hInst) : + FALSE; +} + +static BOOL OnIPC_MLImageLoader_FreeStruct(MLIMAGESOURCE *pmlis) +{ + return (pmlis && pmlis->cbSize == sizeof(MLIMAGESOURCE)) ? + MLImageLoaderI_FreeData((MLIMAGESOURCE_I*)&pmlis->hInst) : + FALSE; +} + +static BOOL OnIPC_MLImageLoader_CheckExist(MLIMAGESOURCE *pmlis) +{ + return (NULL != pmlis && pmlis->cbSize == sizeof(MLIMAGESOURCE)) ? + MLImageLoaderI_CheckExist((MLIMAGESOURCE_I*)&pmlis->hInst) : + FALSE; +} + +static HMLIMGLST OnIPC_MLImageList_Create(MLIMAGELISTCREATE *pmlilCreate) +{ + return (pmlilCreate) ? MLImageListI_Create(pmlilCreate->cx, pmlilCreate->cy, pmlilCreate->flags, pmlilCreate->cInitial, pmlilCreate->cGrow, pmlilCreate->cCacheSize, hmlifMngr) : NULL; +} + +static BOOL OnIPC_MLImageList_Destroy(HMLIMGLST hmlil) +{ + return MLImageListI_Destroy(hmlil); +} + +static HIMAGELIST OnIPC_MLImageList_GetRealList(HMLIMGLST hmlil) +{ + return MLImageListI_GetRealList(hmlil); +} + +static INT OnIPC_MLImageList_GetRealIndex(MLIMAGELISTREALINDEX *pmlilRealIdx) +{ + return (pmlilRealIdx && pmlilRealIdx->cbSize == sizeof(MLIMAGELISTREALINDEX)) ? + MLImageListI_GetRealIndex(pmlilRealIdx->hmlil, pmlilRealIdx->mlilIndex, pmlilRealIdx->rgbBk, pmlilRealIdx->rgbFg) : -1; +} + +static INT OnIPC_MLImageList_Add(MLIMAGELISTITEM *pmlilItem) +{ + MLIMAGESOURCE_I *pis; + + if (!pmlilItem || pmlilItem->cbSize != sizeof(MLIMAGELISTITEM)) return -1; + + if (pmlilItem->pmlImgSource) + { + if (pmlilItem->pmlImgSource->cbSize != sizeof(MLIMAGESOURCE)) return -1; + pis = (MLIMAGESOURCE_I*)(&pmlilItem->pmlImgSource->hInst); + } + else pis = NULL; + pmlilItem->mlilIndex = MLImageListI_Add(pmlilItem->hmlil, pis, pmlilItem->filterUID, pmlilItem->nTag); + return pmlilItem->mlilIndex; +} + +static BOOL OnIPC_MLImageList_Replace(MLIMAGELISTITEM *pmlilItem) +{ + MLIMAGESOURCE_I *pis; + + if (!pmlilItem || pmlilItem->cbSize != sizeof(MLIMAGELISTITEM)) return -1; + + if (pmlilItem->pmlImgSource) + { + if (pmlilItem->pmlImgSource->cbSize != sizeof(MLIMAGESOURCE)) return -1; + pis = (MLIMAGESOURCE_I*)((INT*)pmlilItem->pmlImgSource + 1); + } + else pis = NULL; + return MLImageListI_Replace(pmlilItem->hmlil, pmlilItem->mlilIndex, pis, pmlilItem->filterUID, pmlilItem->nTag); +} + +static BOOL OnIPC_MLImageList_Remove(MLIMAGELISTITEM *pmlilItem) +{ + return (pmlilItem) ? MLImageListI_Remove(pmlilItem->hmlil, pmlilItem->mlilIndex) : FALSE; +} + +static BOOL OnIPC_MLImageList_GetImageSize(MLIMAGELISTIMAGESIZE *pmlilImageSize) +{ + return (pmlilImageSize) ? MLImageListI_GetImageSize(pmlilImageSize->hmlil, &pmlilImageSize->cx, &pmlilImageSize->cy) : FALSE; +} + +static INT OnIPC_MLImageList_GetImageCount(HMLIMGLST hmlil) +{ + return MLImageListI_GetImageCount(hmlil); +} + +static BOOL OnIPC_MLImageList_GetIndexFromTag(MLIMAGELISTTAG *pmlilTag) +{ + if (!pmlilTag) return FALSE; + pmlilTag->mlilIndex = MLImageListI_GetIndexFromTag(pmlilTag->hmlil, pmlilTag->nTag); + return (-1 != pmlilTag->mlilIndex); +} + +static BOOL OnIPC_MLImageList_GetTagFromIndex(MLIMAGELISTTAG *pmlilTag) +{ + return (pmlilTag) ? MLImageListI_GetTagFromIndex(pmlilTag->hmlil, pmlilTag->mlilIndex, &pmlilTag->nTag) : FALSE; +} + +static BOOL OnIPC_MLImageList_CheckItemExist(MLIMAGELISTITEM *pmlilItem) +{ + return (pmlilItem) ? MLImageListI_CheckItemExist(pmlilItem->hmlil, pmlilItem->mlilIndex) : FALSE; +} + +static BOOL OnIPC_MLImageFilter_Register(MLIMAGEFILTERINFO* pmlif) +{ + return (pmlif && pmlif->cbSize == sizeof(MLIMAGEFILTERINFO)) ? + MLImageFilterI_Register(hmlifMngr, (MLIMAGEFILTERINFO_I*)((INT*)pmlif +1)) : FALSE; +} + +static BOOL OnIPC_MLImageFilter_Unregister(const GUID* pUID) +{ + return (pUID) ? MLImageFilterI_Unregister(hmlifMngr, *pUID) : FALSE; +} + +static BOOL OnIPC_MLImageFilter_GetInfo(MLIMAGEFILTERINFO* pmlif) +{ + return (pmlif && pmlif->cbSize == sizeof(MLIMAGEFILTERINFO)) ? + MLImageFilterI_GetInfo(hmlifMngr, (MLIMAGEFILTERINFO_I*)((INT*)pmlif +1)) : FALSE; +} + +static BOOL OnIPC_MLImageFilter_ApplyEx(MLIMAGEFILTERAPPLYEX* pmlifApplyEx) +{ + if (!pmlifApplyEx || pmlifApplyEx->cbSize != sizeof(MLIMAGEFILTERAPPLYEX)) return FALSE; + return MLImageFilterI_ApplyEx(hmlifMngr, &pmlifApplyEx->filterUID, pmlifApplyEx->pData, pmlifApplyEx->cx, + pmlifApplyEx->cy, pmlifApplyEx->bpp, pmlifApplyEx->rgbBk, pmlifApplyEx->rgbFg, pmlifApplyEx->imageTag); +} + +static INT OnIPC_MLNavCtrl_BeginUpdate(UINT fRememberPos) +{ + return NavCtrlI_BeginUpdate(hNavigation, fRememberPos); +} + +static BOOL OnIPC_MLNavCtrl_DeleteItem(HNAVITEM hItem) +{ + return NavCtrlI_DeleteItem(hNavigation, hItem); +} + +static INT OnIPC_MLNavCtrl_EndUpdate(void) +{ + return NavCtrlI_EndUpdate(hNavigation); +} + +static BOOL OnIPC_MLNavCtrl_EnumItems(NAVCTRLENUMPARAMS *pnavEnumParams) +{ + return (pnavEnumParams) ? NavCtrlI_EnumItems(hNavigation, (NAVENUMPROC_I)pnavEnumParams->enumProc, + pnavEnumParams->hItemStart, pnavEnumParams->lParam) : FALSE; +} + +HNAVITEM OnIPC_MLNavCtrl_FindItemById(INT itemId) +{ + return NavCtrlI_FindItem(hNavigation, itemId); +} + +static HNAVITEM OnIPC_MLNavCtrl_FindItemByName(NAVCTRLFINDPARAMS *pnavFindParams) +{ + if (!pnavFindParams) return NULL; + return (!pnavFindParams->fFullNameSearch) ? + NavCtrlI_FindItemByName(hNavigation, pnavFindParams->Locale, pnavFindParams->compFlags, pnavFindParams->pszName, pnavFindParams->cchLength) : + NavCtrlI_FindItemByFullName(hNavigation, pnavFindParams->Locale, pnavFindParams->compFlags, pnavFindParams->pszName, pnavFindParams->cchLength, pnavFindParams->fAncestorOk); +} + +static HMLIMGLST OnIPC_MLNavCtrl_GetImageList(void) +{ + return NavCtrlI_GetImageList(hNavigation); +} + +static HNAVITEM OnIPC_MLNavCtrl_GetFirst(void) +{ + return NavCtrlI_GetRoot(hNavigation); +} + +static INT OnIPC_MLNavCtrl_GetIndent(void) +{ + return NavCtrlI_GetIndent(hNavigation); +} + +static DWORD OnIPC_MLNavCtrl_GetStyle(void) +{ + return NavCtrlI_GetStyle(hNavigation); +} + +static HNAVITEM OnIPC_MLNavCtrl_GetSelection(void) +{ + return NavCtrlI_GetSelection(hNavigation); +} + +static HWND OnIPC_MLNavCtrl_GetHWND(void) +{ + return NavCtrlI_GetHWND(hNavigation); +} + +static HNAVITEM OnIPC_MLNavCtrl_HitTest(NAVHITTEST *pnavHit) +{ + if (!pnavHit) return NULL; + pnavHit->hItem = NavCtrlI_HitTest(hNavigation, &pnavHit->pt, &pnavHit->flags); + return pnavHit->hItem; +} + +static HNAVITEM OnIPC_MLNavCtrl_InsertItem(NAVINSERTSTRUCT *pnavInsert) +{ + if (!hNavigation || !pnavInsert || pnavInsert->item.cbSize != sizeof(NAVITEM)) return NULL; + return NavCtrlI_InsertItem(hNavigation, pnavInsert->hInsertAfter, pnavInsert->hParent, (NAVITEM_I*)&pnavInsert->item.mask); +} + +static BOOL OnIPC_MLNavItem_EnsureVisible(HNAVITEM hItem) +{ + return NavItemI_EnsureVisible(hItem); +} + +static BOOL OnIPC_MLNavItem_Expand(NAVITEMEXPAND *pnavExpand) +{ + return (pnavExpand) ? NavItemI_Expand(pnavExpand->hItem, pnavExpand->fCmdExpand) : FALSE; +} + +static HNAVITEM OnIPC_MLNavItem_GetChild(HNAVITEM hItem) +{ + return NavItemI_GetChild(hItem); +} + +static INT OnIPC_MLNavItem_GetChildrenCount(HNAVITEM hItem) +{ + return NavItemI_GetChildrenCount(hItem); +} + +static INT OnIPC_MLNavItem_GetFullName(NAVITEMFULLNAME* pnavFullName) +{ + return (pnavFullName) ? NavItemI_GetFullName(pnavFullName->hItem, pnavFullName->pszText, pnavFullName->cchTextMax) : 0; +} + +static BOOL OnIPC_MLNavItem_GetInfo(NAVITEM *pnavItem) +{ + return (pnavItem && pnavItem->cbSize == sizeof(NAVITEM)) ? NavItemI_GetIndirect(pnavItem->hItem, (NAVITEM_I*)&pnavItem->mask) : FALSE; +} + +static HNAVITEM OnIPC_MLNavItem_GetNext(HNAVITEM hItem) +{ + return NavItemI_GetNext(hItem); +} + +static WORD OnIPC_MLNavItem_GetOrder(HNAVITEM hItem) +{ + return NavItemI_GetOrder(hItem); +} + +static HNAVITEM OnIPC_MLNavItem_GetParent(HNAVITEM hItem) +{ + return NavItemI_GetParent(hItem); +} + +static HNAVITEM OnIPC_MLNavItem_GetPrevious(HNAVITEM hItem) +{ + return NavItemI_GetPrevious(hItem); +} + +static BOOL OnIPC_MLNavItem_GetRect(NAVITEMGETRECT *pnavRect) +{ + return (pnavRect) ? NavItemI_GetRect(pnavRect->hItem, &pnavRect->rc, pnavRect->fItem) : FALSE; +} + +static UINT OnIPC_MLNavItem_GetState(HNAVITEM hItem) +{ + return NavItemI_GetState(hItem, 0xFFFFFFFF); +} + +static BOOL OnIPC_MLNavItem_HasChildrenReal(HNAVITEM hItem) +{ + return NavItemI_HasChildrenReal(hItem); +} + +static BOOL OnIPC_MLNavItem_Invalidate(NAVITEMINAVLIDATE *pnavInvalidate) +{ + return (pnavInvalidate) ? NavItemI_Invalidate(pnavInvalidate->hItem, pnavInvalidate->prc, pnavInvalidate->fErase) : FALSE; +} + +static BOOL OnIPC_MLNavItem_Move(NAVITEMMOVE *pnavMove) +{ + return (pnavMove) ? NavItemI_Move(pnavMove->hItem, pnavMove->hItemDest, pnavMove->fAfter) : FALSE; +} + +static BOOL OnIPC_MLNavItem_Select(HNAVITEM hItem) +{ + return NavItemI_Select(hItem); +} + +static BOOL OnIPC_MLNavItem_SetInfo(NAVITEM *pnavItem) +{ + return (pnavItem && pnavItem->cbSize == sizeof(NAVITEM)) ? NavItemI_SetIndirect(pnavItem->hItem, (NAVITEM_I*)&pnavItem->mask) : FALSE; +} + +static WORD OnIPC_MLNavItem_SetOrder(NAVITEMORDER *pnavOrder) +{ + return (pnavOrder) ? NavItemI_SetOrder(pnavOrder->hItem, pnavOrder->order, pnavOrder->flags) : 0xFFFF; +} + +static BOOL OnIPC_MLNavItem_EditTitle(HNAVITEM hItem) +{ + return NavItemI_EditTitle(hItem); +} + +static BOOL OnIPC_MLNavCtrl_EndEditTitle(BOOL fCancel) +{ + return NavCtrlI_EndEditTitle(hNavigation, fCancel); +} + +static BOOL OnIPC_MLRating_Draw(RATINGDRAWPARAMS *prdp) +{ + if (!prdp) return FALSE; + return MLRatingI_Draw(prdp->hdcDst, prdp->maxValue, prdp->value, prdp->trackingValue, + (prdp->hMLIL) ? prdp->hMLIL : hmlilRating, + (prdp->hMLIL) ? prdp->index : 0, + &prdp->rc, prdp->fStyle); +} + +static INT OnIPC_MLRating_Hittest(RATINGHITTESTPARAMS *prhtp) +{ + LONG result; + if (!prhtp) return FALSE; + result = MLRatingI_HitTest(prhtp->pt, prhtp->maxValue, + (prhtp->hMLIL) ? prhtp->hMLIL : hmlilRating, + &prhtp->rc, prhtp->fStyle); + prhtp->hitFlags = HIWORD(result); + prhtp->value = LOWORD(result); + return prhtp->value; +} + +static BOOL OnIPC_MLRating_CalcRect(RECT *prc) +{ + return (prc) ? MLRatingI_CalcMinRect(prc->top, (prc->left) ? (HMLIMGLST)(LONG_PTR)prc->left : hmlilRating, prc) : FALSE; +} + +static BOOL OnIPC_MLCloud_Draw(CLOUDDRAWPARAMS *prdp) +{ + if (!prdp) return FALSE; + return MLCloudI_Draw(prdp->hdcDst, prdp->value, (prdp->hMLIL) ? prdp->hMLIL : hmlilCloud, + (prdp->hMLIL) ? prdp->index : 0, &prdp->rc); +} + +static BOOL OnIPC_MLCloud_CalcRect(RECT *prc) +{ + return (prc) ? MLCloudI_CalcMinRect((prc->left) ? (HMLIMGLST)(LONG_PTR)prc->left : hmlilCloud, prc) : FALSE; +} + +static BOOL OnIPC_SkinWindow(MLSKINWINDOW* pmlsw) +{ + return (pmlsw) ? SkinWindowEx(pmlsw->hwndToSkin, pmlsw->skinType, pmlsw->style) : FALSE; +} + +static BOOL OnIPC_UnskinWindow(HWND hwndToUnskin) +{ + return UnskinWindow(hwndToUnskin); +} + +static BOOL OnIPC_TrackSkinnedPopup(MLSKINNEDPOPUP *pmlsp) +{ + if (pmlsp && pmlsp->cbSize == sizeof(MLSKINNEDPOPUP)) + { + return MediaLibrary_TrackPopupEx(pmlsp->hmenu, pmlsp->fuFlags, pmlsp->x, pmlsp->y, pmlsp->hwnd, pmlsp->lptpm, + pmlsp->hmlil, pmlsp->width, pmlsp->skinStyle, pmlsp->customProc, pmlsp->customParam); + } + else + return FALSE; +} + +static HANDLE OnIPC_InitSkinnedPopupHook(MLSKINNEDPOPUP *pmlsp) +{ + if (pmlsp && pmlsp->cbSize == sizeof(MLSKINNEDPOPUP)) + { + return MediaLibrary_InitSkinnedPopupHook(pmlsp->hwnd, pmlsp->hmlil, pmlsp->width, pmlsp->skinStyle, + pmlsp->customProc, pmlsp->customParam); + } + + return NULL; +} + +static BOOL OnIPC_RemoveSkinnedPopupHook(HANDLE hPopupHook) +{ + RemoveSkinnedPopupHook(hPopupHook); + return TRUE; +} + +static BOOL OnIPC_RatingColumn_Paint(RATINGCOLUMNPAINT *prcp) +{ + return (prcp) ? MLRatingColumnI_Paint((RATINGCOLUMNPAINT_I*)prcp) : FALSE; +} + +static BOOL OnIPC_RatingColumn_OnClick(RATINGCOLUMN *pRating) +{ + return (pRating) ? MLRatingColumnI_Click((RATINGCOLUMN_I*)pRating) : FALSE; +} + +static BOOL OnIPC_RatingColumn_OnTrack(RATINGCOLUMN *pRating) +{ + if (!pRating) return FALSE; + MLRatingColumnI_Track((RATINGCOLUMN_I*)pRating); + return TRUE; +} + +static BOOL OnIPC_RatingColumn_CancelTracking(BOOL bRedrawNow) +{ + MLRatingColumnI_CancelTracking(bRedrawNow); + return TRUE; +} + +static BOOL OnIPC_RatingColumn_BeginDrag(RATINGCOLUMN *pRating) +{ + return (pRating) ? MLRatingColumnI_BeginDrag((RATINGCOLUMN_I*)pRating) : FALSE; +} + +static BOOL OnIPC_RatingColumn_OnDrag(POINT *ppt) +{ + return (ppt) ? MLRatingColumnI_Drag(*ppt) : FALSE; +} + +static BOOL OnIPC_RatingColumn_OnEndDrag(RATINGCOLUMN *pRating) +{ + return (pRating) ? MLRatingColumnI_EndDrag((RATINGCOLUMN_I*)pRating) : FALSE; +} + +static BOOL OnIPC_RatingColumn_Animate(RATINGCOLUMN *pRating) +{ + if (!pRating) return FALSE; + MLRatingColumnI_Animate(pRating->hwndList, pRating->iItem, pRating->iSubItem); + return TRUE; +} + +static INT OnIPC_RatingColumn_GetMinWidth() +{ + return MLRatingColumnI_GetMinWidth(); +} + +static BOOL OnIPC_CloudColumn_GetWidth(INT *width) +{ + if (!width) return FALSE; + *width = MLCloudColumnI_GetWidth(*width); + return TRUE; +} + +static BOOL OnIPC_CloudColumn_Paint(RATINGCOLUMNPAINT *prcp) +{ + return (prcp) ? MLCloudColumnI_Paint((CLOUDCOLUMNPAINT_I*)prcp) : FALSE; +} + +static INT OnIPC_CloudColumn_GetMinWidth() +{ + return MLCloudColumnI_GetMinWidth(); +} + +static BOOL OnIPC_RatingColumn_FillBackString(RATINGBACKTEXT *prbt) +{ + if (!prbt) return FALSE; + MLRatingColumnI_FillBackString(prbt->pszText, prbt->cchTextMax, prbt->nColumnWidth, prbt->fStyle); + return TRUE; +} + +static BOOL OnIPC_RatingColumn_GetWidth(RATINGWIDTH *prw) +{ + if (!prw) return FALSE; + prw->width = MLRatingColumnI_GetWidth(prw->width, prw->fStyle); + return TRUE; +} + +static HWND OnIPC_CreateWebInfo(WEBINFOCREATE *pWebInfoCreate) +{ + + if (!pWebInfoCreate) return NULL; + if (-1 == pWebInfoCreate->cy) pWebInfoCreate->cy = WEBINFO_PREFFERED_HEIGHT; + + return CreateWebInfoWindow(pWebInfoCreate->hwndParent, pWebInfoCreate->uMsgQuery, + pWebInfoCreate->x, pWebInfoCreate->y, pWebInfoCreate->cx, pWebInfoCreate->cy, pWebInfoCreate->ctrlId); +} + +static HWND OnIPC_CreateFileView(MLFILEVIEWCREATESTRUCT *pfvcs) +{ + if (!pfvcs) return NULL; + return FileView_CreateDialog(pfvcs->hwndParent, pfvcs->fStyle, pfvcs->hwndInsertAfter, pfvcs->x, pfvcs->y, pfvcs->cx, pfvcs->cy); +} + +INT_PTR pluginHandleIpcMessage(HWND hwndML, int msg, INT_PTR param) +{ + switch (msg) + { + case ML_IPC_PL_GETRATING: return getPlRating(hwndML, param); + case ML_IPC_PL_SETRATING: return IPC_PL_SetRating(hwndML, param); + case ML_IPC_GETRATING: return IPC_GetRating(hwndML, param); + case ML_IPC_SETRATING: return IPC_SetRating(hwndML, param); + case ML_IPC_OPENPREFS: + { + extern prefsDlgRecW myPrefsItem; + SendMessage(plugin.hwndParent, WM_WA_IPC, (WPARAM)&myPrefsItem, IPC_OPENPREFSTOPAGE); + } + return 1; + + case ML_IPC_GETTREE: return IPC_GetTree(param); + case ML_IPC_TREEITEM_INSERT: + case ML_IPC_TREEITEM_INSERTW: + case ML_IPC_ADDTREEITEM_EX: + case ML_IPC_ADDTREEITEM: + case ML_IPC_TREEITEM_ADD: + case ML_IPC_TREEITEM_ADDW: return IPC_InsertView(msg, param); + case ML_IPC_SETTREEITEM_EX: + case ML_IPC_SETTREEITEM: + case ML_IPC_TREEITEM_SETINFO: + case ML_IPC_TREEITEM_SETINFOW: return (INT_PTR)IPC_SetView(msg, param); + case ML_IPC_TREEITEM_GETINFO: + case ML_IPC_TREEITEM_GETINFOW: return (INT_PTR)IPC_GetView(msg, param); + case ML_IPC_GETCURTREEITEM: return (INT_PTR)NavItemI_GetId(NavCtrlI_GetSelection(hNavigation)); + case ML_IPC_TREEITEM_GETSELECTED: return (INT_PTR)NavCtrlI_GetSelection(hNavigation); + case ML_IPC_SETCURTREEITEM: return (INT_PTR)NavItemI_Select(NavCtrlI_FindItem(hNavigation, (INT)param)); + case ML_IPC_TREEITEM_SELECT: return (INT_PTR)NavItemI_Select((HNAVITEM)param); + case ML_IPC_DELTREEITEM: return (INT_PTR)NavCtrlI_DeleteItem(hNavigation, NavCtrlI_FindItem(hNavigation, (INT)param)); + case ML_IPC_TREEITEM_DELETE: return (INT_PTR)NavCtrlI_DeleteItem(hNavigation, (HNAVITEM)param); + case ML_IPC_TREEITEM_GETHANDLE: return (INT_PTR)NavCtrlI_FindItem(hNavigation, (INT)param); + case ML_IPC_TREEITEM_GETCHILD: return (INT_PTR)((MLTI_ROOT == param) ? NavCtrlI_GetRoot(hNavigation) : NavItemI_GetChild((HNAVITEM)param)); + case ML_IPC_TREEITEM_GETNEXT: return (INT_PTR)NavItemI_GetNext((HNAVITEM)param); + case ML_IPC_TREEITEM_GETCHILD_ID: return (INT_PTR)NavItemI_GetId(NavItemI_GetChild(NavCtrlI_FindItem(hNavigation, (INT)param))); + case ML_IPC_TREEITEM_GETNEXT_ID: return (INT_PTR)NavItemI_GetId(NavItemI_GetNext(NavCtrlI_FindItem(hNavigation, (INT)param))); + case ML_IPC_TREEITEM_GETROOT: return (INT_PTR)NavCtrlI_GetRoot(hNavigation); + case ML_IPC_ADDTREEIMAGE: + case ML_IPC_TREEIMAGE_ADD: return (INT_PTR)IPC_RegisterNavigationImage(param); + case ML_IPC_GET_VIEW_BUTTON_TEXT: + { + if (!playBuf[0]) WASABI_API_LNGSTRINGW_BUF(IDS_PLAY, playBuf, ARRAYSIZE(playBuf)); + if (!enqueueBuf[0]) WASABI_API_LNGSTRINGW_BUF(IDS_ENQUEUE, enqueueBuf, ARRAYSIZE(enqueueBuf)); + + viewButtons *view = (viewButtons*)param; + if (view) + { + view->play = playBuf; + view->enqueue = enqueueBuf; + } + break; + } + + case ML_IPC_HANDLEDRAG: return IPC_HandleDrag(hwndML, (mlDropItemStruct*)param); + case ML_IPC_HANDLEDROP: // hmm, not sure bout this one =) + return IPC_HandleDrop(param); + + case ML_IPC_SKIN_COMBOBOX: return (param) ? (INT_PTR)(new ComboSkin((HWND)param)) : NULL; + case ML_IPC_UNSKIN_COMBOBOX: if (param) delete reinterpret_cast(param); return (NULL != param); + case ML_IPC_SKIN_LISTVIEW: return (SkinWindowEx((HWND)param, SKINNEDWND_TYPE_LISTVIEW, SWS_USESKINCOLORS | SWS_USESKINCURSORS | SWLVS_FULLROWSELECT | SWLVS_DOUBLEBUFFER)) ? param : NULL; + case ML_IPC_UNSKIN_LISTVIEW: return UnskinWindow((HWND)param); + case ML_IPC_LISTVIEW_UPDATE: return MLSkinnedScrollWnd_UpdateBars((HWND)param, TRUE); + case ML_IPC_LISTVIEW_DISABLEHSCROLL:return MLSkinnedScrollWnd_ShowHorzBar((HWND)param, FALSE); + case ML_IPC_LISTVIEW_DISABLEVSCROLL:return MLSkinnedScrollWnd_ShowVertBar((HWND)param, FALSE); + + case ML_IPC_LISTVIEW_SHOWSORT: + if (param && ((LV_SKIN_SHOWSORT*)param)->listView) + { + if (((LV_SKIN_SHOWSORT*)param)->showSort) return TRUE; + return SendMessageW((HWND)((LV_SKIN_SHOWSORT*)param)->listView, WM_ML_IPC, + MAKEWPARAM((((LV_SKIN_SHOWSORT*)param)->showSort) ? 0 : -1, TRUE), + ML_IPC_SKINNEDLISTVIEW_DISPLAYSORT); + } + return 0; + case ML_IPC_LISTVIEW_SORT: + if (param && ((LV_SKIN_SORT*)param)->listView) + { + return SendMessageW((HWND)((LV_SKIN_SHOWSORT*)param)->listView, WM_ML_IPC, + MAKEWPARAM(((LV_SKIN_SORT*)param)->columnIndex, ((LV_SKIN_SORT*)param)->ascending), + ML_IPC_SKINNEDLISTVIEW_DISPLAYSORT); + } + return 0; + + case ML_IPC_SENDTOWINAMP: + return IPC_SendToWinamp(param); + + case ML_IPC_SKIN_WADLG_GETFUNC: + switch (param) + { + case 1: return (INT_PTR)WADlg_getColor; + case 2: return (INT_PTR)WADlg_handleDialogMsgs; + case 3: return (INT_PTR)WADlg_DrawChildWindowBorders; + case 4: return (INT_PTR)WADlg_getBitmap; + case 32: return (INT_PTR)childresize_init; + case 33: return (INT_PTR)childresize_resize; + case 66: + if (g_config->ReadInt(L"plfont_everywhere", 1)) + { + INT height = (INT)SendMessageW(plugin.hwndParent, WM_WA_IPC, 3, IPC_GET_GENSKINBITMAP); + DWORD charset = (DWORD)SendMessageW(plugin.hwndParent, WM_WA_IPC, 2, IPC_GET_GENSKINBITMAP); + char *fontname = (char*)SendMessageW(plugin.hwndParent, WM_WA_IPC, 1, IPC_GET_GENSKINBITMAP); + return (INT_PTR)CreateFontA(-height, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, charset, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DRAFT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, fontname); + } + return 0; + + } + break; + case ML_IPC_ADDTOSENDTO: + if (param) + { + mlAddToSendToStruct *p = (mlAddToSendToStruct *)param; + if (p->context) + { + SendToMenu *a = (SendToMenu*)p->context; + a->onAddItem(p); + } + } + break; + case ML_IPC_ADDTOSENDTOW: + if (param) + { + mlAddToSendToStructW *p = (mlAddToSendToStructW *)param; + if (p->context) + { + SendToMenu *a = (SendToMenu*)p->context; + a->onAddItem(p); + } + } + break; + case ML_IPC_BRANCHSENDTO: + if (param) + { + mlAddToSendToStructW *p = (mlAddToSendToStructW *)param; + if (p->context) + { + SendToMenu *a = (SendToMenu*)p->context; + if (p->desc) + a->endBranch(p->desc); + else + a->startBranch(); + } + } + break; + case ML_IPC_ADDTOBRANCH: + if (param) + { + mlAddToSendToStructW *p = (mlAddToSendToStructW *)param; + if (p->context) + { + SendToMenu *a = (SendToMenu*)p->context; + a->addItemToBranch(p); + } + } + break; + + ///////////// plugin adding/removing + case ML_IPC_ADD_PLUGIN: + case ML_IPC_REMOVE_PLUGIN: + if (param) + { + winampMediaLibraryPlugin *p = (winampMediaLibraryPlugin *)param; + int x, l = m_plugins.GetSize(); + for (x = 0; x < l && m_plugins.Get(x) != (void *)param; x ++); + if (x < l) + { + if (msg == ML_IPC_ADD_PLUGIN) return -1; + m_plugins.Del(x); // remove this plugin + return 1; + } + if (msg == ML_IPC_REMOVE_PLUGIN) return -1; //plugin not found to delete + + if (p->version > MLHDR_VER && p->version < MLHDR_VER_OLD /*|| p->hDllInstance*/) return -1; //benski> cut the hDllInstance == 0 requirement, BUT you have to do a LoadLibrary to fake it out + + if (msg == ML_IPC_ADD_PLUGIN && p->hDllInstance) + { + // if they set the hDllInstance, do a LoadLibrary to increment the reference count + // that way we can FreeLibrary it safely later + TCHAR filename[MAX_PATH] = {0}; + GetModuleFileName(p->hDllInstance, filename, MAX_PATH); + HMODULE dummyLoad = LoadLibraryW(filename); + assert(dummyLoad == p->hDllInstance); + } + p->hwndLibraryParent = hwndML; + p->hwndWinampParent = plugin.hwndParent; + + m_plugins.Add(p); + return 1; + } + return -1; + case ML_IPC_SEND_PLUGIN_MESSAGE: + if (param) + { + pluginMessage *m = (pluginMessage*)param; + return plugin_SendMessage(m->messageType, m->param1, m->param2, m->param3); + } + return -1; + /* + case ML_IPC_GRACENOTE: + switch (param) + { + case GRACENOTE_TUID: return(INT_PTR)gracenoteGetTuid(); + case GRACENOTE_IS_WORKING: return(INT_PTR)gracenoteIsWorking(); + case GRACENOTE_DO_TIMER_STUFF: return(INT_PTR)gracenoteDoTimerStuff(); + case GRACENOTE_CANCEL_REQUEST: gracenoteCancelRequest(); return 1; + } + break; + */ + + case ML_IPC_REFRESH_PREFS: + refreshPrefs(param); + return 1; + + case ML_IPC_GETCURRENTVIEW: + return OnGetCurrentView(); + + case ML_IPC_ENSURE_VISIBLE: + if (!IsVisible()) + toggleVisible(); + return 1; + + case ML_IPC_IS_VISIBLE: + return IsVisible() ? 1 : 0; + + case ML_IPC_GET_PARENTAL_RATING: // TODO: store this in api_config + return g_config->ReadInt(L"tvrating", 7); + + case ML_IPC_TOGGLE_VISIBLE: + toggleVisible(); + return 1; + + case ML_IPC_FOCUS_TREE: + SetFocus(NavCtrlI_GetHWND(hNavigation)); + return 1; + + case ML_IPC_FLICKERFIX: return (param) ? OnFlickerFixIPC((FLICKERFIX*)param) : FALSE; + + case ML_IPC_GETVERSION: return PLUGIN_VERSION; + + case ML_IPC_SHOW_HELP: return MediaLibrary_OpenHelpUrl((const wchar_t*)param); + + case ML_IPC_IMAGELOADER_LOADDIB: return (INT_PTR)OnIPC_MLImageLoader_LoadDib((MLIMAGESOURCE*)param); + case ML_IPC_IMAGELOADER_COPYSTRUCT: return (INT_PTR)OnIPC_MLImageLoader_CopyStruct((MLIMAGESOURCECOPY*)param); + case ML_IPC_IMAGELOADER_FREESTRUCT: return (INT_PTR)OnIPC_MLImageLoader_FreeStruct((MLIMAGESOURCE*)param); + case ML_IPC_IMAGELOADER_CHECKEXIST: return (INT_PTR)OnIPC_MLImageLoader_CheckExist((MLIMAGESOURCE*)param); + + case ML_IPC_IMAGELIST_CREATE: return (INT_PTR)OnIPC_MLImageList_Create((MLIMAGELISTCREATE*)param); + case ML_IPC_IMAGELIST_DESTROY: return (INT_PTR)OnIPC_MLImageList_Destroy((HMLIMGLST)param); + case ML_IPC_IMAGELIST_GETREALLIST: return (INT_PTR)OnIPC_MLImageList_GetRealList((HMLIMGLST)param); + case ML_IPC_IMAGELIST_GETREALINDEX: return (INT_PTR)OnIPC_MLImageList_GetRealIndex((MLIMAGELISTREALINDEX*)param); + case ML_IPC_IMAGELIST_ADD: return (INT_PTR)OnIPC_MLImageList_Add((MLIMAGELISTITEM*)param); + case ML_IPC_IMAGELIST_REPLACE: return (INT_PTR)OnIPC_MLImageList_Replace((MLIMAGELISTITEM*)param); + case ML_IPC_IMAGELIST_REMOVE: return (INT_PTR)OnIPC_MLImageList_Remove((MLIMAGELISTITEM*)param); + case ML_IPC_IMAGELIST_GETIMAGESIZE: return (INT_PTR)OnIPC_MLImageList_GetImageSize((MLIMAGELISTIMAGESIZE*)param); + case ML_IPC_IMAGELIST_GETIMAGECOUNT: return (INT_PTR)OnIPC_MLImageList_GetImageCount((HMLIMGLST)param); + case ML_IPC_IMAGELIST_GETINDEXFROMTAG: return (INT_PTR)OnIPC_MLImageList_GetIndexFromTag((MLIMAGELISTTAG*)param); + case ML_IPC_IMAGELIST_GETTAGFROMINDEX: return (INT_PTR)OnIPC_MLImageList_GetTagFromIndex((MLIMAGELISTTAG*)param); + case ML_IPC_IMAGELIST_CHECKEXIST: return (INT_PTR)OnIPC_MLImageList_CheckItemExist((MLIMAGELISTITEM*)param); + case ML_IPC_IMAGEFILTER_REGISTER: return (INT_PTR)OnIPC_MLImageFilter_Register((MLIMAGEFILTERINFO*)param); + case ML_IPC_IMAGEFILTER_UNREGISTER: return (INT_PTR)OnIPC_MLImageFilter_Unregister((const GUID*)param); + case ML_IPC_IMAGEFILTER_GETINFO: return (INT_PTR)OnIPC_MLImageFilter_GetInfo((MLIMAGEFILTERINFO*)param); + case ML_IPC_IMAGEFILTER_APPLYEX: return (INT_PTR)OnIPC_MLImageFilter_ApplyEx((MLIMAGEFILTERAPPLYEX*)param); + + case ML_IPC_NAVCTRL_BEGINUPDATE: return (INT_PTR)OnIPC_MLNavCtrl_BeginUpdate((UINT)param); + case ML_IPC_NAVCTRL_DELETEITEM: return (INT_PTR)OnIPC_MLNavCtrl_DeleteItem((HNAVITEM)param); + case ML_IPC_NAVCTRL_ENDUPDATE: return (INT_PTR)OnIPC_MLNavCtrl_EndUpdate(); + case ML_IPC_NAVCTRL_ENUMITEMS: return (INT_PTR)OnIPC_MLNavCtrl_EnumItems((NAVCTRLENUMPARAMS*)param); + case ML_IPC_NAVCTRL_FINDITEMBYID: return (INT_PTR)OnIPC_MLNavCtrl_FindItemById((INT)param); + case ML_IPC_NAVCTRL_FINDITEMBYNAME: return (INT_PTR)OnIPC_MLNavCtrl_FindItemByName((NAVCTRLFINDPARAMS*)param); + case ML_IPC_NAVCTRL_GETIMAGELIST: return (INT_PTR)OnIPC_MLNavCtrl_GetImageList(); + case ML_IPC_NAVCTRL_GETFIRST: return (INT_PTR)OnIPC_MLNavCtrl_GetFirst(); + case ML_IPC_NAVCTRL_GETINDENT: return (INT_PTR)OnIPC_MLNavCtrl_GetIndent(); + case ML_IPC_NAVCTRL_GETSELECTION: return (INT_PTR)OnIPC_MLNavCtrl_GetSelection(); + case ML_IPC_NAVCTRL_GETHWND: return (INT_PTR)OnIPC_MLNavCtrl_GetHWND(); + case ML_IPC_NAVCTRL_HITTEST: return (INT_PTR)OnIPC_MLNavCtrl_HitTest((NAVHITTEST*)param); + case ML_IPC_NAVCTRL_INSERTITEM: return (INT_PTR)OnIPC_MLNavCtrl_InsertItem((NAVINSERTSTRUCT*)param); + case ML_IPC_NAVCTRL_ENDEDITTITLE: return (INT_PTR)OnIPC_MLNavCtrl_EndEditTitle((BOOL)param); + case ML_IPC_NAVCTRL_GETSTYLE: return (INT_PTR)OnIPC_MLNavCtrl_GetStyle(); + + case ML_IPC_NAVITEM_EDITTITLE: return (INT_PTR)OnIPC_MLNavItem_EditTitle((HNAVITEM)param); + case ML_IPC_NAVITEM_ENSUREVISIBLE: return (INT_PTR)OnIPC_MLNavItem_EnsureVisible((HNAVITEM)param); + case ML_IPC_NAVITEM_EXPAND: return (INT_PTR)OnIPC_MLNavItem_Expand((NAVITEMEXPAND*)param); + case ML_IPC_NAVITEM_GETCHILD: return (INT_PTR)OnIPC_MLNavItem_GetChild((HNAVITEM)param); + case ML_IPC_NAVITEM_GETCHILDRENCOUNT: return (INT_PTR)OnIPC_MLNavItem_GetChildrenCount((HNAVITEM)param); + case ML_IPC_NAVITEM_GETFULLNAME: return (INT_PTR)OnIPC_MLNavItem_GetFullName((NAVITEMFULLNAME*)param); + case ML_IPC_NAVITEM_GETINFO: return (INT_PTR)OnIPC_MLNavItem_GetInfo((NAVITEM*)param); + case ML_IPC_NAVITEM_GETNEXT: return (INT_PTR)OnIPC_MLNavItem_GetNext((HNAVITEM)param); + case ML_IPC_NAVITEM_GETORDER: return (INT_PTR)OnIPC_MLNavItem_GetOrder((HNAVITEM)param); + case ML_IPC_NAVITEM_GETPARENT: return (INT_PTR)OnIPC_MLNavItem_GetParent((HNAVITEM)param); + case ML_IPC_NAVITEM_GETPREVIOUS: return (INT_PTR)OnIPC_MLNavItem_GetPrevious((HNAVITEM)param); + case ML_IPC_NAVITEM_GETRECT: return (INT_PTR)OnIPC_MLNavItem_GetRect((NAVITEMGETRECT*)param); + case ML_IPC_NAVITEM_GETSTATE: return (INT_PTR)OnIPC_MLNavItem_GetState((HNAVITEM)param); + case ML_IPC_NAVITEM_HASCHILDRENREAL:return (INT_PTR)OnIPC_MLNavItem_HasChildrenReal((HNAVITEM)param); + case ML_IPC_NAVITEM_INVALIDATE: return (INT_PTR)OnIPC_MLNavItem_Invalidate((NAVITEMINAVLIDATE*)param); + case ML_IPC_NAVITEM_MOVE: return (INT_PTR)OnIPC_MLNavItem_Move((NAVITEMMOVE*)param); + case ML_IPC_NAVITEM_SELECT: return (INT_PTR)OnIPC_MLNavItem_Select((HNAVITEM)param); + case ML_IPC_NAVITEM_SETINFO: return (INT_PTR)OnIPC_MLNavItem_SetInfo((NAVITEM*)param); + case ML_IPC_NAVITEM_SETORDER: return (INT_PTR)OnIPC_MLNavItem_SetOrder((NAVITEMORDER*)param); + + case ML_IPC_RATING_DRAW: return (INT_PTR)OnIPC_MLRating_Draw((RATINGDRAWPARAMS*)param); + case ML_IPC_RATING_HITTEST: return (INT_PTR)OnIPC_MLRating_Hittest((RATINGHITTESTPARAMS*)param); + case ML_IPC_RATING_CALCRECT: return (INT_PTR)OnIPC_MLRating_CalcRect((RECT*)param); + + case ML_IPC_CLOUD_DRAW: return (INT_PTR)OnIPC_MLCloud_Draw((CLOUDDRAWPARAMS*)param); + case ML_IPC_CLOUD_CALCRECT: return (INT_PTR)OnIPC_MLCloud_CalcRect((RECT*)param); + + case ML_IPC_SKINWINDOW: return (INT_PTR)OnIPC_SkinWindow((MLSKINWINDOW*)param); + case ML_IPC_UNSKINWINDOW: return (INT_PTR)OnIPC_UnskinWindow((HWND)param); + + case ML_IPC_TRACKSKINNEDPOPUPEX: return (INT_PTR)OnIPC_TrackSkinnedPopup((MLSKINNEDPOPUP*)param); + case ML_IPC_INITSKINNEDPOPUPHOOK: return (INT_PTR)OnIPC_InitSkinnedPopupHook((MLSKINNEDPOPUP*)param); + case ML_IPC_REMOVESKINNEDPOPUPHOOK: return (INT_PTR)OnIPC_RemoveSkinnedPopupHook((HANDLE)param); + + case ML_IPC_RATINGCOLUMN_PAINT: return (INT_PTR)OnIPC_RatingColumn_Paint((RATINGCOLUMNPAINT*)param); + case ML_IPC_RATINGCOLUMN_CLICK: return (INT_PTR)OnIPC_RatingColumn_OnClick((RATINGCOLUMN*)param); + case ML_IPC_RATINGCOLUMN_TRACK: return (INT_PTR)OnIPC_RatingColumn_OnTrack((RATINGCOLUMN*)param); + case ML_IPC_RATINGCOLUMN_CANCELTRACKING: return (INT_PTR)OnIPC_RatingColumn_CancelTracking((BOOL)param); + case ML_IPC_RATINGCOLUMN_BEGINDRAG: return (INT_PTR)OnIPC_RatingColumn_BeginDrag((RATINGCOLUMN*)param); + case ML_IPC_RATINGCOLUMN_DRAG: return (INT_PTR)OnIPC_RatingColumn_OnDrag((POINT*)param); + case ML_IPC_RATINGCOLUMN_ENDDRAG: return (INT_PTR)OnIPC_RatingColumn_OnEndDrag((RATINGCOLUMN*)param); + case ML_IPC_RATINGCOLUMN_ANIMATE: return (INT_PTR)OnIPC_RatingColumn_Animate((RATINGCOLUMN*)param); + case ML_IPC_RATINGCOLUMN_GETMINWIDTH: return (INT_PTR)OnIPC_RatingColumn_GetMinWidth(); + case ML_IPC_RATINGCOLUMN_FILLBACKSTRING: return (INT_PTR)OnIPC_RatingColumn_FillBackString((RATINGBACKTEXT*)param); + case ML_IPC_RATINGCOLUMN_GETWIDTH: return (INT_PTR)OnIPC_RatingColumn_GetWidth((RATINGWIDTH*)param); + + case ML_IPC_CLOUDCOLUMN_PAINT: return (INT_PTR)OnIPC_CloudColumn_Paint((RATINGCOLUMNPAINT*)param); + case ML_IPC_CLOUDCOLUMN_GETMINWIDTH: return (INT_PTR)OnIPC_CloudColumn_GetMinWidth(); + case ML_IPC_CLOUDCOLUMN_GETWIDTH: return (INT_PTR)OnIPC_CloudColumn_GetWidth((INT*)param); + + case ML_IPC_CREATEWEBINFO: return (INT_PTR)OnIPC_CreateWebInfo((WEBINFOCREATE*)param); + case ML_IPC_CREATEFILEVIEW: return (INT_PTR)OnIPC_CreateFileView((MLFILEVIEWCREATESTRUCT*)param); + } + return 0; +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/MediaLibraryCOM.cpp b/Src/Plugins/General/gen_ml/MediaLibraryCOM.cpp new file mode 100644 index 00000000..9d9b1b97 --- /dev/null +++ b/Src/Plugins/General/gen_ml/MediaLibraryCOM.cpp @@ -0,0 +1,100 @@ +#include "main.h" +#include "MediaLibraryCOM.h" +#include "OnlineMediaCOM.h" + + +extern "C" extern winampGeneralPurposePlugin plugin; + +extern prefsDlgRecW myPrefsItem; + +OnlineMediaCOM onlineMediaCOM; +enum +{ + DISP_ONLINEMEDIA = 312, + DISP_PREFERENCES, + DISP_HIDDEN, + +}; + +#define CHECK_ID(str, id) if (wcscmp(rgszNames[i], L##str) == 0) { rgdispid[i] = id; continue; } +HRESULT MediaLibraryCOM::GetIDsOfNames(REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgdispid) +{ + bool unknowns = false; + for (unsigned int i = 0;i != cNames;i++) + { + CHECK_ID("OnlineMedia", DISP_ONLINEMEDIA) + CHECK_ID("ShowPreferences", DISP_PREFERENCES); + CHECK_ID("hidden", DISP_HIDDEN); + + rgdispid[i] = DISPID_UNKNOWN; + unknowns = true; + + } + if (unknowns) + return DISP_E_UNKNOWNNAME; + else + return S_OK; +} + +HRESULT MediaLibraryCOM::GetTypeInfo(unsigned int itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo) +{ + return E_NOTIMPL; +} + +HRESULT MediaLibraryCOM::GetTypeInfoCount(unsigned int FAR * pctinfo) +{ + return E_NOTIMPL; +} + + +HRESULT MediaLibraryCOM::Invoke(DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, EXCEPINFO FAR * pexecinfo, unsigned int FAR *puArgErr) +{ + switch (dispid) + { + case DISP_HIDDEN: + VariantInit(pvarResult); + V_VT(pvarResult) = VT_BOOL; + V_BOOL(pvarResult) = !IsVisible(); + return S_OK; + case DISP_ONLINEMEDIA: + VariantInit(pvarResult); + V_VT(pvarResult) = VT_DISPATCH; + V_DISPATCH(pvarResult) = &onlineMediaCOM; + return S_OK; + case DISP_PREFERENCES: + SendMessage(plugin.hwndParent, WM_WA_IPC, (WPARAM)&myPrefsItem, IPC_OPENPREFSTOPAGE); + return S_OK; + } + return DISP_E_MEMBERNOTFOUND; +} + +STDMETHODIMP MediaLibraryCOM::QueryInterface(REFIID riid, PVOID *ppvObject) +{ + if (!ppvObject) + return E_POINTER; + + else if (IsEqualIID(riid, IID_IDispatch)) + *ppvObject = (IDispatch *)this; + else if (IsEqualIID(riid, IID_IUnknown)) + *ppvObject = this; + else + { + *ppvObject = NULL; + return E_NOINTERFACE; + } + + AddRef(); + return S_OK; +} + + +ULONG MediaLibraryCOM::AddRef(void) +{ + return 0; +} + + +ULONG MediaLibraryCOM::Release(void) +{ + return 0; +} diff --git a/Src/Plugins/General/gen_ml/MediaLibraryCOM.h b/Src/Plugins/General/gen_ml/MediaLibraryCOM.h new file mode 100644 index 00000000..84e5cd6e --- /dev/null +++ b/Src/Plugins/General/gen_ml/MediaLibraryCOM.h @@ -0,0 +1,19 @@ +#ifndef NULLSOFT_MEDIALIBRARYCOMH +#define NULLSOFT_MEDIALIBRARYCOMH + +#include + +class MediaLibraryCOM : public IDispatch +{ +public: + STDMETHOD(QueryInterface)(REFIID riid, PVOID *ppvObject); + STDMETHOD_(ULONG, AddRef)(void); + STDMETHOD_(ULONG, Release)(void); + // *** IDispatch Methods *** + STDMETHOD (GetIDsOfNames)(REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgdispid); + STDMETHOD (GetTypeInfo)(unsigned int itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo); + STDMETHOD (GetTypeInfoCount)(unsigned int FAR * pctinfo); + STDMETHOD (Invoke)(DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, EXCEPINFO FAR * pexecinfo, unsigned int FAR *puArgErr); +}; + +#endif \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/MusicID.cpp b/Src/Plugins/General/gen_ml/MusicID.cpp new file mode 100644 index 00000000..e55ee46a --- /dev/null +++ b/Src/Plugins/General/gen_ml/MusicID.cpp @@ -0,0 +1,658 @@ +#include "main.h" + +#include "MusicID.h" +#include +#include "../nu/AutoLock.h" + +#include +#include + +#include "api__gen_ml.h" + +static Nullsoft::Utility::LockGuard musicIDGuard; + +// {113D413A-5D1F-4f4c-8AB7-5BDED46033A4} +static const GUID developerConfigGroupGUID= +{ 0x113d413a, 0x5d1f, 0x4f4c, { 0x8a, 0xb7, 0x5b, 0xde, 0xd4, 0x60, 0x33, 0xa4 } }; + +class MyEventHandler; + +#ifndef IGNORE_API_GRACENOTE + +static void TestAddRef(IDispatch *d) +{ + try + { + ULONG l = d->AddRef(); +#ifdef _DEBUG + char t[55] = {0}; + sprintf(t, "+ %p %x\n", d, l); + OutputDebugStringA(t); +#endif + } + catch(...) + { + } +} + +static void TestRelease(IDispatch *d) +{ + try + { + ULONG l = d->Release(); + #ifdef _DEBUG + char t[55] = {0}; + sprintf(t, "- %p %x\n", d, l); + OutputDebugStringA(t); +#endif + } + catch(...) + { + } +} + + +static IConnectionPoint *GetConnectionPoint(IUnknown *punk, REFIID riid) +{ + if (!punk) + return 0; + + IConnectionPointContainer *pcpc; + IConnectionPoint *pcp = 0; + + HRESULT hr = punk->QueryInterface(IID_IConnectionPointContainer, (void **) & pcpc); + if (SUCCEEDED(hr)) + { + pcpc->FindConnectionPoint(riid, &pcp); + pcpc->Release(); + } + punk->Release(); + return pcp; +} + +// TODO: implement proper reference count so we don't leak the event handler & musicID objects + +static HANDLE DuplicateCurrentThread() +{ + HANDLE fakeHandle = GetCurrentThread(); + HANDLE copiedHandle = 0; + HANDLE processHandle = GetCurrentProcess(); + DuplicateHandle(processHandle, fakeHandle, processHandle, &copiedHandle, 0, FALSE, DUPLICATE_SAME_ACCESS); + return copiedHandle; +} + +static HRESULT FillTag(ICddbFileInfo *info, BSTR filename) +{ + ICddbID3TagPtr infotag = NULL; + infotag.CreateInstance(CLSID_CddbID3Tag); + + ICddbFileTag2_5Ptr tag2_5 = NULL; + infotag->QueryInterface(&tag2_5); + itemRecordW *record = 0; + + if (AGAVE_API_MLDB) + record = AGAVE_API_MLDB->GetFile(filename); + if (record && infotag && tag2_5) + { + wchar_t itemp[64] = {0}; + if (record->artist) + infotag->put_LeadArtist(record->artist); + + if (record->album) + infotag->put_Album(record->album); + + if (record->title) + infotag->put_Title(record->title); + + if (record->genre) + infotag->put_Genre(record->genre); + + if (record->track > 0) + infotag->put_TrackPosition(_itow(record->track, itemp, 10)); + + // TODO: if (record->tracks > 0) + if (record->year > 0) + infotag->put_Year(_itow(record->year, itemp, 10)); + + if (record->publisher) + infotag->put_Label(record->publisher); + + /* + if (GetFileInfo(filename, L"ISRC", meta, 512) && meta[0]) + infotag->put_ISRC(meta); + */ + + if (record->disc > 0) + infotag->put_PartOfSet(_itow(record->disc, itemp, 10)); + + if (record->albumartist) + tag2_5->put_DiscArtist(record->albumartist); + + if (record->composer) + tag2_5->put_Composer(record->composer); + + if (record->length > 0) + tag2_5->put_LengthMS(_itow(record->length*1000, itemp, 10)); + + if (record->bpm > 0) + infotag->put_BeatsPerMinute(_itow(record->bpm, itemp, 10)); + + /* + if (GetFileInfo(filename, L"conductor", meta, 512) && meta[0]) + tag2_5->put_Conductor(meta); + */ + AGAVE_API_MLDB->FreeRecord(record); + } + + if (info) info->put_Tag(infotag); + + return S_OK; +} + +struct Blah +{ + IDispatch *callback; + CComBSTR filename, tagID, artist; +}; + +static VOID CALLBACK InvokeAPC(ULONG_PTR param) +{ + Blah *blah = (Blah *)param; + VARIANT arguments[2]; + VariantInit(&arguments[0]); + arguments[0].vt = VT_BSTR; + arguments[0].bstrVal = blah->tagID; + + VariantInit(&arguments[1]); + arguments[1].vt = VT_BSTR; + arguments[1].bstrVal = blah->filename; + + DISPPARAMS params; + params.cArgs = 2; + params.cNamedArgs = 0; + params.rgdispidNamedArgs = 0; + params.rgvarg = arguments; + unsigned int ret; + + if (!ieDisableSEH) + { + ieDisableSEH = AGAVE_API_CONFIG->GetItem(developerConfigGroupGUID, L"no_ieseh"); + } + + OLECHAR *setID = L"SetID", *setArtist = L"SetArtist", *onFinish=L"OnFinish"; + DISPID dispid; + if (ieDisableSEH->GetBool() == false) + { + try + { + if (SUCCEEDED(blah->callback->GetIDsOfNames(IID_NULL, &setID, 1, LOCALE_SYSTEM_DEFAULT, &dispid))) + blah->callback->Invoke(dispid, GUID_NULL, 0, DISPATCH_METHOD, ¶ms, 0, 0, &ret); + else + blah->callback->Invoke(0, GUID_NULL, 0, DISPATCH_METHOD, ¶ms, 0, 0, &ret); + + arguments[0].bstrVal = blah->artist; + if (SUCCEEDED(blah->callback->GetIDsOfNames(IID_NULL, &setArtist, 1, LOCALE_SYSTEM_DEFAULT, &dispid))) + blah->callback->Invoke(dispid, GUID_NULL, 0, DISPATCH_METHOD, ¶ms, 0, 0, &ret); + + arguments[0].bstrVal = blah->filename; + params.cArgs=1; + if (SUCCEEDED(blah->callback->GetIDsOfNames(IID_NULL, &onFinish, 1, LOCALE_SYSTEM_DEFAULT, &dispid))) + blah->callback->Invoke(dispid, GUID_NULL, 0, DISPATCH_METHOD, ¶ms, 0, 0, &ret); + + } + catch (...) + { + } + } + else + { + if (SUCCEEDED(blah->callback->GetIDsOfNames(IID_NULL, &setID, 1, LOCALE_SYSTEM_DEFAULT, &dispid))) + blah->callback->Invoke(dispid, GUID_NULL, 0, DISPATCH_METHOD, ¶ms, 0, 0, &ret); + else + blah->callback->Invoke(0, GUID_NULL, 0, DISPATCH_METHOD, ¶ms, 0, 0, &ret); + + arguments[0].bstrVal = blah->artist; + if (SUCCEEDED(blah->callback->GetIDsOfNames(IID_NULL, &setArtist, 1, LOCALE_SYSTEM_DEFAULT, &dispid))) + blah->callback->Invoke(dispid, GUID_NULL, 0, DISPATCH_METHOD, ¶ms, 0, 0, &ret); + + arguments[0].bstrVal = blah->filename; + params.cArgs=1; + if (SUCCEEDED(blah->callback->GetIDsOfNames(IID_NULL, &onFinish, 1, LOCALE_SYSTEM_DEFAULT, &dispid))) + blah->callback->Invoke(dispid, GUID_NULL, 0, DISPATCH_METHOD, ¶ms, 0, 0, &ret); + } + + delete blah; +} + +class MyEventHandler : public _ICDDBMusicIDManagerEvents +{ +public: + MyEventHandler(BSTR _filename, IDispatch *_callback, HANDLE _handle) : callback(_callback), threadHandle(_handle) + { + filename = SysAllocString(_filename); + refCount = 1; + TestAddRef(callback); + } + + BSTR filename; + ICDDBMusicIDManager3 *musicID; + HANDLE threadHandle; + IDispatch *callback; + + ULONG STDMETHODCALLTYPE AddRef(void) + { + return InterlockedIncrement(&refCount); + } + + ULONG STDMETHODCALLTYPE Release(void) + { + LONG lRef = InterlockedDecrement(&refCount); + if (lRef == 0) + delete this; + return lRef; + } + +private: + ~MyEventHandler() + { + SysFreeString(filename); + CloseHandle(threadHandle); + TestRelease(callback); + } + LONG refCount; + + STDMETHODIMP STDMETHODCALLTYPE QueryInterface(REFIID riid, PVOID *ppvObject) + { + if (!ppvObject) + return E_POINTER; + + else if (IsEqualIID(riid, __uuidof(_ICDDBMusicIDManagerEvents))) + *ppvObject = (_ICDDBMusicIDManagerEvents *)this; + else if (IsEqualIID(riid, IID_IDispatch)) + *ppvObject = (IDispatch *)this; + else if (IsEqualIID(riid, IID_IUnknown)) + *ppvObject = this; + else + { + *ppvObject = NULL; + return E_NOINTERFACE; + } + + AddRef(); + return S_OK; + } + + + + HRESULT STDMETHODCALLTYPE Invoke(DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, EXCEPINFO FAR * pexecinfo, unsigned int FAR *puArgErr) + { + switch (dispid) + { + case 1: // OnTrackIDStatusUpdate, params: CddbMusicIDStatus Status, BSTR filename, long* Abort + break; + case 2: // OnAlbumIDStatusUpdate, params: CddbMusicIDStatus Status, BSTR filename, long current_file, long total_files, long* Abort + { + //long *abort = pdispparams->rgvarg[0].plVal; + //long total_files = pdispparams->rgvarg[1].lVal; + //long current_file= pdispparams->rgvarg[2].lVal; + CddbMusicIDStatus status = (CddbMusicIDStatus)pdispparams->rgvarg[4].lVal; + BSTR filename = pdispparams->rgvarg[3].bstrVal; + } + break; + case 3: // OnTrackIDComplete, params: LONG match_code, ICddbFileInfo* pInfoIn, ICddbFileInfoList* pListOut + { + IDispatch *disp1 =pdispparams->rgvarg[0].pdispVal; + IDispatch *disp2 =pdispparams->rgvarg[1].pdispVal; + //long match_code = pdispparams->rgvarg[2].lVal; + ICddbFileInfoPtr pInfoIn; + ICddbFileInfoListPtr matchList; + disp1->QueryInterface(&matchList); + disp2->QueryInterface(&pInfoIn); + } + break; + case 4: // OnAlbumIDComplete, params: LONG match_code, ICddbFileInfoList* pListIn, ICddbFileInfoLists* pListsOut + { + IDispatch *disp1 =pdispparams->rgvarg[0].pdispVal; + IDispatch *disp2 =pdispparams->rgvarg[1].pdispVal; + //long match_code = pdispparams->rgvarg[2].lVal; + ICddbFileInfoListPtr pListIn; + ICddbFileInfoListsPtr pListsOut; + disp1->QueryInterface(&pListsOut); + disp2->QueryInterface(&pListIn); + } + break; + + case 10: // OnGetFingerprintInfo + { + long *abort = pdispparams->rgvarg[0].plVal; + IDispatch *disp = pdispparams->rgvarg[1].pdispVal; + BSTR filename = pdispparams->rgvarg[2].bstrVal; + + ICddbFileInfo *info; + disp->QueryInterface(&info); + return AGAVE_API_GRACENOTE->CreateFingerprint(musicID, AGAVE_API_DECODE, info, filename, abort); + } + break; + case 11: // OnGetTagInfo + { + //long *Abort = pdispparams->rgvarg[0].plVal; + IDispatch *disp = pdispparams->rgvarg[1].pdispVal; + BSTR filename = pdispparams->rgvarg[2].bstrVal; + + ICddbFileInfo *info; + disp->QueryInterface(&info); + return FillTag(info, filename); + } + break; + } + return DISP_E_MEMBERNOTFOUND; + } + + HRESULT STDMETHODCALLTYPE GetIDsOfNames(REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgdispid) + { + *rgdispid = DISPID_UNKNOWN; + return DISP_E_UNKNOWNNAME; + } + + HRESULT STDMETHODCALLTYPE GetTypeInfo(unsigned int itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo) + { + return E_NOTIMPL; + } + + HRESULT STDMETHODCALLTYPE GetTypeInfoCount(unsigned int FAR * pctinfo) + { + return E_NOTIMPL; + } + +}; + +static VOID CALLBACK ReleaseAPC(ULONG_PTR param) +{ + MyEventHandler *handler = (MyEventHandler *)param; + handler->Release(); +} + +enum +{ + DISPATCH_MUSICID_GETID = 777, +}; + + +static std::vector tracks; +static HANDLE musicid_killswitch=0, musicid_trigger=0; +static ThreadID *musicIdThread=0; + +class MusicIDContext +{ +public: + MusicIDContext() + { + musicID=0; + com_initted=false; + } + bool Init(); + void Tick(); + void Quit(); + ICDDBMusicIDManager3 *musicID; + bool com_initted; +}; +static MusicIDContext context; + +bool MusicIDContext::Init() +{ + if (SUCCEEDED(CoInitialize(0))) + com_initted=true; + + musicID=AGAVE_API_GRACENOTE->GetMusicID(); + return !!musicID; +} + +void MusicIDContext::Tick() +{ + musicIDGuard.Lock(); + if (tracks.empty()) + { + musicIDGuard.Unlock(); + return; + } + MyEventHandler *track = tracks.front(); + tracks.pop_front(); + musicIDGuard.Unlock(); + + if (!musicID) + { + Blah *blah = new Blah; + blah->filename = SysAllocString(track->filename); + blah->tagID = L""; + blah->artist = L""; + blah->callback=track->callback; + + QueueUserAPC(InvokeAPC, track->threadHandle, (ULONG_PTR)blah); + QueueUserAPC(ReleaseAPC, track->threadHandle, (ULONG_PTR)track); + return; + } + track->musicID = musicID; + IConnectionPoint *icp = GetConnectionPoint(musicID, DIID__ICDDBMusicIDManagerEvents); + + DWORD m_dwCookie=0; + + if (icp) + { + icp->Advise(static_cast(track), &m_dwCookie); + //icp->Release(); + } + + ICddbFileInfoPtr info; + info.CreateInstance(CLSID_CddbFileInfo); + if (info == 0) + { + QueueUserAPC(ReleaseAPC, track->threadHandle, (ULONG_PTR)track); + return ; + } + info->put_Filename(track->filename); + ICddbFileInfoListPtr matchList; + long match_code=666; + + musicID->TrackID(info, MUSICID_RETURN_SINGLE|MUSICID_GET_TAG_FROM_APP|MUSICID_GET_FP_FROM_APP|MUSICID_PREFER_WF_MATCHES, &match_code, &matchList); + + if (matchList) + { + long matchcount; + matchList->get_Count(&matchcount); + assert(matchcount==1); + for (int j = 1;j <= matchcount;j++) + { + ICddbFileInfoPtr match; + matchList->GetFileInfo(j, &match); + + Blah *blah = new Blah; + + match->get_Filename(&blah->filename); + ICddbFileTagPtr tag; + match->get_Tag(&tag); + tag->get_FileId(&blah->tagID); + tag->get_LeadArtist(&blah->artist); + blah->callback=track->callback; + if (blah->tagID && AGAVE_API_MLDB) + AGAVE_API_MLDB->SetField(blah->filename, "GracenoteFileID", blah->tagID); + + QueueUserAPC(InvokeAPC, track->threadHandle, (ULONG_PTR)blah); + + if (AGAVE_API_MLDB) + AGAVE_API_MLDB->Sync(); + //TODO: optionally write metadata to file permanently + } + } + + if (icp) + { + icp->Unadvise(m_dwCookie); + } + QueueUserAPC(ReleaseAPC, track->threadHandle, (ULONG_PTR)track); +} + +void MusicIDContext::Quit() +{ + if (musicID) + { + musicID->Shutdown(); + musicID->Release(); + musicID=0; + } + if (com_initted) + { + CoUninitialize(); + com_initted=false; + } +} + +static int MusicIDTickThreadPoolFunc(HANDLE handle, void *user_data, intptr_t id) +{ + MusicIDContext *context = (MusicIDContext *)user_data; + context->Tick(); + return 0; +} + +static int MusicIDInitThreadPoolFunc(HANDLE handle, void *user_data, intptr_t id) +{ + MusicIDContext *context = (MusicIDContext *)user_data; + if (context->Init()) + { + AGAVE_API_THREADPOOL->AddHandle(musicIdThread, musicid_trigger, MusicIDTickThreadPoolFunc, user_data, id, 0); + } + + return 0; +} + +static int MusicIDQuitThreadPoolFunc(HANDLE handle, void *user_data, intptr_t id) +{ + MusicIDContext *context = (MusicIDContext *)user_data; + AGAVE_API_THREADPOOL->RemoveHandle(musicIdThread, musicid_trigger); + context->Quit(); + SetEvent(musicid_killswitch); + return 0; +} + +HRESULT MusicIDCOM::GetIDsOfNames(REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgdispid) +{ + bool unknowns = false; + for (unsigned int i = 0;i != cNames;i++) + { + if (wcscmp(rgszNames[i], L"GetID") == 0) + rgdispid[i] = DISPATCH_MUSICID_GETID; + else + { + rgdispid[i] = DISPID_UNKNOWN; + unknowns = true; + } + + } + if (unknowns) + return DISP_E_UNKNOWNNAME; + else + return S_OK; +} + +HRESULT MusicIDCOM::GetTypeInfo(unsigned int itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo) +{ + return E_NOTIMPL; +} + +HRESULT MusicIDCOM::GetTypeInfoCount(unsigned int FAR * pctinfo) +{ + return E_NOTIMPL; +} + +HRESULT MusicIDCOM::Invoke(DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, EXCEPINFO FAR * pexecinfo, unsigned int FAR *puArgErr) +{ + if (dispid == DISPATCH_MUSICID_GETID) + { + if (pdispparams->cArgs != 2) + return DISP_E_BADPARAMCOUNT; + + if (pdispparams->rgvarg[0].vt != VT_DISPATCH) + return DISP_E_TYPEMISMATCH; + + if (pdispparams->rgvarg[1].vt != VT_BSTR) + return DISP_E_TYPEMISMATCH; + + if (!AGAVE_API_GRACENOTE) + return E_FAIL; + + if (!musicIdThread) + { + musicIDGuard.Lock(); + if (!musicIdThread) + { + musicIdThread = AGAVE_API_THREADPOOL->ReserveThread(api_threadpool::FLAG_REQUIRE_COM_STA); + musicid_trigger = CreateSemaphore(0, 0, 65536, 0); + AGAVE_API_THREADPOOL->RunFunction(musicIdThread, MusicIDInitThreadPoolFunc, &context, 0, 0); + } + musicIDGuard.Unlock(); + } + + MyEventHandler *event = new MyEventHandler(pdispparams->rgvarg[1].bstrVal, pdispparams->rgvarg[0].pdispVal, DuplicateCurrentThread()); + + musicIDGuard.Lock(); + tracks.push_back(event); + musicIDGuard.Unlock(); + ReleaseSemaphore(musicid_trigger, 1, 0); + + return S_OK; + } + + return DISP_E_MEMBERNOTFOUND; +} + +STDMETHODIMP MusicIDCOM::QueryInterface(REFIID riid, PVOID *ppvObject) +{ + if (!ppvObject) + return E_POINTER; + + else if (IsEqualIID(riid, IID_IDispatch)) + *ppvObject = (IDispatch *)this; + else if (IsEqualIID(riid, IID_IUnknown)) + *ppvObject = this; + else + { + *ppvObject = NULL; + return E_NOINTERFACE; + } + + AddRef(); + return S_OK; +} + +ULONG MusicIDCOM::AddRef(void) +{ + // return ++m_cRefs; + return 0; +} + +ULONG MusicIDCOM::Release(void) +{ /* + if (--m_cRefs) + return m_cRefs; + + delete this; + return 0;*/ + return 0; +} + +void MusicIDCOM::Quit() +{ + if (musicIdThread) + { + musicid_killswitch = CreateEvent(NULL, FALSE, FALSE, NULL); + AGAVE_API_THREADPOOL->RunFunction(musicIdThread, MusicIDQuitThreadPoolFunc, &context, 0, 0); + if (NULL != musicid_killswitch) + { + WaitForSingleObject(musicid_killswitch, INFINITE); + CloseHandle(musicid_killswitch); + } + + CloseHandle(musicid_trigger); + AGAVE_API_THREADPOOL->ReleaseThread(musicIdThread); + } +} +#endif \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/MusicID.h b/Src/Plugins/General/gen_ml/MusicID.h new file mode 100644 index 00000000..5cfdd9b4 --- /dev/null +++ b/Src/Plugins/General/gen_ml/MusicID.h @@ -0,0 +1,21 @@ +#ifndef NULLSOFT_ML_NOWPLAYING_MUSICID_H +#define NULLSOFT_ML_NOWPLAYING_MUSICID_H + +#include + +class MusicIDCOM : public IDispatch +{ +public: + void Quit(); + STDMETHOD(QueryInterface)(REFIID riid, PVOID *ppvObject); + STDMETHOD_(ULONG, AddRef)(void); + STDMETHOD_(ULONG, Release)(void); + // *** IDispatch Methods *** + STDMETHOD (GetIDsOfNames)(REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgdispid); + STDMETHOD (GetTypeInfo)(unsigned int itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo); + STDMETHOD (GetTypeInfoCount)(unsigned int FAR * pctinfo); + STDMETHOD (Invoke)(DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, EXCEPINFO FAR * pexecinfo, unsigned int FAR *puArgErr); +}; + + +#endif \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/OnlineMediaCOM.cpp b/Src/Plugins/General/gen_ml/OnlineMediaCOM.cpp new file mode 100644 index 00000000..8eefe7af --- /dev/null +++ b/Src/Plugins/General/gen_ml/OnlineMediaCOM.cpp @@ -0,0 +1,83 @@ +#include "main.h" +#include "OnlineMediaCOM.h" +#include "RatingsCOM.h" +RatingsCOM ratingsCOM; +enum +{ + DISP_RATINGS = 0, + +}; + +#define CHECK_ID(str, id) if (wcscmp(rgszNames[i], L##str) == 0) { rgdispid[i] = id; continue; } +HRESULT OnlineMediaCOM::GetIDsOfNames(REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgdispid) +{ + bool unknowns = false; + for (unsigned int i = 0;i != cNames;i++) + { + CHECK_ID("Ratings", DISP_RATINGS) + + rgdispid[i] = DISPID_UNKNOWN; + unknowns = true; + + } + if (unknowns) + return DISP_E_UNKNOWNNAME; + else + return S_OK; +} + +HRESULT OnlineMediaCOM::GetTypeInfo(unsigned int itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo) +{ + return E_NOTIMPL; +} + +HRESULT OnlineMediaCOM::GetTypeInfoCount(unsigned int FAR * pctinfo) +{ + return E_NOTIMPL; +} + + +HRESULT OnlineMediaCOM::Invoke(DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, EXCEPINFO FAR * pexecinfo, unsigned int FAR *puArgErr) +{ + switch (dispid) + { + case DISP_RATINGS: + VariantInit(pvarResult); + V_VT(pvarResult) = VT_DISPATCH; + V_DISPATCH(pvarResult) = &ratingsCOM; + return S_OK; + + } + return DISP_E_MEMBERNOTFOUND; +} + +STDMETHODIMP OnlineMediaCOM::QueryInterface(REFIID riid, PVOID *ppvObject) +{ + if (!ppvObject) + return E_POINTER; + + else if (IsEqualIID(riid, IID_IDispatch)) + *ppvObject = (IDispatch *)this; + else if (IsEqualIID(riid, IID_IUnknown)) + *ppvObject = this; + else + { + *ppvObject = NULL; + return E_NOINTERFACE; + } + + AddRef(); + return S_OK; +} + + +ULONG OnlineMediaCOM::AddRef(void) +{ + return 0; +} + + +ULONG OnlineMediaCOM::Release(void) +{ + return 0; +} diff --git a/Src/Plugins/General/gen_ml/OnlineMediaCOM.h b/Src/Plugins/General/gen_ml/OnlineMediaCOM.h new file mode 100644 index 00000000..5847056a --- /dev/null +++ b/Src/Plugins/General/gen_ml/OnlineMediaCOM.h @@ -0,0 +1,19 @@ +#ifndef NULLSOFT_ONLINEMEDIACOMH +#define NULLSOFT_ONLINEMEDIACOMH + +#include + +class OnlineMediaCOM : public IDispatch +{ +public: + STDMETHOD(QueryInterface)(REFIID riid, PVOID *ppvObject); + STDMETHOD_(ULONG, AddRef)(void); + STDMETHOD_(ULONG, Release)(void); + // *** IDispatch Methods *** + STDMETHOD (GetIDsOfNames)(REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgdispid); + STDMETHOD (GetTypeInfo)(unsigned int itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo); + STDMETHOD (GetTypeInfoCount)(unsigned int FAR * pctinfo); + STDMETHOD (Invoke)(DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, EXCEPINFO FAR * pexecinfo, unsigned int FAR *puArgErr); +}; + +#endif \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/RatingsCOM.cpp b/Src/Plugins/General/gen_ml/RatingsCOM.cpp new file mode 100644 index 00000000..67803475 --- /dev/null +++ b/Src/Plugins/General/gen_ml/RatingsCOM.cpp @@ -0,0 +1,119 @@ +#include "main.h" +#include "RatingsCOM.h" +#include "config.h" +extern C_Config *g_config; +enum +{ + DISP_GETRATING = 0, + DISP_ISEVERYONEALLOWED, + DISP_ISTEENALLOWED, + DISP_ISADULTALLOWED, + DISP_ISXRATEDALLOWED, + DISP_ISNOTRATEDALLOWED, + +}; + +#define CHECK_ID(str, id) if (wcscmp(rgszNames[i], L##str) == 0) { rgdispid[i] = id; continue; } +HRESULT RatingsCOM::GetIDsOfNames(REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgdispid) +{ + bool unknowns = false; + for (unsigned int i = 0;i != cNames;i++) + { + CHECK_ID("GetRating", DISP_GETRATING); + CHECK_ID("IsEveryoneAllowed", DISP_ISEVERYONEALLOWED); + CHECK_ID("IsTeenAllowed", DISP_ISTEENALLOWED); + CHECK_ID("IsAdultAllowed", DISP_ISADULTALLOWED); + CHECK_ID("IsXRatedAllowed", DISP_ISXRATEDALLOWED); + CHECK_ID("IsNotRatedAllowed", DISP_ISNOTRATEDALLOWED); + + rgdispid[i] = DISPID_UNKNOWN; + unknowns = true; + + } + if (unknowns) + return DISP_E_UNKNOWNNAME; + else + return S_OK; +} + +HRESULT RatingsCOM::GetTypeInfo(unsigned int itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo) +{ + return E_NOTIMPL; +} + +HRESULT RatingsCOM::GetTypeInfoCount(unsigned int FAR * pctinfo) +{ + return E_NOTIMPL; +} + + +HRESULT RatingsCOM::Invoke(DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, EXCEPINFO FAR * pexecinfo, unsigned int FAR *puArgErr) +{ + int rating = g_config->ReadInt(L"tvrating", 7); + switch (dispid) + { + case DISP_GETRATING: + VariantInit(pvarResult); + V_VT(pvarResult) = VT_I4; + V_I4(pvarResult) = rating; + return S_OK; + + case DISP_ISEVERYONEALLOWED: + VariantInit(pvarResult); + V_VT(pvarResult) = VT_BOOL; + V_BOOL(pvarResult) = (rating & 1) || (rating & 2); + return S_OK; + case DISP_ISTEENALLOWED: + VariantInit(pvarResult); + V_VT(pvarResult) = VT_BOOL; + V_BOOL(pvarResult) = rating & 4; + return S_OK; + case DISP_ISADULTALLOWED: + VariantInit(pvarResult); + V_VT(pvarResult) = VT_BOOL; + V_BOOL(pvarResult) = rating & 8; + return S_OK; + case DISP_ISXRATEDALLOWED: + VariantInit(pvarResult); + V_VT(pvarResult) = VT_BOOL; + V_BOOL(pvarResult) = (rating & 16) || (rating & 32); + return S_OK; + case DISP_ISNOTRATEDALLOWED: + VariantInit(pvarResult); + V_VT(pvarResult) = VT_BOOL; + V_BOOL(pvarResult) = rating & 64 ; + return S_OK; + } + return DISP_E_MEMBERNOTFOUND; +} + +STDMETHODIMP RatingsCOM::QueryInterface(REFIID riid, PVOID *ppvObject) +{ + if (!ppvObject) + return E_POINTER; + + else if (IsEqualIID(riid, IID_IDispatch)) + *ppvObject = (IDispatch *)this; + else if (IsEqualIID(riid, IID_IUnknown)) + *ppvObject = this; + else + { + *ppvObject = NULL; + return E_NOINTERFACE; + } + + AddRef(); + return S_OK; +} + + +ULONG RatingsCOM::AddRef(void) +{ + return 0; +} + + +ULONG RatingsCOM::Release(void) +{ + return 0; +} diff --git a/Src/Plugins/General/gen_ml/RatingsCOM.h b/Src/Plugins/General/gen_ml/RatingsCOM.h new file mode 100644 index 00000000..41b45100 --- /dev/null +++ b/Src/Plugins/General/gen_ml/RatingsCOM.h @@ -0,0 +1,19 @@ +#ifndef NULLSOFT_RATINGSCOMH +#define NULLSOFT_RATINGSCOMH + +#include + +class RatingsCOM : public IDispatch +{ +public: + STDMETHOD(QueryInterface)(REFIID riid, PVOID *ppvObject); + STDMETHOD_(ULONG, AddRef)(void); + STDMETHOD_(ULONG, Release)(void); + // *** IDispatch Methods *** + STDMETHOD (GetIDsOfNames)(REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgdispid); + STDMETHOD (GetTypeInfo)(unsigned int itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo); + STDMETHOD (GetTypeInfoCount)(unsigned int FAR * pctinfo); + STDMETHOD (Invoke)(DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, EXCEPINFO FAR * pexecinfo, unsigned int FAR *puArgErr); +}; + +#endif \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/SmoothScrollList.cpp b/Src/Plugins/General/gen_ml/SmoothScrollList.cpp new file mode 100644 index 00000000..67e8e6ac --- /dev/null +++ b/Src/Plugins/General/gen_ml/SmoothScrollList.cpp @@ -0,0 +1,1837 @@ +#include "main.h" + +#include "../Winamp/gen.h" +#include "../gen_ml/ml.h" +#include "../gen_ml/ml_ipc_0313.h" + +#define WM_EX_GETREALLIST (WM_USER + 0x01) +#define WM_EX_UNLOCKREDRAW (WM_USER + 0x02) +#define WM_EX_UPDATESCROLLINFO (WM_USER + 0x03) +#define WM_EX_GETCOUNTPERPAGE (WM_USER + 0x04) + +#define LVN_EX_SIZECHANGED (LVN_LAST) + +#define IWF_NORMAL 0x0000 +#define IWF_ERASE 0x0001 +#define IWF_UPDATENOW 0x0002 +#define IWF_FRAME 0x0004 + + +typedef enum ScrollPosFlags +{ + SPF_NORMAL = 0, + SPF_NOREDRAW = (1 << 0), + SPF_FORCE = (1 << 1), + SPF_RELATIVE = (1 << 2), +} ScrollPosFlags; +DEFINE_ENUM_FLAG_OPERATORS(ScrollPosFlags); + +BOOL +CopyListColumnToHeaderItem(const LVCOLUMNW *column, HDITEMW *item); +BOOL +CopyHeaderItemToListColumn(const HDITEMW *item, LVCOLUMNW *column); + + +typedef enum PostProcessKeyCommands +{ + PostProcessKeyCmd_Nothing = 0, + PostProcessKeyCmd_UpdateScrollPos = (1 << 0), + PostProcessKeyCmd_EnsureFocusVisible = (1 << 1), +} PostProcessKeyCommands; +DEFINE_ENUM_FLAG_OPERATORS(PostProcessKeyCommands); + +typedef struct SmoothScrollList +{ + unsigned int itemHeight; + unsigned int textHeight; + long viewHeight; + unsigned int listFontHeight; + unsigned int headerFontHeight; + int wheelCarryover; +} SmoothScrollList; + +#define GetUserData(hwnd) ((SmoothScrollList*)(LONG_PTR)GetWindowLongPtrW(hwnd, GWLP_USERDATA)) + + +static LRESULT +SubclassedListView_CallPrevWndProc(HWND hwnd, unsigned int message, WPARAM wParam, LPARAM lParam) +{ + WNDPROC windowProc; + + windowProc = (WNDPROC)(LONG_PTR)GetWindowLongPtrW(hwnd,GWLP_USERDATA); + if (NULL == windowProc) + return DefWindowProcW(hwnd, message, wParam, lParam); + + return CallWindowProcW(windowProc, hwnd, message,wParam,lParam); +} + +static BOOL +GetViewRect(HWND hwnd, RECT *prc) +{ + HWND headerWindow; + GetClientRect(hwnd, prc); + headerWindow = GetDlgItem(hwnd, 3); + if (NULL != headerWindow) + { + RECT rh; + GetWindowRect(headerWindow, &rh); + MapWindowPoints(HWND_DESKTOP, headerWindow, ((POINT*)&rh) + 1, 1); + prc->top = rh.bottom; + } + + if (prc->right < prc->left) + prc->right = prc->left; + + if (prc->bottom < prc->top) + prc->bottom = prc->top; + + return TRUE; +} + +static int +SmoothScrollList_GetScrollPosFromItem(HWND hwnd, int iItem) +{ + HWND listWindow; + RECT listRect, viewRect; + int pos; + int count; + + pos = 0; + + if (FALSE == GetViewRect(hwnd, &viewRect)) + return 0; + + listWindow = GetDlgItem(hwnd, 2); + if (NULL == listWindow || + FALSE == GetWindowRect(listWindow, &listRect)) + { + return 0; + } + + MapWindowPoints(HWND_DESKTOP, hwnd, (POINT*)&listRect, 2); + + count = (int)SendMessageW(listWindow, LVM_GETITEMCOUNT, 0, 0L); + if (0 != count) + { + SmoothScrollList *self; + + if (iItem < 0) + iItem = 0; + + if (iItem >= count) + iItem = count - 1; + + self = GetUserData(hwnd); + if (NULL != self) + pos = iItem * self->itemHeight; + } + + pos += (viewRect.top - listRect.top); + return pos; +} + +static BOOL +UpdateScrollInfo(HWND hwndView, UINT fFlags, BOOL bRedraw) +{ + RECT rv; + HWND hwndList; + SCROLLINFO si; + BOOL needUpdate; + BOOL needRedraw; + HRGN regionUpdate, regionTemp; + + SmoothScrollList* s = GetUserData(hwndView); + + hwndList = GetDlgItem(hwndView, 2); + if (!s || !hwndList ) return FALSE; + + if (FALSE!= bRedraw) + { + GetWindowRect(hwndView, &rv); + MapWindowPoints(HWND_DESKTOP, hwndView, (POINT*)&rv, 2); + regionUpdate = CreateRectRgnIndirect(&rv); + GetClientRect(hwndView, &rv); + regionTemp = CreateRectRgnIndirect(&rv); + CombineRgn(regionUpdate, regionUpdate, regionTemp, RGN_DIFF); + } + else + { + regionUpdate = NULL; + regionTemp = NULL; + } + + si.cbSize = sizeof(SCROLLINFO); + si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS; + + if (!GetScrollInfo(hwndView, SB_VERT, &si)) + return FALSE; + + if (FALSE == GetViewRect(hwndView, &rv)) + SetRectEmpty(&rv); + + needUpdate = FALSE; + needRedraw = FALSE; + + if (SIF_RANGE & fFlags) + { + unsigned int count, nPage, nMax; + + nPage = rv.bottom - rv.top; + + count = (INT)SendMessageW(hwndList, LVM_GETITEMCOUNT, 0, 0L); + nMax = count * s->itemHeight; + + if (si.nPage != nPage || si.nMax != nMax) + { + BOOL forcePos; + unsigned int windowStyle; + + si.fMask = SIF_PAGE | SIF_RANGE; + si.nPage = nPage; + si.nMax = nMax; + + windowStyle = GetWindowLongPtrW(hwndView, GWL_STYLE); + + SetScrollInfo(hwndView, SB_VERT, &si, FALSE); + + needUpdate = TRUE; + needRedraw = FALSE; + forcePos = FALSE; + + if (nPage >= nMax && + 0 != (WS_VSCROLL & windowStyle)) + { + SetWindowLongPtrW(hwndView, GWL_STYLE, windowStyle & ~WS_VSCROLL); + + RECT rc; + HWND hwndHeader; + // MLSkinnedScrollWnd_UpdateBars(hwndView, bRedraw); + GetClientRect(hwndView, &rc); + hwndHeader = GetDlgItem(hwndView, 3); + if (hwndHeader) + { + HDLAYOUT headerLayout; + WINDOWPOS headerPos; + + headerLayout.prc = &rc; + headerLayout.pwpos = &headerPos; + + if (FALSE != SendMessageW(hwndHeader, HDM_LAYOUT, 0, (LPARAM)&headerLayout)) + { + headerPos.flags |= SWP_NOREDRAW | SWP_NOCOPYBITS; + headerPos.flags &= ~SWP_NOZORDER; + headerPos.hwndInsertAfter = HWND_TOP; + SetWindowPos(hwndHeader, headerPos.hwndInsertAfter, headerPos.x, headerPos.y, + headerPos.cx, headerPos.cy, headerPos.flags); + + InvalidateRect(hwndHeader, NULL, FALSE); + } + } + rv.right = rc.right; + forcePos = TRUE; + needUpdate = FALSE; + needRedraw = TRUE; + } + + if (nPage >= nMax || forcePos) + { + RECT rl; + GetWindowRect(hwndList, &rl); + MapWindowPoints(HWND_DESKTOP, hwndView, (POINT*)&rl, 2); + if (rv.top != rl.top || forcePos) + { + SetWindowPos(hwndList, NULL, rv.left, rv.top, rv.right - rv.left, rv.bottom - rv.top, + SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW | SWP_NOCOPYBITS); + needRedraw = TRUE; + } + } + } + } + if (SIF_POS & fFlags) + { + INT nTop; + + nTop = (INT)SendMessageW(hwndList, LVM_GETTOPINDEX, 0, 0L); + nTop = SmoothScrollList_GetScrollPosFromItem(hwndView, nTop); + + if(si.nMax > 0) + { + if (nTop >= (si.nMax - (int)si.nPage)) + nTop = (si.nMax - si.nPage) + 1; + } + else + nTop = 0; + + if (nTop < si.nMin) + nTop = si.nMin; + + if (si.nPos != nTop) + { + si.fMask = SIF_POS; + si.nPos = nTop; + + SetScrollInfo(hwndView, SB_VERT, &si, (FALSE == needRedraw && FALSE != bRedraw)); + + needUpdate = TRUE; + } + } + + if (FALSE != needUpdate) + MLSkinnedScrollWnd_UpdateBars(hwndView, (FALSE == needRedraw && FALSE != bRedraw)); + + if (FALSE != bRedraw && FALSE != needRedraw) + { + HRGN regionTemp2; + GetWindowRect(hwndView, &rv); + MapWindowPoints(HWND_DESKTOP, hwndView, (POINT*)&rv, 2); + SetRectRgn(regionTemp, rv.left, rv.top, rv.right, rv.bottom); + GetClientRect(hwndView, &rv); + regionTemp2 = CreateRectRgnIndirect(&rv); + CombineRgn(regionTemp, regionTemp, regionTemp2, RGN_DIFF); + CombineRgn(regionUpdate, regionUpdate, regionTemp, RGN_OR); + DeleteObject(regionTemp2); + + RedrawWindow(hwndView, NULL, regionUpdate, RDW_INVALIDATE | RDW_ALLCHILDREN | RDW_FRAME); + } + + if (NULL != regionUpdate) + DeleteObject(regionUpdate); + if (NULL != regionTemp) + DeleteObject(regionTemp); + + return TRUE; +} + +static BOOL +SmoothScrollList_SetScrollPos(HWND hwnd, int position, ScrollPosFlags flags) +{ + SmoothScrollList *self; + HWND listWindow; + BOOL invalidate, failed; + unsigned long viewStyle; + int y, scrollPos; + RECT rv, rl; + SCROLLINFO si; + + + listWindow = GetDlgItem(hwnd, 2); + if (NULL == listWindow) + return FALSE; + + self = GetUserData(hwnd); + if (NULL == self) + return FALSE; + + si.cbSize = sizeof(si); + si.fMask = SIF_POS | SIF_RANGE | SIF_PAGE; + if (FALSE == GetScrollInfo(hwnd, SB_VERT, &si)) + return FALSE; + + scrollPos = si.nPos; + + viewStyle = GetWindowLongPtrW(hwnd, GWL_STYLE); + + invalidate = FALSE; + failed = FALSE; + y = 0; + + if (0 != (SPF_RELATIVE & flags)) + { + position = si.nPos + position; + if (si.nPos > (si.nMax - (int)si.nPage)) + position -= (si.nPos - (si.nMax - (int)si.nPage)); + } + + if (position < si.nMin) + position = si.nMin; + + if (position > (si.nMax - (INT)si.nPage + 1)) + position = si.nMax - si.nPage + 1; + + if (position == si.nPos && 0 == (SPF_FORCE & flags)) + return TRUE; + + if (FALSE == GetViewRect(hwnd, &rv)) + SetRectEmpty(&rv); + + if (FALSE == GetWindowRect(listWindow, &rl)) + SetRectEmpty(&rl); + + MapWindowPoints(HWND_DESKTOP, hwnd, (POINT*)&rl, 2); + + if (0 != (WS_VISIBLE & viewStyle)) + SetWindowLongPtrW(hwnd, GWL_STYLE, viewStyle & ~WS_VISIBLE); + + if (si.nMin == position) + { + if (rl.top != rv.top || rl.bottom != rv.bottom || rl.left != rv.left || rl.right != rv.right) + { + SetWindowPos(listWindow, NULL, rv.left, rv.top, rv.right - rv.left, rv.bottom - rv.top, + SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW | SWP_NOCOPYBITS); + invalidate = TRUE; + } + + if (0 != (int)SendMessageW(listWindow, LVM_GETITEMCOUNT, 0, 0L)) + { + RECT rect; + rect.left = LVIR_BOUNDS; + if (FALSE != SendMessageW(listWindow, LVM_GETITEMRECT, 0, (LPARAM)&rect) && + 0 != rect.top) + { + int scrollY; + scrollY = rect.top; + failed = !SendMessageW(listWindow, LVM_SCROLL, 0, scrollY); + invalidate = TRUE; + } + } + } + else + { + int iTop, iPos; + + iTop = (int)SendMessageW(listWindow, LVM_GETTOPINDEX, 0, 0L); + if (position > (si.nMax - (int)si.nPage)) + position = (si.nMax - si.nPage); + + iPos = position/self->itemHeight; + y = (position - iPos*self->itemHeight); + + if (iTop > iPos) + { + failed = !SendMessageW(listWindow, LVM_SCROLL, 0, (iPos - iTop) * self->itemHeight); + invalidate = TRUE; + } + + if (rl.top != rv.top + y || rl.bottom != rv.bottom || rl.left != rv.left || rl.right != rv.right) + { + SetWindowPos(listWindow, NULL, rv.left, rv.top - y, rv.right - rv.left, rv.bottom - rv.top + y, + SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW | SWP_NOCOPYBITS); + invalidate = TRUE; + } + + if (iTop < iPos) + { + failed = !SendMessageW(listWindow, LVM_SCROLL, 0, (iPos - iTop)*self->itemHeight); + invalidate = TRUE; + } + + } + + if (FALSE == failed) + { + if (position == si.nMax - si.nPage && 0 != si.nMax) + position++; + + if (scrollPos != position) + { + si.nPos = position; + si.fMask = SIF_POS; + SetScrollInfo(hwnd, SB_VERT, &si, (0 == (SPF_NOREDRAW & flags))); + } + + } + + if (0 != (WS_VISIBLE & viewStyle)) + { + viewStyle = GetWindowLongPtrW(hwnd, GWL_STYLE); + if (0 == (WS_VISIBLE & viewStyle)) + { + viewStyle |= WS_VISIBLE; + SetWindowLongPtrW(hwnd, GWL_STYLE, viewStyle); + } + + if (0 == (SPF_NOREDRAW & flags) && + FALSE != invalidate) + { + InvalidateRect(listWindow, NULL, TRUE); + } + } + + + + return TRUE; +} + +static BOOL +SmoothScrollList_EnsureVisible(HWND hwnd, int iItem, BOOL partialOk) +{ + int itemTop, itemBottom; + int pageTop, pageBottom, delta; + SCROLLINFO scrollInfo; + SmoothScrollList *self; + + if (NULL == hwnd || iItem < 0) + return FALSE; + + self = GetUserData(hwnd); + if (NULL == self) + return FALSE; + + scrollInfo.cbSize = sizeof(scrollInfo); + scrollInfo.fMask = SIF_POS | SIF_PAGE; + if (FALSE == GetScrollInfo(hwnd, SB_VERT, &scrollInfo)) + return FALSE; + + itemTop = iItem * self->itemHeight; + itemBottom = itemTop + self->itemHeight; + + pageTop = scrollInfo.nPos; + pageBottom = pageTop + scrollInfo.nPage; + + if (FALSE != partialOk) + { + if (itemTop < pageBottom && + itemBottom > pageTop) + { + return TRUE; + } + } + else + { + if (itemTop >= pageTop && + itemBottom <= pageBottom) + { + return TRUE; + } + } + + if (itemTop < pageTop) + delta = itemTop - pageTop; + else + { + delta = itemBottom - pageBottom; + if ((itemTop - delta) < pageTop) + delta = itemTop - pageTop; + } + + if (FALSE == SmoothScrollList_SetScrollPos(hwnd, delta, SPF_RELATIVE | SPF_FORCE)) + return FALSE; + + MLSkinnedScrollWnd_UpdateBars(hwnd, TRUE); + return TRUE; +} + + +static BOOL +SmoothScrollList_PreProcessKey(HWND hwnd, unsigned int vKey, unsigned int keyFlags, PostProcessKeyCommands *postProcessCommands) +{ + HWND listWindow; + RECT viewRect; + SmoothScrollList *self; + int iItem, iNextItem, count; + BOOL shortView; + + if (NULL != postProcessCommands) + *postProcessCommands = PostProcessKeyCmd_Nothing; + + switch(vKey) + { + case VK_UP: + case VK_DOWN: + case VK_HOME: + case VK_END: + case VK_PRIOR: + case VK_NEXT: + break; + + default: + return TRUE; + } + + if (NULL == hwnd || FALSE == GetViewRect(hwnd, &viewRect)) + return FALSE; + + self = GetUserData(hwnd); + if (NULL == self) + return FALSE; + + listWindow = GetDlgItem(hwnd, 2); + if (NULL == listWindow) + return FALSE; + + iItem = (int)SendMessageW(listWindow, LVM_GETNEXTITEM, (WPARAM)-1, (LPARAM)(LVNI_ALL | LVNI_FOCUSED)); + if (-1 == iItem) + return FALSE; + + count = (int)SendMessageW(listWindow, LVM_GETITEMCOUNT, 0, 0L); + + iNextItem = iItem; + + shortView = ((viewRect.bottom - viewRect.top) < (long)self->itemHeight); + + switch(vKey) + { + case VK_UP: + if (iNextItem > 0) + iNextItem--; + + if (FALSE != shortView) + { + if (NULL != postProcessCommands) + *postProcessCommands |= PostProcessKeyCmd_EnsureFocusVisible; + } + break; + + case VK_DOWN: + if (FALSE == shortView) + { + if ((iNextItem + 1) < count) + iNextItem++; + } + else + { + if (NULL != postProcessCommands) + *postProcessCommands |= PostProcessKeyCmd_EnsureFocusVisible; + } + break; + + case VK_HOME: + + if (FALSE == shortView) + { + iNextItem = 0; + } + else + { + iNextItem = 1; + if (NULL != postProcessCommands) + *postProcessCommands |= PostProcessKeyCmd_UpdateScrollPos; + } + break; + case VK_END: + if (FALSE == shortView) + { + iNextItem = count - 1; + } + else + { + iNextItem = -1; + if (NULL != postProcessCommands) + *postProcessCommands |= PostProcessKeyCmd_EnsureFocusVisible; + } + break; + case VK_PRIOR: + { + RECT listRect; + + GetWindowRect(listWindow, &listRect); + MapWindowPoints(HWND_DESKTOP, hwnd, (POINT*)&listRect, 2); + if (listRect.top != viewRect.top) + SmoothScrollList_SetScrollPos(hwnd, listRect.top - viewRect.top, SPF_RELATIVE); + + if (FALSE == shortView) + { + iNextItem = (viewRect.bottom - viewRect.top)/self->itemHeight; + iNextItem = iItem - iNextItem; + if (iNextItem < 0) + iNextItem = 0; + } + else + { + if (0 == iItem) + iNextItem = 1; + + if (NULL != postProcessCommands) + *postProcessCommands |= PostProcessKeyCmd_UpdateScrollPos; + } + + } + break; + case VK_NEXT: + { + RECT listRect; + int reminder; + + GetWindowRect(listWindow, &listRect); + MapWindowPoints(HWND_DESKTOP, hwnd, (POINT*)&listRect, 2); + + reminder = (listRect.bottom - listRect.top)%self->itemHeight; + if (0 != reminder) + SmoothScrollList_SetScrollPos(hwnd, self->itemHeight - reminder, SPF_RELATIVE); + + if (FALSE == shortView) + { + iNextItem = (viewRect.bottom - viewRect.top)/self->itemHeight; + iNextItem = iItem + iNextItem; + if (iNextItem >= count) + iNextItem = count - 1; + + } + else + { + if (NULL != postProcessCommands) + *postProcessCommands |= (PostProcessKeyCmd_UpdateScrollPos | PostProcessKeyCmd_EnsureFocusVisible); + } + + + } + break; + } + + if (iNextItem >= 0 && iNextItem < count) + SmoothScrollList_EnsureVisible(hwnd, iNextItem, FALSE); + + return TRUE; +} + +static void +SmoothScrollList_PostProcessKey(HWND hwnd, unsigned int vKey, unsigned int keyFlags, PostProcessKeyCommands processCommands) +{ + if (0 != (PostProcessKeyCmd_UpdateScrollPos & processCommands)) + UpdateScrollInfo(hwnd, SIF_POS, TRUE); + + if (0 != (PostProcessKeyCmd_EnsureFocusVisible & processCommands)) + { + HWND listWindow = GetDlgItem(hwnd, 2); + if (NULL != listWindow) + { + int iItem = (int)SendMessageW(listWindow, LVM_GETNEXTITEM, (WPARAM)-1, (LPARAM)(LVNI_ALL | LVNI_FOCUSED)); + if (-1 != iItem) + { + SmoothScrollList_EnsureVisible(hwnd, iItem, FALSE); + } + } + } +} + +static void +SmoothScrollList_UpdateFontMetrics(HWND hwnd, BOOL redraw) +{ + SmoothScrollList *self; + HWND controlWindow; + unsigned int windowStyle; + HFONT font, prevFont; + HDC hdc; + TEXTMETRICW textMetrics; + unsigned int fontHeight; + + self = GetUserData(hwnd); + if (NULL == self) + return; + + windowStyle = GetWindowLongPtr(hwnd, GWL_STYLE); + if(0 != (WS_VISIBLE & windowStyle)) + SetWindowLongPtrW(hwnd, GWL_STYLE, windowStyle & ~WS_VISIBLE); + + hdc = GetDCEx(hwnd, NULL, DCX_CACHE | DCX_NORESETATTRS); + if (NULL != hdc) + prevFont = (HFONT)GetCurrentObject(hdc, OBJ_FONT); + else + prevFont = NULL; + + controlWindow = GetDlgItem(hwnd, 3); + if (NULL != controlWindow) + { + font = (HFONT)SendMessageW(controlWindow, WM_GETFONT, 0, 0L); + fontHeight = 0; + + if (NULL != hdc) + { + SelectObject(hdc, font); + if (FALSE != GetTextMetricsW(hdc, &textMetrics)) + fontHeight = textMetrics.tmHeight; + } + + if (self->headerFontHeight != fontHeight) + { + self->headerFontHeight = fontHeight; + MLSkinnedHeader_SetHeight(controlWindow, -1); + } + } + + controlWindow = GetDlgItem(hwnd, 2); + if (NULL != controlWindow) + { + font = (HFONT)SendMessageW(controlWindow, WM_GETFONT, 0, 0L); + fontHeight = 0; + + if (NULL != hdc) + { + SelectObject(hdc, font); + if (FALSE != GetTextMetricsW(hdc, &textMetrics)) + fontHeight = textMetrics.tmHeight; + } + + if (self->listFontHeight != fontHeight) + { + self->listFontHeight = fontHeight; + + SmoothScrollList_SetScrollPos(hwnd, 0, SPF_NOREDRAW | SPF_FORCE); + MLSkinnedScrollWnd_UpdateBars(hwnd, FALSE); + } + } + + + if (NULL != hdc) + { + SelectObject(hdc, prevFont); + ReleaseDC(hwnd, hdc); + } + + if (0 != (WS_VISIBLE & windowStyle)) + { + windowStyle = GetWindowLongPtr(hwnd, GWL_STYLE); + if (0 == (WS_VISIBLE & windowStyle)) + { + windowStyle |= WS_VISIBLE; + SetWindowLongPtrW(hwnd, GWL_STYLE, windowStyle); + } + + if (FALSE != redraw) + RedrawWindow(hwnd, NULL, NULL, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_ALLCHILDREN); + } +} + + +static HHOOK hook = NULL; +static HWND hwndToMonitor = NULL; +static LRESULT CALLBACK HookProc(int nCode, WPARAM wParam, LPARAM lParam) +{ + MSG *pMsg = (MSG*)lParam; + if (pMsg->hwnd == hwndToMonitor) + { + static INT lastScrollPos = -1; + switch(pMsg->message) + { + case WM_LBUTTONUP: + case WM_RBUTTONUP: + { + LRESULT result = CallNextHookEx(hook, nCode, wParam, lParam); + UnhookWindowsHookEx(hook); + hwndToMonitor = NULL; + hook = NULL; + lastScrollPos = -1; + return result; + } + case WM_MOUSEMOVE: + if ((MK_LBUTTON | MK_RBUTTON) & pMsg->wParam) + { + RECT rw; + POINTS pts(MAKEPOINTS(pMsg->lParam)); + POINT pt; + POINTSTOPOINT(pt, pts); + MapWindowPoints(pMsg->hwnd, HWND_DESKTOP, &pt, 1); + GetWindowRect(pMsg->hwnd, &rw); + if (pt.y < rw.top || pt.y > rw.bottom) + { + HWND hwndParent = GetParent(pMsg->hwnd); + if (hwndParent) + { + SCROLLINFO si; + si.cbSize = sizeof(SCROLLINFO); + si.fMask = SIF_RANGE | SIF_POS | SIF_PAGE; + if (GetScrollInfo(hwndParent, SB_VERT, &si)) + { + if ((si.nPos > si.nMin && pt.y < rw.top) || (si.nPos <= (si.nMax - (INT)si.nPage) && pt.y > rw.bottom)) + { + LRESULT result; + if (lastScrollPos == si.nPos) + { + result = CallNextHookEx(hook, nCode, wParam, lParam); + SmoothScrollList_SetScrollPos(hwndParent, (pt.y < rw.top) ? --si.nPos : ++si.nPos, SPF_NORMAL); + } + else + { + unsigned long windowStyle; + windowStyle = GetWindowLongPtrW(hwndParent, GWL_STYLE); + + if (0 != (WS_VISIBLE & windowStyle)) + SetWindowLongPtrW(hwndParent, GWL_STYLE, windowStyle & ~WS_VISIBLE); + + result = CallNextHookEx(hook, nCode, wParam, lParam); + PostMessageW(hwndParent, WM_EX_UPDATESCROLLINFO, SIF_POS, TRUE); + + if (0 != (WS_VISIBLE & windowStyle)) + PostMessageW(hwndParent, WM_EX_UNLOCKREDRAW, IWF_UPDATENOW | IWF_FRAME, 0L); + } + lastScrollPos = si.nPos; + return result; + } + } + } + } + SleepEx(1, TRUE); + } + break; + } + } + return CallNextHookEx(hook, nCode, wParam, lParam); +} + +static LRESULT CALLBACK ListViewSubclass(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { + + if(uMsg == WM_MOUSEMOVE || + uMsg == WM_LBUTTONDOWN) + { + LVHITTESTINFO ht = {{LOWORD(lParam),HIWORD(lParam)},LVHT_ONITEM,-1,0}; + int item = ListView_SubItemHitTest(hwnd, &ht); + { + RECT r={0}; + ListView_GetItemRect(hwnd,item,&r,LVIR_BOUNDS); + ht.pt.x -= r.left; + ht.pt.y -= r.top; + typedef struct { + int x,y,item; + HWND hwnd; + UINT msg; + } hitinfo; + hitinfo info = { + ht.pt.x, ht.pt.y, item, hwnd, uMsg, + }; + SendMessage(GetParent(GetParent(hwnd)),WM_USER+700,(WPARAM)&info,0); + } + } + + switch(uMsg) + { + case WM_HSCROLL: + case WM_VSCROLL: + case WM_MOUSEWHEEL: + { + HWND parentWindow; + + KillTimer(hwnd, 43); + + parentWindow = GetAncestor(hwnd, GA_PARENT); + if (NULL != parentWindow) + return SendMessageW(parentWindow, uMsg, wParam, lParam); + } + break; + case WM_TIMER: + if (43 == wParam) + { + HWND parentWindow; + + KillTimer(hwnd, wParam); + + parentWindow = GetAncestor(hwnd, GA_PARENT); + if (NULL != parentWindow) + { + int iFocused = (int)SendMessageW(hwnd, LVM_GETNEXTITEM, (WPARAM)-1, (LPARAM)(LVNI_ALL | LVNI_FOCUSED)); + if (-1 != iFocused) + SmoothScrollList_EnsureVisible(parentWindow, iFocused, FALSE); + + return 0; + } + } + break; + case WM_LBUTTONDOWN: + case WM_RBUTTONDOWN: + case WM_MBUTTONDOWN: + case WM_XBUTTONDOWN: + hwndToMonitor = hwnd; + hook = SetWindowsHookEx(WH_MSGFILTER, HookProc, NULL, GetCurrentThreadId()); + + { + unsigned int windowStyle; + windowStyle = GetWindowLongPtrW(hwnd, GWL_STYLE); + if (0 != (LVS_OWNERDRAWFIXED & windowStyle)) + { + LRESULT result; + + SetWindowLongPtrW(hwnd, GWL_STYLE, windowStyle & ~LVS_OWNERDRAWFIXED); + + result = SubclassedListView_CallPrevWndProc(hwnd, uMsg, wParam, lParam); + + windowStyle = GetWindowLongPtrW(hwnd, GWL_STYLE); + if (0 == (LVS_OWNERDRAWFIXED & windowStyle)) + { + windowStyle |= LVS_OWNERDRAWFIXED; + SetWindowLongPtrW(hwnd, GWL_STYLE, windowStyle); + } + + return result; + } + } + break; + + case WM_KEYDOWN: + { + HWND parentWindow; + parentWindow = GetAncestor(hwnd, GA_PARENT); + if (NULL != parentWindow) + { + PostProcessKeyCommands postProcessKeyCommands; + if (FALSE == SmoothScrollList_PreProcessKey(parentWindow, + (unsigned int)wParam, + (unsigned int)lParam, + &postProcessKeyCommands)) + { + postProcessKeyCommands = PostProcessKeyCmd_UpdateScrollPos; + } + + SubclassedListView_CallPrevWndProc(hwnd, uMsg, wParam, lParam); + + SmoothScrollList_PostProcessKey(parentWindow, + (unsigned int)wParam, + (unsigned int)lParam, + postProcessKeyCommands); + return 0; + } + } + break; + + case WM_CHAR: + case WM_UNICHAR: + { + HWND parentWindow; + parentWindow = GetAncestor(hwnd, GA_PARENT); + if (NULL != parentWindow) + { + int iFocused; + unsigned int windowStyle; + + windowStyle = GetWindowLongPtrW(hwnd, GWL_STYLE); + + if (0 != (WS_VISIBLE & windowStyle)) + SetWindowLongPtr(hwnd, GWL_STYLE, windowStyle & ~WS_VISIBLE); + + SubclassedListView_CallPrevWndProc(hwnd, uMsg, wParam, lParam); + + iFocused = (int)SendMessageW(hwnd, LVM_GETNEXTITEM, (WPARAM)-1, (LPARAM)(LVNI_ALL | LVNI_FOCUSED)); + + if (-1 != iFocused) + SmoothScrollList_EnsureVisible(parentWindow, iFocused, FALSE); + + if (0 != (WS_VISIBLE & windowStyle)) + { + windowStyle = GetWindowLongPtrW(hwnd, GWL_STYLE); + windowStyle |= WS_VISIBLE; + SetWindowLongPtr(hwnd, GWL_STYLE, windowStyle); + } + + InvalidateRect(hwnd, NULL, FALSE); + return 0; + } + + } + break; + + case LVM_ENSUREVISIBLE: + { + HWND parentWindow = GetAncestor(hwnd, GA_PARENT); + if (NULL != parentWindow) + return SmoothScrollList_EnsureVisible(parentWindow, (int)wParam, (BOOL)lParam); + } + break; + + + + + } + + return SubclassedListView_CallPrevWndProc(hwnd, uMsg, wParam, lParam); + + +} + +static LRESULT +SmoothScrollList_OnCreate(HWND hwnd, CREATESTRUCT *createStruct) +{ + HWND hwndList, hwndHeader; + MLSKINWINDOW m = {0}; + RECT rc; + DWORD style; + SmoothScrollList *self = (SmoothScrollList *)calloc(1, sizeof(SmoothScrollList)); + if (NULL == self) + return -1; + + self->itemHeight = 1; + self->textHeight = 1; + SetWindowLongPtrW(hwnd, GWLP_USERDATA, (LONGX86)(LONG_PTR)self); + + m.skinType = SKINNEDWND_TYPE_SCROLLWND; + m.style = SWS_USESKINFONT | SWS_USESKINCOLORS | SWS_USESKINCURSORS; + m.hwndToSkin = hwnd; + MLSkinWindow(g_hwnd, &m); + + SetScrollRange(hwnd, SB_VERT, 0, 0, FALSE); + MLSkinnedScrollWnd_UpdateBars(hwnd, FALSE); + + if (FALSE == GetClientRect(hwnd, &rc)) + SetRectEmpty(&rc); + + style = WS_CLIPSIBLINGS | WS_CHILD | WS_VISIBLE | HDS_BUTTONS | HDS_FULLDRAG; + hwndHeader = CreateWindowExW(WS_EX_NOPARENTNOTIFY, WC_HEADERW, NULL, style, + 0, 0, rc.right - rc.left, 0, hwnd, (HMENU)3,0,0); + + if (NULL != hwndHeader) + { + m.hwndToSkin = hwndHeader; + m.skinType = SKINNEDWND_TYPE_HEADER; + m.style = SWS_USESKINFONT | SWS_USESKINCOLORS | SWS_USESKINCURSORS; + MLSkinWindow(g_hwnd, &m); + } + + style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_CHILD | WS_TABSTOP | WS_VISIBLE | + LVS_REPORT | LVS_SHOWSELALWAYS | LVS_OWNERDRAWFIXED | LVS_OWNERDATA | LVS_NOCOLUMNHEADER; + + hwndList = CreateWindowExW(WS_EX_NOPARENTNOTIFY, WC_LISTVIEWW, NULL, style, + 0, 0, rc.right - rc.left, rc.bottom - rc.top, hwnd,(HMENU)2,0,0); + if (NULL != hwndList) + { + WNDPROC oldp = (WNDPROC)(LONG_PTR)SetWindowLongPtrW(hwndList, GWLP_WNDPROC, (LONGX86)(LONG_PTR)ListViewSubclass); + SetWindowLongPtrW(hwndList,GWLP_USERDATA, (LONGX86)(LONG_PTR)oldp); + + if(NULL != hwndHeader) + SetWindowPos(hwndHeader, hwndList, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); + + m.skinType = SKINNEDWND_TYPE_LISTVIEW; + m.hwndToSkin = hwndList; + m.style = SWS_USESKINFONT | SWS_USESKINCOLORS | SWS_USESKINCURSORS | + SWLVS_FULLROWSELECT | SWLVS_DOUBLEBUFFER | SWLVS_ALTERNATEITEMS; + + MLSkinWindow(g_hwnd, &m); + MLSkinnedScrollWnd_SetMode(hwndList, SCROLLMODE_STANDARD); + MLSkinnedScrollWnd_ShowHorzBar(hwndList, FALSE); + MLSkinnedScrollWnd_ShowVertBar(hwndList, FALSE); + } + + SmoothScrollList_UpdateFontMetrics(hwnd, FALSE); + + return 0; +} + +static void +SmoothScrollList_OnDestroy(HWND hwnd) +{ + SmoothScrollList *self; + + self = (SmoothScrollList*)(LONG_PTR)SetWindowLongPtrW(hwnd, GWLP_USERDATA, 0); + if (NULL == self) + return; + + free(self); +} + +static void +SmoothScrollList_OnWindowPosChanged(HWND hwnd, WINDOWPOS *windowPos) +{ + HWND controlWindow; + RECT rect; + long clientWidth; + HWND parentWindow; + SmoothScrollList *self; + + if ((SWP_NOSIZE | SWP_NOMOVE) == ((SWP_NOSIZE | SWP_NOMOVE | SWP_FRAMECHANGED) & windowPos->flags)) + return; + + self = GetUserData(hwnd); + if (NULL == self) + return; + + if (FALSE == GetClientRect(hwnd, &rect)) + return; + + clientWidth = rect.right - rect.left; + + controlWindow = GetDlgItem(hwnd, 3); + if (NULL != controlWindow) + { + HDLAYOUT headerLayout; + WINDOWPOS headerPos; + + headerLayout.prc = ▭ + headerLayout.pwpos = &headerPos; + + if (FALSE != SendMessageW(controlWindow, HDM_LAYOUT, 0, (LPARAM)&headerLayout)) + { + headerPos.flags |= ((SWP_NOREDRAW | SWP_NOCOPYBITS) & windowPos->flags); + headerPos.flags &= ~SWP_NOZORDER; + headerPos.hwndInsertAfter = HWND_TOP; + SetWindowPos(controlWindow, headerPos.hwndInsertAfter, headerPos.x, headerPos.y, + headerPos.cx, headerPos.cy, headerPos.flags); + } + } + + if (self->viewHeight != windowPos->cy || + 0 != (SWP_FRAMECHANGED & windowPos->flags)) + { + ScrollPosFlags scrollFlags; + + scrollFlags = SPF_FORCE | SPF_RELATIVE; + if (0 != (SWP_NOREDRAW & windowPos->flags)) + scrollFlags |= SPF_NOREDRAW; + + self->viewHeight = windowPos->cy; + + UpdateScrollInfo(hwnd, SIF_RANGE | SIF_POS, TRUE); + + SmoothScrollList_SetScrollPos(hwnd, 0, scrollFlags); + } + else + { + controlWindow = GetDlgItem(hwnd, 2); + if (NULL != controlWindow) + { + if (FALSE != GetWindowRect(controlWindow, &rect) && + (rect.right - rect.left) != clientWidth) + { + SetWindowPos(controlWindow, NULL, 0, 0, clientWidth, rect.bottom - rect.top, + SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | ((SWP_NOREDRAW | SWP_NOCOPYBITS) & windowPos->flags)); + } + } + } + + parentWindow = GetAncestor(hwnd, GA_PARENT); + if (NULL != parentWindow) + { + NMHDR hdr; + hdr.code = LVN_EX_SIZECHANGED; + hdr.hwndFrom = hwnd; + hdr.idFrom = GetWindowLongPtrW(hwnd, GWLP_ID); + SendMessageW(parentWindow, WM_NOTIFY, hdr.idFrom, (LPARAM)&hdr); + } +} + +static void +SmoothScrollList_OnMouseWheel(HWND hwnd, INT virtualKeys, INT distance, LONG pointer_s) +{ + SmoothScrollList *self; + int pos; + unsigned int wheelScroll; + int scrollLines; + + KillTimer(hwnd, 43); + + self = GetUserData(hwnd); + if (NULL == self) + return; + + if (FALSE == SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &wheelScroll, 0)) + wheelScroll = 3; + + if (0 == wheelScroll) + return; + + if (WHEEL_PAGESCROLL == wheelScroll) + { + SendMessageW(hwnd, WM_VSCROLL, MAKEWPARAM(((distance > 0) ? SB_PAGEUP : SB_PAGEDOWN), 0), 0L); + SendMessageW(hwnd, WM_VSCROLL, MAKEWPARAM(SB_ENDSCROLL, 0), 0L); + return; + } + + distance += self->wheelCarryover; + scrollLines = distance * (int)wheelScroll / WHEEL_DELTA; + self->wheelCarryover = distance - scrollLines * WHEEL_DELTA / (int)wheelScroll; + + pos = scrollLines * (int)self->textHeight; + + SmoothScrollList_SetScrollPos(hwnd, -pos, SPF_RELATIVE | SPF_NORMAL); + MLSkinnedScrollWnd_UpdateBars(hwnd, TRUE); +} + +static void +SmoothScrollList_OnVertScroll(HWND hwnd, INT actionLayout, INT trackPosition, HWND scrollBar) +{ + SmoothScrollList *s; + SCROLLINFO si; + int pos; + ScrollPosFlags scrollFlags; + unsigned int lineHeight; + + KillTimer(hwnd, 43); + + s = GetUserData(hwnd); + if (NULL == s) + return; + + si.cbSize =sizeof(si); + si.fMask = SIF_PAGE | SIF_POS | SIF_TRACKPOS | SIF_RANGE; + + if (FALSE == GetScrollInfo(hwnd, SB_VERT, &si)) + return; + + scrollFlags = SPF_NORMAL; + + if (si.nPos > (si.nMax - (INT)si.nPage)) + si.nPos = si.nMax - si.nPage; + + lineHeight = s->textHeight * 3; + if (lineHeight > s->itemHeight) + lineHeight = s->itemHeight; + if (lineHeight > si.nPage) + lineHeight = si.nPage; + + switch(actionLayout) + { + case SB_TOP: pos = si.nMin; break; + case SB_BOTTOM: pos = si.nMax; break; + case SB_LINEDOWN: pos = si.nPos + lineHeight; break; + case SB_LINEUP: pos = si.nPos - lineHeight; break; + case SB_PAGEDOWN: pos = si.nPos + (si.nPage / s->itemHeight) * s->itemHeight; break; + case SB_PAGEUP: pos = si.nPos - (si.nPage / s->itemHeight) * s->itemHeight; break; + case SB_THUMBPOSITION: + case SB_THUMBTRACK: pos = si.nTrackPos; scrollFlags |= SPF_FORCE; break; + case SB_ENDSCROLL: MLSkinnedScrollWnd_UpdateBars(hwnd, TRUE); return; + default: pos = si.nPos; + } + + SmoothScrollList_SetScrollPos(hwnd, pos, scrollFlags); +} + +static LRESULT +SmoothScrollList_OnMeasureItem(HWND hwnd, MEASUREITEMSTRUCT *measureItem) +{ + LRESULT result; + HWND parentWindow; + SmoothScrollList *self; + unsigned int itemHeight, textHeight; + BOOL updateScroll; + + if(2 != measureItem->CtlID) + return FALSE; + + self = GetUserData(hwnd); + + + updateScroll = FALSE; + itemHeight = measureItem->itemHeight; + + parentWindow = GetAncestor(hwnd, GA_PARENT); + if (NULL != parentWindow) + { + measureItem->CtlID = GetWindowLongPtrW(hwnd, GWLP_ID); + result = SendMessageW(parentWindow, WM_MEASUREITEM, measureItem->CtlID, (LPARAM)measureItem); + itemHeight = measureItem->itemHeight; + } + else + result = 0; + + textHeight = 12; + HDC hdc = GetDCEx(hwnd, NULL, DCX_CACHE | DCX_NORESETATTRS); + if (NULL != hdc) + { + HFONT font, fontPrev; + TEXTMETRIC textMetrics; + + font = (HFONT)SendMessageW(hwnd, WM_GETFONT, 0, 0L); + fontPrev = (HFONT)SelectObject(hdc, font); + + if (FALSE != GetTextMetrics(hdc, &textMetrics)) + textHeight = textMetrics.tmHeight; + + SelectObject(hdc, fontPrev); + ReleaseDC(hwnd, hdc); + } + + if (NULL != self) + { + if (self->itemHeight != itemHeight) + { + SmoothScrollList_SetScrollPos(hwnd, 0, SPF_NOREDRAW); + self->itemHeight = itemHeight; + updateScroll = TRUE; + } + + if (self->textHeight != textHeight) + { + self->textHeight = textHeight; + updateScroll = TRUE; + } + } + + if (FALSE != updateScroll) + { + UpdateScrollInfo(hwnd, SIF_RANGE | SIF_POS, TRUE); + } + + return result; +} + +static void +SmoothScrollList_OnSetFont(HWND hwnd, HFONT font, BOOL redraw) +{ + if (0 == (SWS_USESKINFONT & MLSkinnedWnd_GetStyle(hwnd))) + { + HWND controlWindow; + + controlWindow = GetDlgItem(hwnd,3); + if (NULL != controlWindow) + SendMessageW(controlWindow, WM_SETFONT, (WPARAM)font, MAKELPARAM(0, 0L)); + + controlWindow = GetDlgItem(hwnd,2); + if (NULL != controlWindow) + SendMessageW(controlWindow, WM_SETFONT, (WPARAM)font, MAKELPARAM(0, 0L)); + + SmoothScrollList_UpdateFontMetrics(hwnd, redraw); + } +} + + +static LRESULT +SmoothScrollList_OnGetFont(HWND hwnd) +{ + HWND listWindow; + + listWindow = GetDlgItem(hwnd, 2); + if (NULL != listWindow) + return SendMessageW(listWindow, WM_GETFONT, 0, 0L); + + return DefWindowProcW(hwnd, WM_GETFONT, 0, 0L); +} + +static void +SmoothScrollList_OnSetRedraw(HWND hwnd, BOOL enableRedraw) +{ + HWND childWindow; + + DefWindowProcW(hwnd, WM_SETREDRAW, enableRedraw, 0L); + + childWindow = GetDlgItem(hwnd, 3); + if (NULL != childWindow) + { + SendMessage(childWindow, WM_SETREDRAW, enableRedraw, 0L); + if (FALSE != enableRedraw) + InvalidateRect(childWindow, NULL, TRUE); + } + + childWindow = GetDlgItem(hwnd, 2); + if (NULL != childWindow) + { + SendMessage(childWindow, WM_SETREDRAW, enableRedraw, 0L); + if (FALSE != enableRedraw) + InvalidateRect(childWindow, NULL, TRUE); + } +} + + +static void +SmoothScrollList_OnSkinUpdated(HWND hwnd, BOOL notifyChildren, BOOL redraw) +{ + SmoothScrollList_UpdateFontMetrics(hwnd, redraw); +} + +static LRESULT +SmoothScrollList_OnDisplaySort(HWND hwnd, int sortIndex, BOOL ascendingOrder) +{ + HWND headerWindow; + + headerWindow = GetDlgItem(hwnd, 3); + if (NULL == headerWindow) + return 0; + + return SENDMLIPC(headerWindow, ML_IPC_SKINNEDHEADER_DISPLAYSORT, MAKEWPARAM(sortIndex, ascendingOrder)); +} + +static LRESULT +SmoothScrollList_OnGetSort(HWND hwnd) +{ + HWND headerWindow; + + headerWindow = GetDlgItem(hwnd, 3); + if (NULL == headerWindow) + return 0; + + return SENDMLIPC(headerWindow, ML_IPC_SKINNEDHEADER_GETSORT, 0); +} + +static void +SmoothScrollList_OnKeyDown(HWND hwnd, unsigned int vKey, unsigned int keyFlags) +{ + HWND listWindow; + listWindow = GetDlgItem(hwnd, 2); + + if (NULL != listWindow && + WS_VISIBLE == ((WS_VISIBLE | WS_DISABLED) & GetWindowLongPtrW(listWindow, GWL_STYLE))) + { + SendMessageW(listWindow, WM_KEYDOWN, vKey, (LPARAM)keyFlags); + } + + DefWindowProcW(hwnd, WM_KEYDOWN, vKey, (LPARAM)keyFlags); +} + +static LRESULT +SmoothScrollList_OnMediaLibraryIPC(HWND hwnd, INT msg, INT_PTR param) +{ + switch(msg) + { + case ML_IPC_SKINNEDWND_SKINUPDATED: SmoothScrollList_OnSkinUpdated(hwnd, LOWORD(param), HIWORD(param)); break; + case ML_IPC_SKINNEDLISTVIEW_DISPLAYSORT: return SmoothScrollList_OnDisplaySort(hwnd, LOWORD(param), HIWORD(param)); + case ML_IPC_SKINNEDLISTVIEW_GETSORT: return SmoothScrollList_OnGetSort(hwnd); + } + return 0; +} + +static LRESULT CALLBACK SmoothScrollMsgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { + switch(uMsg) + { + case WM_CREATE: return SmoothScrollList_OnCreate(hwnd, (CREATESTRUCT*)lParam); + case WM_DESTROY: SmoothScrollList_OnDestroy(hwnd); return 0; + case WM_WINDOWPOSCHANGED: SmoothScrollList_OnWindowPosChanged(hwnd, (WINDOWPOS*)lParam); return 0; + case WM_MOUSEWHEEL: SmoothScrollList_OnMouseWheel(hwnd, LOWORD(wParam), (short)HIWORD(wParam), (LONG)lParam); return 0; + case WM_VSCROLL: SmoothScrollList_OnVertScroll(hwnd, LOWORD(wParam), (short)HIWORD(wParam), (HWND)lParam); return 0; + case WM_ERASEBKGND: return 1; + case WM_MEASUREITEM: return SmoothScrollList_OnMeasureItem(hwnd, (MEASUREITEMSTRUCT*)lParam); + case WM_SETFONT: SmoothScrollList_OnSetFont(hwnd, (HFONT)wParam, LOWORD(lParam)); return 0; + case WM_GETFONT: return SmoothScrollList_OnGetFont(hwnd); + case WM_SETREDRAW: SmoothScrollList_OnSetRedraw(hwnd, (BOOL)wParam); return 0; + + case LVM_GETHEADER: + return (LRESULT)GetDlgItem(hwnd,3); + case LVM_INSERTCOLUMNA: + case LVM_INSERTCOLUMNW: + { + LVCOLUMNW *listColumn = (LVCOLUMNW*)lParam; + HWND controlWindow; + LRESULT result; + + result = -1; + + controlWindow = GetDlgItem(hwnd,3); + if (NULL != controlWindow) + { + HDITEMW headerItem; + + if (FALSE == CopyListColumnToHeaderItem(listColumn, &headerItem)) + return -1; + + if (0 == (HDI_FORMAT & headerItem.mask)) + { + headerItem.mask |= HDI_FORMAT; + headerItem.fmt = HDF_LEFT; + } + + result = SendMessageW(controlWindow, + (LVM_INSERTCOLUMNW == uMsg) ? HDM_INSERTITEMW : HDM_INSERTITEMA, + wParam, (LPARAM)&headerItem); + + if (-1 == result) + return result; + } + + controlWindow = GetDlgItem(hwnd, 2); + if (NULL != controlWindow) + result = SendMessageW(controlWindow, uMsg, wParam, lParam); + + return result; + } + break; + case LVM_DELETECOLUMN: + { + HWND controlWindow; + controlWindow = GetDlgItem(hwnd,3); + if (NULL != controlWindow && + FALSE ==SendMessageW(controlWindow, HDM_DELETEITEM, wParam, 0L)) + { + return FALSE; + } + + controlWindow = GetDlgItem(hwnd,2); + if (NULL != controlWindow) + return SendMessageW(controlWindow ,uMsg,wParam,lParam); + } + return FALSE; + case LVM_GETCOLUMNW: + case LVM_GETCOLUMNA: + { + LVCOLUMNW *l = (LVCOLUMNW *)lParam; + HDITEMW h; + HWND headerWindow; + + headerWindow = GetDlgItem(hwnd, 3); + if (NULL == headerWindow) + return FALSE; + + if (FALSE == CopyListColumnToHeaderItem(l, &h)) + return FALSE; + + if(!SendMessageW(headerWindow, + (LVM_GETCOLUMNW == uMsg) ? HDM_GETITEMW : HDM_GETITEMA, + wParam, + (LPARAM)&h)) + { + return FALSE; + } + + if (FALSE == CopyHeaderItemToListColumn(&h, l)) + return FALSE; + } + return TRUE; + case LVM_GETCOLUMNWIDTH: + { + HWND controlWindow; + + controlWindow = GetDlgItem(hwnd,3); + if (NULL != controlWindow) + { + HDITEMW h; + h.mask = HDI_WIDTH; + if (FALSE == SendMessageW(controlWindow, HDM_GETITEM, wParam, (LPARAM)&h)) + return 0; + + return h.cxy; + } + + controlWindow = GetDlgItem(hwnd, 2); + if (NULL != controlWindow) + return SendMessageW(controlWindow, uMsg, wParam, lParam); + } + break; + case LVM_SETCOLUMNW: + case LVM_SETCOLUMNA: + { + LVCOLUMNW *l = (LVCOLUMNW *)lParam; + HWND controlWindow; + LRESULT result; + + controlWindow = GetDlgItem(hwnd, 3); + if (NULL != controlWindow) + { + HDITEMW h; + + if (FALSE == CopyListColumnToHeaderItem(l, &h)) + return FALSE; + + if(!SendMessageW(controlWindow, + (LVM_SETCOLUMNW == uMsg) ? HDM_SETITEMW : HDM_SETITEMA, + wParam, (LPARAM)&h)) + { + return FALSE; + } + + if (FALSE == CopyHeaderItemToListColumn(&h, l)) + return FALSE; + + result = TRUE; + } + else result = FALSE; + + controlWindow = GetDlgItem(hwnd,2); + if (NULL != controlWindow) + result = SendMessageW(controlWindow, uMsg, wParam, lParam); + + return result; + } + break; + + case LVM_SETCOLUMNWIDTH: + { + HWND controlWindow; + LRESULT result; + + controlWindow = GetDlgItem(hwnd, 3); + if (NULL != controlWindow) + { + HDITEMW headerItem; + + if (LVSCW_AUTOSIZE == lParam) + return FALSE; + + if (LVSCW_AUTOSIZE_USEHEADER == lParam) + return FALSE; + + headerItem.mask = HDI_WIDTH; + headerItem.cxy = (int)lParam; + + result = SendMessageW(controlWindow, HDM_SETITEMW, (WPARAM)wParam, (LPARAM)&headerItem); + if (FALSE == result) + return FALSE; + } + else + result = FALSE; + + controlWindow = GetDlgItem(hwnd,2); + if (NULL != controlWindow) + result = SendMessageW(controlWindow, uMsg, wParam, lParam); + + return result; + } + break; + + case LVM_SETITEMCOUNT: + { + LRESULT result; + HWND controlWindow = GetDlgItem(hwnd,2); + + result = (NULL != controlWindow) ? + SendMessageW(controlWindow, uMsg, wParam,lParam) : + 0; + + UpdateScrollInfo(hwnd, SIF_RANGE | SIF_POS, TRUE); + return result; + } + break; + case LVM_ENSUREVISIBLE: + return SmoothScrollList_EnsureVisible(hwnd, (int)wParam, (BOOL)lParam); + + case WM_EX_UPDATESCROLLINFO: + return UpdateScrollInfo(hwnd, (UINT)wParam, (BOOL)lParam); + + case WM_EX_UNLOCKREDRAW: + { + unsigned long windowStyle; + unsigned int redrawFlags; + HRGN regionInvalid; + RECT rect; + + windowStyle = GetWindowLongPtrW(hwnd, GWL_STYLE); + if (0 == (WS_VISIBLE & windowStyle)) + SetWindowLongPtrW(hwnd, GWL_STYLE, windowStyle | WS_VISIBLE); + + redrawFlags = RDW_INVALIDATE | RDW_ALLCHILDREN; + + + if (0 != (IWF_FRAME & wParam)) + { + redrawFlags |= RDW_FRAME; + GetWindowRect(hwnd, &rect); + MapWindowPoints(HWND_DESKTOP, hwnd, (POINT*)&rect, 2); + } + else + GetClientRect(hwnd, &rect); + + if (0 != (IWF_ERASE & wParam)) + redrawFlags |= RDW_ERASE; + + if (0 != (IWF_UPDATENOW & wParam)) + { + redrawFlags |= RDW_UPDATENOW; + if (0 != (IWF_ERASE & wParam)) + redrawFlags |= RDW_ERASENOW; + } + + + + regionInvalid = CreateRectRgnIndirect(&rect); + if (NULL != regionInvalid) + { + HWND headerWindow; + + headerWindow = GetDlgItem(hwnd, 3); + if (NULL != headerWindow && + 0 != (WS_VISIBLE & GetWindowLongPtrW(headerWindow, GWL_STYLE))) + { + HRGN regionHeader; + GetWindowRect(headerWindow, &rect); + MapWindowPoints(HWND_DESKTOP, hwnd, (POINT*)&rect, 2); + regionHeader = CreateRectRgnIndirect(&rect); + if (NULL != regionHeader) + { + CombineRgn(regionInvalid, regionInvalid, regionHeader, RGN_DIFF); + DeleteObject(regionHeader); + } + } + } + + RedrawWindow(hwnd, NULL, regionInvalid, redrawFlags); + + if (NULL != regionInvalid) + DeleteObject(regionInvalid); + } + break; + case WM_NOTIFY: + { + LPNMHDR l=(LPNMHDR)lParam; + if(l->idFrom == 2) + { + l->idFrom = GetWindowLongPtrW(hwnd,GWLP_ID); + l->hwndFrom = hwnd; // this is prevents double reflecting + return SendMessageW(GetParent(hwnd),uMsg,l->idFrom,lParam); + } + else if(l->idFrom == 3) + { + switch(l->code) + { + case HDN_ITEMCLICKA: + case HDN_ITEMCLICKW: + { + NMHEADER *nm = (NMHEADER*)lParam; + HWND hwndParent; + hwndParent = GetParent(hwnd); + if (hwndParent) + { + wParam = GetWindowLongPtrW(hwnd,GWLP_ID); + if(nm->iButton == 0) { // left click + NMLISTVIEW p = {{hwnd, wParam, LVN_COLUMNCLICK},-1,nm->iItem,0}; + return SendMessageW(hwndParent,WM_NOTIFY,wParam,(LPARAM)&p); + } else if(nm->iButton == 1) { // right click + NMHDR p = {nm->hdr.hwndFrom,wParam,NM_RCLICK}; + return SendMessageW(hwndParent,WM_NOTIFY,wParam,(LPARAM)&p); + } + } + } + break; + case HDN_ITEMCHANGINGA: + case HDN_ITEMCHANGINGW: + case HDN_ITEMCHANGEDA: + case HDN_ITEMCHANGEDW: + { + LRESULT result; + NMHEADER *nm = (NMHEADER*)lParam; + + result = SendMessageW(GetParent(hwnd),uMsg, wParam,lParam); + if (FALSE != result && + (HDN_ITEMCHANGINGW == l->code || HDN_ITEMCHANGINGA == l->code)) + { + return result; + } + + if (NULL != nm->pitem && + 0 != (HDI_WIDTH & nm->pitem->mask)) + { + HWND hwndList; + hwndList = GetDlgItem(hwnd,2); + if (hwndList) + { + unsigned long windowStyle; + windowStyle = GetWindowLongPtrW(hwnd, GWL_STYLE); + if (0 != (WS_VISIBLE & windowStyle)) + SetWindowLongPtrW(hwnd, GWL_STYLE, windowStyle & ~WS_VISIBLE); + + ListView_SetColumnWidth(hwndList, nm->iItem,nm->pitem->cxy); + + if (0 != (WS_VISIBLE & windowStyle)) + { + windowStyle = GetWindowLongPtrW(hwnd, GWL_STYLE); + if (0 == (WS_VISIBLE & windowStyle)) + { + windowStyle |= WS_VISIBLE; + SetWindowLongPtrW(hwnd, GWL_STYLE, windowStyle); + } + + InvalidateRect(hwndList, NULL, FALSE); + } + } + } + + return result; + } + break; + } + return SendMessageW(GetParent(hwnd),uMsg, wParam,lParam); + } + } + break; + + case WM_EX_GETREALLIST: + return (LRESULT)GetDlgItem(hwnd, 2); + case WM_EX_GETCOUNTPERPAGE: + return SendMessageW(GetDlgItem(hwnd, 2), LVM_GETCOUNTPERPAGE, 0, 0L) + 1; + + case WM_KEYDOWN: SmoothScrollList_OnKeyDown(hwnd, (unsigned int)wParam, (unsigned int)lParam); return 0; + + case WM_ML_IPC: + return SmoothScrollList_OnMediaLibraryIPC(hwnd, (INT)lParam, (INT_PTR)wParam); + + default: + if(uMsg >= LVM_FIRST && uMsg < LVM_FIRST + 0x100) + { + HWND hwndList = GetDlgItem(hwnd,2); + if (hwndList) return ListViewSubclass(hwndList, uMsg, wParam, lParam); + } + break; + } + return DefWindowProcW(hwnd,uMsg,wParam,lParam); +} + +void InitSmoothScrollList() { + WNDCLASSW wc = {0, }; + + if (GetClassInfoW(plugin.hDllInstance, L"SmoothScrollList", &wc)) return; + wc.style = CS_DBLCLKS; + wc.lpfnWndProc = SmoothScrollMsgProc; + wc.hInstance = plugin.hDllInstance; + wc.lpszClassName = L"SmoothScrollList"; + RegisterClassW(&wc); +} diff --git a/Src/Plugins/General/gen_ml/api__gen_ml.h b/Src/Plugins/General/gen_ml/api__gen_ml.h new file mode 100644 index 00000000..aae9312a --- /dev/null +++ b/Src/Plugins/General/gen_ml/api__gen_ml.h @@ -0,0 +1,68 @@ +#ifndef NULLSOFT_API_H +#define NULLSOFT_API_H + +#include "../Agave/Agave.h" +#include +#include +#include +#include "../Winamp/api_decodefile.h" +#ifndef IGNORE_API_GRACENOTE +#include "../gracenote/api_gracenote.h" +#endif + + +#include "api\service\api_service.h" +extern api_service *serviceApi; +#define WASABI_API_SVC serviceApi + +#include "api\syscb\api_syscb.h" +extern api_syscb *syscbApi; +#define WASABI_API_SYSCB syscbApi + +#include "..\Agave\Language\api_language.h" +//DECLARE_EXTERNAL_SERVICE(api_language, WASABI_API_LNG); + +#include +extern api_application *applicationApi; +#define WASABI_API_APP applicationApi + +#include "../Plugins/Library/ml_local/api_mldb.h" +extern api_mldb *mldbApi; +#define AGAVE_API_MLDB mldbApi + +#include "../nu/threadpool/api_threadpool.h" +extern api_threadpool *threadPoolApi; +#define WASABI_API_THREADPOOL threadPoolApi + +#include +extern obj_ombrowser *browserManager; +#define OMBROWSERMNGR browserManager + +#include "../Winamp/api_decodefile.h" +extern api_decodefile *decodeApi; +#define AGAVE_API_DECODE decodeApi + +#include "api/wnd/wndapi.h" +extern wnd_api *wndApi; +#define WASABI_API_WND wndApi + +#include +extern api_skin *skinApi; +#define WASABI_API_SKIN skinApi + +#include "../Agave/Config/api_config.h" + +#include +extern api_palette *paletteManagerApi; +#define WASABI_API_PALETTE paletteManagerApi + + +#ifndef IGNORE_API_GRACENOTE +DECLARE_EXTERNAL_SERVICE(api_gracenote, AGAVE_API_GRACENOTE); +#endif + +#include "../Winamp/JSAPI2_api_security.h" +extern JSAPI2::api_security *jsapi2_security; +#define AGAVE_API_JSAPI2_SECURITY jsapi2_security + +#endif \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/banner.cpp b/Src/Plugins/General/gen_ml/banner.cpp new file mode 100644 index 00000000..f701625f --- /dev/null +++ b/Src/Plugins/General/gen_ml/banner.cpp @@ -0,0 +1,215 @@ +#include "banner.h" +#include "graphics.h" + + +MLBanner::MLBanner(void) +{ + bmpBck = NULL; + bmpLogo = NULL; + bmpLogoMask = NULL; + bmpBanner = NULL; + m_hwnd = NULL; + oldWndProc = NULL; + + color1 = RGB(0,0,0); + color2 = RGB(255,255,255); + + hInstance = NULL; + logoResId = 0; + bgndResId = 0; + + SetRect(&rcBanner, 0,0,0,0); +} +MLBanner::~MLBanner(void) +{ + DestroyImages(); + SetWindowLong(m_hwnd, GWL_WNDPROC, (LONG)oldWndProc); + oldWndProc = NULL; +} + +void MLBanner::SetColors(int color1, int color2) +{ + this->color1 = color1; + this->color2 = color2; + ReloadImages(); +} + +void MLBanner::SetImages(HINSTANCE hInstance, int bgndResId, int logoResId) +{ + this->hInstance = hInstance; + this->logoResId = logoResId; + this->bgndResId = bgndResId; + ReloadImages(); +} + +void MLBanner::ReloadImages(void) +{ + DestroyImages(); + if (hInstance) + { + if (bgndResId) + { + bmpBck = (HBITMAP)LoadImage(hInstance, MAKEINTRESOURCE(bgndResId), IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION); + if (bmpBck) bmpBck = PatchBitmapColors24(bmpBck, color1, color2, Filter1); + } + if (logoResId) + { + bmpLogo = (HBITMAP)LoadImage(hInstance, MAKEINTRESOURCE(logoResId), IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION); + if (bmpLogo) + { + bmpLogoMask = CreateBitmapMask(bmpLogo, 1,1); + } + } + } + +} + +void MLBanner::DestroyImages(void) +{ + if (bmpBck) DeleteObject(bmpBck); + bmpBck = NULL; + + if (bmpLogo) DeleteObject(bmpLogo); + bmpLogo = NULL; + + if (bmpLogoMask) DeleteObject(bmpLogoMask); + bmpLogoMask = NULL; + + if (bmpBanner) DeleteObject(bmpBanner); + bmpBanner = NULL; +} + + + +void MLBanner::UpdateBunnerBmp(void) +{ + if (bmpBanner) DeleteObject(bmpBanner); + + HDC hdc = GetDC(m_hwnd); + + bmpBanner = CreateCompatibleBitmap(hdc, rcBanner.right, rcBanner.bottom); + HDC memDstDC = CreateCompatibleDC (hdc); + HDC memSrcDC = CreateCompatibleDC (hdc); + SelectObject(memDstDC, bmpBanner); + SelectObject(memSrcDC, bmpBck); + + for (int i = 0; i < rcBanner.right; i++) + { + BitBlt(memDstDC, + i,0, + 1, rcBanner.bottom, + memSrcDC, + 0,0, + SRCCOPY); + + } + + BITMAP bm; + GetObject(bmpLogo, sizeof(BITMAP), &bm); + + SelectObject(memSrcDC, bmpLogoMask); + BitBlt(memDstDC, + 6, + max(2, (rcBanner.bottom - bm.bmHeight) / 2), + min(rcBanner.right - 4, bm.bmWidth), + min(rcBanner.bottom - 2, bm.bmHeight), + memSrcDC, + 0,0, + SRCAND); + + SelectObject(memSrcDC, bmpLogo); + BitBlt(memDstDC, + 6, + max(2, (rcBanner.bottom - bm.bmHeight) / 2), + min(rcBanner.right - 4, bm.bmWidth), + min(rcBanner.bottom - 2, bm.bmHeight), + memSrcDC, + 0,0, + SRCPAINT); + + ReleaseDC(m_hwnd, hdc); + DeleteDC(memDstDC); + DeleteDC(memSrcDC); + +} + +void MLBanner::Init(HWND hwnd) +{ + m_hwnd = hwnd; + SetWindowLong(hwnd,GWL_USERDATA,(LONG)this); + oldWndProc= (WNDPROC) SetWindowLong(hwnd, GWL_WNDPROC, (LONG)newWndProc); + UpdateBunnerBmp(); +} + +BOOL CALLBACK MLBanner::newWndProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam) +{ + MLBanner *banner = (MLBanner*)GetWindowLong(hwndDlg, GWL_USERDATA); + + switch(uMsg) + { + case WM_SIZE: + if (SIZE_MINIMIZED != wParam) + { + SetRect(&banner->rcBanner, 0,0,LOWORD(lParam),HIWORD(lParam)); + banner->UpdateBunnerBmp(); + } + break; + case WM_ERASEBKGND: + { + HDC hdc = GetDC(hwndDlg); + if (banner->bmpBanner) + { + HDC memSrcDC = CreateCompatibleDC (hdc); + SelectObject(memSrcDC, banner->bmpBanner); + StretchBlt( hdc, + banner->rcBanner.left, + banner->rcBanner.top, + banner->rcBanner.right - banner->rcBanner.left, + banner->rcBanner.bottom - banner->rcBanner.top, + memSrcDC, + banner->rcBanner.left, + banner->rcBanner.top, + banner->rcBanner.right - banner->rcBanner.left, + banner->rcBanner.bottom - banner->rcBanner.top, + SRCCOPY); + DeleteDC(memSrcDC); + } + ReleaseDC(hwndDlg, hdc); + } + return TRUE; + case WM_PAINT: + { + PAINTSTRUCT pt; + HDC hdc = BeginPaint(hwndDlg, &pt); + if (!banner->bmpBanner) + { + SetRect(&banner->rcBanner, 0,0,pt.rcPaint.right - pt.rcPaint.left, pt.rcPaint.bottom - pt.rcPaint.top); + banner->UpdateBunnerBmp(); + } + if (banner->bmpBanner) + { + HDC memSrcDC = CreateCompatibleDC (hdc); + SelectObject(memSrcDC, banner->bmpBanner); + StretchBlt( hdc, + pt.rcPaint.left, + pt.rcPaint.top, + pt.rcPaint.right - pt.rcPaint.left, + pt.rcPaint.bottom - pt.rcPaint.top, + memSrcDC, + pt.rcPaint.left, + pt.rcPaint.top, + pt.rcPaint.right - pt.rcPaint.left, + pt.rcPaint.bottom - pt.rcPaint.top, + SRCCOPY); + DeleteDC(memSrcDC); + ValidateRect(hwndDlg, &pt.rcPaint); + } + EndPaint(hwndDlg, &pt); + } + break; + } + + return CallWindowProc(banner->oldWndProc, hwndDlg, uMsg, wParam, lParam); +} + + \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/banner.h b/Src/Plugins/General/gen_ml/banner.h new file mode 100644 index 00000000..9b7409cf --- /dev/null +++ b/Src/Plugins/General/gen_ml/banner.h @@ -0,0 +1,44 @@ +#ifndef NULLSOFT_ML_BANNER_HEADER +#define NULLSOFT_ML_BANNER_HEADER + +#include + +class MLBanner +{ +public: + MLBanner(void); + ~MLBanner(void); + +public: + + void SetColors(int color1, int color2); + void SetImages(HINSTANCE hInstance, int bgndResId, int logoResId); + void Init(HWND hwnd); + void ReloadImages(void); + +protected: + void DestroyImages(void); + void UpdateBunnerBmp(void); + static BOOL CALLBACK newWndProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam); + +private: + + HWND m_hwnd; + HBITMAP bmpBck; + HBITMAP bmpLogo; + HBITMAP bmpLogoMask; + HBITMAP bmpBanner; + + WNDPROC oldWndProc; + + HINSTANCE hInstance; + int logoResId; + int bgndResId; + + int color1; + int color2; + + RECT rcBanner; +}; + +#endif // NULLSOFT_ML_BANNER_HEADER \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/childwnd.cpp b/Src/Plugins/General/gen_ml/childwnd.cpp new file mode 100644 index 00000000..a5e35ce1 --- /dev/null +++ b/Src/Plugins/General/gen_ml/childwnd.cpp @@ -0,0 +1,235 @@ +#include "main.h" +#include "childwnd.h" +#include "resource.h" + +typedef struct _CHILDREMOVERGN +{ + HWND hwndParent; + HRGN rgnUpdate; + HRGN rgnChild; +} CHILDREMOVERGN; + +static BOOL useDeferWndPos = TRUE; + + +void childresize_init(HWND hwndDlg, ChildWndResizeItem *list, int num) +{ + RECT r; + int x; + + useDeferWndPos = (GetVersion() < 0x80000000); + + GetClientRect(hwndDlg, &r); + + for (x = 0; x < num; x ++) + { + RECT r2; + GetWindowRect(GetDlgItem(hwndDlg,list[x].id), &r2); + MapWindowPoints(HWND_DESKTOP, hwndDlg, (LPPOINT)&r2, 2); + + list[x].rinfo.left = (0xF000 & list[x].type) ? (r.right - r2.left) : r2.left; + list[x].rinfo.top = (0x0F00 & list[x].type) ? (r.bottom -r2.top) : r2.top; + list[x].rinfo.right = (0x00F0 & list[x].type) ? (r.right - r2.right) : r2.right; + list[x].rinfo.bottom= (0x000F & list[x].type) ? (r.bottom - r2.bottom) : r2.bottom; + + list[x].type |= 0xF0000; + } + +} + +BOOL CALLBACK childresize_enumRemoveRegion(HWND hwnd, LPARAM lParam) +{ + if (IsWindowVisible(hwnd) && GetParent(hwnd) == ((CHILDREMOVERGN*)lParam)->hwndParent) + { + RECT r; + GetWindowRect(hwnd, &r); + MapWindowPoints(HWND_DESKTOP, ((CHILDREMOVERGN*)lParam)->hwndParent, (LPPOINT)&r, 2); + + SetRectRgn(((CHILDREMOVERGN*)lParam)->rgnChild, r.left, r.top, r.right, r.bottom); + CombineRgn(((CHILDREMOVERGN*)lParam)->rgnUpdate, ((CHILDREMOVERGN*)lParam)->rgnUpdate, ((CHILDREMOVERGN*)lParam)->rgnChild, RGN_DIFF); + } + + return TRUE; +} + +void childresize_resize_to_rectlist(HWND hwndDlg, ChildWndResizeItem *list, int num, RECT *rectout) +{ + RECT r; + int x; + GetClientRect(hwndDlg,&r); + + for (x = 0; x < num; x ++) if (list[x].type&0xf0000) + { + RECT r2; + if (list[x].type&0xF000) r2.left=r.right-list[x].rinfo.left; + else r2.left=list[x].rinfo.left; + + if (list[x].type&0x0F00) r2.top=r.bottom-list[x].rinfo.top; + else r2.top=list[x].rinfo.top; + + if (list[x].type&0x00F0) r2.right=r.right-list[x].rinfo.right; + else r2.right=list[x].rinfo.right; + + if (list[x].type&0x000F) r2.bottom=r.bottom-list[x].rinfo.bottom; + else r2.bottom=list[x].rinfo.bottom; + + *rectout = r2; + rectout++; + } +} + +void childresize_resize_from_rectlist(HWND hwndDlg, ChildWndResizeItem *list, int num, RECT *rectin) +{ + RECT r, *pr; + CHILDREMOVERGN crr; + int x; + HDWP hdwp; + HWND h; + + GetClientRect(hwndDlg,&r); + + crr.hwndParent = hwndDlg; + crr.rgnUpdate = CreateRectRgnIndirect(&r); + crr.rgnChild = CreateRectRgn(0,0,0,0); + + EnumChildWindows(hwndDlg,&childresize_enumRemoveRegion,(LPARAM)&crr); + + + hdwp = (useDeferWndPos) ? BeginDeferWindowPos(num) : NULL; + for (pr = rectin, x = 0; x < num && hdwp; x ++) + { + if (0xF0000 & list[x].type) + { + h = GetDlgItem(hwndDlg,list[x].id); + if (h && pr) hdwp = DeferWindowPos(hdwp, h, NULL, pr->left, pr->top, pr->right - pr->left, pr->bottom - pr->top, SWP_NOZORDER | SWP_NOACTIVATE); + pr++; + } + } + if (hdwp) EndDeferWindowPos(hdwp); + else + { + for (pr = rectin, x = 0; x < num; x ++) + { + if (0xF0000 & list[x].type) + { + h = GetDlgItem(hwndDlg,list[x].id); + if (h && pr) SetWindowPos(h, NULL, pr->left, pr->top, pr->right - pr->left, pr->bottom - pr->top, SWP_NOZORDER | SWP_NOACTIVATE); + pr++; + } + } + } + EnumChildWindows(hwndDlg,&childresize_enumRemoveRegion,(LPARAM)&crr); + InvalidateRgn(hwndDlg, crr.rgnUpdate, TRUE); + DeleteObject(crr.rgnUpdate); + DeleteObject(crr.rgnChild); +} + +void childresize_resize(HWND hwndDlg, ChildWndResizeItem *list, int num) +{ + RECT rc, rw; + CHILDREMOVERGN crr; + int x, y, cx, cy; + DWORD flags; + HDWP hdwp; + ChildWndResizeItem *pi; + + GetClientRect(hwndDlg,&rc); + crr.hwndParent = hwndDlg; + crr.rgnUpdate = CreateRectRgnIndirect(&rc); + crr.rgnChild = CreateRectRgn(0,0,0,0); + + EnumChildWindows(hwndDlg,&childresize_enumRemoveRegion,(LPARAM)&crr); + + hdwp = (useDeferWndPos) ? BeginDeferWindowPos(num) : NULL; + + for (pi = list + num - 1; pi >= list && (!useDeferWndPos || hdwp); pi--) + { + HWND hwnd = GetDlgItem(hwndDlg, pi->id); + if (hwnd && (0xF0000 & pi->type)) + { + x = (0xF000 & pi->type) ? (rc.right - pi->rinfo.left) : pi->rinfo.left; + y = (0x0F00 & pi->type) ? (rc.bottom - pi->rinfo.top) : pi->rinfo.top; + cx = ((0x00F0 & pi->type) ? (rc.right - pi->rinfo.right) : pi->rinfo.right) - x; + cy = ((0x000F & pi->type) ? (rc.bottom - pi->rinfo.bottom) : pi->rinfo.bottom) - y; + flags = SWP_NOZORDER | SWP_NOACTIVATE; + + GetWindowRect(hwnd, &rw); + MapWindowPoints(HWND_DESKTOP, hwndDlg, (LPPOINT)&rw, 2); + if (rw.left == x && rw.top == y) flags |= SWP_NOMOVE; + if (rw.right == (x + cx) && rw.bottom == (y + cy)) flags |= SWP_NOSIZE; + + if ((SWP_NOSIZE | SWP_NOMOVE) != ((SWP_NOSIZE | SWP_NOMOVE) & flags)) + { + if (useDeferWndPos) hdwp = DeferWindowPos(hdwp, hwnd, NULL, x, y, cx, cy, flags); + else SetWindowPos( hwnd, NULL, x, y, cx, cy, flags); + } + } + } + if (hdwp) EndDeferWindowPos(hdwp); + + EnumChildWindows(hwndDlg,&childresize_enumRemoveRegion,(LPARAM)&crr); + InvalidateRgn(hwndDlg, crr.rgnUpdate, TRUE); + + DeleteObject(crr.rgnUpdate); + DeleteObject(crr.rgnChild); +} + +void childresize_resize2(HWND hwndDlg, ChildWndResizeItem *list, int num, BOOL fRedraw, HRGN rgnUpdate) +{ + RECT rc, rw; + CHILDREMOVERGN crr; + int x, y, cx, cy; + DWORD flags; + HDWP hdwp; + ChildWndResizeItem *pi; + + GetClientRect(hwndDlg,&rc); + crr.hwndParent = hwndDlg; + crr.rgnUpdate = CreateRectRgnIndirect(&rc); + crr.rgnChild = CreateRectRgn(0,0,0,0); + +// EnumChildWindows(hwndDlg,&childresize_enumRemoveRegion,(LPARAM)&crr); + + hdwp = (useDeferWndPos) ? BeginDeferWindowPos(num) : NULL; + + for (pi = list + num - 1; pi >= list && (!useDeferWndPos || hdwp); pi--) + { + HWND hwnd = GetDlgItem(hwndDlg, pi->id); + if (hwnd && (0xF0000 & pi->type)) + { + x = (0xF000 & pi->type) ? (rc.right - pi->rinfo.left) : pi->rinfo.left; + y = (0x0F00 & pi->type) ? (rc.bottom - pi->rinfo.top) : pi->rinfo.top; + cx = ((0x00F0 & pi->type) ? (rc.right - pi->rinfo.right) : pi->rinfo.right) - x; + cy = ((0x000F & pi->type) ? (rc.bottom - pi->rinfo.bottom) : pi->rinfo.bottom) - y; + flags = SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW; + + GetWindowRect(hwnd, &rw); + MapWindowPoints(HWND_DESKTOP, hwndDlg, (LPPOINT)&rw, 2); + if (rw.left == x && rw.top == y) flags |= SWP_NOMOVE; + if (rw.right == (x + cx) && rw.bottom == (y + cy)) flags |= SWP_NOSIZE; + + if ((SWP_NOSIZE | SWP_NOMOVE) != ((SWP_NOSIZE | SWP_NOMOVE) & flags)) + { + if (useDeferWndPos) hdwp = DeferWindowPos(hdwp, hwnd, NULL, x, y, cx, cy, flags); + else SetWindowPos( hwnd, NULL, x, y, cx, cy, flags); + } + else + { + SetRectRgn(crr.rgnChild, rw.left, rw.top, rw.right, rw.bottom); + CombineRgn(crr.rgnUpdate, crr.rgnUpdate, crr.rgnChild, RGN_DIFF); + } + } + } + if (hdwp) EndDeferWindowPos(hdwp); + +// EnumChildWindows(hwndDlg,&childresize_enumRemoveRegion,(LPARAM)&crr); + + if (fRedraw) + { + InvalidateRgn(hwndDlg, crr.rgnUpdate, TRUE); + RedrawWindow(hwndDlg, NULL, crr.rgnUpdate, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASENOW | RDW_ERASE | RDW_ALLCHILDREN); + //RedrawWindow(hwndDlg, NULL, crr.rgnUpdate, RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASENOW | RDW_FRAME | RDW_ALLCHILDREN); + } + DeleteObject(crr.rgnUpdate); + DeleteObject(crr.rgnChild); +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/childwnd.h b/Src/Plugins/General/gen_ml/childwnd.h new file mode 100644 index 00000000..ade2dea1 --- /dev/null +++ b/Src/Plugins/General/gen_ml/childwnd.h @@ -0,0 +1,38 @@ +/* +** Copyright (C) 2003 Nullsoft, Inc. +** +** This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held +** liable for any damages arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, including commercial applications, and to +** alter it and redistribute it freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. +** If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +** +** 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +** +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#ifndef _CHILDWND_H_ +#define _CHILDWND_H_ + +typedef struct { + int id; + int type; // 0xLTRB + RECT rinfo; +} ChildWndResizeItem; + +/* If you are including this file from a plugin, you can't call these functions directly. they are here to help you create the function pointer typedefs */ +void childresize_init(HWND hwndDlg, ChildWndResizeItem *list, int num); +void childresize_resize(HWND hwndDlg, ChildWndResizeItem *list, int num); +void childresize_resize2(HWND hwndDlg, ChildWndResizeItem *list, int num, BOOL fRedraw = FALSE, HRGN rgnUpdate = NULL); + +//extended versions we use so we can modify the list before actually setting it +void childresize_resize_to_rectlist(HWND hwndDlg, ChildWndResizeItem *list, int num, RECT *rectout); +void childresize_resize_from_rectlist(HWND hwndDlg, ChildWndResizeItem *list, int num, RECT *rectin); + + +#endif//_CHILDWND_H_ \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/colors.cpp b/Src/Plugins/General/gen_ml/colors.cpp new file mode 100644 index 00000000..465df4c5 --- /dev/null +++ b/Src/Plugins/General/gen_ml/colors.cpp @@ -0,0 +1,207 @@ +#include "main.h" +#include "./colors.h" +#include "api__gen_ml.h" +#include "./ml_ipc_0313.h" +#include "../Wasabi/bfc/platform/types.h" +#include +#include +#include + +#define RGB_MENU_BACKGROUND 0 +#define RGB_MENU_BACKGROUND_SELECTED 1 +#define RGB_MENU_TEXT 2 +#define RGB_MENU_TEXT_SELECTED 3 +#define RGB_MENU_TEXT_GRAYED 4 +#define RGB_MENU_FRAME 5 +#define RGB_MENU_SEPARATOR 6 +#define RGB_MENU_BACKROUND_SELECTEDFRAME 7 + +#define RGB_TOOLTIP_BACKGROUND 8 +#define RGB_TOOLTIP_TEXT 9 +#define RGB_TOOLTIP_FRAME 10 + +#define RGB_OMBROWSER_BACKGROUND 11 +#define RGB_OMBROWSER_TEXT 12 +#define RGB_OMBROWSER_TEXT_DISABLED 13 +#define RGB_OMBROWSER_LINK 14 +#define RGB_OMBROWSER_LINK_ACTIVE 15 +#define RGB_OMBROWSER_LINK_VISITED 16 +#define RGB_OMBROWSER_LINK_HOVER 17 + +#define COLORCACHE_MAX (RGB_OMBROWSER_LINK_HOVER + 1) + +static COLORREF szColorCache[COLORCACHE_MAX]; +static BOOL requestColorReset = TRUE; +static HBITMAP bitmapCache = NULL; + + +#define WACOLOR(__index) WADlg_getColor(__index) +#define BlendWaColors(__index1, __index2, __k) BlendColors(WACOLOR(__index1), WACOLOR(__index2), __k) + +INT GetColorDistance(COLORREF rgb1, COLORREF rgb2) +{ + return (1000 * ((GetRValue(rgb1) - GetRValue(rgb2)) + + (GetGValue(rgb1) - GetGValue(rgb2)) + + (GetBValue(rgb1) - GetBValue(rgb2))))/ (3 * 255); +} + +COLORREF GetDarkerColor(COLORREF rgb1, COLORREF rgb2) +{ + INT g1 = (GetRValue(rgb1)*299 + GetGValue(rgb1)*587 + GetBValue(rgb1)*114); + INT g2 = (GetRValue(rgb2)*299 + GetGValue(rgb2)*587 + GetBValue(rgb2)*114); + return (g1 < g2) ? rgb1 : rgb2; +} + +COLORREF BlendColors(COLORREF rgbTop, COLORREF rgbBottom, INT alpha) +{ + if (alpha > 254) return rgbTop; + if (alpha < 0) return rgbBottom; + + WORD k = (((255 - alpha)*255 + 127)/255); + + return RGB( (GetRValue(rgbTop)*alpha + k*GetRValue(rgbBottom) + 127)/255, + (GetGValue(rgbTop)*alpha + k*GetGValue(rgbBottom) + 127)/255, + (GetBValue(rgbTop)*alpha + k*GetBValue(rgbBottom) + 127)/255); +} + +static COLORREF GetSkinColor(LPCWSTR colorName, COLORREF colorDefault) +{ + return SkinColor::GetColor(colorName, NULL, colorDefault); +} + + +#define QUERYSKINCOLOR(__cacheColorIndex, __skinColorId, __fallback)\ + { szColorCache[(__cacheColorIndex)] = GetSkinColor((__skinColorId), (__fallback)); } + + +void ResetColors(BOOL fImmediate) +{ + requestColorReset = TRUE; + + if (FALSE == fImmediate) + { + bitmapCache = NULL; + } + else + { + WADlg_init(plugin.hwndParent); + bitmapCache = WADlg_getBitmap(); + } +} + +static BOOL UpdateColorCache() +{ + if (NULL == bitmapCache || bitmapCache != WADlg_getBitmap()) + { + WADlg_init(plugin.hwndParent); + bitmapCache = WADlg_getBitmap(); + } + + + COLORREF rgbFrame1 = WACOLOR((WACOLOR(WADLG_WNDBG) == WACOLOR(WADLG_HILITE)) ? WADLG_LISTHEADER_BGCOLOR : WADLG_HILITE); + COLORREF rgbSelected = GetSkinColor(L"wasabi.popupmenu.background.selected", WACOLOR(WADLG_HILITE)); + + QUERYSKINCOLOR(RGB_MENU_BACKGROUND, L"wasabi.popupmenu.background", WACOLOR(WADLG_WNDBG)); + + if (rgbSelected == szColorCache[RGB_MENU_BACKGROUND]) + rgbSelected = WACOLOR(WADLG_SELBAR_BGCOLOR); + + szColorCache[RGB_MENU_BACKGROUND_SELECTED] = BlendColors(rgbSelected, szColorCache[RGB_MENU_BACKGROUND], 127); + szColorCache[RGB_MENU_BACKROUND_SELECTEDFRAME] = ColorAdjustLuma(rgbSelected, -100, TRUE); + + QUERYSKINCOLOR(RGB_MENU_TEXT, L"wasabi.popupmenu.text", WACOLOR(WADLG_WNDFG)); + QUERYSKINCOLOR(RGB_MENU_TEXT_SELECTED, L"wasabi.popupmenu.text.selected", WACOLOR(WADLG_WNDFG)); + QUERYSKINCOLOR(RGB_MENU_TEXT_GRAYED, L"wasabi.popupmenu.text.inactive", BlendWaColors(WADLG_WNDFG, WADLG_WNDBG, 76)); + QUERYSKINCOLOR(RGB_MENU_FRAME, L"wasabi.popupmenu.frame", rgbFrame1); + QUERYSKINCOLOR(RGB_MENU_SEPARATOR, L"wasabi.popupmenu.separator", rgbFrame1); + + QUERYSKINCOLOR(RGB_TOOLTIP_BACKGROUND, L"wasabi.tooltip.background", WACOLOR(WADLG_WNDBG)); + QUERYSKINCOLOR(RGB_TOOLTIP_TEXT, L"wasabi.tooltip.text", WACOLOR(WADLG_WNDFG)); + QUERYSKINCOLOR(RGB_TOOLTIP_FRAME, L"wasabi.tooltip.frame", rgbFrame1); + + COLORREF rgbLinkBase, rgbBrowserBk; + INT szLinkBaseIndex[] = { WADLG_SELBAR_FGCOLOR, WADLG_SELBAR_BGCOLOR, WADLG_SELCOLOR, WADLG_HILITE, WADLG_ITEMFG}; + + rgbBrowserBk = WACOLOR(WADLG_ITEMBG); + for(INT i = 0; i < ARRAYSIZE(szLinkBaseIndex); i++) + { + rgbLinkBase = WACOLOR(szLinkBaseIndex[i]); + INT d = GetColorDistance(rgbLinkBase, rgbBrowserBk); + if (d < 0) d = - d; + if (d > 40) break; + } + + QUERYSKINCOLOR(RGB_OMBROWSER_BACKGROUND, L"wasabi.ombrowser.background", rgbBrowserBk); + QUERYSKINCOLOR(RGB_OMBROWSER_TEXT, L"wasabi.ombrowser.text", BlendWaColors(WADLG_ITEMFG, WADLG_ITEMBG, 230)); + QUERYSKINCOLOR(RGB_OMBROWSER_TEXT_DISABLED, L"wasabi.ombrowser.text.disabled", BlendWaColors(WADLG_ITEMFG, WADLG_ITEMBG, 120)); + QUERYSKINCOLOR(RGB_OMBROWSER_LINK, L"wasabi.ombrowser.link", BlendColors(rgbLinkBase, rgbBrowserBk, 225)); + QUERYSKINCOLOR(RGB_OMBROWSER_LINK_ACTIVE, L"wasabi.ombrowser.link.active", rgbLinkBase); + QUERYSKINCOLOR(RGB_OMBROWSER_LINK_VISITED, L"wasabi.ombrowser.link.visited", BlendColors(rgbLinkBase, rgbBrowserBk, 135)); + QUERYSKINCOLOR(RGB_OMBROWSER_LINK_HOVER, L"wasabi.ombrowser.link.hover", BlendColors(rgbLinkBase, rgbBrowserBk, 245)); + + return TRUE; +} + + +HRESULT MLGetSkinColor(UINT uObject, UINT uPart, UINT uState, COLORREF *pColor) +{ + + #define SELECT_PART(__id) case (__id): switch(uPart) { + #define SELECT_STATE(__id) case (__id): switch(uState) { + #define END_SELECT } break; + #define SETRGB(__id, __rgb) case (__id): *pColor = (__rgb); return S_OK; + #define SETWACOLOR(__id, __waIndex) case (__id): *pColor = WACOLOR(__waIndex); return S_OK; + #define SETCACHECOLOR(__id, __cacheIndex) case (__id): *pColor = szColorCache[(__cacheIndex)]; return S_OK; + + if (FALSE != requestColorReset && FALSE != UpdateColorCache()) + requestColorReset = FALSE; + + switch(uObject) + { + SELECT_PART(MLSO_WINDOW) + SELECT_STATE(WP_BACKGROUND) + SETWACOLOR(WBS_NORMAL, WADLG_WNDBG) + SETWACOLOR(WBS_HILITED, WADLG_HILITE) + END_SELECT + SETWACOLOR(WP_TEXT, WADLG_WNDFG) + SETWACOLOR(WP_FRAME, WADLG_HILITE) + END_SELECT + + SELECT_PART(MLSO_MENU) + SELECT_STATE(MP_BACKGROUND) + SETCACHECOLOR(MBS_NORMAL, RGB_MENU_BACKGROUND) + SETCACHECOLOR(MBS_SELECTED, RGB_MENU_BACKGROUND_SELECTED) + SETCACHECOLOR(MBS_SELECTEDFRAME, RGB_MENU_BACKROUND_SELECTEDFRAME) + END_SELECT + SELECT_STATE(MP_TEXT) + SETCACHECOLOR(MTS_NORMAL, RGB_MENU_TEXT) + SETCACHECOLOR(MTS_SELECTED, RGB_MENU_TEXT_SELECTED) + SETCACHECOLOR(MTS_DISABLED, RGB_MENU_TEXT_GRAYED) + END_SELECT + SETCACHECOLOR(MP_SEPARATOR, RGB_MENU_SEPARATOR) + SETCACHECOLOR(MP_FRAME, RGB_MENU_FRAME) + END_SELECT + + SELECT_PART(MLSO_TOOLTIP) + SETCACHECOLOR(TTP_BACKGROUND, RGB_TOOLTIP_BACKGROUND) + SETCACHECOLOR(TTP_TEXT, RGB_TOOLTIP_TEXT) + SETCACHECOLOR(TTP_FRAME, RGB_TOOLTIP_FRAME) + END_SELECT + + SELECT_PART(MLSO_OMBROWSER) + SETCACHECOLOR(BP_BACKGROUND, RGB_OMBROWSER_BACKGROUND) + SELECT_STATE(BP_TEXT) + SETCACHECOLOR(BTS_NORMAL, RGB_OMBROWSER_TEXT) + SETCACHECOLOR(BTS_DISABLED, RGB_OMBROWSER_TEXT_DISABLED) + END_SELECT + SELECT_STATE(BP_LINK) + SETCACHECOLOR(BLS_NORMAL, RGB_OMBROWSER_LINK) + SETCACHECOLOR(BLS_ACTIVE, RGB_OMBROWSER_LINK_ACTIVE) + SETCACHECOLOR(BLS_VISITED, RGB_OMBROWSER_LINK_VISITED) + SETCACHECOLOR(BLS_HOVER, RGB_OMBROWSER_LINK_HOVER) + END_SELECT + END_SELECT + + } + return E_INVALIDARG; +} diff --git a/Src/Plugins/General/gen_ml/colors.h b/Src/Plugins/General/gen_ml/colors.h new file mode 100644 index 00000000..e8dc5ca5 --- /dev/null +++ b/Src/Plugins/General/gen_ml/colors.h @@ -0,0 +1,65 @@ +#ifndef NULLOSFT_MEDIALIBRARY_COLORS_HEADER +#define NULLOSFT_MEDIALIBRARY_COLORS_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include + +//******************************* Window +#define MLSO_WINDOW 0 + +#define WP_BACKGROUND 0 +#define WBS_NORMAL 0 +#define WBS_HILITED 1 + +#define WP_TEXT 1 +#define WP_FRAME 2 + +//******************************* Menu +#define MLSO_MENU 2 + +#define MP_BACKGROUND 0 +#define MBS_NORMAL 0 +#define MBS_SELECTED 1 +#define MBS_SELECTEDFRAME 2 + +#define MP_TEXT 1 +#define MTS_NORMAL 0 +#define MTS_SELECTED 1 +#define MTS_DISABLED 2 + +#define MP_SEPARATOR 2 +#define MP_FRAME 3 + +//******************************* ToolTip +#define MLSO_TOOLTIP 3 + +#define TTP_BACKGROUND 0 +#define TTP_TEXT 1 +#define TTP_FRAME 2 + +//******************************* omBrowser +#define MLSO_OMBROWSER 5 + +#define BP_BACKGROUND 0 + +#define BP_TEXT 1 +#define BTS_NORMAL 0 +#define BTS_DISABLED 1 + +#define BP_LINK 2 +#define BLS_NORMAL 0 +#define BLS_ACTIVE 1 +#define BLS_VISITED 2 +#define BLS_HOVER 3 + +void ResetColors(BOOL fImmediate); +HRESULT MLGetSkinColor(UINT uObject, UINT uPart, UINT uState, COLORREF *pColor); + +INT GetColorDistance(COLORREF rgb1, COLORREF rgb2); +COLORREF GetDarkerColor(COLORREF rgb1, COLORREF rgb2); +COLORREF BlendColors(COLORREF rgbTop, COLORREF rgbBottom, INT alpha); + +#endif //NULLOSFT_MEDIALIBRARY_COLORS_HEADER \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/comboskin.cpp b/Src/Plugins/General/gen_ml/comboskin.cpp new file mode 100644 index 00000000..886005ce --- /dev/null +++ b/Src/Plugins/General/gen_ml/comboskin.cpp @@ -0,0 +1,148 @@ +#include "main.h" + +#include "comboskin.h" + +static int RectInRect(RECT *rect1, RECT *rect2) +{ + // this has a bias towards true + + // this could probably be optimized a lot + return ((rect1->top >= rect2->top && rect1->top <= rect2->bottom) || + (rect1->bottom >= rect2->top && rect1->bottom <= rect2->bottom) || + (rect2->top >= rect1->top && rect2->top <= rect1->bottom) || + (rect2->bottom >= rect1->top && rect2->bottom <= rect1->bottom)) // vertical intersect + && + ((rect1->left >= rect2->left && rect1->left <= rect2->right) || + (rect1->right >= rect2->left && rect1->right <= rect2->right) || + (rect2->left >= rect1->left && rect2->left <= rect1->right) || + (rect2->right >= rect1->left && rect2->right <= rect1->right)) // horiz intersect + ; +} + +static INT_PTR CALLBACK wndproc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam) +{ + ComboSkin *cs=(ComboSkin *)(LONG_PTR)GetWindowLongPtr(hwndDlg,GWLP_USERDATA); + if (uMsg == WM_PAINT) + { + RECT r; + GetClientRect(hwndDlg,&r); + RECT ur; + GetUpdateRect(hwndDlg,&ur,FALSE); + if(RectInRect(&r,&ur)) //hmmm not sure about testing this, should probably use a backbuffer or something + { + //Fill with bg color + HDC hdc=GetDC(hwndDlg); + HBRUSH b=CreateSolidBrush(WADlg_getColor(WADLG_WNDBG)); + HBRUSH b2=CreateSolidBrush(WADlg_getColor(WADLG_HILITE)); //sunken + + //top line + { + RECT a={0,0,r.right-r.left,3}; + FillRect(hdc,&a,b); + ValidateRect(hwndDlg,&a); + } + //bottom lines + { + RECT a={0,r.bottom-2,r.right-r.left,r.bottom}; + FillRect(hdc,&a,b); + ValidateRect(hwndDlg,&a); + } + { + //sunken part + RECT a={0,r.bottom-3,r.right-r.left,r.bottom-2}; + FillRect(hdc,&a,b2); + ValidateRect(hwndDlg,&a); + } + //left + { + RECT a={0,0,2,r.bottom-r.top}; + FillRect(hdc,&a,b); + ValidateRect(hwndDlg,&a); + } + //right + { + RECT a={r.right-2,0,r.right,r.bottom-r.top}; + FillRect(hdc,&a,b); + ValidateRect(hwndDlg,&a); + } + + //paint the arrow + HBITMAP bmp=WADlg_getBitmap(); + HDC hdcbmp = CreateCompatibleDC(hdc); + SelectObject(hdcbmp,bmp); + int pushed=0; + if(GetAsyncKeyState(MK_LBUTTON) & 0x8000) + { + //check if in arrow down area + POINT cursor; + GetCursorPos(&cursor); + ScreenToClient(hwndDlg,&cursor); + if(cursor.x >= r.right-20 && cursor.x <= r.right) + { + pushed=1; + } + } + int startx=14; + int starty=31; + if(pushed) startx+=28; + int left=r.right-18; + int top=r.top+4; + StretchBlt(hdc,left,top,14,14,hdcbmp,startx,starty,14,14,SRCCOPY); + DeleteDC(hdcbmp); + RECT a={left,top,left+14,top+14}; + ValidateRect(hwndDlg,&a); + //paint arrow borders + { + HBRUSH b=CreateSolidBrush(WADlg_getColor(WADLG_ITEMBG)); + RECT a={left,3,left+14,4}; + FillRect(hdc,&a,b); + ValidateRect(hwndDlg,&a); + + RECT c={left,17,left+14,18}; + FillRect(hdc,&c,b); + ValidateRect(hwndDlg,&c); + + RECT d={left+14,3,left+15,18}; + FillRect(hdc,&d,b); + ValidateRect(hwndDlg,&d); + + RECT e={left+15,3,left+16,19}; + FillRect(hdc,&e,b2); + ValidateRect(hwndDlg,&e); + + DeleteObject(b); + } + + DeleteObject(b); + DeleteObject(b2); + ReleaseDC(hwndDlg,hdc); + } + } + + if(uMsg == WM_WINDOWPOSCHANGING) + { + //move it up 1 pixel so it's correctly centered + WINDOWPOS *wp=(WINDOWPOS *)lParam; + wp->y--; + } + + if(uMsg == WM_KILLFOCUS || uMsg == WM_LBUTTONUP) + { + InvalidateRect(hwndDlg,NULL,TRUE); + } + + return CallWindowProc(cs->m_old_wndproc,hwndDlg,uMsg,wParam,lParam); +} + +ComboSkin::ComboSkin(HWND hwnd) +{ + m_hwnd=hwnd; + + SetWindowLongPtr(m_hwnd,GWLP_USERDATA, (LONGX86)(LONG_PTR)this); + m_old_wndproc=(WNDPROC)(LONG_PTR)SetWindowLongPtr(m_hwnd,GWLP_WNDPROC, (LONGX86)(LONG_PTR)wndproc); +} + +ComboSkin::~ComboSkin() +{ + SetWindowLongPtr(m_hwnd,GWLP_WNDPROC, (LONGX86)(LONG_PTR)m_old_wndproc); +} diff --git a/Src/Plugins/General/gen_ml/comboskin.h b/Src/Plugins/General/gen_ml/comboskin.h new file mode 100644 index 00000000..c3fbbdc8 --- /dev/null +++ b/Src/Plugins/General/gen_ml/comboskin.h @@ -0,0 +1,18 @@ +#ifndef _COMBOSKIN_H +#define _COMBOSKIN_H + +#include + +//class ScrollWnd; + +class ComboSkin +{ +public: + ComboSkin(HWND hwnd); + ~ComboSkin(); + + HWND m_hwnd; + WNDPROC m_old_wndproc; +}; + +#endif \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/config.cpp b/Src/Plugins/General/gen_ml/config.cpp new file mode 100644 index 00000000..7cd519df --- /dev/null +++ b/Src/Plugins/General/gen_ml/config.cpp @@ -0,0 +1,128 @@ +#include +#include + +#include "config.h" +#include "../nu/AutoCharFn.h" + + + +C_Config::C_Config( wchar_t *ini ) +{ + memset( m_strbuf, 0, sizeof( m_strbuf ) ); + memset( m_strbufA, 0, sizeof( m_strbufA ) ); + + m_inifile = _wcsdup( ini ); + m_inifileA = _strdup( AutoCharFn( m_inifile ) ); + m_section = 0; +} + +C_Config::~C_Config() +{ + if ( m_inifile ) + free( m_inifile ); + + if ( m_inifileA ) + free( m_inifileA ); + + if ( m_section ) + free( m_section ); +} + + +void C_Config::WriteInt( wchar_t *name, int value ) +{ + wchar_t buf[ 32 ] = { 0 }; + StringCchPrintfW( buf, 32, L"%d", value ); + WriteString( name, buf ); +} + +int C_Config::ReadInt( wchar_t *name, int defvalue ) +{ + return GetPrivateProfileIntW( L"gen_ml_config", name, defvalue, m_inifile ); +} + + +wchar_t *C_Config::WriteString( wchar_t *name, wchar_t *string ) +{ + WritePrivateProfileStringW( L"gen_ml_config", name, string, m_inifile ); + + return name; +} + +char *C_Config::WriteString( char *name, char *string ) +{ + WritePrivateProfileStringA( "gen_ml_config", name, string, m_inifileA ); + + return name; +} + +wchar_t *C_Config::ReadString( wchar_t *name, wchar_t *defstr ) +{ + static wchar_t foobuf[] = L"___________gen_ml_lameness___________"; + m_strbuf[ 0 ] = 0; + GetPrivateProfileStringW( L"gen_ml_config", name, foobuf, m_strbuf, ARRAYSIZE( m_strbuf ), m_inifile ); + if ( !wcscmp( foobuf, m_strbuf ) ) + return defstr; + + m_strbuf[ ARRAYSIZE( m_strbuf ) - 1 ] = 0; + + return m_strbuf; +} + +bool C_Config::ReadString( const wchar_t *name, const wchar_t *defvalue, wchar_t *storage, size_t len ) +{ + static wchar_t foobuf[] = L"___________gen_ml_lameness___________"; + storage[ 0 ] = 0; + GetPrivateProfileStringW( L"gen_ml_config", name, foobuf, storage, (DWORD)len, m_inifile ); + if ( !wcscmp( foobuf, storage ) ) + { + if ( defvalue ) + lstrcpynW( storage, defvalue, (int)len ); + + return false; + } + + return true; +} + +char *C_Config::ReadString( const char *name, char *defstr ) +{ + static char foobuf[] = "___________gen_ml_lameness___________"; + m_strbufA[ 0 ] = 0; + GetPrivateProfileStringA( "gen_ml_config", name, foobuf, m_strbufA, ARRAYSIZE( m_strbufA ), m_inifileA ); + if ( !strcmp( foobuf, m_strbufA ) ) + return defstr; + + m_strbufA[ ARRAYSIZE( m_strbufA ) - 1 ] = 0; + + return m_strbufA; +} + +char *C_Config::ReadString( const char *section_name, const char *key_name, char *defvalue ) +{ + static char foobuf[] = "___________lameness___________"; + m_strbufA[ 0 ] = 0; + GetPrivateProfileStringA( section_name, key_name, foobuf, m_strbufA, ARRAYSIZE( m_strbufA ), m_inifileA ); + if ( !strcmp( foobuf, m_strbufA ) ) + return defvalue; + + m_strbufA[ ARRAYSIZE( m_strbufA ) - 1 ] = 0; + + return m_strbufA; +} + +bool C_Config::ReadString( const char *name, const char *defvalue, char *storage, size_t len ) +{ + static char foobuf[] = "___________gen_ml_lameness___________"; + storage[ 0 ] = 0; + GetPrivateProfileStringA( "gen_ml_config", name, foobuf, storage, (DWORD)len, m_inifileA ); + if ( !strcmp( foobuf, storage ) ) + { + if ( defvalue ) + lstrcpynA( storage, defvalue, (int)len ); + + return false; + } + + return true; +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/config.h b/Src/Plugins/General/gen_ml/config.h new file mode 100644 index 00000000..7cc00ebd --- /dev/null +++ b/Src/Plugins/General/gen_ml/config.h @@ -0,0 +1,31 @@ +#ifndef _C_CONFIG_H_ +#define _C_CONFIG_H_ + +class C_Config +{ +public: + C_Config( wchar_t *ini ); + ~C_Config(); + + void WriteInt( wchar_t *name, int value ); + wchar_t *WriteString( wchar_t *name, wchar_t *string ); + char *WriteString( char *name, char *string ); + + int ReadInt( wchar_t *name, int defvalue ); + wchar_t *ReadString( wchar_t *name, wchar_t *defvalue ); + char *ReadString( const char *name, char *defvalue ); + char *ReadString( const char *section_name, const char *key_name, char *defvalue ); + bool ReadString( const char *name, const char *defvalue, char *storage, size_t len ); // returns true + bool ReadString( const wchar_t *name, const wchar_t *defvalue, wchar_t *storage, size_t len ); // returns true + + wchar_t *GetIniFile() { return m_inifile; } + +private: + wchar_t m_strbuf[ 8192 ]; + char m_strbufA[ 8192 ]; + wchar_t *m_inifile; + char *m_inifileA; + wchar_t *m_section; +}; + +#endif//_C_CONFIG_H_ \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/fileview.cpp b/Src/Plugins/General/gen_ml/fileview.cpp new file mode 100644 index 00000000..705bea3f --- /dev/null +++ b/Src/Plugins/General/gen_ml/fileview.cpp @@ -0,0 +1,2059 @@ +#include "main.h" +#include "./fileview.h" +#include "./fileview_internal.h" +#include "./resource.h" +#include "./stockobjects.h" +#include "../winamp/wa_dlg.h" +#include "../nu/menushortcuts.h" +#include "../nu/CGlobalAtom.h" +#include +#include + +#ifndef ARRAYSIZE +#define ARRAYSIZE(blah) (sizeof(blah)/sizeof(*blah)) +#endif + +#define FOLDERBROWSER_MIN_HEIGHT 64 +#define FILELIST_MIN_HEIGHT 120 + +#define IDC_FILEVIEW_TOOLBAR 100001 +#define IDC_FILELIST 100010 +#define IDC_FILELIST_HEADER 100011 + +#define IDT_INVALIDATELIST 1982 +#define IDT_ACTIVATELISTITEM 1983 +#define DELAY_INVALIDATELIST 75 +#define DELAY_ACTIVATELISTITEM 0 + +#define ID_INTERNAL_UPDATE_COLUMN_INFO 20000 + +typedef struct _FILEVIEW +{ + FILEDATA fileData; + UINT style; + UINT sortColumn; + BOOL bAscending; + INT nMaxLineCount; + INT infoTipIndex; + BOOL infoTipFolded; + INT statusIndex; + HMENU hMenu; + FILEVIEWCOLUMN szColumns[128 + 1]; // max allowed number of columns + INT columnCount; + LONG cyDivider; + LONG cyToolbar; + LONG yDivider; +} FILEVIEW; + +#if (_WIN32_WINNT < 0x501) +typedef struct tagLVSETINFOTIP +{ + UINT cbSize; + DWORD dwFlags; + LPWSTR pszText; + int iItem; + int iSubItem; +} LVSETINFOTIP, *PLVSETINFOTIP; + +#define LVM_SETINFOTIP (LVM_FIRST + 173) + +#endif // (_WIN32_WINNT < 0x501) + +static CGlobalAtom FILEVIEW_DATAW(L"FILEVIEW"); + +#define GetFileView(__hwnd) ((FILEVIEW*)GetPropW((__hwnd), FILEVIEW_DATAW)) + +static HMLIMGLST hmlilFileTypesSmall = NULL; +static HMLIMGLST hmlilFileTypesLarge = NULL; + + +static INT_PTR CALLBACK FileView_DialogProc(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam); + +HWND FileView_CreateDialog(HWND hwndParent, UINT fStyle, HWND hwndInsertAfter, INT x, INT y, INT cx, INT cy) +{ + HWND hwnd = WASABI_API_CREATEDIALOGPARAMW(IDD_FILEVIEW, hwndParent, FileView_DialogProc, 0L); + if (NULL == hwnd) return NULL; + SetWindowPos(hwnd, hwndInsertAfter, x, y, cx, cy, SWP_NOACTIVATE); + FileView_SetStyle(hwnd, fStyle, 0xFFFFFFFF); + return hwnd; +} + +static void FileView_LoadImages() +{ + MLIMAGESOURCE_I mlis; + ZeroMemory(&mlis, sizeof(MLIMAGESOURCE_I)); + mlis.type = SRC_TYPE_PNG; + mlis.hInst = plugin.hDllInstance; + + if (NULL == hmlilFileTypesSmall) + { + hmlilFileTypesSmall = MLImageListI_Create(16, 16, MLILC_COLOR32_I, 3, 2, 3, hmlifMngr); + if (NULL != hmlilFileTypesSmall) + { + INT imageList[] = { IDB_FILETYPE_UNKNOWN_SMALL, IDB_FILETYPE_AUDIO_SMALL, IDB_FILETYPE_VIDEO_SMALL, IDB_FILETYPE_PLAYLIST_SMALL,}; + for(int i = 0; i < sizeof(imageList)/sizeof(imageList[0]); i++) + { + mlis.lpszName = MAKEINTRESOURCEW(imageList[i]); + MLImageListI_Add(hmlilFileTypesSmall, &mlis, MLIF_FILTER1_UID, imageList[i]); + } + } + } + + if (NULL == hmlilFileTypesLarge) + { + hmlilFileTypesLarge = MLImageListI_Create(32, 32, MLILC_COLOR32_I, 3, 2, 3, hmlifMngr); + if (NULL != hmlilFileTypesLarge) + { + INT imageList[] = { IDB_FILETYPE_UNKNOWN_LARGE, IDB_FILETYPE_AUDIO_LARGE, IDB_FILETYPE_VIDEO_LARGE, IDB_FILETYPE_PLAYLIST_LARGE, }; + for(int i = 0; i < sizeof(imageList)/sizeof(imageList[0]); i++) + { + mlis.lpszName = MAKEINTRESOURCEW(imageList[i]); + MLImageListI_Add(hmlilFileTypesLarge, &mlis, MLIF_FILTER1_UID, imageList[i]); + } + } + } +} + +static LRESULT FileView_NotifyParent(HWND hdlg, UINT uCode, NMHDR *phdr) +{ + HWND hParent = GetParent(hdlg); + if (!phdr || !hParent) return 0L; + phdr->code = uCode; + phdr->hwndFrom = hdlg; + phdr->idFrom = GetDlgCtrlID(hdlg); + return SendMessageW(hParent, WM_NOTIFY, (WPARAM)phdr->idFrom, (LPARAM)phdr); +} + +static BOOL FileView_OnSetRoot(HWND hwnd, LPCWSTR pszRoot) +{ + HWND hBrowser = GetDlgItem(hwnd, IDC_FOLDER_BROWSER); + if (NULL == hBrowser) return FALSE; + + BOOL br = FolderBrowser_SetRoot(hBrowser, pszRoot); + PostMessageW(hwnd, FVM_REFRESH, 0, 0L); + return br; +} + +static INT FileView_OnGetRoot(HWND hwnd, LPCWSTR pszBufffer, INT cchMax) +{ + HWND hBrowser = GetDlgItem(hwnd, IDC_FOLDER_BROWSER); + if (NULL == hBrowser) return -1; + return FolderBrowser_GetRoot(hBrowser, pszBufffer, cchMax); +} + +static void FileView_AdjustSizes(HWND hdlg) +{ + HWND hctrl; + RECT rc, rw; + UINT flags; + INT nAddressBarHeight = 18; + + GetClientRect(hdlg, &rc); + + HDWP hdwp = BeginDeferWindowPos(4); + if (NULL == hdwp) return; + flags = SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOREDRAW | SWP_NOMOVE; + + if (NULL != (hctrl = GetDlgItem(hdlg, IDC_LBL_ADDRESS)) && GetWindowRect(hctrl, &rw)) + { + HDC hdc = GetDCEx(hctrl, NULL, DCX_CACHE); + if (hdc) + { + HFONT hf = (HFONT)SendMessageW(hctrl, WM_GETFONT, 0, 0L); + if (NULL == hf) hf = (HFONT)MlStockObjects_Get(DEFAULT_FONT); + if (NULL != hf) + { + SIZE size; + WCHAR szText[128] = {0}; + INT cch = (INT)SendMessageW(hctrl, WM_GETTEXT, (WPARAM)(sizeof(szText)/sizeof(szText[0])), (LPARAM)szText); + HFONT hfo = (HFONT)SelectObject(hdc, hf); + if (GetTextExtentPoint32W(hdc, szText, cch, &size)) + { + nAddressBarHeight = size.cy + 4; + rw.right = rw.left + size.cx; + } + SelectObject(hdc, hfo); + } + ReleaseDC(hctrl, hdc); + } + hdwp = DeferWindowPos(hdwp, hctrl, NULL, 0, 0, rw.right - rw.left, nAddressBarHeight, flags); + } + + if (NULL != (hctrl = GetDlgItem(hdlg, IDC_EDT_PATH)) && GetWindowRect(hctrl, &rw)) + { + hdwp = DeferWindowPos(hdwp, hctrl, NULL, 0, 0, rc.right - rc.left, nAddressBarHeight, flags); + } + + if (NULL != (hctrl = GetDlgItem(hdlg, IDC_FILEVIEW_TOOLBAR)) && GetWindowRect(hctrl, &rw)) + { + INT height = (INT)SendMessageW(hctrl, FVM_GETIDEALHEIGHT, 0, 0L); + + FILEVIEW *pfv = GetFileView(hdlg); + if (pfv) pfv->cyToolbar = height; + + hdwp = DeferWindowPos(hdwp, hctrl, NULL, 0, 0, rc.right - rc.left, height, flags); + } + + EndDeferWindowPos(hdwp); +} + +static void FileView_LayoutWindows(HWND hdlg, BOOL bRedraw) +{ + INT idList[] = {IDC_LBL_ADDRESS, IDC_EDT_PATH, IDC_FOLDER_BROWSER, IDC_HDELIM, IDC_FILEVIEW_TOOLBAR, IDC_FILELIST, }; + WINDOWPOS szwp[ARRAYSIZE(idList)], *pwp; + RECT rc; + LONG top, left, bottom; + + FILEVIEW *pfv = GetFileView(hdlg); + if (!pfv) return; + + HDWP hdwp = BeginDeferWindowPos(ARRAYSIZE(idList)); + if (NULL == hdwp) return; + + for (INT i = 0; i < ARRAYSIZE(idList); i++) + { + szwp[i].hwnd = GetDlgItem(hdlg, idList[i]); + szwp[i].flags = SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOCOPYBITS | ((bRedraw) ? 0 : SWP_NOREDRAW); + + if (!szwp[i].hwnd || !GetWindowRect(szwp[i].hwnd, &rc)) SetRect(&rc, 0, 0, 0, 0); + + szwp[i].x = rc.left; + szwp[i].y = rc.top; + szwp[i].cx = rc.right - rc.left; + szwp[i].cy = rc.bottom - rc.top; + } + + GetClientRect(hdlg, &rc); + + top = rc.top + 2; + left = rc.left + 2; + if ((pwp = &szwp[0])->hwnd) // label address + { + pwp->y = top; + pwp->x = left; + hdwp = DeferWindowPos(hdwp, pwp->hwnd, NULL, pwp->x, pwp->y, pwp->cx, pwp->cy, pwp->flags); + left += (pwp->cx + 2); + } + if ((pwp = &szwp[1])->hwnd) // edit path + { + pwp->y = top; + pwp->x = left; + pwp->cx = rc.right - pwp->x; + if (pwp->cx < 0) pwp->cx = 0; + hdwp = DeferWindowPos(hdwp, pwp->hwnd, NULL, pwp->x, pwp->y, pwp->cx, pwp->cy, pwp->flags); + DWORD style = GetWindowLongPtrW(pwp->hwnd, GWL_STYLE); + if ((pwp->cx == 0) != (0 != (WS_DISABLED & style))) + SetWindowLongPtrW(pwp->hwnd, GWL_STYLE, (style & ~WS_DISABLED) | ((0 == pwp->cx) ? WS_DISABLED : 0)); + } + + left = rc.left; + top = max((szwp[0].y + szwp[0].cy), (szwp[1].y + szwp[1].cy)) + 2; + + LONG yDividerReal = pfv->yDivider; + LONG cyList = (rc.bottom - (yDividerReal + pfv->cyDivider)); + if (cyList < FILELIST_MIN_HEIGHT) cyList = FILELIST_MIN_HEIGHT; + yDividerReal = rc.bottom - cyList - pfv->cyDivider; + + if (((yDividerReal + pfv->cyDivider) - top) < FOLDERBROWSER_MIN_HEIGHT) + { + cyList -= (FOLDERBROWSER_MIN_HEIGHT - ((yDividerReal + pfv->cyDivider) - top)); + if (cyList < FILELIST_MIN_HEIGHT) cyList = FILELIST_MIN_HEIGHT; + yDividerReal = rc.bottom - cyList - pfv->cyDivider; + } + + if ((pwp = &szwp[2])->hwnd) // folderbrowser + { + pwp->x = left; + pwp->y = top; + pwp->cx = rc.right - rc.left; + pwp->cy = yDividerReal - top; + if (pwp->cy < (FOLDERBROWSER_MIN_HEIGHT - pfv->cyDivider)) pwp->cy = 0; + hdwp = DeferWindowPos(hdwp, pwp->hwnd, NULL, pwp->x, pwp->y, pwp->cx, pwp->cy, pwp->flags); + DWORD style = GetWindowLongPtrW(pwp->hwnd, GWL_STYLE); + if ((pwp->cy == 0) != (0 != (WS_DISABLED & style))) + SetWindowLongPtrW(pwp->hwnd, GWL_STYLE, (style & ~WS_DISABLED) | ((0 == pwp->cy) ? WS_DISABLED : 0)); + top += pwp->cy; + } + + if ((pwp = &szwp[3])->hwnd) // delimiter + { + pwp->x = left; + pwp->y = top; + pwp->cx = rc.right - rc.left; + pwp->cy = (szwp[2].cy > 0) ? pfv->cyDivider : 0; + if (pwp->cy < 0) pwp->cy = 0; + hdwp = DeferWindowPos(hdwp, pwp->hwnd, NULL, pwp->x, pwp->y, pwp->cx, pwp->cy, pwp->flags); + top += pwp->cy; + DWORD style = GetWindowLongPtrW(pwp->hwnd, GWL_STYLE); + if ((pwp->cy == 0) != (0 != (WS_DISABLED & style))) + SetWindowLongPtrW(pwp->hwnd, GWL_STYLE, (style & ~WS_DISABLED) | ((0 == pwp->cy) ? WS_DISABLED : 0)); + } + + bottom = rc.bottom; + + if ((pwp = &szwp[4])->hwnd) // toolbar + { + pwp->x = left; + pwp->y = top; + pwp->cx = rc.right - rc.left; + pwp->cy = pfv->cyToolbar; + if ((bottom - pwp->cy) < 64) pwp->cy = 0; + hdwp = DeferWindowPos(hdwp, pwp->hwnd, NULL, pwp->x, pwp->y, pwp->cx, pwp->cy, pwp->flags); + top += pwp->cy; + DWORD style = GetWindowLongPtrW(pwp->hwnd, GWL_STYLE); + if ((pwp->cy == 0) != (0 != (WS_DISABLED & style))) + SetWindowLongPtrW(pwp->hwnd, GWL_STYLE, (style & ~WS_DISABLED) | ((0 == pwp->cy) ? WS_DISABLED : 0)); + } + + if ((pwp = &szwp[5])->hwnd) // file list + { + pwp->x = left; + pwp->y = top; + pwp->cx = rc.right - rc.left; + pwp->cy = bottom - top; + if (pwp->cy < 0) pwp->cy = 0; + hdwp = DeferWindowPos(hdwp, pwp->hwnd, NULL, pwp->x, pwp->y, pwp->cx, pwp->cy, pwp->flags); + DWORD style = GetWindowLongPtrW(pwp->hwnd, GWL_STYLE); + if ((pwp->cy == 0) != (0 != (WS_DISABLED & style))) + SetWindowLongPtrW(pwp->hwnd, GWL_STYLE, (style & ~WS_DISABLED) | ((0 == pwp->cy) ? WS_DISABLED : 0)); + } + EndDeferWindowPos(hdwp); + + pfv->nMaxLineCount = (INT)SendDlgItemMessageW(hdlg, IDC_FILELIST, LVM_GETCOUNTPERPAGE, 0, 0L); +} + +static INT FileView_GetPreferredColumnWidth(HWND hdlg, UINT columnId) +{ + HWND hctrl; + FILEDATA *pfd; + INT nameWidth = 0, prevMaxLen = 0; + HDC hdc; + HFONT hf, hfo = NULL; + SIZE size; + + FILEVIEW *pfv = GetFileView(hdlg); + if (!pfv) return -1; + + pfd = &pfv->fileData; + + hctrl = GetDlgItem(hdlg, IDC_FILELIST); + if (!hctrl) return -1; + + hdc = GetDCEx(hctrl, NULL, DCX_CACHE); + if (!hdc) return -1; + + hf = (HFONT)SendMessageW(hctrl, WM_GETFONT, 0, 0L); + if (NULL == hf) hf = (HFONT)MlStockObjects_Get(DEFAULT_FONT); + if (NULL != hf) hfo = (HFONT)SelectObject(hdc, hf); + + for (size_t i = 0; i < pfd->count; i++) + { + LPCWSTR pszText = pfd->pRec[i].Info.cFileName; + INT len = (pszText) ? lstrlenW(pszText) : 0; + + if (len > 0 && len > (prevMaxLen - 3) && + hdc && GetTextExtentPoint32W(hdc, pszText, len, &size) && + size.cx > nameWidth) + { + nameWidth = size.cx; + prevMaxLen = len; + } + } + + if (NULL != hfo) SelectObject(hdc, hfo); + ReleaseDC(hctrl, hdc); + + if (nameWidth < 100) nameWidth = 100; + return nameWidth; +} + +static void FileView_UpdateFileView(HWND hdlg, LPCWSTR pszPath) +{ + HWND hwndList; + + FILEVIEW *pfv = GetFileView(hdlg); + if (!pfv) return; + + if (pszPath && L'\0' != *pszPath && + CSTR_EQUAL == CompareStringW(MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT), + NORM_IGNORECASE, pszPath, -1, pfv->fileData.szPath, -1)) return; + + pfv->fileData.count = 0; + pfv->fileData.szPath[0] = 0x00; + + hwndList = GetDlgItem(hdlg, IDC_FILELIST); + if (hwndList) SendMessageW(hwndList, LVM_SETITEMCOUNT, 0, 0L); + + size_t count = ((size_t)-1); + if (pszPath && L'\0' != *pszPath) + { + FILESYSTEMINFO fi = {sizeof(FILESYSTEMINFO), }; + HWND hfb = GetDlgItem(hdlg, IDC_FOLDER_BROWSER); + if (!hfb || !FolderBrowser_GetFileSystemInfo(hfb, &fi)) return; + count = FileView_ReadFileData(&pfv->fileData, pszPath, pfv->style, &fi); + } + + FileViewMeta_TruncateQueue(0); + + if (((size_t)-1) != count) + { + StringCchCopyW(pfv->fileData.szPath, sizeof(pfv->fileData.szPath)/sizeof(pfv->fileData.szPath[0]), pszPath); + FileView_SortByColumn(&pfv->fileData, pfv->sortColumn); + if (hwndList) + { + SendMessageW(hwndList, LVM_SETITEMCOUNT, (WPARAM)count, LVSICF_NOINVALIDATEALL); + if (FVS_LISTVIEW == (FVS_VIEWMASK & pfv->style)) + { + INT w = FileView_GetPreferredColumnWidth(hdlg, FVCOLUMN_NAME); + if (-1 != w) SendMessageW(hwndList, LVM_SETCOLUMNWIDTH, 0, w + 20); + } + } + } + + NMHDR nmhdr; + FileView_NotifyParent(hdlg, FVN_FOLDERCHANGED, &nmhdr); +} + +static BOOL FileView_IsTypePlayable(UINT fileType, UINT uEnqueueFilter) +{ + switch(fileType) + { + case FVFT_AUDIO: return (0 != (FVEF_AUDIO & uEnqueueFilter)); + case FVFT_VIDEO: return (0 != (FVEF_VIDEO & uEnqueueFilter)); + case FVFT_PLAYLIST: return (0 != (FVEF_PLAYLIST & uEnqueueFilter)); + case FVFT_UNKNOWN: return (0 != (FVEF_UNKNOWN & uEnqueueFilter)); + } + return FALSE; +} + +static void FileView_InitializeListColumn(HWND hdlg, LVCOLUMNW *pListColumn, const FILEVIEWCOLUMN *pViewColumn) +{ + pListColumn->mask = LVCF_TEXT | LVCF_WIDTH | LVCF_FMT; + pListColumn->pszText = IS_INTRESOURCE(pViewColumn->pszText) ? WASABI_API_LNGSTRINGW((UINT)(UINT_PTR)pViewColumn->pszText) : pViewColumn->pszText; + pListColumn->cx = pViewColumn->width; + pListColumn->fmt = pViewColumn->format; + if (-1 == pListColumn->cx) + { + pListColumn->cx = FileView_GetPreferredColumnWidth(hdlg, pViewColumn->id); + if (-1 == pListColumn->cx) pListColumn->cx = 140; + pListColumn->cx += 24; + } +} + +static void CALLBACK OnDividerMoved(HWND hdiv, INT nPos, LPARAM param) +{ + HWND hParent; + hParent = GetParent(hdiv); + if (hParent) FileView_SetDividerPos(hParent, nPos, FVRF_VALIDATE); +} + +static BOOL FileView_OnInitDialog(HWND hdlg, HWND hwndFocus, LPARAM lParam) +{ + FILEVIEW *pfv; + RECT rw; + pfv = (FILEVIEW*)calloc(1, sizeof(FILEVIEW)); + if (pfv) + { + pfv->style = ((UINT)-1); + pfv->bAscending = TRUE; + pfv->sortColumn = -1; + pfv->yDivider = 240; + } + else + { + DestroyWindow(hdlg); + return 0; + } + + HWND hctrl; + SkinWindowEx(hdlg, SKINNEDWND_TYPE_AUTO, SWS_USESKINCOLORS | SWS_USESKINCURSORS | SWS_USESKINFONT); + + hctrl = GetDlgItem(hdlg, IDC_LBL_ADDRESS); + SkinWindowEx(hctrl, SKINNEDWND_TYPE_STATIC, SWS_USESKINCOLORS | SWS_USESKINCURSORS | SWS_USESKINFONT); + + hctrl = GetDlgItem(hdlg, IDC_EDT_PATH); + SkinWindowEx(hctrl, SKINNEDWND_TYPE_AUTO, SWS_USESKINCOLORS | SWS_USESKINCURSORS | SWS_USESKINFONT | SWES_VCENTER | SWES_SELECTONCLICK); + + hctrl = GetDlgItem(hdlg, IDC_FOLDER_BROWSER); + SkinWindowEx(hctrl, SKINNEDWND_TYPE_AUTO, SWS_USESKINCOLORS | SWS_USESKINCURSORS | SWS_USESKINFONT); + + hctrl = GetDlgItem(hdlg, IDC_HDELIM); + SkinWindowEx(hctrl, SKINNEDWND_TYPE_DIVIDER, SWS_USESKINCOLORS | SWS_USESKINCURSORS | SWS_USESKINFONT | SWDIV_HORZ /*| SWDIV_NOHILITE*/); + MLSkinnedDivider_SetCallback(hctrl, OnDividerMoved, NULL); + pfv->cyDivider = (GetWindowRect(hctrl, &rw)) ? (rw.bottom - rw.top) : 0; + + HWND htool = FileViewToolbar_Create(hdlg); + if (htool) + { + SetWindowLongPtrW(htool, GWLP_ID, IDC_FILEVIEW_TOOLBAR); + SetWindowPos(htool, GetDlgItem(hdlg, IDC_HDELIM), 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE); + SkinWindowEx(htool, SKINNEDWND_TYPE_AUTO, SWS_USESKINCOLORS | SWS_USESKINCURSORS | SWS_USESKINFONT); + pfv->cyToolbar = (GetWindowRect(htool, &rw)) ? (rw.bottom - rw.top) : 0; + } + + + INT viewIndex = 0; + for (int i = 0; i < RegisteredColumnsCount; i++) + { + if (FVCOLUMN_NAME == szRegisteredColumns[i].id) + { + CopyMemory(&pfv->szColumns[viewIndex], &szRegisteredColumns[i], sizeof(FILEVIEWCOLUMN)); + viewIndex++; + break; + } + } + pfv->columnCount = viewIndex; + + + if (!SetPropW(hdlg, FILEVIEW_DATAW, pfv)) + { + free(pfv); + DestroyWindow(hdlg); + return FALSE; + } + + FileViewMeta_InitializeStorage(hdlg); + FileView_LoadImages(); + FileView_SetStyle(hdlg, FVS_LISTVIEW | FVS_HIDEEXTENSION | FVS_IGNOREHIDDEN | FVS_SHOWAUDIO | FVS_SHOWVIDEO | FVS_SHOWPLAYLIST | FVS_SHOWUNKNOWN, 0xFFFFFFFF); + FileView_AdjustSizes(hdlg); + FileView_LayoutWindows(hdlg, FALSE); + + if (WASABI_API_APP) + { + HACCEL hAccel = WASABI_API_LOADACCELERATORSW(IDR_ACCELERATOR_FILEVIEW); + if (hAccel) WASABI_API_APP->app_addAccelerators(hdlg, &hAccel, 1, TRANSLATE_MODE_CHILD); + } + + return FALSE; +} + +static void FileView_OnDestroy(HWND hdlg) +{ + FILEVIEW *pfv = GetFileView(hdlg); + if (pfv) + { + RemovePropW(hdlg,FILEVIEW_DATAW); + if (pfv->fileData.allocated) + { + if (pfv->fileData.pRec) free(pfv->fileData.pRec); + if (pfv->fileData.pSort) free(pfv->fileData.pSort); + } + free(pfv); + } + FileViewMeta_ReleaseStorage(hdlg); + if (WASABI_API_APP) WASABI_API_APP->app_removeAccelerators(hdlg); +} + +static void FileView_OnWindowPosChanged(HWND hdlg, WINDOWPOS *pwp) +{ + if (SWP_NOSIZE == ((SWP_NOSIZE | SWP_FRAMECHANGED) & pwp->flags)) return; + FileView_LayoutWindows(hdlg, (0 == (SWP_NOREDRAW & pwp->flags))); +} + +static BOOL FileView_OnRefresh(HWND hdlg, BOOL bIncludeFolder, BOOL bForce) +{ + NMHDR nmhdr; + + FILEVIEW *pfv = GetFileView(hdlg); + if (!pfv) return FALSE; + + if (bForce) pfv->fileData.szPath[0] = 0x00; + + if (bIncludeFolder) + { + wchar_t szText[MAX_PATH*4] = {0}; + HWND hctrl = GetDlgItem(hdlg, IDC_FOLDER_BROWSER); + if (FolderBrowser_GetCurrentPath(hctrl, szText, sizeof(szText)/sizeof(szText[0]))) + { + FolderBrowser_SetCurrentPath(hctrl, L"", FALSE); + FolderBrowser_SetCurrentPath(hctrl, szText, TRUE); + } + } + + nmhdr.code = FBN_SELCHANGED; + nmhdr.idFrom = IDC_FOLDER_BROWSER; + nmhdr.hwndFrom = GetDlgItem(hdlg, (INT)nmhdr.idFrom); + SendMessageW(hdlg, WM_NOTIFY, nmhdr.idFrom, (LPARAM)&nmhdr); + + return TRUE; +} + +static BOOL FileView_OnSetView(HWND hdlg, UINT uView, BOOL bRefresh) +{ + FILEVIEW *pfv = GetFileView(hdlg); + if (!pfv) return FALSE; + + if ((FVS_VIEWMASK & pfv->style) == uView) return TRUE; + + HWND hctrl, hnew; + RECT rw; + BOOL bFocused; + DWORD style; + + switch(uView) + { + case FVS_LISTVIEW: style = LVS_LIST; break; + case FVS_ICONVIEW: style = LVS_ICON; break; + case FVS_DETAILVIEW: style = LVS_REPORT; break; + default: return FALSE; + } + + style |= WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_TABSTOP | + LVS_SHOWSELALWAYS | LVS_ALIGNTOP | LVS_OWNERDATA | LVS_SHAREIMAGELISTS; + + hctrl = GetDlgItem(hdlg, IDC_FILELIST); + if (!hctrl || !GetWindowRect(hctrl, &rw)) SetRect(&rw, 0, 0, 1, 1); + + MapWindowPoints(HWND_DESKTOP, hdlg, (POINT*)&rw, 2); + bFocused = (hctrl != NULL && hctrl == GetFocus()); + + + hnew = CreateWindowExW(WS_EX_NOPARENTNOTIFY, WC_LISTVIEWW, L"Files", style, + rw.left, rw.top, rw.right - rw.left, rw.bottom - rw.top, + hdlg, NULL, plugin.hDllInstance, 0L); + if (!hnew) return FALSE; + + pfv->style = (pfv->style & ~FVS_VIEWMASK) | uView; + + if (hctrl) SetWindowLongPtrW(hctrl, GWLP_ID, 0); + SetWindowLongPtrW(hnew, GWLP_ID, IDC_FILELIST); + DWORD exStyle; + + exStyle = SWS_USESKINCOLORS | SWS_USESKINCURSORS | SWS_USESKINFONT | SWLVS_DOUBLEBUFFER; + if (FVS_DETAILVIEW == uView) exStyle |= SWLVS_FULLROWSELECT; + + SkinWindowEx(hnew, SKINNEDWND_TYPE_LISTVIEW, exStyle); + if (FVS_DETAILVIEW == uView) + { + LVCOLUMNW lvc; + INT sortIndex = -1; + for (int i = 0; i < pfv->columnCount; i++) + { + FileView_InitializeListColumn(hdlg, &lvc, &pfv->szColumns[i]); + if (pfv->szColumns[i].id == pfv->sortColumn) sortIndex = i; + SendMessageW(hnew, LVM_INSERTCOLUMNW, i, (LPARAM)&lvc); + } + MLSkinnedListView_DisplaySort(hnew, sortIndex, pfv->bAscending); + } + + SendMessageW(hnew, LVM_SETIMAGELIST, LVSIL_NORMAL, (LPARAM)MLImageListI_GetRealList(hmlilFileTypesLarge)); + SendMessageW(hnew, LVM_SETIMAGELIST, LVSIL_SMALL, (LPARAM)MLImageListI_GetRealList(hmlilFileTypesSmall)); + if (FVS_DETAILVIEW != uView) SendMessageW(hnew, LVM_ARRANGE, LVA_ALIGNLEFT | LVA_ALIGNTOP | LVA_SNAPTOGRID, 0L); +// if (FVS_ICONVIEW == uView) SendMessageW(hnew, LVM_SETICONSPACING, 0, MAKELPARAM(96, 96)); + exStyle = LVS_EX_INFOTIP | LVS_EX_LABELTIP; + if (FVS_DETAILVIEW == uView) exStyle |= LVS_EX_HEADERDRAGDROP; + SendMessageW(hnew, LVM_SETEXTENDEDLISTVIEWSTYLE, exStyle, exStyle); + + HWND hHeader = (HWND)SendMessageW(hnew, LVM_GETHEADER, 0, 0L); + if (hHeader) SetWindowLongPtrW(hHeader, GWLP_ID, IDC_FILELIST_HEADER); + + FileViewMeta_TruncateQueue(0); + + SetWindowPos(hnew, (hctrl) ? hctrl : GetDlgItem(hdlg, IDC_HDELIM), 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); + ShowWindow(hnew, SW_SHOWNA); + if (bFocused) SetFocus(hnew); + if (hctrl) + { + if (WASABI_API_APP) WASABI_API_APP->app_removeAccelerators(hctrl); + DestroyWindow(hctrl); + } + + SendDlgItemMessageW(hdlg,IDC_FILEVIEW_TOOLBAR, FVM_SETSTYLE, FVS_VIEWMASK, (LPARAM)uView); + if (bRefresh) FileView_OnRefresh(hdlg, FALSE, TRUE); + return TRUE; +} + +static void CALLBACK FileView_OnInvalidateListElapsed(HWND hdlg, UINT uMsg, UINT_PTR idEvent, DWORD dwTime) +{ + KillTimer(hdlg, idEvent); + HWND hctrl = GetDlgItem(hdlg, IDC_FILELIST); + if (hctrl) InvalidateRect(hctrl, NULL, FALSE); +} + +static void CALLBACK FileView_OnMetaDataDiscovered(LPCWSTR pszFileName, ULONG_PTR param) +{ + if (!param) return; + HWND hdlg = (HWND)param; + FILEVIEW *pfv = GetFileView(hdlg); + if (!pfv) return; + + LPCWSTR pszFile = PathFindFileNameW(pszFileName); + INT cchPath = (INT)(ULONG_PTR)(pszFile - pszFileName); + if (cchPath) cchPath--; + + if (CSTR_EQUAL != CompareStringW(CSTR_INVARIANT, NORM_IGNORECASE, pfv->fileData.szPath, -1, pszFileName, cchPath)) + return; + + SetTimer(hdlg, IDT_INVALIDATELIST, DELAY_INVALIDATELIST, FileView_OnInvalidateListElapsed); + + if (pfv && (((size_t)pfv->infoTipIndex) < pfv->fileData.count || ((size_t)pfv->statusIndex) < pfv->fileData.count)) + { + WCHAR szBuffer[2048] = {0}; + LPCWSTR pszTipFile; + FILEDATA *pfd = &pfv->fileData; + FILERECORD *pfr; + size_t index; + + HWND hctrl = GetDlgItem(hdlg, IDC_FILELIST); + + if (((size_t)pfv->infoTipIndex) < pfv->fileData.count) + { + index = ((pfv->bAscending) ? pfv->infoTipIndex : (pfd->count - pfv->infoTipIndex - 1)); + pfr = &pfd->pRec[pfd->pSort[index]]; + + if (pfr->extOffset == (lstrlenW(pfr->Info.cFileName) + 1)) + { + StringCchPrintfW(szBuffer, sizeof(szBuffer)/sizeof(szBuffer[0]), L"%s.%s", pfr->Info.cFileName, (pfr->Info.cFileName + pfr->extOffset)); + pszTipFile = szBuffer; + } + else pszTipFile = pfr->Info.cFileName; + + if (CSTR_EQUAL == CompareStringW(CSTR_INVARIANT, NORM_IGNORECASE, pszFile, -1, pszTipFile, -1)) + { + if (FileViewMeta_Discover(pfv->fileData.szPath, pfr, NULL, NULL, 0) && pfr->pMeta) + { + LVSETINFOTIP tip; + ZeroMemory(&tip, sizeof(LVSETINFOTIP)); + tip.cbSize = sizeof(LVSETINFOTIP); + tip.dwFlags = 0; + tip.iItem = pfv->infoTipIndex; + tip.iSubItem = 0; + tip.pszText = szBuffer; + + if (pfv->infoTipFolded) + { + StringCchPrintfExW(tip.pszText, sizeof(szBuffer)/sizeof(szBuffer[0]), &tip.pszText, NULL, STRSAFE_IGNORE_NULLS, L"%s\r\n", pszTipFile); + } + FileView_FormatFileInfo(pfr, tip.pszText, sizeof(szBuffer)/sizeof(szBuffer[0]) - (ULONG_PTR)(tip.pszText - szBuffer), FIF_TOOLTIP); + tip.pszText = szBuffer; + SendMessageW(hctrl, LVM_SETINFOTIP, 0, (LPARAM)&tip); + + if (pfv->statusIndex == pfv->infoTipIndex) + { + NMHDR nmhdr; + FileView_NotifyParent(hdlg, FVN_STATUSCHANGED, &nmhdr); + pfv->statusIndex = -1; + } + } + pfv->infoTipIndex = -1; + pfv->infoTipFolded = FALSE; + } + } + + if (((size_t)pfv->statusIndex) < pfv->fileData.count) + { + index = ((pfv->bAscending) ? pfv->statusIndex : (pfd->count - pfv->statusIndex - 1)); + pfr = &pfd->pRec[pfd->pSort[index]]; + + if (pfr->extOffset == (lstrlenW(pfr->Info.cFileName) + 1)) + { + StringCchPrintfW(szBuffer, sizeof(szBuffer)/sizeof(szBuffer[0]), L"%s.%s", pfr->Info.cFileName, (pfr->Info.cFileName + pfr->extOffset)); + pszTipFile = szBuffer; + } + else pszTipFile = pfr->Info.cFileName; + + if (CSTR_EQUAL == CompareStringW(CSTR_INVARIANT, NORM_IGNORECASE, pszFile, -1, pszTipFile, -1)) + { + if (FileViewMeta_Discover(pfv->fileData.szPath, pfr, NULL, NULL, 0) && pfr->pMeta) + { + NMHDR nmhdr; + FileView_NotifyParent(hdlg, FVN_STATUSCHANGED, &nmhdr); + } + pfv->statusIndex = -1; + } + } + } +} + +static COLORREF rgbText, rgbTextBk; +static INT_PTR FileList_OnCustomDraw(HWND hdlg, NMLVCUSTOMDRAW *pcd) +{ + switch(pcd->nmcd.dwDrawStage) + { + case CDDS_PREPAINT: + return CDRF_NOTIFYITEMDRAW; + case CDDS_ITEMPREPAINT: + rgbText = pcd->clrText; + rgbTextBk = pcd->clrTextBk; + break; + } + return CDRF_DODEFAULT; +} + +static void FileList_OnGetDispInfo(HWND hdlg, NMLVDISPINFOW *pdi) +{ + FILEVIEW *pfv = GetFileView(hdlg); + + if (!pfv) return; + + FILEDATA *pfd = &pfv->fileData; + if (pdi->item.iItem >= (INT)pfd->count) + { + pdi->item.pszText = L"Bad Index"; + return; + } + size_t index = ((pfv->bAscending) ? pdi->item.iItem : (pfd->count - pdi->item.iItem - 1)); + FILERECORD *pfr = &pfd->pRec[pfd->pSort[index]]; + + if ((LVIF_TEXT & pdi->item.mask)) + { + switch(pfv->szColumns[pdi->item.iSubItem].id) + { + case FVCOLUMN_NAME: pdi->item.pszText = pfr->Info.cFileName; break; + case FVCOLUMN_SIZE: WASABI_API_LNG->FormattedSizeString(pdi->item.pszText, pdi->item.cchTextMax, (((__int64)pfr->Info.nFileSizeHigh << 32) | pfr->Info.nFileSizeLow)); break; + case FVCOLUMN_TYPE: FileView_FormatType(pfr->fileType, pdi->item.pszText, pdi->item.cchTextMax); break; + case FVCOLUMN_MODIFIED: FileView_FormatFileTime(&pfr->Info.ftLastWriteTime, pdi->item.pszText, pdi->item.cchTextMax); break; + case FVCOLUMN_CREATED: FileView_FormatFileTime(&pfr->Info.ftCreationTime, pdi->item.pszText, pdi->item.cchTextMax); break; + case FVCOLUMN_EXTENSION: + if (pfr->extOffset) + { + StringCchCopyW(pdi->item.pszText, pdi->item.cchTextMax, &pfr->Info.cFileName[pfr->extOffset]); + CharUpperBuffW(pdi->item.pszText, lstrlenW(pdi->item.pszText)); + } + else pdi->item.pszText = NULL; + break; + case FVCOLUMN_ATTRIBUTES: FileView_FormatAttributes(pfr->Info.dwFileAttributes, pdi->item.pszText, pdi->item.cchTextMax); break; + case FVCOLUMN_ARTIST: + case FVCOLUMN_ALBUM: + case FVCOLUMN_TITLE: + case FVCOLUMN_INMLDB: + case FVCOLUMN_GENRE: + case FVCOLUMN_YEAR: + case FVCOLUMN_LENGTH: + case FVCOLUMN_BITRATE: + case FVCOLUMN_TRACK: + case FVCOLUMN_DISC: + case FVCOLUMN_COMMENT: + case FVCOLUMN_PUBLISHER: + case FVCOLUMN_COMPOSER: + case FVCOLUMN_ALBUMARTIST: + if (FileViewMeta_Discover(pfv->fileData.szPath, pfr, FileView_OnMetaDataDiscovered, (ULONG_PTR)hdlg, pfv->nMaxLineCount) && + pfr->pMeta) + { + INT nVal, nVal2; + switch(pfv->szColumns[pdi->item.iSubItem].id) + { + case FVCOLUMN_ARTIST: FileViewMeta_GetString(pfr->pMeta, MF_ARTIST, (LPCWSTR*)&pdi->item.pszText); break; + case FVCOLUMN_ALBUM: FileViewMeta_GetString(pfr->pMeta, MF_ALBUM, (LPCWSTR*)&pdi->item.pszText); break; + case FVCOLUMN_TITLE: FileViewMeta_GetString(pfr->pMeta, MF_TITLE, (LPCWSTR*)&pdi->item.pszText); break; + case FVCOLUMN_INMLDB: + if (FileViewMeta_GetInt(pfr->pMeta, MF_SOURCE, &nVal) && METADATA_SOURCE_UNKNOWN != nVal) + FileView_FormatYesNo(METADATA_SOURCE_MLDB == nVal, pdi->item.pszText, pdi->item.cchTextMax); + break; + case FVCOLUMN_GENRE: FileViewMeta_GetString(pfr->pMeta, MF_GENRE, (LPCWSTR*)&pdi->item.pszText); break; + case FVCOLUMN_YEAR: + if (FileViewMeta_GetInt(pfr->pMeta, MF_YEAR, &nVal)) FileView_FormatYear(nVal, pdi->item.pszText, pdi->item.cchTextMax); + break; + case FVCOLUMN_LENGTH: + if (FileViewMeta_GetInt(pfr->pMeta, MF_LENGTH, &nVal)) FileView_FormatLength(nVal, pdi->item.pszText, pdi->item.cchTextMax); + break; + case FVCOLUMN_BITRATE: + if (FileViewMeta_GetInt(pfr->pMeta, MF_BITRATE, &nVal)) + FileView_FormatBitrate(nVal, pdi->item.pszText, pdi->item.cchTextMax); break; + case FVCOLUMN_TRACK: + if (!FileViewMeta_GetInt(pfr->pMeta, MF_TRACKNUM, &nVal)) nVal = -1; + if (!FileViewMeta_GetInt(pfr->pMeta, MF_TRACKCOUNT, &nVal2)) nVal2 = -1; + if (nVal != -1) FileView_FormatIntSlashInt(nVal, nVal2, pdi->item.pszText, pdi->item.cchTextMax); + break; + case FVCOLUMN_DISC: + if (!FileViewMeta_GetInt(pfr->pMeta, MF_DISCNUM, &nVal)) nVal = -1; + if (!FileViewMeta_GetInt(pfr->pMeta, MF_DISCCOUNT, &nVal2)) nVal2 = -1; + if (nVal != -1) FileView_FormatIntSlashInt(nVal, nVal2, pdi->item.pszText, pdi->item.cchTextMax); + break; + case FVCOLUMN_COMMENT: FileViewMeta_GetString(pfr->pMeta, MF_COMMENT, (LPCWSTR*)&pdi->item.pszText); break; + case FVCOLUMN_PUBLISHER: FileViewMeta_GetString(pfr->pMeta, MF_PUBLISHER, (LPCWSTR*)&pdi->item.pszText); break; + case FVCOLUMN_COMPOSER: FileViewMeta_GetString(pfr->pMeta, MF_COMPOSER, (LPCWSTR*)&pdi->item.pszText); break; + case FVCOLUMN_ALBUMARTIST: FileViewMeta_GetString(pfr->pMeta, MF_ALBUMARTIST, (LPCWSTR*)&pdi->item.pszText); break; + } + } + break; + } + } + if(LVIF_IMAGE & pdi->item.mask) + { + INT index; + switch(pfr->fileType) + { + case FVFT_AUDIO: index = 1; break; + case FVFT_VIDEO: index = 2; break; + case FVFT_PLAYLIST: index = 3; break; + default: index = 0; break; + } + pdi->item.iImage = MLImageListI_GetRealIndex((FVS_ICONVIEW == (FVS_VIEWMASK & pfv->style)) ? hmlilFileTypesLarge : hmlilFileTypesSmall, index, rgbTextBk, rgbText); + } +} + +static void CALLBACK FileView_OnActivateListItemElapsed(HWND hdlg, UINT uMsg, UINT_PTR idEvent, DWORD dwTime) +{ + KillTimer(hdlg, idEvent); + HWND hctrl = GetDlgItem(hdlg, IDC_FILELIST); + if (!hctrl) return; + INT index = (INT)SendMessageW(hctrl, LVM_GETNEXTITEM, 0, (LPARAM)(LVNI_ALL | LVNI_FOCUSED)); + if (-1 == index) + { + LVITEM lvi; + lvi.stateMask = LVIS_FOCUSED | LVIS_SELECTED; + lvi.state = LVIS_FOCUSED | LVIS_SELECTED; + SendMessageW(hctrl, LVM_SETITEMSTATE, 0, (LPARAM)&lvi); + } +} + +static void FileList_OnSetFocus(HWND hdlg, NMHDR *phdr) +{ + SetTimer(hdlg, IDT_ACTIVATELISTITEM, DELAY_ACTIVATELISTITEM, FileView_OnActivateListItemElapsed); +} + +static void FileList_OnGetInfoTip(HWND hdlg, NMLVGETINFOTIPW *pit) +{ + if (LVGIT_UNFOLDED & pit->dwFlags) pit->pszText[0] = L'\0'; + else StringCchCatW(pit->pszText, pit->cchTextMax, L"\r\n"); + + FILEVIEW *pfv = GetFileView(hdlg); + if (!pfv) return; + + FILEDATA *pfd = &pfv->fileData; + if (pit->iItem < (INT)pfd->count) + { + size_t index = ((pfv->bAscending) ? pit->iItem : (pfd->count - pit->iItem - 1)); + FILERECORD *pfr = &pfd->pRec[pfd->pSort[index]]; + + if (FVFT_UNKNOWN != pfr->fileType && + !FileViewMeta_Discover(pfv->fileData.szPath, pfr, FileView_OnMetaDataDiscovered, (ULONG_PTR)hdlg, 0)) + { + pfv->infoTipFolded = (0 == (LVGIT_UNFOLDED & pit->dwFlags)); + pfv->infoTipIndex = pit->iItem; + } + + INT len = lstrlenW(pit->pszText); + FileView_FormatFileInfo(pfr, pit->pszText + len, pit->cchTextMax - len, FIF_TOOLTIP); + } +} + +static void FileList_OnKeyDown(HWND hdlg, NMLVKEYDOWN *pkd) +{ +} + +static void FileList_OnColumnClick(HWND hdlg, NMLISTVIEW *pnmv) +{ + FILEVIEW *pfv = GetFileView(hdlg); + if (!pfv) return; + + UINT uColumn = pfv->szColumns[pnmv->iSubItem].id; + BOOL bAscending = (uColumn == pfv->sortColumn) ? !pfv->bAscending : TRUE; + HCURSOR hc = SetCursor(LoadCursor(NULL, IDC_WAIT)); + FileView_SetSort(hdlg, uColumn, bAscending); + SetCursor(hc); +} + +static void FileList_OnItemChanged(HWND hdlg, NMLISTVIEW *pnmv) +{ + if (LVIF_STATE & pnmv->uChanged) + { + + NMFVSTATECHANGED nmsc; + nmsc.iFrom = pnmv->iItem; + nmsc.iTo = pnmv->iItem; + nmsc.uNewState = pnmv->uNewState; + nmsc.uOldState = pnmv->uOldState; + FileView_NotifyParent(hdlg, FVN_STATECHANGED, (NMHDR*)&nmsc); + } +} + +static void FileList_OnStateChanged(HWND hdlg, NMLVODSTATECHANGE *pnmsc) +{ + NMFVSTATECHANGED nmsc; + nmsc.iFrom = pnmsc->iFrom; + nmsc.iTo = pnmsc->iTo; + nmsc.uNewState = pnmsc->uNewState; + nmsc.uOldState = pnmsc->uOldState; + FileView_NotifyParent(hdlg, FVN_STATECHANGED, (NMHDR*)&nmsc); +} + +static HMENU activePopup = NULL; + +void FileView_DisplayPopupMenu(HWND hdlg, UINT uMenu, UINT uFlags, POINT pt) +{ + FILEVIEW *pfv = GetFileView(hdlg); + if (!pfv) return; + + HMENU hMenu = FileView_GetMenu(hdlg, uMenu); + if (!hMenu) return; + uMenu = FileViewMenu_GetMenuType(hdlg, pfv->hMenu, hMenu); + if (WASABI_API_APP) + { + HACCEL szAccel[24] = {0}; + INT c = WASABI_API_APP->app_getAccelerators(hdlg, szAccel, sizeof(szAccel)/sizeof(szAccel[0]), FALSE); + AppendMenuShortcuts(hMenu, szAccel, c, MSF_REPLACE); + } + + NMFVMENU popup; + ZeroMemory(&popup, sizeof(NMFVMENU)); + popup.hMenu = hMenu; + popup.uMenuType = uMenu; + popup.ptAction = pt; + + activePopup = hMenu; + + if (0 == FileView_NotifyParent(hdlg, FVN_INITMENU, (NMHDR*)&popup)) + { + SendMessageW(g_hwnd, WM_SETCURSOR, (WPARAM)hdlg, MAKELPARAM(HTCLIENT, WM_MOUSEMOVE)); + popup.uCommand = (UINT)MediaLibrary_TrackPopup(hMenu, uFlags | TPM_RETURNCMD, + popup.ptAction.x, popup.ptAction.y, hdlg); + + if (0 == FileView_NotifyParent(hdlg, FVN_MENUCOMMAND, (NMHDR*)&popup) && 0 != popup.uCommand) + { + SendMessageW(hdlg, WM_COMMAND, MAKEWPARAM(popup.uCommand, 0), 0L); + } + } + activePopup = NULL; +} + +static void FileView_OnInitMenuPopup(HWND hdlg, HMENU hMenu, UINT iIndex, BOOL bWinMenu) +{ + FILEVIEW *pfv = GetFileView(hdlg); + + if (!pfv || hMenu == activePopup) return; + + UINT uType = FileViewMenu_GetMenuType(hdlg, pfv->hMenu, hMenu); + if (((UINT)-1) != uType && FileViewMenu_GetSubMenu(hdlg, pfv->hMenu, uType)) + { + if (WASABI_API_APP) + { + HACCEL szAccel[24] = {0}; + INT c = WASABI_API_APP->app_getAccelerators(hdlg, szAccel, sizeof(szAccel)/sizeof(szAccel[0]), FALSE); + AppendMenuShortcuts(hMenu, szAccel, c, MSF_REPLACE); + } + + NMFVMENU popup; + ZeroMemory(&popup, sizeof(NMFVMENU)); + + popup.hMenu = hMenu; + popup.uMenuType = uType; + FileView_NotifyParent(hdlg, FVN_INITMENU, (NMHDR*)&popup); + } +} + +static void FileView_OnUninitMenuPopup(HWND hdlg, HMENU hMenu) +{ + FILEVIEW *pfv = GetFileView(hdlg); + if (!pfv) return; + + UINT uType = FileViewMenu_GetMenuType(hdlg, pfv->hMenu, hMenu); + if (((UINT)-1) != uType) + { + NMFVMENU popup; + ZeroMemory(&popup, sizeof(NMFVMENU)); + popup.hMenu = hMenu; + popup.uMenuType = uType; + FileView_NotifyParent(hdlg, FVN_UNINITMENU, (NMHDR*)&popup); + } +} + +static void FileView_DisplayColumnMenu(HWND hdlg) +{ + POINT pt; + GetCursorPos(&pt); + FileView_DisplayPopupMenu(hdlg, FVMENU_COLUMNS, TPM_RIGHTBUTTON | TPM_LEFTBUTTON | TPM_TOPALIGN | TPM_LEFTALIGN, pt); +} + +static void FileView_DisplayFileMenu(HWND hdlg) +{ + POINT pt; + GetCursorPos(&pt); + FileView_DisplayPopupMenu(hdlg, FVMENU_FILECONTEXT, TPM_RIGHTBUTTON | TPM_LEFTBUTTON | TPM_TOPALIGN | TPM_LEFTALIGN, pt); +} + +static void FileListHeader_OnRClick(HWND hdlg, NMHDR *phdr) +{ + FileView_DisplayColumnMenu(hdlg); +} + +static BOOL FileListHeader_OnEndDrag(HWND hdlg, NMHEADER *phdr) +{ + PostMessage(hdlg, WM_COMMAND, MAKEWPARAM(ID_INTERNAL_UPDATE_COLUMN_INFO, 0), (LPARAM)phdr->hdr.hwndFrom); + return FALSE; +} + +static BOOL FileListHeader_OnItemChanging(HWND hdlg, NMHEADER *phdr) +{ + if (phdr->pitem && (HDI_WIDTH & phdr->pitem->mask)) + { + FILEVIEW *pfv = GetFileView(hdlg); + if (pfv && phdr->iItem < pfv->columnCount) + { + FILEVIEWCOLUMN *pc = &pfv->szColumns[phdr->iItem]; + if (phdr->pitem->cxy < pc->widthMin) phdr->pitem->cxy = pc->widthMin; + if (pc->widthMax > 0 && phdr->pitem->cxy > pc->widthMax) phdr->pitem->cxy = pc->widthMax; + } + } + return FALSE; +} + +static void FileListHeader_OnItemChanged(HWND hdlg, NMHEADER *phdr) +{ + SendMessage(hdlg, WM_COMMAND, MAKEWPARAM(ID_INTERNAL_UPDATE_COLUMN_INFO, 0), (LPARAM)phdr->hdr.hwndFrom); +} + +static void FileList_NotifyActivate(HWND hdlg, NMITEMACTIVATE *pnma, UINT uCode) +{ + NMFVFILEACTIVATE fva; + + KillTimer(hdlg, IDT_ACTIVATELISTITEM); + + fva.iFile = pnma->iItem; + fva.ptAction = pnma->ptAction; + fva.uKeyFlags = pnma->uKeyFlags; + fva.uNewState = pnma->uNewState; + fva.uOldState = pnma->uOldState; + FileView_NotifyParent(hdlg, uCode, (NMHDR*)&fva); +} + +static void FileList_OnClick(HWND hdlg, NMITEMACTIVATE *pnma) +{ + FileList_NotifyActivate(hdlg, pnma, NM_CLICK); +} + +static void FileList_OnDblClick(HWND hdlg, NMITEMACTIVATE *pnma) +{ + FileList_NotifyActivate(hdlg, pnma, NM_DBLCLK); +} + +static void FileList_OnRClick(HWND hdlg, NMITEMACTIVATE *pnma) +{ + FileList_NotifyActivate(hdlg, pnma, NM_RCLICK); + FileView_DisplayFileMenu(hdlg); +} + +static void FileList_OnRDblClick(HWND hdlg, NMITEMACTIVATE *pnma) +{ + FileList_NotifyActivate(hdlg, pnma, NM_RDBLCLK); +} + +static INT_PTR FileView_OnNotify(HWND hdlg, INT ctrlId, NMHDR *phdr) +{ + switch(phdr->idFrom) + { + case IDC_FILELIST: + switch(phdr->code) + { + case LVN_GETDISPINFOW: FileList_OnGetDispInfo(hdlg, (NMLVDISPINFOW*)phdr); return TRUE; + case NM_SETFOCUS: FileList_OnSetFocus(hdlg, phdr); break; + case LVN_GETINFOTIPW: FileList_OnGetInfoTip(hdlg, (NMLVGETINFOTIPW*)phdr); break; + case LVN_KEYDOWN: FileList_OnKeyDown(hdlg, (NMLVKEYDOWN*)phdr); break; + case LVN_COLUMNCLICK: FileList_OnColumnClick(hdlg, (NMLISTVIEW*)phdr); break; + case LVN_ITEMCHANGED: FileList_OnItemChanged(hdlg, (NMLISTVIEW*)phdr); break; + case LVN_ODSTATECHANGED: FileList_OnStateChanged(hdlg, (NMLVODSTATECHANGE*)phdr); break; + case NM_CLICK: FileList_OnClick(hdlg, (NMITEMACTIVATE*)phdr); break; + case NM_DBLCLK: FileList_OnDblClick(hdlg, (NMITEMACTIVATE*)phdr); break; + case NM_RCLICK: FileList_OnRClick(hdlg, (NMITEMACTIVATE*)phdr); break; + case NM_RDBLCLK: FileList_OnRDblClick(hdlg, (NMITEMACTIVATE*)phdr); break; + case NM_CUSTOMDRAW: return FileList_OnCustomDraw(hdlg, (NMLVCUSTOMDRAW*)phdr); + case NM_KILLFOCUS: KillTimer(hdlg, IDT_ACTIVATELISTITEM); break; + } + break; + case IDC_FILELIST_HEADER: + switch(phdr->code) + { + case NM_RCLICK: FileListHeader_OnRClick(hdlg, phdr); break; + case HDN_ENDDRAG: return FileListHeader_OnEndDrag(hdlg, (NMHEADER*)phdr); + case HDN_ITEMCHANGINGW: return FileListHeader_OnItemChanging(hdlg, (NMHEADER*)phdr); + case HDN_ITEMCHANGEDW: FileListHeader_OnItemChanged(hdlg, (NMHEADER*)phdr); break; + } + case IDC_FOLDER_BROWSER: + switch(phdr->code) + { + case FBN_SELCHANGED: + { + wchar_t szText[MAX_PATH*2] = {0}; + FolderBrowser_GetCurrentPath(phdr->hwndFrom, szText, sizeof(szText)/sizeof(szText[0])); + HWND hedt = GetDlgItem(hdlg, IDC_EDT_PATH); + if (hedt && hedt != GetFocus()) + { + SetWindowTextW(hedt, szText); + } + FileView_UpdateFileView(hdlg, szText); + } + break; + } + break; + case IDC_FILEVIEW_TOOLBAR: + switch(phdr->code) + { + case FVN_INITMENU: + case FVN_UNINITMENU: + return FileView_NotifyParent(hdlg, phdr->code, phdr); + } + break; + } + return 0; +} + +static void FileView_OnSetFont(HWND hdlg, BOOL bRedraw) +{ + FileView_AdjustSizes(hdlg); + FileView_LayoutWindows(hdlg, bRedraw); +} + +static BOOL FileView_OnSetStyle(HWND hdlg, UINT uStyle, UINT uStyleMask) +{ + FILEVIEW *pfv = GetFileView(hdlg); + if (!pfv) return FALSE; + + if (FVS_VIEWMASK & uStyleMask) FileView_OnSetView(hdlg, (FVS_VIEWMASK & uStyle), FALSE); + uStyleMask &= ~FVS_VIEWMASK; + pfv->style = (pfv->style & ~uStyleMask) | (uStyle & uStyleMask); + if (FVS_IGNOREHIDDEN & uStyleMask) + { + HWND hctrl = GetDlgItem(hdlg, IDC_FOLDER_BROWSER); + if (hctrl) SetWindowLongPtrW(hctrl, GWL_STYLE, + (GetWindowLongPtrW(hctrl, GWL_STYLE) & ~FBS_IGNOREHIDDEN) | ((FVS_IGNOREHIDDEN & uStyle) ? FBS_IGNOREHIDDEN : 0)); + } + return TRUE; +} + +static void FileView_InvertStyle(HWND hdlg, UINT style) +{ + FILEVIEW *pfv = GetFileView(hdlg); + if (!pfv) return; + FileView_OnSetStyle(hdlg, pfv->style ^ style, style); + FileView_Refresh(hdlg, FALSE); +} + +static UINT FileView_OnGetStyle(HWND hdlg) +{ + FILEVIEW *pfv = GetFileView(hdlg); + return (pfv) ? pfv->style : 0; +} + +static BOOL FileView_OnSetSort(HWND hdlg, UINT uColumn, BOOL bAscending) +{ + FILEVIEW *pfv = GetFileView(hdlg); + if (!pfv) return FALSE; + + INT index = 0; + for (index = 0; index < pfv->columnCount && uColumn != pfv->szColumns[index].id; index++); + if (index == pfv->columnCount) return FALSE; + + if (uColumn == (INT)pfv->sortColumn) + { + if (pfv->bAscending == bAscending) return TRUE; + pfv->bAscending = bAscending; + } + else + { + pfv->sortColumn = uColumn; + pfv->bAscending = bAscending; + FileView_SortByColumn(&pfv->fileData, pfv->sortColumn); + } + + HWND hctrl = GetDlgItem(hdlg, IDC_FILELIST); + if (hctrl) + { + MLSkinnedListView_DisplaySort(hctrl, index, pfv->bAscending); + SendMessageW(hctrl, LVM_REDRAWITEMS, 0, pfv->fileData.count); + InvalidateRect(hctrl, NULL, TRUE); + UpdateWindow(hctrl); + } + + return TRUE; +} + +static UINT FileView_OnGetSort(HWND hdlg) +{ + FILEVIEW *pfv = GetFileView(hdlg); + return (pfv) ? MAKELONG(pfv->sortColumn, pfv->bAscending) : ((UINT)-1); +} + +static INT FileView_OnGetColumnCount(HWND hdlg) +{ + FILEVIEW *pfv = GetFileView(hdlg); + return (pfv) ? pfv->columnCount : 0; +} + +typedef struct _CORDER +{ + INT id; + INT order; +} CORDER; + +static INT __cdecl QSort_CompareOrder(const void *p1, const void *p2) +{ + return (((CORDER*)p1)->order - ((CORDER*)p2)->order); +} + +static INT FileView_OnGetColumnArray(HWND hdlg, INT iCount, UINT *puArray) +{ + FILEVIEW *pfv = GetFileView(hdlg); + if (!pfv) return -1; + CORDER szProxy[512] = {0}; + INT count; + for (count = 0; count < pfv->columnCount; count++) + { + szProxy[count].id = pfv->szColumns[count].id; + szProxy[count].order = pfv->szColumns[count].order; + } + qsort(szProxy, pfv->columnCount, sizeof(szProxy[0]), QSort_CompareOrder); + for (count = 0; count < pfv->columnCount && count < iCount; count++) puArray[count] = szProxy[count].id; + + return count; +} + +static BOOL FileView_OnDeleteColumn(HWND hdlg, UINT uColumn) +{ + FILEVIEW *pfv = GetFileView(hdlg); + if (!pfv) return FALSE; + + INT index = 0; + for (index = 0; index < pfv->columnCount && uColumn != pfv->szColumns[index].id; index++); + if (index == pfv->columnCount) return FALSE; + + if (index < (pfv->columnCount - 1)) + { + MoveMemory(&pfv->szColumns[index], &pfv->szColumns[index + 1], sizeof(FILEVIEWCOLUMN)*(pfv->columnCount - index - 1)); + } + + pfv->columnCount--; + HWND hList = GetDlgItem(hdlg, IDC_FILELIST); + if (hList && (FVS_DETAILVIEW == (FVS_VIEWMASK & pfv->style))) + { + SendMessageW(hList, LVM_DELETECOLUMN, index, 0L); + + UINT c = pfv->sortColumn; + pfv->sortColumn = ((UINT)-1); + + if (c == uColumn) + { + if (index > 0) index--; + c = pfv->szColumns[index].id; + } + + FileView_SetSort(hdlg, c, pfv->bAscending); + } + + return TRUE; +} + +static INT FileView_OnInsertColumn(HWND hdlg, FVCOLUMN *pColumn) +{ + FILEVIEW *pfv = GetFileView(hdlg); + if (!pfv) return -1; + + INT registeredIndex; + for( registeredIndex = 0; + registeredIndex < RegisteredColumnsCount && szRegisteredColumns[registeredIndex].id != pColumn->id; + registeredIndex++); + + if (szRegisteredColumns[registeredIndex].id != pColumn->id) return -1; + + INT index = 0; + for (index = 0; index < pfv->columnCount && pColumn->id != pfv->szColumns[index].id; index++); + if (index != pfv->columnCount) + { + return index; + } + + INT order = 0; + UINT insertPos = (UINT)pfv->columnCount; + + if (FVCF_ORDER & pColumn->mask) + { + order = pColumn->order; + if (FVCO_DEFAULT_ORDER == order) order = szRegisteredColumns[registeredIndex].order; + + insertPos = 0; + for (INT index = 0; index < pfv->columnCount; index++) + { + if (pfv->szColumns[index].order < order) insertPos = (index + 1); + else break; + } + } + else order = pfv->columnCount; + + + if (insertPos < (UINT)pfv->columnCount) + { + index = insertPos; + MoveMemory(&pfv->szColumns[index + 1], &pfv->szColumns[index], sizeof(FILEVIEWCOLUMN)*(pfv->columnCount - index)); + } + + else index = pfv->columnCount; + CopyMemory(&pfv->szColumns[index], &szRegisteredColumns[registeredIndex], sizeof(FILEVIEWCOLUMN)); + pfv->szColumns[index].order = order; + if (FVCF_WIDTH & pColumn->mask) pfv->szColumns[index].width = pColumn->width; + if (FVCF_WIDTHMIN & pColumn->mask) pfv->szColumns[index].widthMin = pColumn->widthMin; + if (FVCF_WIDTHMAX & pColumn->mask) pfv->szColumns[index].widthMax = pColumn->widthMax; + + if (pfv->szColumns[index].width < pfv->szColumns[index].widthMin) + pfv->szColumns[index].width = pfv->szColumns[index].widthMin; + if (pfv->szColumns[index].widthMax > 0 && pfv->szColumns[index].width > pfv->szColumns[index].widthMax) + pfv->szColumns[index].width = pfv->szColumns[index].widthMax; + + pfv->columnCount++; + + HWND hList = GetDlgItem(hdlg, IDC_FILELIST); + if (hList && (FVS_DETAILVIEW == (FVS_VIEWMASK & pfv->style))) + { + LVCOLUMNW lvc; + FileView_InitializeListColumn(hdlg, &lvc, &pfv->szColumns[index]); + SendMessageW(hList, LVM_INSERTCOLUMNW, insertPos, (LPARAM)&lvc); + } + + UINT c = pfv->sortColumn; + pfv->sortColumn = ((UINT)-1); + FileView_SetSort(hdlg, c, pfv->bAscending); + return index; +} + +static BOOL FileView_OnGetColumnName(HWND hdlg, UINT uColumn, INT cchTextMax, LPWSTR pszText) +{ + FILEVIEW *pfv = GetFileView(hdlg); + if (!pfv || !pszText) return FALSE; + LPWSTR pszColumn = NULL; + for(INT i = 0; i < pfv->columnCount; i++) + { + if (uColumn == pfv->szColumns[i].id) + { + pszColumn = pfv->szColumns[i].pszText; + break; + } + } + + if (NULL == pszColumn) + { + for(INT i = 0; i < RegisteredColumnsCount; i++) + { + if (szRegisteredColumns[i].id == uColumn) + { + pszColumn = szRegisteredColumns[i].pszText; + return TRUE; + } + } + } + if (NULL != pszColumn) + { + if (IS_INTRESOURCE(pszColumn)) WASABI_API_LNGSTRINGW_BUF((UINT)(UINT_PTR)pszColumn, pszText, cchTextMax); + else StringCchCopyW(pszText, cchTextMax, pszColumn); + return TRUE; + } + + return FALSE; +} + +static INT FileView_OnGetColumnWidth(HWND hdlg, UINT uColumn) +{ + FILEVIEW *pfv = GetFileView(hdlg); + if (!pfv) return -1; + + INT index = 0; + for (index = 0; index < pfv->columnCount && uColumn != pfv->szColumns[index].id; index++); + + return (index == pfv->columnCount) ? -1 : pfv->szColumns[index].width; +} + +static BOOL FileView_OnSetFileSystemInfo(HWND hdlg, FILESYSTEMINFO *pfsi) +{ + HWND hctrl = GetDlgItem(hdlg, IDC_FOLDER_BROWSER); + return (hctrl) ? FolderBrowser_SetFileSystemInfo(hctrl, pfsi) : FALSE; +} + +static BOOL FileView_OnGetFileSystemInfo(HWND hdlg, FILESYSTEMINFO *pfsi) +{ + HWND hctrl = GetDlgItem(hdlg, IDC_FOLDER_BROWSER); + return (hctrl) ? FolderBrowser_GetFileSystemInfo(hctrl, pfsi) : FALSE; +} + +static INT FileView_OnGetFileCount(HWND hdlg) +{ + FILEVIEW *pfv = GetFileView(hdlg); + return (pfv) ? (INT)pfv->fileData.count : 0; +} + +static INT FileView_OnGetSelectedCount(HWND hdlg) +{ + HWND hctrl = GetDlgItem(hdlg, IDC_FILELIST); + return ((NULL != hctrl) ? (INT)SendMessageW(hctrl, LVM_GETSELECTEDCOUNT, 0, 0L) : 0); +} + +static INT FileView_OnGetNextFile(HWND hdlg, INT iStart, UINT uFlags) +{ + INT iFile; + HWND hctrl = GetDlgItem(hdlg, IDC_FILELIST); + UINT lvFlags = (uFlags & 0xFF0F); + FILEVIEW *pfv = GetFileView(hdlg); + if (!pfv) return -1; + + iFile = ((NULL != hctrl) ? (INT)SendMessageW(hctrl, LVM_GETNEXTITEM, iStart, (LPARAM)lvFlags) : -1); + if ((0x00F0 & uFlags) && -1 != iFile) + { + if (FVNF_PLAYABLE & uFlags) + { + size_t index = ((pfv->bAscending) ? iFile : (pfv->fileData.count - iFile - 1)); + FILERECORD *pfr = &pfv->fileData.pRec[pfv->fileData.pSort[index]]; + while (!FileView_IsTypePlayable(pfr->fileType, FVEF_ALLKNOWN)) + { + iFile = (INT)SendMessageW(hctrl, LVM_GETNEXTITEM, iFile, (LPARAM)lvFlags); + if (-1 == iFile) return -1; + index = ((pfv->bAscending) ? iFile : (pfv->fileData.count - iFile - 1)); + pfr = &pfv->fileData.pRec[pfv->fileData.pSort[index]]; + } + } + } + return iFile; +} + +static BOOL FileView_OnGetFile(HWND hdlg, INT iFile, FVITEM *pFile) +{ + FILEVIEW *pfv = GetFileView(hdlg); + if (!pfv || ((size_t)iFile) >= pfv->fileData.count || !pFile) return FALSE; + + size_t index = ((pfv->bAscending) ? iFile : (pfv->fileData.count - iFile - 1)); + FILERECORD *pfr = &pfv->fileData.pRec[pfv->fileData.pSort[index]]; + + if (FVIF_TEXT & pFile->mask) + { + if (NULL == pFile->pszText) return FALSE; + + if (pfr->extOffset != (lstrlenW(pfr->Info.cFileName) + 1)) pFile->pszText = pfr->Info.cFileName; + else + { + HRESULT hr; + hr = StringCchPrintfW(pFile->pszText, pFile->cchTextMax, L"%s.%s", + pfr->Info.cFileName, (pfr->Info.cFileName + pfr->extOffset)); + if (S_OK != hr) return FALSE; + } + } + + if (FVIF_STATE & pFile->mask) + { + HWND hctrl = GetDlgItem(hdlg, IDC_FILELIST); + if (!hctrl) return FALSE; + pFile->state = (UINT)SendMessageW(hctrl, LVM_GETITEMSTATE, iFile, (LPARAM)pFile->stateMask); + } + + if (FVIF_SIZE & pFile->mask) + { + pFile->dwSizeLow = pfr->Info.nFileSizeLow; + pFile->dwSizeHigh = pfr->Info.nFileSizeHigh; + } + + if (FVIF_ATTRIBUTES & pFile->mask) pFile->uAttributes = pfr->Info.dwFileAttributes; + if (FVIF_CREATETIME & pFile->mask) pFile->ftCreationTime = pfr->Info.ftCreationTime; + if (FVIF_ACCESSTIME & pFile->mask) pFile->ftLastAccessTime = pfr->Info.ftLastAccessTime; + if (FVIF_WRITETIME & pFile->mask) pFile->ftLastWriteTime = pfr->Info.ftLastWriteTime; + if (FVIF_TYPE & pFile->mask) pFile->wType = pfr->fileType; + + return TRUE; +} + +static BOOL FileView_OnGetCurrentPath(HWND hdlg, LPWSTR pszBuffer, INT cchBuffer) +{ + if (!pszBuffer || !cchBuffer) return FALSE; + pszBuffer[0] = L'\0'; + + HWND hctrl = GetDlgItem(hdlg, IDC_FOLDER_BROWSER); + if (!hctrl) return FALSE; + + return FolderBrowser_GetCurrentPath(hctrl, pszBuffer, cchBuffer); +} + +static BOOL FileView_OnSetCurrentPath(HWND hdlg, LPCWSTR pszPath, BOOL bRedraw) +{ + HWND hctrl = GetDlgItem(hdlg, IDC_FOLDER_BROWSER); + if (!hctrl) return FALSE; + return FolderBrowser_SetCurrentPath(hctrl, pszPath, bRedraw); +} + +static DWORD FileView_OnGetFolderSize(HWND hdlg, BOOL bSelectedOnly, DWORD *pdwSizeHigh) +{ + ULONGLONG size = 0; + FILEVIEW *pfv = GetFileView(hdlg); + if (pfv) + { + if (!bSelectedOnly) size = pfv->fileData.folderSize; + else + { + HWND hctrl = GetDlgItem(hdlg, IDC_FILELIST); + if (hctrl) + { + INT i = -1; + while( -1 != (i = (INT)SendMessage(hctrl, LVM_GETNEXTITEM, i, LVNI_SELECTED))) + { + size_t index = ((pfv->bAscending) ? i : (pfv->fileData.count - i - 1)); + FILERECORD *pfr = &pfv->fileData.pRec[pfv->fileData.pSort[index]]; + size += ((ULONGLONG)(((__int64)pfr->Info.nFileSizeHigh << 32) | pfr->Info.nFileSizeLow)); + } + } + } + } + + if (pdwSizeHigh) *pdwSizeHigh = (DWORD)(size >> 32); + return (DWORD)(MAXDWORD & size); +} + +static BOOL FileView_OnGetStatusText(HWND hdlg, LPWSTR pszText, INT cchTextMax) +{ + if (!pszText || !cchTextMax) return FALSE; + pszText[0] = L'\0'; + + FILEVIEW *pfv = GetFileView(hdlg); + if (!pfv) return FALSE; + + pfv->statusIndex = -1; + if (!pfv->fileData.count) + { + WASABI_API_LNGSTRINGW_BUF(IDS_FILEVIEW_EMPTYFOLDER, pszText, cchTextMax); + return TRUE; + } + + HWND hctrl = GetDlgItem(hdlg, IDC_FILELIST); + INT selectedCount = 0, selectedIndex = -1; + if (hctrl) + { + selectedCount = (INT)SendMessageW(hctrl, LVM_GETSELECTEDCOUNT, 0, 0L); + if (1 == selectedCount) + { + selectedIndex = (INT)SendMessageW(hctrl, LVM_GETNEXTITEM, -1, (LPARAM)FVNF_SELECTED); + } + } + + WCHAR szTemplate[256] = {0}, szSize[64] = {0}; + DWORD sizeLow, sizeHigh; + + if (1 == selectedCount && -1 != selectedIndex) + { + size_t index = ((pfv->bAscending) ? selectedIndex : (pfv->fileData.count - selectedIndex - 1)); + FILERECORD *pfr = &pfv->fileData.pRec[pfv->fileData.pSort[index]]; + if (FVFT_UNKNOWN != pfr->fileType && + !FileViewMeta_Discover(pfv->fileData.szPath, pfr, FileView_OnMetaDataDiscovered, (ULONG_PTR)hdlg, 0)) + { + pfv->statusIndex = selectedIndex; + } + FileView_FormatFileInfo(pfr, pszText, cchTextMax, FIF_STATUS); + return TRUE; + } + + WASABI_API_LNGSTRINGW_BUF(((selectedCount < 1) ? IDS_FILEVIEWSTATUS_GROUPTEMPLATE : IDS_FILEVIEWSTATUS_SELECTEDGROUPTEMPLATE), + szTemplate, sizeof(szTemplate)/sizeof(szTemplate[0])); + + sizeLow = FileView_OnGetFolderSize(hdlg, (selectedCount > 1), &sizeHigh); + WASABI_API_LNG->FormattedSizeString(szSize, sizeof(szSize)/sizeof(szSize[0]), (((__int64)sizeHigh << 32) | sizeLow)); + return (S_OK == StringCchPrintfW(pszText, cchTextMax, szTemplate, ((selectedCount < 1) ? pfv->fileData.count : selectedCount), szSize)); +} + +#define IsKeyword(__keyword, __val) (CSTR_EQUAL == CompareStringW(CSTR_INVARIANT, NORM_IGNORECASE,(__keyword), -1, (__val), -1)) +wchar_t *FileView_TagFunc(const wchar_t *tag, void *p) +{ + static WCHAR szBuffer[1024]; + LPCWSTR pszVal; + INT nVal, nVal2; + + FILERECORD *pfr = (FILERECORD*)p; + + if (!pfr) return 0; + + if (pfr->pMeta) + { + if (IsKeyword(L"artist", tag)) return (FileViewMeta_GetString(pfr->pMeta, MF_ARTIST, &pszVal)) ? (LPWSTR)pszVal : 0; + else if (IsKeyword(L"album", tag)) return (FileViewMeta_GetString(pfr->pMeta, MF_ALBUM, &pszVal)) ? (LPWSTR)pszVal : 0; + else if (IsKeyword(L"title", tag)) return (FileViewMeta_GetString(pfr->pMeta, MF_TITLE, &pszVal)) ? (LPWSTR)pszVal : 0; + else if (IsKeyword(L"albumartist", tag)) return (FileViewMeta_GetString(pfr->pMeta, MF_ALBUMARTIST, &pszVal)) ? (LPWSTR)pszVal : 0; + else if (IsKeyword(L"comment", tag)) return (FileViewMeta_GetString(pfr->pMeta, MF_COMMENT, &pszVal)) ? (LPWSTR)pszVal : 0; + else if (IsKeyword(L"composer", tag)) return (FileViewMeta_GetString(pfr->pMeta, MF_COMPOSER, &pszVal)) ? (LPWSTR)pszVal : 0; + else if (IsKeyword(L"genre", tag)) return (FileViewMeta_GetString(pfr->pMeta, MF_GENRE, &pszVal)) ? (LPWSTR)pszVal : 0; + else if (IsKeyword(L"publisher", tag)) return (FileViewMeta_GetString(pfr->pMeta, MF_PUBLISHER, &pszVal)) ? (LPWSTR)pszVal : 0; + else if (IsKeyword(L"bitrate", tag)) + { + if (!FileViewMeta_GetInt(pfr->pMeta, MF_BITRATE, &nVal)) return 0; + FileView_FormatBitrate(nVal, szBuffer, sizeof(szBuffer)/sizeof(szBuffer[0])); + return szBuffer; + } + else if (IsKeyword(L"year", tag)) + { + if (!FileViewMeta_GetInt(pfr->pMeta, MF_YEAR, &nVal)) return 0; + FileView_FormatBitrate(nVal, szBuffer, sizeof(szBuffer)/sizeof(szBuffer[0])); + return szBuffer; + } + else if (IsKeyword(L"tracknumber", tag) || IsKeyword(L"track", tag) ) + { + if (!FileViewMeta_GetInt(pfr->pMeta, MF_TRACKNUM, &nVal)) return 0; + if (!FileViewMeta_GetInt(pfr->pMeta, MF_TRACKCOUNT, &nVal2)) nVal2 = -1; + FileView_FormatIntSlashInt(nVal, nVal2, szBuffer, sizeof(szBuffer)/sizeof(szBuffer[0])); + return szBuffer; + } + else if (IsKeyword(L"disc", tag)) + { + if (!FileViewMeta_GetInt(pfr->pMeta, MF_DISCNUM, &nVal)) return 0; + if (!FileViewMeta_GetInt(pfr->pMeta, MF_DISCCOUNT, &nVal2)) nVal2 = -1; + FileView_FormatIntSlashInt(nVal, nVal2, szBuffer, sizeof(szBuffer)/sizeof(szBuffer[0])); + return szBuffer; + } + } + if (IsKeyword(L"filename", tag)) + { + if (pfr->extOffset == (lstrlenW(pfr->Info.cFileName) + 1)) + { + StringCchPrintfW(szBuffer, sizeof(szBuffer)/sizeof(szBuffer[0]), L"%s.%s", + pfr->Info.cFileName, (pfr->Info.cFileName + pfr->extOffset)); + return szBuffer; + } + return pfr->Info.cFileName; + } + return 0; +} + +static BOOL FileView_OnIsFilePlayable(HWND hdlg, INT iFile, UINT uEnqueueFilter) +{ + FILEVIEW *pfv = GetFileView(hdlg); + if (!pfv || ((size_t)iFile) >= pfv->fileData.count) return FALSE; + size_t index = ((pfv->bAscending) ? iFile : (pfv->fileData.count - iFile - 1)); + FILERECORD *pfr = &pfv->fileData.pRec[pfv->fileData.pSort[index]]; + return FileView_IsTypePlayable(pfr->fileType, uEnqueueFilter); +} + +static INT FileView_OnEnqueueSelection(HWND hdlg, UINT uEnqueueFilter, INT *pnFocused) +{ + FILERECORD *pfr; + wchar_t szPath[2*MAX_PATH] = {0}, szAtf[2048] = {0}; + + enqueueFileWithMetaStructW efs = {0}; + waFormatTitleExtended ft = {0}; + + FILEVIEW *pfv = GetFileView(hdlg); + if (!pfv || 0 == uEnqueueFilter) return 0; + + HWND hctrl = GetDlgItem(hdlg, IDC_FILELIST); + if (!hctrl) return 0; + INT sCount = (INT)SendMessageW(hctrl, LVM_GETSELECTEDCOUNT, 0, 0L); + if (0 == sCount) return 0; + + ft.spec = NULL; + ft.TAGFUNC = FileView_TagFunc; + ft.TAGFREEFUNC = NULL; + + ft.useExtendedInfo = 1; + + sCount = 0; + INT iFile = -1, iFocused = -1; + + if (pnFocused) + { + *pnFocused = -1; + iFocused = (INT)SendMessageW(hctrl, LVM_GETNEXTITEM, iFile, (LPARAM)LVIS_FOCUSED); + } + + while (-1 != (iFile = (INT)SendMessageW(hctrl, LVM_GETNEXTITEM, iFile, (LPARAM)LVIS_SELECTED))) + { + size_t index = ((pfv->bAscending) ? iFile : (pfv->fileData.count - iFile - 1)); + pfr = &pfv->fileData.pRec[pfv->fileData.pSort[index]]; + + if (FileView_IsTypePlayable(pfr->fileType, uEnqueueFilter) && + PathCombineW(szPath, pfv->fileData.szPath, pfr->Info.cFileName)) + { + if (pfr->extOffset == (lstrlenW(pfr->Info.cFileName) + 1)) + { + StringCchCatW(szPath, sizeof(szPath)/sizeof(szPath[0]), L"."); + StringCchCatW(szPath, sizeof(szPath)/sizeof(szPath[0]), (pfr->Info.cFileName + pfr->extOffset)); + } + + if (pfr->pMeta && METATYPE_PLAYLIST != pfr->pMeta->type) + { + szAtf[0] = L'\0'; + ft.filename = szPath; + ft.out = szAtf; + ft.out_len = sizeof(szAtf)/sizeof(szAtf[0]); + ft.p = pfr; + SendMessageW(plugin.hwndParent, WM_WA_IPC, (WPARAM)&ft, IPC_FORMAT_TITLE_EXTENDED); + FileViewMeta_GetInt(pfr->pMeta, MF_LENGTH, &efs.length); + if (szAtf[0]) efs.title = szAtf; + else if (!FileViewMeta_GetString(pfr->pMeta, MF_TITLE, &efs.title)) efs.title = NULL; + } + else + { + efs.length = -1; + efs.title = NULL; + } + + efs.filename = szPath; + efs.ext = NULL; + + if (iFocused == iFile && pnFocused) *pnFocused = (INT)SENDWAIPC(plugin.hwndParent, IPC_GETLISTLENGTH, 0); + + if(SENDWAIPC(plugin.hwndParent, IPC_ENQUEUEFILEW, (WPARAM)&efs)) sCount++; + else if (iFocused == iFile && pnFocused) *pnFocused = -1; + } + } + + return sCount; +} + +static void FileView_OnPlaySelection(HWND hdlg, BOOL bShiftPressed) +{ + FILEVIEW *pfv = GetFileView(hdlg); + if (!pfv) return; + BOOL bPlay = ((FALSE == bShiftPressed) == (0 == (FVS_ENQUEUE & pfv->style))); + if (bPlay) SENDWAIPC(plugin.hwndParent, IPC_DELETE, 0); + if (FileView_OnEnqueueSelection(hdlg, FVEF_ALLKNOWN, NULL) && bPlay) + { + SENDWAIPC(plugin.hwndParent, IPC_SETPLAYLISTPOS, 0); + SENDWAIPC(plugin.hwndParent, IPC_STARTPLAY, 0); + } +} + +static void FileView_UpdateColumnInfo(HWND hdlg, HWND hHeader) +{ + FILEVIEW *pfv = GetFileView(hdlg); + if (!pfv || NULL == hHeader || 0 == pfv->columnCount) return; + + INT count = (INT)SendMessage(hHeader, HDM_GETITEMCOUNT, 0, 0L); + if (count) + { + HDITEM item; + item.mask = HDI_ORDER | HDI_WIDTH; + if (count > pfv->columnCount) count = pfv->columnCount; + + for (int i = 0; i < count; i++) + { + if (SendMessage(hHeader, HDM_GETITEM, (WPARAM)i, (LPARAM)&item)) + { + pfv->szColumns[i].order = item.iOrder; + pfv->szColumns[i].width = item.cxy; + } + } + } +} + +static void FileView_OnCommand(HWND hdlg, INT eventId, INT ctrlId, HWND hwndCtrl) +{ + FILEVIEW *pfv = GetFileView(hdlg); + + switch (ctrlId) + { + case IDC_EDT_PATH: + switch(eventId) + { + case EN_CHANGE: + if (hwndCtrl == GetFocus()) + { + HWND hctrl = GetDlgItem(hdlg, IDC_FOLDER_BROWSER); + if (IsWindow(hctrl)) + { + WCHAR szPath[MAX_PATH*2] = {0}; + GetWindowTextW(hwndCtrl, szPath, sizeof(szPath)/sizeof(szPath[0])); + FolderBrowser_SetCurrentPath(hctrl, szPath, TRUE); + PostMessageW(hdlg, FVM_REFRESH, 0, 0L); + } + } + break; + } + break; + + case ID_FILEVIEW_SETMODE_ICON: FileView_OnSetView(hdlg, FVS_ICONVIEW, TRUE); break; + case ID_FILEVIEW_SETMODE_LIST: FileView_OnSetView(hdlg, FVS_LISTVIEW, TRUE); break; + case ID_FILEVIEW_SETMODE_DETAIL: FileView_OnSetView(hdlg, FVS_DETAILVIEW, TRUE); break; + case ID_FILEVIEW_REFRESH: FileView_OnRefresh(hdlg, TRUE, TRUE); break; + case ID_FILEVIEW_PLAYSELECTION: + if (1 != eventId || GetFocus() == GetDlgItem(hdlg, IDC_FILELIST)) + FileView_OnPlaySelection(hdlg, FALSE); + else if (1 == eventId) + { + HWND hFocus = GetFocus(); + if (hFocus == GetDlgItem(hdlg, IDC_EDT_PATH) || + hFocus == GetDlgItem(hdlg, IDC_FOLDER_BROWSER) || + IsChild(GetDlgItem(hdlg, IDC_FOLDER_BROWSER), hFocus)) + SendMessageW(hdlg, WM_NEXTDLGCTL, 0, MAKELPARAM(0, 0)); + } + break; + case ID_FILEVIEW_PLAYSELECTION_SHIFT: + if (1 != eventId || GetFocus() == GetDlgItem(hdlg, IDC_FILELIST)) + FileView_OnPlaySelection(hdlg, TRUE); + break; + case ID_FILEVIEW_SELECT_ALL: + if (1 != eventId || GetFocus() == GetDlgItem(hdlg, IDC_FILELIST)) + { + LVITEMW lvi; + lvi.stateMask = LVIS_SELECTED; + lvi.state = LVIS_SELECTED; + SendMessageW(GetDlgItem(hdlg, IDC_FILELIST), LVM_SETITEMSTATE, (WPARAM)-1, (LPARAM)&lvi); + } + else if (1 == eventId) + { + HWND hFocus = GetFocus(); + if (hFocus == GetDlgItem(hdlg, IDC_EDT_PATH)) SendMessageW(hFocus, EM_SETSEL, 0, -1); + } + break; + case IDM_FILEVIEW_SORTASCENDING: + if (pfv) FileView_OnSetSort(hdlg, pfv->sortColumn, !pfv->bAscending); + break; + case IDM_FILEVIEW_HIDEEXTENSION: FileView_InvertStyle(hdlg, FVS_HIDEEXTENSION); break; + case IDM_FILEVIEW_IGNOREHIDDEN: FileView_InvertStyle(hdlg, FVS_IGNOREHIDDEN); break; + case IDM_FILEVIEW_SHOWAUDIO: FileView_InvertStyle(hdlg, FVS_SHOWAUDIO); break; + case IDM_FILEVIEW_SHOWVIDEO: FileView_InvertStyle(hdlg, FVS_SHOWVIDEO); break; + case IDM_FILEVIEW_SHOWPLAYLIST: FileView_InvertStyle(hdlg, FVS_SHOWPLAYLIST); break; + case IDM_FILEVIEW_SHOWUNKNOWN: FileView_InvertStyle(hdlg, FVS_SHOWUNKNOWN); break; + case ID_INTERNAL_UPDATE_COLUMN_INFO: FileView_UpdateColumnInfo(hdlg, hwndCtrl); break; + } + if (ctrlId >= IDM_COLUMN_SHOW_MIN && ctrlId <= IDM_COLUMN_SHOW_MAX) + { + INT cid = ctrlId - IDM_COLUMN_SHOW_MIN; + INT index; + for (index = 0; index < pfv->columnCount && cid != pfv->szColumns[index].id; index++); + if (index == pfv->columnCount) + { + FVCOLUMN fvc; + fvc.id = cid; + fvc.order = FVCO_DEFAULT_ORDER; + fvc.mask = FVCF_ORDER; + FileView_InsertColumn(hdlg, &fvc); + } + else FileView_DeleteColumn(hdlg, cid); + } + else if (ctrlId >= IDM_COLUMN_ARRANGE_MIN && ctrlId <= IDM_COLUMN_ARRANGE_MAX) + { + if (pfv && pfv->sortColumn != (ctrlId - IDM_COLUMN_ARRANGE_MIN)) + FileView_OnSetSort(hdlg, ctrlId - IDM_COLUMN_ARRANGE_MIN, pfv->bAscending); + } +} + +static BOOL FileView_OnSetFileState(HWND hdlg, INT iFile, FVITEM *pFile) +{ + if (!pFile) return FALSE; + HWND hctrl = GetDlgItem(hdlg, IDC_FILELIST); + if (!hctrl) return FALSE; + LVITEMW lvi; + lvi.state = pFile->state; + lvi.stateMask = pFile->stateMask; + return (BOOL)SendMessageW(hctrl, LVM_SETITEMSTATE, (WPARAM)iFile, (LPARAM)&lvi); +} + +static HMENU FileView_OnGetMenu(HWND hdlg, UINT uMenuType) +{ + FILEVIEW *pfv = GetFileView(hdlg); + if (!pfv) return NULL; + if (!pfv->hMenu) + { + pfv->hMenu = FileViewMenu_Initialize(); + if (!pfv->hMenu) return NULL; + } + return FileViewMenu_GetSubMenu(hdlg, pfv->hMenu, uMenuType); +} + +static UINT FileView_OnGetActionCommand(HWND hdlg, UINT uAction) +{ + FILEVIEW *pfv = GetFileView(hdlg); + if (!pfv) return 0; + + switch(uAction) + { + case FVA_PLAY: + return (0 == (FVS_ENQUEUE & pfv->style)) ? ID_FILEVIEW_PLAYSELECTION : ID_FILEVIEW_PLAYSELECTION_SHIFT; + case FVA_ENQUEUE: + return (0 != (FVS_ENQUEUE & pfv->style)) ? ID_FILEVIEW_PLAYSELECTION : ID_FILEVIEW_PLAYSELECTION_SHIFT; + } + return 0; +} + +static INT FileView_OnHitTest(HWND hdlg, FVHITTEST *pht) +{ + HWND hctrl = GetDlgItem(hdlg, IDC_FILELIST); + if (!pht || !hctrl) return -1; + + LVHITTESTINFO ht; + ht.pt = pht->pt; + MapWindowPoints(hdlg, hctrl, &ht.pt, 1); + + INT r = (INT)SendMessageW(hctrl, LVM_HITTEST, 0, (LPARAM)&ht); + pht->iItem = ht.iItem; + pht->uFlags = (0xFF & ht.flags); + return r; +} + +static INT FileView_OnGetDividerPos(HWND hdlg) +{ + FILEVIEW *pfv = GetFileView(hdlg); + return (pfv) ? pfv->yDivider : 0; +} + +static INT FileView_OnSetDividerPos(HWND hdlg, INT nPos, UINT uFlags) +{ + FILEVIEW *pfv = GetFileView(hdlg); + RECT rc; + if (!pfv) return 0; + + if (FVRF_VALIDATE & uFlags) + { + if (nPos < pfv->yDivider && GetWindowRect(GetDlgItem(hdlg, IDC_FOLDER_BROWSER), &rc)) + { + MapWindowPoints(HWND_DESKTOP, hdlg, (POINT*)&rc, 2); + if (((nPos + pfv->cyDivider) - rc.top) < FOLDERBROWSER_MIN_HEIGHT) + nPos = rc.top + (FOLDERBROWSER_MIN_HEIGHT - pfv->cyDivider); + } + + if (nPos > pfv->yDivider && GetClientRect(hdlg, &rc)) + { + if ((nPos + pfv->cyDivider) > (rc.bottom - FILELIST_MIN_HEIGHT)) + nPos = rc.bottom - FILELIST_MIN_HEIGHT - pfv->cyDivider; + } + } + + if (nPos != pfv->yDivider) + { + pfv->yDivider = nPos; + if (0 == (FVRF_NOREDRAW & uFlags)) + { + FileView_LayoutWindows(hdlg, TRUE); + UpdateWindow(hdlg); + } + } + + return pfv->yDivider; +} + +static INT_PTR CALLBACK FileView_DialogProc(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch(uMsg) + { + case WM_INITDIALOG: return FileView_OnInitDialog(hdlg, (HWND)wParam, lParam); + case WM_DESTROY: FileView_OnDestroy(hdlg); return TRUE; + case WM_WINDOWPOSCHANGED: FileView_OnWindowPosChanged(hdlg, (WINDOWPOS*)lParam); return TRUE; + case WM_COMMAND: FileView_OnCommand(hdlg, HIWORD(wParam), LOWORD(wParam), (HWND)lParam); return TRUE; + case WM_NOTIFY: MSGRESULT(hdlg, FileView_OnNotify(hdlg, (INT)wParam, (LPNMHDR) lParam)); + case WM_SETFONT: FileView_OnSetFont(hdlg, LOWORD(lParam)); break; + case WM_INITMENUPOPUP: FileView_OnInitMenuPopup(hdlg, (HMENU)wParam, LOWORD(lParam), HIWORD(lParam)); break; + case WM_UNINITMENUPOPUP: FileView_OnUninitMenuPopup(hdlg, (HMENU)wParam); break; + + case FVM_SETROOT: MSGRESULT(hdlg, FileView_OnSetRoot(hdlg, (LPCWSTR)lParam)); + case FVM_GETROOT: MSGRESULT(hdlg, FileView_OnGetRoot(hdlg, (LPCWSTR)lParam, (INT)wParam)); + case FVM_REFRESH: MSGRESULT(hdlg, FileView_OnRefresh(hdlg, (BOOL)wParam, TRUE)); + case FVM_SETSTYLE: MSGRESULT(hdlg, FileView_OnSetStyle(hdlg, (UINT)lParam, (UINT)wParam)); + case FVM_GETSTYLE: MSGRESULT(hdlg, FileView_OnGetStyle(hdlg)); + case FVM_SETSORT: MSGRESULT(hdlg, FileView_OnSetSort(hdlg, LOWORD(wParam), HIWORD(wParam))); + case FVM_GETSORT: MSGRESULT(hdlg, FileView_OnGetSort(hdlg)); + case FVM_GETCOLUMNCOUNT: MSGRESULT(hdlg, FileView_OnGetColumnCount(hdlg)); + case FVM_GETCOLUMNARRAY: MSGRESULT(hdlg, FileView_OnGetColumnArray(hdlg, (INT)wParam, (UINT*)lParam)); + case FVM_DELETECOLUMN: MSGRESULT(hdlg, FileView_OnDeleteColumn(hdlg, (UINT)wParam)); + case FVM_INSERTCOLUMN: MSGRESULT(hdlg, FileView_OnInsertColumn(hdlg, (FVCOLUMN*)lParam)); + case FVM_GETCOLUMNAME: MSGRESULT(hdlg, FileView_OnGetColumnName(hdlg, (UINT)LOWORD(wParam), (INT)HIWORD(wParam), (LPWSTR)lParam)); + case FVM_SETFILESYSTEMINFO: MSGRESULT(hdlg, FileView_OnSetFileSystemInfo(hdlg, (FILESYSTEMINFO*)lParam)); + case FVM_GETFILESYSTEMINFO: MSGRESULT(hdlg, FileView_OnGetFileSystemInfo(hdlg, (FILESYSTEMINFO*)lParam)); + case FVM_GETFILECOUNT: MSGRESULT(hdlg, FileView_OnGetFileCount(hdlg)); + case FVM_GETSELECTEDCOUNT: MSGRESULT(hdlg, FileView_OnGetSelectedCount(hdlg)); + case FVM_GETNEXTFILE: MSGRESULT(hdlg, FileView_OnGetNextFile(hdlg, (INT)wParam, (UINT)lParam)); + case FVM_GETFILE: MSGRESULT(hdlg, FileView_OnGetFile(hdlg, (INT)wParam, (FVITEM*)lParam)); + case FVM_GETCURRENTPATH: MSGRESULT(hdlg, FileView_OnGetCurrentPath(hdlg, (LPWSTR)lParam, (INT)wParam)); + case FVM_SETCURRENTPATH: MSGRESULT(hdlg, FileView_OnSetCurrentPath(hdlg, (LPCWSTR)lParam, (BOOL)wParam)); + + case FVM_GETFOLDERBROWSER: MSGRESULT(hdlg, GetDlgItem(hdlg, IDC_FOLDER_BROWSER)); + case FVM_GETFOLDERSIZE: MSGRESULT(hdlg, FileView_OnGetFolderSize(hdlg, (BOOL)wParam, (DWORD*)lParam)); + case FVM_GETSTATUSTEXT: MSGRESULT(hdlg, FileView_OnGetStatusText(hdlg, (LPWSTR)lParam, (INT)wParam)); + case FVM_ENQUEUESELECTION: MSGRESULT(hdlg, FileView_OnEnqueueSelection(hdlg, (UINT)wParam, (INT*)lParam)); + case FVM_ISFILEPLAYABLE: MSGRESULT(hdlg, FileView_OnIsFilePlayable(hdlg, (INT)lParam, (UINT)wParam)); + case FVM_SETFILESTATE: MSGRESULT(hdlg, FileView_OnSetFileState(hdlg, (INT)wParam, (FVITEM*)lParam)); + case FVM_GETMENU: MSGRESULT(hdlg, FileView_OnGetMenu(hdlg, (UINT)wParam)); + case FVM_GETACTIONCMD: MSGRESULT(hdlg, FileView_OnGetActionCommand(hdlg, (UINT)wParam)); + case FVM_HITTEST: MSGRESULT(hdlg, FileView_OnHitTest(hdlg, (FVHITTEST*)lParam)); + case FVM_GETCOLUMNWIDTH: MSGRESULT(hdlg, FileView_OnGetColumnWidth(hdlg, (UINT)wParam)); + case FVM_GETDIVIDERPOS: MSGRESULT(hdlg, FileView_OnGetDividerPos(hdlg)); + case FVM_SETDIVIDERPOS: MSGRESULT(hdlg, FileView_OnSetDividerPos(hdlg, (INT)wParam, (UINT)lParam)); + } + return 0; +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/fileview.h b/Src/Plugins/General/gen_ml/fileview.h new file mode 100644 index 00000000..980adf6a --- /dev/null +++ b/Src/Plugins/General/gen_ml/fileview.h @@ -0,0 +1,23 @@ +#ifndef NULLOSFT_MEDIALIBRARY_FILEVIEW_CONTROL_HEADER +#define NULLOSFT_MEDIALIBRARY_FILEVIEW_CONTROL_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +HWND FileView_CreateDialog(HWND hwndParent, UINT fStyle, HWND hwndInsertAfter, INT x, INT y, INT cx, INT cy); + + +#ifdef __cplusplus +} +#endif + + + +#endif // NULLOSFT_MEDIALIBRARY_FILEVIEW_CONTROL_HEADER \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/fileview_columns.cpp b/Src/Plugins/General/gen_ml/fileview_columns.cpp new file mode 100644 index 00000000..01913692 --- /dev/null +++ b/Src/Plugins/General/gen_ml/fileview_columns.cpp @@ -0,0 +1,39 @@ +#include "./fileview.h" +#include "./fileview_internal.h" +#include "./resource.h" +#include "../nu/menushortcuts.h" +#include +#include + +#define COLUMN_WIDTH_MIN 16 +#define COLUMN_WIDTH_MAX 600 +#define COLUMN_WIDTH_MAX_LONG 2000 + +const static FILEVIEWCOLUMN szRegisteredColumns[] = +{ + { FVCOLUMN_NAME, MAKEINTRESOURCEW(IDS_FILEVIEW_COL_NAME), -1, LVCFMT_LEFT, 0, COLUMN_WIDTH_MIN, COLUMN_WIDTH_MAX_LONG}, + { FVCOLUMN_SIZE, MAKEINTRESOURCEW(IDS_FILEVIEW_COL_SIZE), 72, LVCFMT_RIGHT, 1, COLUMN_WIDTH_MIN, COLUMN_WIDTH_MAX}, + { FVCOLUMN_TYPE, MAKEINTRESOURCEW(IDS_FILEVIEW_COL_TYPE), 80, LVCFMT_LEFT, 2, COLUMN_WIDTH_MIN, COLUMN_WIDTH_MAX}, + { FVCOLUMN_EXTENSION, MAKEINTRESOURCEW(IDS_FILEVIEW_COL_EXTENSION), 60, LVCFMT_LEFT, 3, COLUMN_WIDTH_MIN, COLUMN_WIDTH_MAX}, + { FVCOLUMN_MODIFIED, MAKEINTRESOURCEW(IDS_FILEVIEW_COL_MODIFIED), 132, LVCFMT_LEFT, 4, COLUMN_WIDTH_MIN, COLUMN_WIDTH_MAX}, + { FVCOLUMN_CREATED, MAKEINTRESOURCEW(IDS_FILEVIEW_COL_CREATED), 132, LVCFMT_LEFT, 5, COLUMN_WIDTH_MIN, COLUMN_WIDTH_MAX}, + { FVCOLUMN_ATTRIBUTES, MAKEINTRESOURCEW(IDS_FILEVIEW_COL_ATTRIBUTES), 60, LVCFMT_LEFT, 6, COLUMN_WIDTH_MIN, COLUMN_WIDTH_MAX}, + { FVCOLUMN_ARTIST, MAKEINTRESOURCEW(IDS_FILEVIEW_COL_ARTIST), 140, LVCFMT_LEFT, 7, COLUMN_WIDTH_MIN, COLUMN_WIDTH_MAX_LONG}, + { FVCOLUMN_ALBUM, MAKEINTRESOURCEW(IDS_FILEVIEW_COL_ALBUM), 140, LVCFMT_LEFT, 8, COLUMN_WIDTH_MIN, COLUMN_WIDTH_MAX_LONG}, + { FVCOLUMN_TITLE, MAKEINTRESOURCEW(IDS_FILEVIEW_COL_TITLE), 140, LVCFMT_LEFT, 9, COLUMN_WIDTH_MIN, COLUMN_WIDTH_MAX_LONG}, + { FVCOLUMN_GENRE, MAKEINTRESOURCEW(IDS_FILEVIEW_COL_GENRE), 64, LVCFMT_LEFT, 10, COLUMN_WIDTH_MIN, COLUMN_WIDTH_MAX}, + { FVCOLUMN_YEAR, MAKEINTRESOURCEW(IDS_FILEVIEW_COL_YEAR), 48, LVCFMT_LEFT, 11, COLUMN_WIDTH_MIN, COLUMN_WIDTH_MAX}, + { FVCOLUMN_LENGTH, MAKEINTRESOURCEW(IDS_FILEVIEW_COL_LENGTH), 48, LVCFMT_LEFT, 12, COLUMN_WIDTH_MIN, COLUMN_WIDTH_MAX}, + { FVCOLUMN_BITRATE, MAKEINTRESOURCEW(IDS_FILEVIEW_COL_BITRATE), 48, LVCFMT_LEFT, 13, COLUMN_WIDTH_MIN, COLUMN_WIDTH_MAX}, + { FVCOLUMN_INMLDB, MAKEINTRESOURCEW(IDS_FILEVIEW_COL_INMLDB), 40, LVCFMT_LEFT, 14, COLUMN_WIDTH_MIN, COLUMN_WIDTH_MAX}, + { FVCOLUMN_TRACK, MAKEINTRESOURCEW(IDS_FILEVIEW_COL_TRACK), 48, LVCFMT_LEFT, 15, COLUMN_WIDTH_MIN, COLUMN_WIDTH_MAX}, + { FVCOLUMN_DISC, MAKEINTRESOURCEW(IDS_FILEVIEW_COL_DISC), 48, LVCFMT_LEFT, 16, COLUMN_WIDTH_MIN, COLUMN_WIDTH_MAX}, + { FVCOLUMN_COMMENT, MAKEINTRESOURCEW(IDS_FILEVIEW_COL_COMMENT), 140, LVCFMT_LEFT, 17, COLUMN_WIDTH_MIN, COLUMN_WIDTH_MAX_LONG}, + { FVCOLUMN_PUBLISHER, MAKEINTRESOURCEW(IDS_FILEVIEW_COL_PUBLISHER), 140, LVCFMT_LEFT, 18, COLUMN_WIDTH_MIN, COLUMN_WIDTH_MAX_LONG}, + { FVCOLUMN_COMPOSER, MAKEINTRESOURCEW(IDS_FILEVIEW_COL_COMPOSER), 120, LVCFMT_LEFT, 19, COLUMN_WIDTH_MIN, COLUMN_WIDTH_MAX_LONG}, + { FVCOLUMN_ALBUMARTIST, MAKEINTRESOURCEW(IDS_FILEVIEW_COL_ALBUMARTIST), 120, LVCFMT_LEFT, 20, COLUMN_WIDTH_MIN, COLUMN_WIDTH_MAX_LONG}, +}; + + + +const static INT RegisteredColumnsCount = sizeof(szRegisteredColumns)/sizeof(szRegisteredColumns[0]); \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/fileview_compare.cpp b/Src/Plugins/General/gen_ml/fileview_compare.cpp new file mode 100644 index 00000000..a4a5685f --- /dev/null +++ b/Src/Plugins/General/gen_ml/fileview_compare.cpp @@ -0,0 +1,255 @@ +#include "main.h" +#include "./fileview.h" +#include "./fileview_internal.h" +#include "./resource.h" +#include + + +typedef struct _TYPEORDERINTERNAL +{ + UINT index; + WCHAR szName[64]; +} TYPEORDERINTERNAL; + +typedef INT (CALLBACK *SHLWAPI_STRCMPLOGICALW)(LPCWSTR, LPCWSTR); + +static SHLWAPI_STRCMPLOGICALW fnStrCmpLogicalW = NULL; +static UINT szFileTypesSort[FVFT_LAST + 1]; + +static FILEDATA *g_pCompareData = NULL; + +#define FILEREC(__idx) (g_pCompareData->pRec[(*(size_t*)(__idx))]) +#define FILEINFO(__idx) (FILEREC(__idx).Info) + +#define COMPARE_META(__elem1, __elem2)\ +{\ + if (NULL == FILEREC(__elem1).pMeta) FileViewMeta_Discover(g_pCompareData->szPath, &FILEREC(__elem1), NULL, NULL, 0);\ + if (NULL == FILEREC(__elem2).pMeta) FileViewMeta_Discover(g_pCompareData->szPath, &FILEREC(__elem2), NULL, NULL, 0);\ + if (NULL == FILEREC(__elem1).pMeta || NULL == FILEREC(__elem2).pMeta)\ + return ((INT)(ULONG_PTR)(FILEREC(__elem1).pMeta - FILEREC(__elem2).pMeta));\ +} + +#define COMPARE_STR_I(__str1, __str2)\ + ((NULL == (__str1) || NULL == (__str2)) ? ((INT)(ULONG_PTR)((__str1) - (__str2))) :\ + (CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, (__str1), -1, (__str2), -1) - 2)) + +#define COMPARE_META_STR_I(__elem1, __elem2, __metaField)\ +{\ + LPCWSTR s1, s2;\ + BOOL b1, b2;\ + COMPARE_META(__elem1, __elem2);\ + b1 = FileViewMeta_GetString(FILEREC(__elem1).pMeta, (__metaField), &s1);\ + b2 = FileViewMeta_GetString(FILEREC(__elem2).pMeta, (__metaField), &s2);\ + return (!b1 || !b2) ? (b1 - b2) : COMPARE_STR_I(s1, s2);\ +} + +#define COMPARE_META_INT(__elem1, __elem2, __metaField)\ +{\ + INT i1, i2;\ + BOOL b1, b2;\ + COMPARE_META(__elem1, __elem2);\ + b1 = FileViewMeta_GetInt(FILEREC(__elem1).pMeta, (__metaField), &i1);\ + b2 = FileViewMeta_GetInt(FILEREC(__elem2).pMeta, (__metaField), &i2);\ + return (!b1 || !b2) ? (b1 - b2) : (i1 - i2);\ +} + + +__inline static int __cdecl FileRecord_CompareByName(const void *elem1, const void *elem2) +{ + return (CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, + FILEINFO(elem1).cFileName, -1, FILEINFO(elem2).cFileName, -1) - 2); +} + +__inline static int __cdecl FileRecord_CompareByNameLogical(const void *elem1, const void *elem2) +{ + return fnStrCmpLogicalW(FILEINFO(elem1).cFileName, FILEINFO(elem2).cFileName); +} + +__inline static int __cdecl FileRecord_CompareBySize(const void *elem1, const void *elem2) +{ + return ((FILEINFO(elem1).nFileSizeHigh != FILEINFO(elem2).nFileSizeHigh) ? + (FILEINFO(elem1).nFileSizeHigh - FILEINFO(elem2).nFileSizeHigh) : + (FILEINFO(elem1).nFileSizeLow - FILEINFO(elem2).nFileSizeLow)); +} + +__inline static int __cdecl FileRecord_CompareByLastWriteTime(const void *elem1, const void *elem2) +{ + return CompareFileTime(&FILEINFO(elem1).ftLastWriteTime, &FILEINFO(elem2).ftLastWriteTime); +} + +__inline static int __cdecl FileRecord_CompareByType(const void *elem1, const void *elem2) +{ + return szFileTypesSort[FILEREC(elem1).fileType] - szFileTypesSort[FILEREC(elem2).fileType]; +} + +__inline static int __cdecl FileRecord_CompareByAttributes(const void *elem1, const void *elem2) +{ + wchar_t szTest1[32] = {0}, szTest2[32] = {0}; + FileView_FormatAttributes(FILEINFO(elem1).dwFileAttributes, szTest1, sizeof(szTest1)/sizeof(szTest1[0])); + FileView_FormatAttributes(FILEINFO(elem2).dwFileAttributes, szTest2, sizeof(szTest2)/sizeof(szTest2[0])); + return (CompareStringW(LOCALE_USER_DEFAULT, 0, szTest1, -1, szTest2, -1) - 2); +} +__inline static int __cdecl FileRecord_CompareByExtension(const void *elem1, const void *elem2) +{ + if (0 == FILEREC(elem1).extOffset || 0 == FILEREC(elem2).extOffset) + return ((INT)(FILEREC(elem1).extOffset - (INT)FILEREC(elem2).extOffset)); + + return (CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, + FILEINFO(elem1).cFileName + FILEREC(elem1).extOffset, -1, + FILEINFO(elem2).cFileName + FILEREC(elem2).extOffset, -1) - 2); +} +__inline static int __cdecl FileRecord_CompareByCreationTime(const void *elem1, const void *elem2) +{ + return CompareFileTime(&FILEINFO(elem1).ftCreationTime, &FILEINFO(elem1).ftCreationTime); +} + +__inline static int __cdecl FileRecord_CompareByAlbum(const void *elem1, const void *elem2) +{ + COMPARE_META_STR_I(elem1, elem2, MF_ALBUM); +} + +__inline static int __cdecl FileRecord_CompareByArtist(const void *elem1, const void *elem2) +{ + COMPARE_META_STR_I(elem1, elem2, MF_ARTIST); +} + +__inline static int __cdecl FileRecord_CompareByTitle(const void *elem1, const void *elem2) +{ + COMPARE_META_STR_I(elem1, elem2, MF_TITLE); +} + +__inline static int __cdecl FileRecord_CompareByMLDB(const void *elem1, const void *elem2) +{ + COMPARE_META_INT(elem1, elem2, MF_SOURCE); +} +__inline static int __cdecl FileRecord_CompareByGenre(const void *elem1, const void *elem2) +{ + COMPARE_META_STR_I(elem1, elem2, MF_GENRE); +} + +__inline static int __cdecl FileRecord_CompareByYear(const void *elem1, const void *elem2) +{ + COMPARE_META_INT(elem1, elem2, MF_YEAR); +} + +__inline static int __cdecl FileRecord_CompareByLength(const void *elem1, const void *elem2) +{ + COMPARE_META_INT(elem1, elem2, MF_LENGTH); +} + +__inline static int __cdecl FileRecord_CompareByBitrate(const void *elem1, const void *elem2) +{ + COMPARE_META_INT(elem1, elem2, MF_BITRATE); +} + +__inline static int __cdecl FileRecord_CompareByTrack(const void *elem1, const void *elem2) +{ + COMPARE_META_INT(elem1, elem2, MF_TRACKNUM); +} + +__inline static int __cdecl FileRecord_CompareByDisc(const void *elem1, const void *elem2) +{ + COMPARE_META_INT(elem1, elem2, MF_DISCNUM); +} + +__inline static int __cdecl FileRecord_CompareByComment(const void *elem1, const void *elem2) +{ + COMPARE_META_STR_I(elem1, elem2, MF_COMMENT); +} + +__inline static int __cdecl FileRecord_CompareByPublisher(const void *elem1, const void *elem2) +{ + COMPARE_META_STR_I(elem1, elem2, MF_PUBLISHER); +} + +__inline static int __cdecl FileRecord_CompareByComposer(const void *elem1, const void *elem2) +{ + COMPARE_META_STR_I(elem1, elem2, MF_COMPOSER); +} + +__inline static int __cdecl FileRecord_CompareByAlbumArtist(const void *elem1, const void *elem2) +{ + COMPARE_META_STR_I(elem1, elem2, MF_ALBUMARTIST); +} + +__inline static int __cdecl CompareTypeOrderInternal(const void *elem1, const void *elem2) +{ + return (CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, ((TYPEORDERINTERNAL*)elem1)->szName, -1, ((TYPEORDERINTERNAL*)elem2)->szName, -1) - 2); +} + + +void FileView_SortByColumnEx(FILEDATA *pFileData, UINT uColumn, size_t *pOrder, size_t count) +{ + if (pFileData && pFileData->pRec && pOrder && count > 1) + { + static BOOL bLoadFailed = FALSE; + int (__cdecl *fnComparer)(const void *, const void *) = NULL; + + switch(uColumn) + { + case FVCOLUMN_NAME: + if (NULL == fnStrCmpLogicalW && !bLoadFailed) + { + UINT prevErrorMode; + HMODULE hModule; + prevErrorMode = SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS); + hModule = LoadLibraryW(L"Shlwapi.dll"); + SetErrorMode(prevErrorMode); + if (hModule) + { + fnStrCmpLogicalW = (SHLWAPI_STRCMPLOGICALW)GetProcAddress(hModule, "StrCmpLogicalW"); + FreeLibrary(hModule); + } + bLoadFailed = FALSE; + } + fnComparer = (fnStrCmpLogicalW) ? FileRecord_CompareByNameLogical : FileRecord_CompareByName; + break; + case FVCOLUMN_SIZE: fnComparer = FileRecord_CompareBySize; break; + case FVCOLUMN_MODIFIED: fnComparer = FileRecord_CompareByLastWriteTime; break; + case FVCOLUMN_TYPE: + { + TYPEORDERINTERNAL szOrder[sizeof(szFileTypesSort)/sizeof(szFileTypesSort[0])]; + for (int i = 0; i < sizeof(szFileTypesSort)/sizeof(szFileTypesSort[0]); i++) + { + szOrder[i].index = i; + WASABI_API_LNGSTRINGW_BUF(i, szOrder[i].szName, sizeof(szOrder[i].szName)/sizeof(szOrder[i].szName[0])); + } + qsort(szOrder, sizeof(szFileTypesSort)/sizeof(szFileTypesSort[0]), sizeof(TYPEORDERINTERNAL), CompareTypeOrderInternal); + for (int i = 0; i < sizeof(szFileTypesSort)/sizeof(szFileTypesSort[0]); i++) szFileTypesSort[szOrder[i].index] = i; + } + fnComparer = FileRecord_CompareByType; + break; + case FVCOLUMN_CREATED: fnComparer = FileRecord_CompareByCreationTime; break; + case FVCOLUMN_ATTRIBUTES: fnComparer = FileRecord_CompareByAttributes; break; + case FVCOLUMN_EXTENSION: fnComparer = FileRecord_CompareByExtension; break; + case FVCOLUMN_ARTIST: fnComparer = FileRecord_CompareByArtist; break; + case FVCOLUMN_ALBUM: fnComparer = FileRecord_CompareByAlbum; break; + case FVCOLUMN_TITLE: fnComparer = FileRecord_CompareByTitle; break; + case FVCOLUMN_INMLDB: fnComparer = FileRecord_CompareByMLDB; break; + case FVCOLUMN_GENRE: fnComparer = FileRecord_CompareByGenre; break; + case FVCOLUMN_YEAR: fnComparer = FileRecord_CompareByYear; break; + case FVCOLUMN_LENGTH: fnComparer = FileRecord_CompareByLength; break; + case FVCOLUMN_BITRATE: fnComparer = FileRecord_CompareByBitrate; break; + case FVCOLUMN_TRACK: fnComparer = FileRecord_CompareByTrack; break; + case FVCOLUMN_DISC: fnComparer = FileRecord_CompareByDisc; break; + case FVCOLUMN_COMMENT: fnComparer = FileRecord_CompareByComment; break; + case FVCOLUMN_PUBLISHER: fnComparer = FileRecord_CompareByPublisher; break; + case FVCOLUMN_COMPOSER: fnComparer = FileRecord_CompareByComposer; break; + case FVCOLUMN_ALBUMARTIST: fnComparer = FileRecord_CompareByAlbumArtist; break; + } + + if (fnComparer) + { + g_pCompareData = pFileData; + qsort(pOrder, count, sizeof(size_t), fnComparer); + g_pCompareData = NULL; + } + } +} + +void FileView_SortByColumn(FILEDATA *pFileData, UINT uColumn) +{ + if (!pFileData) return; + FileView_SortByColumnEx(pFileData, uColumn, pFileData->pSort, pFileData->count); +} + diff --git a/Src/Plugins/General/gen_ml/fileview_filesystem.cpp b/Src/Plugins/General/gen_ml/fileview_filesystem.cpp new file mode 100644 index 00000000..de953788 --- /dev/null +++ b/Src/Plugins/General/gen_ml/fileview_filesystem.cpp @@ -0,0 +1,289 @@ +#include "main.h" +#include "./fileview.h" +#include "./fileview_internal.h" +#include +#include "../playlist/svc_playlisthandler.h" +#include "../playlist/api_playlistmanager.h" +#include +#include + +#include +#include +#include + +#define FILEREC_ALLOCATION_STEP 1000 + + +#define FILETYPE_REREAD ((UINT)(0 - 1)) + +typedef struct _FILETYPEREC +{ + WCHAR szExtension[32]; + WCHAR szFamily[128]; + UINT Type; +} FILETYPEREC; + +static std::vector supportedFilesList; + + + +//__inline static int __cdecl QSort_StrCmpI(const void *elem1, const void *elem2) +//{ +// return (CompareStringW(CSTR_INVARIANT, NORM_IGNORECASE, (LPCWSTR)elem1, -1, (LPCWSTR)elem2, -1) - 2); +//} + +static BOOL FileView_GetPLExtensionName(LPCWSTR pszExt, LPWSTR pszDest, INT cchDest) +{ + BOOL result(FALSE); + DWORD lcid; + int n(0); + waServiceFactory *sf = 0; + LPCWSTR ext; + lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT); + + while (NULL != (sf = WASABI_API_SVC->service_enumService(WaSvc::PLAYLISTHANDLER, n++))) + { + svc_playlisthandler * handler = static_cast(sf->getInterface()); + if (handler) + { + int k(0); + while (NULL != (ext = handler->EnumerateExtensions(k++))) + { + if (CSTR_EQUAL == CompareStringW(lcid, NORM_IGNORECASE, pszExt, -1, ext, -1)) + { + result = (S_OK == StringCchCopyW(pszDest, cchDest, handler->GetName())); + if (result && CSTR_EQUAL == CompareStringW(lcid, NORM_IGNORECASE, pszExt, -1, L"M3U8", -1)) // ugly... + result = (S_OK == StringCchCatW(pszDest, cchDest, L" (Unicode)")); + break; + } + } + sf->releaseInterface(handler); + } + } + return result; +} + +static void FileView_ReadSupportedTypesInfo() +{ + LPWSTR pszTypes, p; + pszTypes = (LPWSTR)SendMessageW(plugin.hwndParent, WM_WA_IPC, 0, IPC_GET_EXTLISTW); + if (pszTypes) + { + INT i; + for(i = 0, p = pszTypes; *p != 0 && *(p+1) != 0; p += lstrlenW(p) + 1, i++) + { + FILETYPEREC ftr; + if (S_OK == StringCchCopyW(ftr.szExtension, sizeof(ftr.szExtension) / sizeof(ftr.szExtension[0]), p)) + { + ftr.Type = FILETYPE_REREAD; + supportedFilesList.push_back(ftr); + } + } + GlobalFree(pszTypes); + } + + if (WASABI_API_SVC) + { + api_playlistmanager *plMngr = 0; + waServiceFactory *factory = WASABI_API_SVC->service_getServiceByGuid(api_playlistmanagerGUID); + if (factory) plMngr = (api_playlistmanager*) factory->getInterface(); + if (plMngr) + { + FILETYPEREC ftr; + ftr.Type = FVFT_PLAYLIST; + size_t playlistEnum=0; + const wchar_t *playlistExt=0; + while (NULL != (playlistExt = plMngr->EnumExtension(playlistEnum++))) + { + if (S_OK ==StringCchCopyW(ftr.szExtension, sizeof(ftr.szExtension) / sizeof(ftr.szExtension[0]), playlistExt)) + { + FileView_GetPLExtensionName(ftr.szExtension, ftr.szFamily, sizeof(ftr.szFamily)/sizeof(ftr.szFamily[0])); + supportedFilesList.push_back(ftr); + } + } + factory->releaseInterface(plMngr); + } + } +} + + +static UINT FileView_GetFileType(LPCWSTR pszExtension) +{ + FILETYPEREC *pftr = nullptr; + + if (!pszExtension) return FVFT_UNKNOWN; + + if ( 0 == supportedFilesList.size()) + { + FileView_ReadSupportedTypesInfo(); + if (supportedFilesList.size()) { + //qsort(supportedFilesList.begin(), supportedFilesList.size(), sizeof(FILETYPEREC), QSort_StrCmpI); + std::sort(supportedFilesList.begin(), supportedFilesList.end(), + [&](const FILETYPEREC &lhs, const FILETYPEREC &rhs) -> bool + { + return CSTR_LESS_THAN == CompareStringW(CSTR_INVARIANT, NORM_IGNORECASE, (LPCWSTR)(&lhs), -1, (LPCWSTR)(&rhs), -1); + } + ); + } + } + + //pftr = (FILETYPEREC*)bsearch(pszExtension, supportedFilesList.begin(), supportedFilesList.size(), sizeof(FILETYPEREC), QSort_StrCmpI); + auto it = std::find_if(supportedFilesList.begin(), supportedFilesList.end(), + [&](const FILETYPEREC& rec) -> bool + { + return CSTR_EQUAL == CompareStringW(CSTR_INVARIANT, NORM_IGNORECASE, (LPCWSTR)(&rec), -1, (LPCWSTR)(pszExtension), -1); + } + ); + if (it != supportedFilesList.end()) + { + pftr = &(*it); + } + + if (pftr) + { + if (FILETYPE_REREAD == pftr->Type) + { + wchar_t szTest[MAX_PATH] = {0}; + + pftr->Type = FVFT_UNKNOWN; + + if (S_OK == StringCchPrintfW(szTest, sizeof(szTest)/sizeof(wchar_t), L"test.%s", pftr->szExtension)) + { + wchar_t szResult[MAX_PATH] = {0}; + extendedFileInfoStructW efis = { szTest, L"type", szResult, sizeof(szResult)/sizeof(szResult[0]), }; + if (SendMessageW(plugin.hwndParent, WM_WA_IPC, (WPARAM)&efis, IPC_GET_EXTENDED_FILE_INFOW)) + { + switch(szResult[0]) + { + case L'0': pftr->Type = FVFT_AUDIO; break; + case L'1': pftr->Type = FVFT_VIDEO; break; + } + pftr->szFamily[0] = L'\0'; + switch(pftr->Type) + { + case FVFT_AUDIO: + case FVFT_VIDEO: + extendedFileInfoStructW efis = { szTest, L"family", pftr->szFamily, sizeof(pftr->szFamily)/sizeof(pftr->szFamily[0]), }; + SendMessageW(plugin.hwndParent, WM_WA_IPC, (WPARAM)&efis, IPC_GET_EXTENDED_FILE_INFOW); + break; + } + } + } + + } + return pftr->Type; + } + return FVFT_UNKNOWN; +} + + +size_t FileView_ReadFileData(FILEDATA *pfd, LPCWSTR pszPath, UINT fStyle, FILESYSTEMINFO *pfsi) +{ + HANDLE hFile; + wchar_t szSearch[2 * MAX_PATH + 4] = {0}; + + if (!pfd) return ((size_t)-1); + + size_t c = 0; + pfd->count = 0; + pfd->folderSize = 0; + + if (S_OK != StringCchPrintfW(szSearch, sizeof(szSearch)/sizeof(szSearch[0]), L"%s\\*", pszPath)) return ((size_t)-1); + + if (0 == pfd->allocated) + { + pfd->pRec = (FILERECORD*)calloc(FILEREC_ALLOCATION_STEP, sizeof(FILERECORD)); + if (!pfd->pRec) + { + return ((size_t)-1); + } + pfd->pSort = (size_t*)calloc(FILEREC_ALLOCATION_STEP, sizeof(size_t)); + if (!pfd->pSort) + { + if (pfd->pRec) free(pfd->pRec); + ZeroMemory(&pfd, sizeof(FILEDATA)); + return ((size_t)-1); + } + pfd->allocated = FILEREC_ALLOCATION_STEP; + } + + hFile = pfsi->fnFindFirstFile(szSearch, &pfd->pRec[c].Info); + if (INVALID_HANDLE_VALUE != hFile) + { + do + { + if (0 == (FILE_ATTRIBUTE_DIRECTORY & pfd->pRec[c].Info.dwFileAttributes) && + (0 == (FVS_IGNOREHIDDEN & fStyle) || 0 == (FILE_ATTRIBUTE_HIDDEN & pfd->pRec[c].Info.dwFileAttributes))) + { + LPCWSTR pszExt = PathFindExtensionW(pfd->pRec[c].Info.cFileName); + if (L'.' == *pszExt) pszExt++; + else pszExt = NULL; + + pfd->pRec[c].extOffset = (pszExt && *pszExt) ? (pszExt - pfd->pRec[c].Info.cFileName) : 0; + + pfd->pRec[c].fileType = FileView_GetFileType(pszExt); + pfd->pRec[c].pMeta = NULL; + + if ((FVFT_UNKNOWN == pfd->pRec[c].fileType && (FVS_SHOWUNKNOWN & fStyle)) || + (FVFT_AUDIO == pfd->pRec[c].fileType && (FVS_SHOWAUDIO & fStyle)) || + (FVFT_VIDEO == pfd->pRec[c].fileType && (FVS_SHOWVIDEO & fStyle)) || + (FVFT_PLAYLIST == pfd->pRec[c].fileType && (FVS_SHOWPLAYLIST & fStyle))) + { + + if (NULL != pfd->pRec[c].extOffset && FVFT_UNKNOWN != pfd->pRec[c].fileType && (FVS_HIDEEXTENSION & fStyle)) + pfd->pRec[c].Info.cFileName[pfd->pRec[c].extOffset -1] = L'\0'; + + pfd->pSort[c] = c; + pfd->folderSize += (ULONGLONG)(((__int64)pfd->pRec[c].Info.nFileSizeHigh << 32) | pfd->pRec[c].Info.nFileSizeLow); + c++; + + if (c == pfd->allocated) + { + void *pData; + pData = realloc(pfd->pRec, sizeof(FILERECORD) * (pfd->allocated + FILEREC_ALLOCATION_STEP)); + if (!pData) { c = ((size_t)-1); break; } + pfd->pRec = (FILERECORD*)pData; + + pData = realloc(pfd->pSort, sizeof(size_t) * (pfd->allocated + FILEREC_ALLOCATION_STEP)); + if (!pData) { c = ((size_t)-1); break; } + pfd->pSort= (size_t*)pData; + + pfd->allocated += FILEREC_ALLOCATION_STEP; + + } + } + } + + } while (pfsi->fnFindNextFile(hFile, &pfd->pRec[c].Info)); + pfsi->fnFindClose(hFile); + } + else + { + DWORD e = GetLastError(); + if (0 != e) c = ((size_t)-1); + } + + if (((size_t)-1) != c) pfd->count = c; + return c; +} + +LPCWSTR FileView_GetTypeFamily(LPCWSTR pszExtension) +{ + if (!pszExtension || 0 == supportedFilesList.size()) + return NULL; + + //FILETYPEREC *pftr = (FILETYPEREC*)bsearch(pszExtension, supportedFilesList.begin(), supportedFilesList.size(), sizeof(FILETYPEREC), QSort_StrCmpI); + FILETYPEREC* pftr = nullptr; + auto it = std::find_if(supportedFilesList.begin(), supportedFilesList.end(), + [&](const FILETYPEREC& rec) -> bool + { + return CSTR_EQUAL == CompareStringW(CSTR_INVARIANT, NORM_IGNORECASE, (LPCWSTR)(&rec), -1, (LPCWSTR)(pszExtension), -1); + } + ); + if (it != supportedFilesList.end()) + { + pftr = &(*it); + } + + return (pftr) ? pftr->szFamily : NULL; +} diff --git a/Src/Plugins/General/gen_ml/fileview_format.cpp b/Src/Plugins/General/gen_ml/fileview_format.cpp new file mode 100644 index 00000000..262c454a --- /dev/null +++ b/Src/Plugins/General/gen_ml/fileview_format.cpp @@ -0,0 +1,335 @@ +#include "./fileview.h" +#include "./fileview_internal.h" +#include "./resource.h" +#include + +INT FileView_FormatFileTime(FILETIME *pft, LPWSTR pszDest, INT cchDest) +{ + SYSTEMTIME st; + + if (!pszDest) return 0; + + + pszDest[0] = 0x00; + if (!pft || (0 == pft->dwHighDateTime && 0 == pft->dwLowDateTime)) return 0; + if (FileTimeToSystemTime(pft, &st)) + { + INT len; + len = GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &st, NULL, pszDest, cchDest); + if (0 == len) return 0; + cchDest -= len; + if (cchDest > 0) + { + pszDest += len; + *(pszDest - 1) = L' '; + + INT len2; + len2 = GetTimeFormatW(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &st, NULL, pszDest, cchDest); + if (0 == len2) return 0; + len += len2; + return len - 1; + } + return len; + } + return 0; +} + +INT FileView_FormatType(UINT fileType, LPWSTR pszDest, INT cchDest) +{ + if (!pszDest) return 0; + pszDest[0] = 0x00; + + INT strId; + switch(fileType) + { + case FVFT_AUDIO: strId = IDS_FILETYPE_AUDIO; break; + case FVFT_VIDEO: strId = IDS_FILETYPE_VIDEO; break; + case FVFT_PLAYLIST: strId = IDS_FILETYPE_PLAYLIST; break; + default: strId = IDS_FILETYPE_UNKNOWN; break; + } + + WASABI_API_LNGSTRINGW_BUF(strId, pszDest, cchDest); + return lstrlenW(pszDest); +} + +INT FileView_FormatAttributes(UINT uAttributes, LPWSTR pszDest, INT cchDest) +{ + if (!pszDest) return 0; + pszDest[0] = 0x00; + + wchar_t szAttrib[32] = {0}; + if(!WASABI_API_LNGSTRINGW_BUF(IDS_FILE_ATTRIBUTES, szAttrib, sizeof(szAttrib)/sizeof(szAttrib[0]))) + szAttrib[0] = L'\0'; + + INT len = 0; + if (len < cchDest && (FILE_ATTRIBUTE_READONLY & uAttributes)) { pszDest[len] = szAttrib[0]; len++; } + if (len < cchDest && (FILE_ATTRIBUTE_HIDDEN & uAttributes)) { pszDest[len] = szAttrib[1]; len++; } + if (len < cchDest && (FILE_ATTRIBUTE_SYSTEM & uAttributes)) { pszDest[len] = szAttrib[2]; len++; } + if (len < cchDest && (FILE_ATTRIBUTE_ARCHIVE & uAttributes)) { pszDest[len] = szAttrib[3]; len++; } + if (len < cchDest && (FILE_ATTRIBUTE_COMPRESSED & uAttributes)) { pszDest[len] = szAttrib[4]; len++; } + if (len < cchDest && (FILE_ATTRIBUTE_ENCRYPTED & uAttributes)) { pszDest[len] = szAttrib[5]; len++; } + if (len < cchDest) pszDest[len] = 0x00; + return len; +} + + +INT FileView_FormatYesNo(BOOL bValue, LPWSTR pszDest, INT cchDest) +{ + if (!pszDest) return 0; + pszDest[0] = 0x00; + + WASABI_API_LNGSTRINGW_BUF(((bValue) ? IDS_YES : IDS_NO), pszDest, cchDest); + return lstrlenW(pszDest); +} + +INT FileView_FormatYear(INT nYear, LPWSTR pszDest, INT cchDest) +{ + if (nYear < 1 || S_OK != StringCchPrintfW(pszDest, cchDest, L"%d", nYear)) *pszDest = L'\0'; + return lstrlenW(pszDest); +} +INT FileView_FormatBitrate(INT nBitrate, LPWSTR pszDest, INT cchDest) +{ + if (nBitrate < 1 || S_OK != StringCchPrintfW(pszDest, cchDest, L"%d%s", nBitrate, WASABI_API_LNGSTRINGW(IDS_KBPS))) *pszDest = L'\0'; + return lstrlenW(pszDest); +} + +INT FileView_FormatLength(INT nLength, LPWSTR pszDest, INT cchDest) +{ + if (nLength < 1 || S_OK != StringCchPrintfW(pszDest, cchDest, L"%d:%02d:%02d", + nLength / (60 * 60), (nLength % (60 * 60)) / 60, nLength % 60)) *pszDest = L'\0'; + return lstrlenW(pszDest); +} + +INT FileView_FormatIntSlashInt(INT part1, INT part2, LPWSTR pszDest, INT cchDest) +{ + if (part1 > 0) + { + size_t remaining = cchDest; + HRESULT hr = (part2 > 0) ? StringCchPrintfExW(pszDest, cchDest, NULL, &remaining, STRSAFE_IGNORE_NULLS, L"%d/%d", part1, part2) : + StringCchPrintfExW(pszDest, cchDest, NULL, &remaining, STRSAFE_IGNORE_NULLS, L"%d", part1); + if (S_OK != hr) *pszDest = L'\0'; + if (remaining != (size_t)cchDest) remaining; + return (S_OK == hr) ? (cchDest - (INT)remaining) : 0; + } + else + { + *pszDest = L'\0'; + return 0; + } +} + +static void FileView_FormatPlaylistTip(FILERECORD *pfr, LPWSTR pszText, size_t cchTextMax, LPCWSTR pszSeparator) +{ + INT nLines = 0; + if (!pfr->pMeta || METATYPE_PLAYLIST != pfr->pMeta->type) return; + + PLAYLISTMETA *plm = &pfr->pMeta->playlist; + + if (plm->pszTitle && *plm->pszTitle && cchTextMax > 2) + { + if (S_OK != StringCchPrintfExW(pszText, cchTextMax, &pszText, &cchTextMax, STRSAFE_IGNORE_NULLS, + L"%s: %s", WASABI_API_LNGSTRINGW(IDS_FILEVIEW_COL_TITLE), plm->pszTitle)) return; + nLines++; + } + + LPCWSTR pszExt = FileView_GetTypeFamily(pfr->Info.cFileName + pfr->extOffset); + if (pszExt && *pszExt) + { + if (S_OK != StringCchPrintfExW(pszText, cchTextMax, &pszText, &cchTextMax, STRSAFE_IGNORE_NULLS, + L"%s%s: %s", ((!nLines) ? L"" : pszSeparator), WASABI_API_LNGSTRINGW(IDS_FILEVIEW_COL_TYPE), pszExt)) return; + nLines++; + } + + + if (plm->nLength > 0 && cchTextMax > 2) + { + if (S_OK != StringCchPrintfExW(pszText, cchTextMax, &pszText, &cchTextMax, STRSAFE_IGNORE_NULLS, + L"%s%s: ", ((!nLines) ? L"" : pszSeparator), WASABI_API_LNGSTRINGW(IDS_FILEVIEW_COL_TOTALLENGTH))) return; + size_t len = FileView_FormatLength(plm->nLength, pszText, (INT)cchTextMax); + pszText += len; + cchTextMax -= (len + 1); + nLines++; + } + + if (cchTextMax > 2) + { + if (S_OK != StringCchPrintfExW(pszText, cchTextMax, &pszText, &cchTextMax, STRSAFE_IGNORE_NULLS, + L"%s%s: %d", ((!nLines) ? L"" : pszSeparator), WASABI_API_LNGSTRINGW(IDS_FILEVIEW_COL_ENTRYCOUNT), plm->nCount)) return; + nLines++; + } + + + for (UINT i = 0; i < sizeof(plm->szEntries)/sizeof(plm->szEntries[0]) && i < plm->nCount; i++) + { + if (S_OK != StringCchPrintfExW(pszText, cchTextMax, &pszText, &cchTextMax, STRSAFE_IGNORE_NULLS, + L"%s%02d. %s", ((!nLines) ? L"" : pszSeparator), (i + 1), plm->szEntries[i].pszTitle)) return; + nLines++; + } + if (plm->nCount > sizeof(plm->szEntries)/sizeof(plm->szEntries[0])) + { + if (S_OK != StringCchPrintfExW(pszText, cchTextMax, &pszText, &cchTextMax, STRSAFE_IGNORE_NULLS, + L"%s>>>", ((!nLines) ? L"" : pszSeparator))) return; + nLines++; + } +} + +static void FileView_FormatAudioTip(FILERECORD *pfr, LPWSTR pszText, size_t cchTextMax, LPCWSTR pszSeparator) +{ + size_t len; + INT nLines = 0; + if (!pfr->pMeta || METATYPE_AUDIO != pfr->pMeta->type) return; + + AUDIOMETA *pam = &pfr->pMeta->audio; + + if (pam->pszArtist && *pam->pszArtist && cchTextMax > 2) + { + if (S_OK != StringCchPrintfExW(pszText, cchTextMax, &pszText, &cchTextMax, STRSAFE_IGNORE_NULLS, + L"%s: %s", WASABI_API_LNGSTRINGW(IDS_FILEVIEW_COL_ARTIST), pam->pszArtist)) return; + nLines++; + } + + if (pam->pszAlbum && *pam->pszAlbum && cchTextMax > 2) + { + if (S_OK != StringCchPrintfExW(pszText, cchTextMax, &pszText, &cchTextMax, STRSAFE_IGNORE_NULLS, + L"%s%s: %s", ((!nLines) ? L"" : pszSeparator), WASABI_API_LNGSTRINGW(IDS_FILEVIEW_COL_ALBUM), pam->pszAlbum)) return; + nLines++; + } + + if (pam->pszTitle && *pam->pszTitle && cchTextMax > 2) + { + if (S_OK != StringCchPrintfExW(pszText, cchTextMax, &pszText, &cchTextMax, STRSAFE_IGNORE_NULLS, + L"%s%s: %s", ((!nLines) ? L"" : pszSeparator), WASABI_API_LNGSTRINGW(IDS_FILEVIEW_COL_TITLE), pam->pszTitle)) return; + nLines++; + } + + if (pam->nYear > 0 && cchTextMax > 2) + { + if (S_OK != StringCchPrintfExW(pszText, cchTextMax, &pszText, &cchTextMax, STRSAFE_IGNORE_NULLS, + L"%s%s: %d", ((!nLines) ? L"" : pszSeparator), WASABI_API_LNGSTRINGW(IDS_FILEVIEW_COL_YEAR), pam->nYear)) return; + nLines++; + } + + if (pam->pszGenre && *pam->pszGenre && cchTextMax > 2) + { + if (S_OK != StringCchPrintfExW(pszText, cchTextMax, &pszText, &cchTextMax, STRSAFE_IGNORE_NULLS, + L"%s%s: %s", ((!nLines) ? L"" : pszSeparator), WASABI_API_LNGSTRINGW(IDS_FILEVIEW_COL_GENRE), pam->pszGenre)) return; + nLines++; + } + + if (pam->nLength > 0 && cchTextMax > 2) + { + if (S_OK != StringCchPrintfExW(pszText, cchTextMax, &pszText, &cchTextMax, STRSAFE_IGNORE_NULLS, + L"%s%s: ", ((!nLines) ? L"" : pszSeparator), WASABI_API_LNGSTRINGW(IDS_FILEVIEW_COL_LENGTH))) return; + len = FileView_FormatLength(pam->nLength, pszText, (INT)cchTextMax); + pszText += len; + cchTextMax -= (len + 1); + nLines++; + } + + if (pam->nTrackNum > 0 && cchTextMax > 2) + { + if (S_OK != StringCchPrintfExW(pszText, cchTextMax, &pszText, &cchTextMax, STRSAFE_IGNORE_NULLS, + L"%s%s: ", ((!nLines) ? L"" : pszSeparator), WASABI_API_LNGSTRINGW(IDS_FILEVIEW_COL_TRACK))) return; + len = FileView_FormatIntSlashInt(pam->nTrackNum, pam->nTrackCount, pszText, (INT)cchTextMax); + pszText += len; + cchTextMax -= (len + 1); + nLines++; + } + + if (pam->nDiscNum > 0 && cchTextMax > 2) + { + if (S_OK != StringCchPrintfExW(pszText, cchTextMax, &pszText, &cchTextMax, STRSAFE_IGNORE_NULLS, + L"%s%s: ", ((!nLines) ? L"" : pszSeparator), WASABI_API_LNGSTRINGW(IDS_FILEVIEW_COL_DISC))) return; + len = FileView_FormatIntSlashInt(pam->nDiscNum, pam->nDiscCount, pszText, (INT)cchTextMax); + pszText += len; + cchTextMax -= (len + 1); + nLines++; + } + + if (pam->nBitrate > 0 && cchTextMax > 2) + { + if (S_OK != StringCchPrintfExW(pszText, cchTextMax, &pszText, &cchTextMax, STRSAFE_IGNORE_NULLS, + L"%s%s: ", ((!nLines) ? L"" : pszSeparator), WASABI_API_LNGSTRINGW(IDS_FILEVIEW_COL_BITRATE))) return; + len = FileView_FormatBitrate(pam->nBitrate, pszText, (INT)cchTextMax); + pszText += len; + cchTextMax -= (len + 1); + nLines++; + } + + LPCWSTR pszExt = FileView_GetTypeFamily(pfr->Info.cFileName + pfr->extOffset); + if (pszExt && *pszExt) + { + if (S_OK != StringCchPrintfExW(pszText, cchTextMax, &pszText, &cchTextMax, STRSAFE_IGNORE_NULLS, + L"%s%s: %s", ((!nLines) ? L"" : pszSeparator), WASABI_API_LNGSTRINGW(IDS_FILEVIEW_COL_TYPE), pszExt)) return; + nLines++; + } + + if (pam->nSource != METADATA_SOURCE_UNKNOWN && cchTextMax > 2) + { + if (S_OK != StringCchPrintfExW(pszText, cchTextMax, &pszText, &cchTextMax, STRSAFE_IGNORE_NULLS, + L"%s%s: ", ((!nLines) ? L"" : pszSeparator), WASABI_API_LNGSTRINGW(IDS_FILEVIEW_COL_INMLDB))) return; + + len = FileView_FormatYesNo(METADATA_SOURCE_MLDB == pam->nSource, pszText, (INT)cchTextMax); + pszText += len; + cchTextMax -= (len + 1); + nLines++; + } + + if (cchTextMax > 2) + { + if (S_OK != StringCchPrintfExW(pszText, cchTextMax, &pszText, &cchTextMax, STRSAFE_IGNORE_NULLS, + L"%s%s: ", ((!nLines) ? L"" : pszSeparator), WASABI_API_LNGSTRINGW(IDS_FILEVIEW_COL_SIZE))) return; + + WASABI_API_LNG->FormattedSizeString(pszText, (INT)cchTextMax, (((__int64)pfr->Info.nFileSizeHigh << 32) | pfr->Info.nFileSizeLow)); + len = lstrlenW(pszText); + pszText += len; + cchTextMax -= (len + 1); + nLines++; + } +} + +void FileView_FormatDefaultTip(FILERECORD *pfr, LPWSTR pszText, size_t cchTextMax, LPCWSTR pszSeparator) +{ + size_t len; + if ((len = lstrlenW(pszText)) > 0) { cchTextMax -= len; pszText += len; } + if (S_OK != StringCchPrintfExW(pszText, cchTextMax, &pszText, &cchTextMax, STRSAFE_IGNORE_NULLS, + L"%s: ", WASABI_API_LNGSTRINGW(IDS_FILEVIEW_COL_TYPE))) return; + len = FileView_FormatType(pfr->fileType, pszText, (INT)cchTextMax); + pszText += len; + cchTextMax -= (len + 1); + + if (S_OK != StringCchPrintfExW(pszText, cchTextMax, &pszText, &cchTextMax, STRSAFE_IGNORE_NULLS, + L"%s%s: ", pszSeparator, WASABI_API_LNGSTRINGW(IDS_FILEVIEW_COL_MODIFIED))) return; + len = FileView_FormatFileTime(&pfr->Info.ftLastWriteTime, pszText, (INT)cchTextMax); + pszText += len; + cchTextMax -= (len + 1); + + if (S_OK != StringCchPrintfExW(pszText, cchTextMax, &pszText, &cchTextMax, STRSAFE_IGNORE_NULLS, + L"%s%s: ", pszSeparator, WASABI_API_LNGSTRINGW(IDS_FILEVIEW_COL_SIZE))) return; + WASABI_API_LNG->FormattedSizeString(pszText, (INT)cchTextMax, (((__int64)pfr->Info.nFileSizeHigh << 32) | pfr->Info.nFileSizeLow)); + len = lstrlenW(pszText); + pszText += len; + cchTextMax -= (len + 1); +} + +void FileView_FormatFileInfo(FILERECORD *pfr, LPWSTR pszText, size_t cchTextMax, UINT uMode) +{ + LPCWSTR pszSeparator; + switch(uMode) + { + case FIF_STATUS: pszSeparator = L" "; break; + case FIF_TOOLTIP: + default: pszSeparator = L"\r\n"; break; + } + + switch(pfr->fileType) + { + case FVFT_AUDIO: + if (pfr->pMeta) { FileView_FormatAudioTip(pfr, pszText, cchTextMax, pszSeparator); return; } + break; + case FVFT_PLAYLIST: + if (pfr->pMeta) { FileView_FormatPlaylistTip(pfr, pszText, cchTextMax, pszSeparator); return; } + break; + } + FileView_FormatDefaultTip(pfr, pszText, cchTextMax, pszSeparator); +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/fileview_internal.h b/Src/Plugins/General/gen_ml/fileview_internal.h new file mode 100644 index 00000000..5526bf32 --- /dev/null +++ b/Src/Plugins/General/gen_ml/fileview_internal.h @@ -0,0 +1,207 @@ +#ifndef NULLOSFT_MEDIALIBRARY_FILEVIEW_CONTROL_INTERNAL_HEADER +#define NULLOSFT_MEDIALIBRARY_FILEVIEW_CONTROL_INTERNAL_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include +#include "../winamp/gen.h" +#include "api__gen_ml.h" +#include "./ml_ipc_0313.h" +#include "../nu/trace.h" +#include "./ml_imagelist.h" +#include "./ml_imagefilter.h" +#include "./skinning.h" + +#ifndef LONGX86 +#ifdef _WIN64 + #define LONGX86 LONG_PTR +#else /*_WIN64*/ + #define LONGX86 LONG +#endif /*_WIN64*/ +#endif // LONGX86 + +#define METADATA_SOURCE_UNKNOWN 0 +#define METADATA_SOURCE_FILEINFO 1 +#define METADATA_SOURCE_MLDB 2 + + +#define FVM_GETIDEALHEIGHT (MLFVM_FIRST + 101) // internal use + +#define CSTR_INVARIANT MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT) + +#define METATYPE_AUDIO 0 +#define METATYPE_VIDEO 1 +#define METATYPE_PLAYLIST 2 + +#define MF_NAME 0 +#define MF_SIZE 1 +#define MF_TYPE 2 +#define MF_MODIFIED 3 +#define MF_CREATED 4 +#define MF_EXTENSION 5 +#define MF_ATTRIBUTES 6 +#define MF_ARTIST 7 +#define MF_ALBUM 8 +#define MF_TITLE 9 +#define MF_INMLDB 10 +#define MF_GENRE 11 +#define MF_COMMENT 12 +#define MF_LENGTH 13 +#define MF_BITRATE 14 +#define MF_TRACKNUM 15 +#define MF_TRACKCOUNT 16 +#define MF_DISCNUM 17 +#define MF_DISCCOUNT 18 +#define MF_YEAR 19 +#define MF_PUBLISHER 20 +#define MF_COMPOSER 21 +#define MF_ALBUMARTIST 22 +#define MF_SOURCE 23 + +typedef struct __AUDIOMETA +{ + UINT nSource; + LPWSTR pszArtist; + LPWSTR pszTitle; + LPWSTR pszAlbum; + LPWSTR pszGenre; + LPWSTR pszComment; + INT nLength; + INT nBitrate; + INT nTrackNum; + INT nTrackCount; + INT nDiscNum; + INT nDiscCount; + INT nYear; + LPWSTR pszAlbumArtist; + LPWSTR pszPublisher; + LPWSTR pszComposer; +} AUDIOMETA; + +typedef __AUDIOMETA VIDEOMETA; + +#define MAX_PLAYLIST_ENTRIES 20 +typedef struct __PLENTRY +{ + LPWSTR pszTitle; + INT nLength; +} PLENTRY; +typedef struct __PLAYLISTMETA +{ + UINT nCount; + LPWSTR pszTitle; + INT nLength; + PLENTRY szEntries[MAX_PLAYLIST_ENTRIES]; +} PLAYLISTMETA; + + +typedef struct _FILEMETARECORD +{ + DWORD type; + union + { + AUDIOMETA audio; + VIDEOMETA video; + PLAYLISTMETA playlist; + }; +}FILEMETARECORD; + +typedef struct _FILERECORD +{ + WIN32_FIND_DATAW Info; + INT fileType; + size_t extOffset; + FILEMETARECORD *pMeta; +} FILERECORD; + +typedef struct _FILEDATA +{ + size_t count; + size_t allocated; + FILERECORD *pRec; + size_t *pSort; + ULONGLONG folderSize; + WCHAR szPath[MAX_PATH*2]; +} FILEDATA; + +#ifdef __cplusplus +extern "C" { +#endif + +extern winampGeneralPurposePlugin plugin; + +#ifdef __cplusplus +} +#endif +extern HWND g_hwnd; +extern HMLIMGFLTRMNGR hmlifMngr; // default gen_ml fitler manager + + + + +// toolbar +HWND FileViewToolbar_Create(HWND hwndParent); + +// filesystem +size_t FileView_ReadFileData(FILEDATA *pfd, LPCWSTR pszPath, UINT fStyle, FILESYSTEMINFO *pfsi); +LPCWSTR FileView_GetTypeFamily(LPCWSTR pszExtension); + +// metadata +typedef void (CALLBACK *DISCOVERCALLBACK)(LPCWSTR /*pszFileName*/, ULONG_PTR /*param*/); + +void FileViewMeta_InitializeStorage(HWND hView); +void FileViewMeta_ReleaseStorage(HWND hView); +FILEMETARECORD *FileViewMeta_GetFromCache(LPCWSTR pszPath, FILERECORD *pfr); +BOOL FileViewMeta_Discover(LPCWSTR pszPath, FILERECORD *pfr, DISCOVERCALLBACK fnCallback, ULONG_PTR param, INT queueMax); +void FileViewMeta_TruncateQueue(size_t max); +BOOL FileViewMeta_GetString(FILEMETARECORD *pMeta, UINT uMetaField, LPCWSTR *ppszOut); +BOOL FileViewMeta_GetInt(FILEMETARECORD *pMeta, UINT uMetaField, INT *pOut); + +// formatting +INT FileView_FormatFileTime(FILETIME *pft, LPWSTR pszDest, INT cchDest); +INT FileView_FormatType(UINT fileType, LPWSTR pszDest, INT cchDest); +INT FileView_FormatAttributes(UINT uAttributes, LPWSTR pszDest, INT cchDest); +INT FileView_FormatYesNo(BOOL bValue, LPWSTR pszDest, INT cchDest); +INT FileView_FormatYear(INT nYear, LPWSTR pszDest, INT cchDest); +INT FileView_FormatBitrate(INT nBitrate, LPWSTR pszDest, INT cchDest); +INT FileView_FormatLength(INT nLength, LPWSTR pszDest, INT cchDest); +INT FileView_FormatIntSlashInt(INT part1, INT part2, LPWSTR pszDest, INT cchDest); + +#define FIF_TOOLTIP 0 +#define FIF_STATUS 1 +void FileView_FormatFileInfo(FILERECORD *pfr, LPWSTR pszText, size_t cchTextMax, UINT mode); +void FileView_FormatAudioTip(FILERECORD *pfr, LPWSTR pszText, size_t cchTextMax, LPCWSTR pszSeparator); +void FileView_FormatDefaultTip(FILERECORD *pfr, LPWSTR pszText, size_t cchTextMax, LPCWSTR pszSeparator); + +// sorting +void FileView_SortByColumn(FILEDATA *pFileData, UINT uColumn); +void FileView_SortByColumnEx(FILEDATA *pFileData, UINT uColumn, size_t *pOrder, size_t count); + +// menu +HMENU FileViewMenu_Initialize(); +HMENU FileViewMenu_GetSubMenu(HWND hView, HMENU hViewMenu, UINT uMenuType); +UINT FileViewMenu_GetMenuType(HWND hView, HMENU hViewMenu, HMENU hMenu); +// view +void FileView_DisplayPopupMenu(HWND hdlg, UINT uMenu, UINT uFlags, POINT pt); + +// registered columns + +typedef struct _FILEVIEWCOLUMN +{ + UINT id; + LPWSTR pszText; + INT width; + UINT format; + INT order; + INT widthMin; + INT widthMax; +} FILEVIEWCOLUMN; + +extern const FILEVIEWCOLUMN szRegisteredColumns[]; +extern const INT RegisteredColumnsCount; + + + +#endif // NULLOSFT_MEDIALIBRARY_FILEVIEW_CONTROL_INTERNAL_HEADER \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/fileview_menu.cpp b/Src/Plugins/General/gen_ml/fileview_menu.cpp new file mode 100644 index 00000000..624cf232 --- /dev/null +++ b/Src/Plugins/General/gen_ml/fileview_menu.cpp @@ -0,0 +1,331 @@ +#include "./fileview.h" +#include "./fileview_internal.h" +#include "./resource.h" +#include "../nu/menushortcuts.h" +#include +#include + +#define SUBMENU_VIEWMODE 0 +#define SUBMENU_OPTIONS 1 +#define SUBMENU_ARRANGEBY 2 +#define SUBMENU_COLUMNS 3 +#define SUBMENU_FILELIST 4 +#define SUBMENU_COMMON 5 +#define SUBMENU_FILELIST_PLAY 0 +#define SUBMENU_FILELIST_VIEWCONTEXT 1 +#define SUBMENU_FILELIST_OPCONTEXT 2 + + +static INT MenuCopyEx(HMENU hDest, INT iDstStart, HMENU hSource, INT iSrcStart, INT iSrcCount) +{ + if (!hDest || !hSource) return 0; + wchar_t szText[1024] = {0}; + INT pos; + + MENUITEMINFOW mii = {sizeof(MENUITEMINFOW), }; + mii.fMask = MIIM_BITMAP | MIIM_CHECKMARKS | MIIM_DATA | MIIM_FTYPE | MIIM_ID | MIIM_STATE | MIIM_STRING | MIIM_SUBMENU; + mii.dwTypeData = szText; + + if (iDstStart < 0) iDstStart = 0; + if (iSrcStart < 0) iSrcStart = 0; + + pos = iDstStart; + + if ( 0 != iSrcCount) + { + INT c = GetMenuItemCount(hSource); + if (iSrcStart > c) return 0; + if (iSrcCount < 0) iSrcCount = c - iSrcStart; + else if (iSrcCount < (c - iSrcStart)) c = iSrcCount + iSrcStart; + + for (int i = iSrcStart; i < c; i++) + { + mii.cch = sizeof(szText)/sizeof(szText[0]); + if (GetMenuItemInfoW(hSource, i, TRUE, &mii)) + { + if(InsertMenuItemW(hDest, pos, TRUE, &mii)) + { + pos++; + } + } + } + } + else + { + mii.cch = sizeof(szText)/sizeof(szText[0]); + if (GetMenuItemInfoW(hSource, iSrcStart, FALSE, &mii)) + { + if (InsertMenuItemW(hDest, pos, TRUE, &mii)) + { + pos++; + } + } + } + + return pos - iDstStart; +} + +static INT MenuCopy(HMENU hDest, INT iDstStart, HMENU hSource) +{ + return MenuCopyEx(hDest, iDstStart, hSource, 0, -1); +} + +static BOOL MenuInsertSeparator(HMENU hMenu, INT iPos) +{ + if (!hMenu) return FALSE; + MENUITEMINFOW mii = {sizeof(MENUITEMINFOW), }; + mii.fMask = MIIM_FTYPE; + mii.fType = MFT_SEPARATOR; + return InsertMenuItemW(hMenu, iPos, TRUE, &mii); +} + +static BOOL MenuInsertCommonItem(HMENU hMenu, INT iPos, HMENU hViewMenu, UINT itemId) +{ + if (!hMenu) return FALSE; + HMENU hCommon = GetSubMenu(hViewMenu, SUBMENU_COMMON); + if (!hCommon) return FALSE; + return (MenuCopyEx(hMenu, iPos, hCommon, itemId, 0) > 0); +} + +static HMENU FileViewMenu_GetViewModeMenu(HWND hView, HMENU hViewMenu) +{ + HMENU hMenu = GetSubMenu(hViewMenu, SUBMENU_VIEWMODE); + if (!hMenu) return NULL; + UINT style = (FVS_VIEWMASK & FileView_GetStyle(hView)); + MENUITEMINFOW mii = {sizeof(MENUITEMINFOW), }; + mii.fMask = MIIM_STATE | MIIM_FTYPE; + if (GetMenuItemInfoW(hMenu, ID_FILEVIEW_SETMODE_ICON, FALSE, &mii)) + { + mii.fType |= MFT_RADIOCHECK; + mii.fState = (FVS_ICONVIEW == style) ? MFS_CHECKED : MFS_UNCHECKED; + SetMenuItemInfoW(hMenu, ID_FILEVIEW_SETMODE_ICON, FALSE, &mii); + } + if (GetMenuItemInfoW(hMenu, ID_FILEVIEW_SETMODE_LIST, FALSE, &mii)) + { + mii.fType |= MFT_RADIOCHECK; + mii.fState = (FVS_LISTVIEW == style) ? MFS_CHECKED : MFS_UNCHECKED; + SetMenuItemInfoW(hMenu, ID_FILEVIEW_SETMODE_LIST, FALSE, &mii); + } + if (GetMenuItemInfoW(hMenu, ID_FILEVIEW_SETMODE_DETAIL, FALSE, &mii)) + { + mii.fType |= MFT_RADIOCHECK; + mii.fState = (FVS_DETAILVIEW == style) ? MFS_CHECKED : MFS_UNCHECKED; + SetMenuItemInfoW(hMenu, ID_FILEVIEW_SETMODE_DETAIL, FALSE, &mii); + } + return hMenu; +} + +static HMENU FileViewMenu_GetOptionsMenu(HWND hView, HMENU hViewMenu) +{ + HMENU hMenu = GetSubMenu(hViewMenu, SUBMENU_OPTIONS); + if (!hMenu) return NULL; + UINT style = FileView_GetStyle(hView); + CheckMenuItem(hMenu, IDM_FILEVIEW_HIDEEXTENSION, MF_BYCOMMAND | ((FVS_HIDEEXTENSION & style) ? MF_CHECKED : MF_UNCHECKED)); + CheckMenuItem(hMenu, IDM_FILEVIEW_IGNOREHIDDEN, MF_BYCOMMAND | ((FVS_IGNOREHIDDEN & style) ? MF_CHECKED : MF_UNCHECKED)); + CheckMenuItem(hMenu, IDM_FILEVIEW_SHOWAUDIO, MF_BYCOMMAND | ((FVS_SHOWAUDIO & style) ? MF_CHECKED : MF_UNCHECKED)); + CheckMenuItem(hMenu, IDM_FILEVIEW_SHOWVIDEO, MF_BYCOMMAND | ((FVS_SHOWVIDEO & style) ? MF_CHECKED : MF_UNCHECKED)); + CheckMenuItem(hMenu, IDM_FILEVIEW_SHOWPLAYLIST, MF_BYCOMMAND | ((FVS_SHOWPLAYLIST & style) ? MF_CHECKED : MF_UNCHECKED)); + CheckMenuItem(hMenu, IDM_FILEVIEW_SHOWUNKNOWN, MF_BYCOMMAND | ((FVS_SHOWUNKNOWN & style) ? MF_CHECKED : MF_UNCHECKED)); + return hMenu; +} + +static HMENU FileViewMenu_GetArrangeByMenu(HWND hView, HMENU hViewMenu) +{ + HMENU hMenu = GetSubMenu(hViewMenu, SUBMENU_ARRANGEBY); + if (!hMenu) return NULL; + + UINT sort = FileView_GetSort(hView); + UINT sortCol = LOWORD(sort); + UINT szColumn[512] = {0}; + WCHAR szText[256] = {0}; + INT count; + + MENUITEMINFOW mii = {sizeof(MENUITEMINFOW), }; + mii.fMask = MIIM_ID; + count = GetMenuItemCount(hMenu); + + while(count--) + { + if (GetMenuItemInfoW(hMenu, count, TRUE, &mii) && + mii.wID >= IDM_COLUMN_ARRANGE_MIN && mii.wID <= IDM_COLUMN_ARRANGE_MAX) + { + RemoveMenu(hMenu, count, MF_BYPOSITION); + } + } + + mii.fMask = MIIM_STRING | MIIM_STATE | MIIM_ID | MIIM_FTYPE; + mii.fType = MFT_RADIOCHECK; + + count = FileView_GetColumnArray(hView, 512, &szColumn); + + for (int i = 0; i < count; i++) + { + FileView_GetColumnName(hView, szColumn[i], sizeof(szText)/sizeof(szText[0]), szText); + mii.dwTypeData = szText; + mii.wID = IDM_COLUMN_ARRANGE_MIN + szColumn[i]; + mii.fState = (sortCol == szColumn[i]) ? MFS_CHECKED : 0; + InsertMenuItemW(hMenu, i, TRUE, &mii); + } + + CheckMenuItem(hMenu, IDM_FILEVIEW_SORTASCENDING, MF_BYCOMMAND | (HIWORD(sort) ? MF_CHECKED : MF_UNCHECKED)); + + return hMenu; +} + +static HMENU FileViewMenu_GetColumnsMenu(HWND hView, HMENU hViewMenu) +{ + HMENU hMenu = GetSubMenu(hViewMenu, SUBMENU_COLUMNS); + if (!hMenu) return NULL; + + UINT szColumn[512] = {0}; + INT count; + + MENUITEMINFOW mii = {sizeof(MENUITEMINFOW), }; + mii.fMask = MIIM_ID; + count = GetMenuItemCount(hMenu); + + while(count--) + { + if (GetMenuItemInfoW(hMenu, count, TRUE, &mii) && + mii.wID >= IDM_COLUMN_SHOW_MIN && mii.wID <= IDM_COLUMN_SHOW_MAX) + { + RemoveMenu(hMenu, count, MF_BYPOSITION); + } + } + + mii.fMask = MIIM_STRING | MIIM_STATE | MIIM_ID; + + count = FileView_GetColumnArray(hView, 512, &szColumn); + + for (INT i = 0; i < RegisteredColumnsCount; i++) + { + mii.dwTypeData = IS_INTRESOURCE(szRegisteredColumns[i].pszText) ? + WASABI_API_LNGSTRINGW((UINT)(UINT_PTR)szRegisteredColumns[i].pszText) : szRegisteredColumns[i].pszText; + mii.wID = IDM_COLUMN_SHOW_MIN + szRegisteredColumns[i].id; + + INT index = 0; + while(index < count && szRegisteredColumns[i].id != szColumn[index]) index++; + mii.fState = (index < count) ? MFS_CHECKED : 0; + if(FVCOLUMN_NAME == szRegisteredColumns[i].id) mii.fState |= MFS_DISABLED; + InsertMenuItemW(hMenu, i, TRUE, &mii); + } + return hMenu; +} + +static HMENU FileViewMenu_GetPlayMenu(HWND hView, HMENU hViewMenu) +{ + HMENU hMenu = GetSubMenu(hViewMenu, SUBMENU_FILELIST); + if (hMenu) hMenu = GetSubMenu(hMenu, SUBMENU_FILELIST_PLAY); + if (!hMenu) return NULL; + + BOOL bEnablePlay = (FileView_GetSelectedCount(hView) && + (-1 != FileView_GetNextFile(hView, -1, FVNF_PLAYABLE | FVNF_SELECTED))); + + MENUITEMINFOW mii = {sizeof(MENUITEMINFOW), }; + mii.fMask = MIIM_ID | MIIM_STATE; + mii.fState = (bEnablePlay) ? MFS_ENABLED : MFS_DISABLED; + + mii.wID = FileView_GetActionCommand(hView, FVA_PLAY); + SetMenuItemInfoW(hMenu, 0, TRUE, &mii); + mii.wID = mii.wID = FileView_GetActionCommand(hView, FVA_ENQUEUE); + SetMenuItemInfoW(hMenu, 1, TRUE, &mii); + + return hMenu; +} + +static void FileViewMenu_PopulateFileOpMenu(HMENU hMenu, HWND hView, HMENU hViewMenu) +{ + INT c, pos = 0; + + c = MenuCopy(hMenu, pos, FileViewMenu_GetPlayMenu(hView, hViewMenu)); + pos += c; + if (MenuInsertCommonItem(hMenu, pos, hViewMenu, ID_FILEVIEW_SELECT_ALL) && c) + MenuInsertSeparator(hMenu, pos); +} + +static HMENU FileViewMenu_GetFileViewMenu(HWND hView, HMENU hViewMenu) +{ + HMENU hMenu = GetSubMenu(hViewMenu, SUBMENU_FILELIST); + if (hMenu) hMenu = GetSubMenu(hMenu, SUBMENU_FILELIST_VIEWCONTEXT); + if (!hMenu) return NULL; + + INT c, pos= 0; + + c = GetMenuItemCount(hMenu); + while(c--) RemoveMenu(hMenu, c, MF_BYPOSITION); + + c = MenuCopyEx(hMenu, pos, hViewMenu, SUBMENU_VIEWMODE, 1); + pos += c; + if (c && MenuInsertSeparator(hMenu, pos)) pos++; + pos += MenuCopyEx(hMenu, pos, hViewMenu, SUBMENU_ARRANGEBY, 1); + if (MenuInsertCommonItem(hMenu, pos, hViewMenu, ID_FILEVIEW_REFRESH)) pos++; + if (MenuInsertSeparator(hMenu, pos)) pos++; + MenuCopyEx(hMenu, pos, hViewMenu, SUBMENU_OPTIONS, 1); + return hMenu; +} + +static HMENU FileViewMenu_GetFileOpMenu(HWND hView, HMENU hViewMenu) +{ + HMENU hMenu = GetSubMenu(hViewMenu, SUBMENU_FILELIST); + if (hMenu) hMenu = GetSubMenu(hMenu, SUBMENU_FILELIST_OPCONTEXT); + if (!hMenu) return NULL; + + INT c, pos = 0; + + c = GetMenuItemCount(hMenu); + while(c--) RemoveMenu(hMenu, c, MF_BYPOSITION); + + c = MenuCopy(hMenu, pos, FileViewMenu_GetPlayMenu(hView, hViewMenu)); + pos += c; + if (MenuInsertCommonItem(hMenu, pos, hViewMenu, ID_FILEVIEW_SELECT_ALL) && c) + MenuInsertSeparator(hMenu, pos); + return hMenu; +} + +static HMENU FileViewMenu_GetFileMenu(HWND hView, HMENU hViewMenu) +{ + FVHITTEST ht; + GetCursorPos(&ht.pt); + MapWindowPoints(HWND_DESKTOP, hView, &ht.pt, 1); + + if (-1 != FileView_HitTest(hView, &ht) && (FVHT_ONFILE & ht.uFlags)) + return FileViewMenu_GetFileOpMenu(hView, hViewMenu); + else + return FileViewMenu_GetFileViewMenu(hView, hViewMenu);; +} + +HMENU FileViewMenu_Initialize() +{ + return WASABI_API_LOADMENUW(IDR_MENU_FILEVIEW); +} + +HMENU FileViewMenu_GetSubMenu(HWND hView, HMENU hViewMenu, UINT uMenuType) +{ + HMENU hMenu = NULL; + switch(uMenuType) + { + case FVMENU_OPTIONS: hMenu = FileViewMenu_GetOptionsMenu(hView, hViewMenu); break; + case FVMENU_ARRANGEBY: hMenu = FileViewMenu_GetArrangeByMenu(hView, hViewMenu); break; + case FVMENU_COLUMNS: hMenu = FileViewMenu_GetColumnsMenu(hView, hViewMenu); break; + case FVMENU_PLAY: hMenu = FileViewMenu_GetPlayMenu(hView, hViewMenu); break; + case FVMENU_FILECONTEXT: hMenu = FileViewMenu_GetFileMenu(hView, hViewMenu); break; + case FVMENU_FILEVIEWCONTEXT: hMenu = FileViewMenu_GetFileViewMenu(hView, hViewMenu); break; + case FVMENU_FILEOPCONTEXT: hMenu = FileViewMenu_GetFileOpMenu(hView, hViewMenu); break; + case FVMENU_VIEWMODE: hMenu = FileViewMenu_GetViewModeMenu(hView, hViewMenu); break; + } + return hMenu; +} + +UINT FileViewMenu_GetMenuType(HWND hView, HMENU hViewMenu, HMENU hMenu) +{ + UINT type = ((UINT)-1); + if (hMenu == GetSubMenu(hViewMenu, SUBMENU_VIEWMODE)) type = FVMENU_VIEWMODE; + else if (hMenu == GetSubMenu(hViewMenu, SUBMENU_OPTIONS)) type = FVMENU_OPTIONS; + else if (hMenu == GetSubMenu(hViewMenu, SUBMENU_ARRANGEBY)) type = FVMENU_ARRANGEBY; + else if (hMenu == GetSubMenu(hViewMenu, SUBMENU_COLUMNS)) type = FVMENU_COLUMNS; + else if (hMenu == GetSubMenu(GetSubMenu(hViewMenu, SUBMENU_FILELIST), SUBMENU_FILELIST_PLAY)) type = FVMENU_PLAY; + else if (hMenu == GetSubMenu(GetSubMenu(hViewMenu, SUBMENU_FILELIST), SUBMENU_FILELIST_VIEWCONTEXT)) type = FVMENU_FILEVIEWCONTEXT; + else if (hMenu == GetSubMenu(GetSubMenu(hViewMenu, SUBMENU_FILELIST), SUBMENU_FILELIST_OPCONTEXT)) type = FVMENU_FILEOPCONTEXT; + + return type; +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/fileview_metadata.cpp b/Src/Plugins/General/gen_ml/fileview_metadata.cpp new file mode 100644 index 00000000..be3f9acc --- /dev/null +++ b/Src/Plugins/General/gen_ml/fileview_metadata.cpp @@ -0,0 +1,789 @@ +#include "main.h" +#include "./fileview.h" +#include "./fileview_internal.h" +#include +#include +#include "../nu/threadname.h" +#include + +#include "../playlist/api_playlistmanager.h" +#include "../playlist/ifc_playlistloadercallback.h" +#include "../Agave/Metadata/api_metadata.h" + +#include +#include +#include + +typedef struct _METASEARCHKEY +{ + INT pathId; + LPCWSTR pszFileName; +} METASEARCHKEY; + +typedef struct _PATHID +{ + INT id; + LPWSTR pszPath; +} PATHID; + +typedef struct _METAINFO +{ + INT pathId; + LPWSTR pszFileName; + FILETIME lastWriteTime; + DWORD fileSizeLow; + DWORD fileSizeHigh; + BOOL bBusy; + FILEMETARECORD *pFileMeta; +} METAINFO; + +typedef std::vector PATHLIST; +typedef std::vector METARECORS; + +typedef struct _METADB +{ + UINT ref; + PATHLIST pPath; + METARECORS pRec; + size_t lastPathIndex; + INT maxPathId; + HANDLE hDiscoverThread; + HANDLE hDiscoverWake; + HANDLE hDiscoverKill; +} METADB; + +#define METATHREAD_KILL 0 +#define METATHREAD_WAKE 1 + +typedef void (CALLBACK *DISCOVERCALLBACK)(LPCWSTR /*pszFileName*/, ULONG_PTR /*param*/); + +typedef struct _DISCOVERJOB +{ + METAINFO *pMeta; + HANDLE hCaller; + DISCOVERCALLBACK fnCallback; + ULONG_PTR param; + UINT fileType; + WCHAR szFileName[2*MAX_PATH]; +} DISCOVERJOB; + +typedef std::deque DISCOVERDEQUE; + +static DISCOVERDEQUE discoverJobs; +static api_metadata *apiMetaData = NULL; +static api_playlistmanager *apiPlaylistManager = NULL; +static METADB metaDb = { 0, }; +static CRITICAL_SECTION g_cs_discover; + +static BOOL MetaDiscovery_InitializeThread(METADB *pMetaDb); +static void MetaDiscovery_KillThread(METADB *pMetaDb); +static BOOL MetaDiscovery_ScheduleJob(HANDLE hWakeEvent, LPCWSTR pszPath, METAINFO *pMeta, UINT fileType, DISCOVERCALLBACK fnCallback, ULONG_PTR param); + +class PlMetaLoader : public ifc_playlistloadercallback +{ +public: + PlMetaLoader( PLAYLISTMETA *plm ) + { + this->plm = plm; plm->nCount = 0; plm->nLength = 0; + } + ~PlMetaLoader() + {}; + + + void OnFile( const wchar_t *filename, const wchar_t *title, int lengthInMS, ifc_plentryinfo *info ) + { + if ( plm->nCount < sizeof( plm->szEntries ) / sizeof( plm->szEntries[ 0 ] ) ) + { + PLENTRY *pe = &plm->szEntries[ plm->nCount ]; + + if ( title && *title ) + pe->pszTitle = _wcsdup( title ); + else if ( filename && *filename ) + { + pe->pszTitle = _wcsdup( filename ); + } + else + pe->pszTitle = NULL; + + pe->nLength = lengthInMS / 1000; + } + + plm->nCount++; + plm->nLength += lengthInMS; + } + + void OnPlaylistInfo( const wchar_t *playlistName, size_t numEntries, ifc_plentryinfo *info ) + { + plm->pszTitle = ( playlistName && *playlistName ) ? _wcsdup( playlistName ) : NULL; + } + + const wchar_t *GetBasePath() + { + return L"."; + } + +protected: + RECVS_DISPATCH; + +private: + PLAYLISTMETA *plm; +}; + +#define CBCLASS PlMetaLoader +START_DISPATCH; +VCB( IFC_PLAYLISTLOADERCALLBACK_ONFILE, OnFile ) +VCB( IFC_PLAYLISTLOADERCALLBACK_ONPLAYLISTINFO, OnPlaylistInfo ) +CB( IFC_PLAYLISTLOADERCALLBACK_GETBASEPATH, GetBasePath ) +END_DISPATCH; + + +void FileViewMeta_InitializeStorage(HWND hView) +{ + if (0 == metaDb.ref) + { + InitializeCriticalSection(&g_cs_discover); + if (WASABI_API_SVC) + { + waServiceFactory *factory; + + factory = WASABI_API_SVC->service_getServiceByGuid(api_metadataGUID); + if (factory) apiMetaData = (api_metadata*) factory->getInterface(); + else apiMetaData = NULL; + + factory = WASABI_API_SVC->service_getServiceByGuid(api_playlistmanagerGUID); + if (factory) apiPlaylistManager = (api_playlistmanager*) factory->getInterface(); + else apiPlaylistManager = NULL; + } + + + + } + metaDb.ref++; +} + +static void FileViewMeta_FreeAudioMeta(AUDIOMETA *pMeta) +{ + if (pMeta->pszAlbum) { free(pMeta->pszAlbum); pMeta->pszAlbum = NULL; } + if (pMeta->pszArtist) { free(pMeta->pszArtist); pMeta->pszArtist = NULL; } + if (pMeta->pszTitle) { free(pMeta->pszTitle); pMeta->pszTitle = NULL; } + if (pMeta->pszAlbumArtist) { free(pMeta->pszAlbumArtist); pMeta->pszAlbumArtist = NULL; } + if (pMeta->pszComment) { free(pMeta->pszComment); pMeta->pszComment = NULL; } + if (pMeta->pszComposer) { free(pMeta->pszComposer); pMeta->pszComposer = NULL; } + if (pMeta->pszGenre) { free(pMeta->pszGenre); pMeta->pszGenre = NULL; } + if (pMeta->pszPublisher) { free(pMeta->pszPublisher); pMeta->pszPublisher = NULL; } +} + +static void FileViewMeta_FreeVideoMeta(VIDEOMETA *pMeta) +{ + if (pMeta->pszAlbum) { free(pMeta->pszAlbum); pMeta->pszAlbum = NULL; } + if (pMeta->pszArtist) { free(pMeta->pszArtist); pMeta->pszArtist = NULL; } + if (pMeta->pszTitle) { free(pMeta->pszTitle); pMeta->pszTitle = NULL; } + if (pMeta->pszAlbumArtist) { free(pMeta->pszAlbumArtist); pMeta->pszAlbumArtist = NULL; } + if (pMeta->pszComment) { free(pMeta->pszComment); pMeta->pszComment = NULL; } + if (pMeta->pszComposer) { free(pMeta->pszComposer); pMeta->pszComposer = NULL; } + if (pMeta->pszGenre) { free(pMeta->pszGenre); pMeta->pszGenre = NULL; } + if (pMeta->pszPublisher) { free(pMeta->pszPublisher); pMeta->pszPublisher = NULL; } +} + +static void FileViewMeta_FreePlaylistMeta(PLAYLISTMETA *pMeta) +{ + if (!pMeta) return; + if (pMeta->pszTitle) { free(pMeta->pszTitle); pMeta->pszTitle = NULL; } + for (int i = 0; i < sizeof(pMeta->szEntries)/sizeof(pMeta->szEntries[0]); i++) + { + if (pMeta->szEntries[i].pszTitle) { free(pMeta->szEntries[i].pszTitle); pMeta->szEntries[i].pszTitle = NULL; } + } + if (pMeta->pszTitle) { free(pMeta->pszTitle); pMeta->pszTitle = NULL; } +} + +static void FileViewMeta_FreeFileMeta(FILEMETARECORD *pFileMeta) +{ + if (pFileMeta) + { + switch(pFileMeta->type) + { + case METATYPE_AUDIO: + FileViewMeta_FreeAudioMeta(&pFileMeta->audio); break; + case METATYPE_VIDEO: + FileViewMeta_FreeVideoMeta(&pFileMeta->video); break; + case METATYPE_PLAYLIST: + FileViewMeta_FreePlaylistMeta(&pFileMeta->playlist); break; + } + + free(pFileMeta); + } +} + +static void FileViewMeta_FreeMetaInfo(METAINFO *pMeta) +{ + if (pMeta) + { + if (pMeta->pszFileName) { free(pMeta->pszFileName); pMeta->pszFileName = NULL; } + FileViewMeta_FreeFileMeta(pMeta->pFileMeta); + } +} + +void FileViewMeta_TruncateQueue(size_t max) +{ + EnterCriticalSection(&g_cs_discover); + while(discoverJobs.size() > max) + { + DISCOVERJOB *pdj = discoverJobs.front(); + if (pdj) + { + if (pdj->pMeta) + { + ZeroMemory(&pdj->pMeta->lastWriteTime, sizeof(FILETIME)); + pdj->pMeta->fileSizeLow = 0; + pdj->pMeta->bBusy = FALSE; + if (pdj->hCaller) CloseHandle(pdj->hCaller); + } + free(pdj); + } + + discoverJobs.pop_front(); + } + LeaveCriticalSection(&g_cs_discover); +} + +void FileViewMeta_ReleaseStorage(HWND hView) +{ + if (0 == metaDb.ref) return; + + metaDb.ref--; + if (0 == metaDb.ref) + { + FileViewMeta_TruncateQueue(0); + MetaDiscovery_KillThread(&metaDb); + if (WASABI_API_SVC) + { + if(apiMetaData) + { + waServiceFactory *factory = WASABI_API_SVC->service_getServiceByGuid(api_metadataGUID); + if (factory) factory->releaseInterface(apiMetaData); + } + if(apiPlaylistManager) + { + waServiceFactory *factory = WASABI_API_SVC->service_getServiceByGuid(api_playlistmanagerGUID); + if (factory) factory->releaseInterface(apiPlaylistManager); + } + } + + while(metaDb.pPath.size() > 0) + { + if (metaDb.pPath.back().pszPath) free(metaDb.pPath.back().pszPath); + metaDb.pPath.pop_back(); + } + while(metaDb.pRec.size() > 0) + { + FileViewMeta_FreeMetaInfo(metaDb.pRec.back()); + metaDb.pRec.pop_back(); + } + metaDb.maxPathId = 0; + metaDb.lastPathIndex = 0; + DeleteCriticalSection(&g_cs_discover); + } +} + +__inline static int __cdecl FileViewMeta_SortPathId(const void *elem1, const void *elem2) +{ + return (CompareStringW(CSTR_INVARIANT, NORM_IGNORECASE, + ((PATHID*)elem1)->pszPath, -1, ((PATHID*)elem2)->pszPath, -1) - 2); +} + +__inline static int __cdecl FileViewMeta_SearhPath(const void *elem1, const void *elem2) +{ + return (CompareStringW(CSTR_INVARIANT, NORM_IGNORECASE, + (LPCWSTR)elem1, -1, ((PATHID*)elem2)->pszPath, -1) - 2); +} + +__inline static int __cdecl FileViewMeta_SortMetaInfo(const void *elem1, const void *elem2) +{ + METAINFO *pmi1 = ((METAINFO*)elem1); + METAINFO *pmi2 = ((METAINFO*)elem2); + if (pmi1->pathId != pmi2->pathId) return (pmi1->pathId - pmi2->pathId); + return (CompareStringW(CSTR_INVARIANT, NORM_IGNORECASE, pmi1->pszFileName, -1, pmi2->pszFileName, -1) - 2); +} + +__inline static int __cdecl FileViewMeta_SortMetaInfo_V2(const void* elem1, const void* elem2) +{ + return FileViewMeta_SortMetaInfo(elem1, elem2) < 0; +} +__inline static int __cdecl FileViewMeta_SearhMetaInfo(const void *elem1, const void *elem2) +{ + METASEARCHKEY *pKey = (METASEARCHKEY*)elem1; + if (pKey->pathId != (((METAINFO*)elem2))->pathId) + return (pKey->pathId - (((METAINFO*)elem2))->pathId); + return (CompareStringW(CSTR_INVARIANT, NORM_IGNORECASE, + pKey->pszFileName, -1, (((METAINFO*)elem2))->pszFileName, -1) - 2); +} + +static INT FileViewMeta_GetPathId(LPCWSTR pszPath) +{ + if (!pszPath || L'\0' == pszPath) return 0; + + //PATHID *psr; + PATHLIST::iterator psr; + if (metaDb.lastPathIndex < metaDb.pPath.size() && + CSTR_EQUAL == CompareStringW(CSTR_INVARIANT, NORM_IGNORECASE, pszPath, -1, + metaDb.pPath.at(metaDb.lastPathIndex).pszPath, -1)) + { + //psr = &metaDb.pPath.at(metaDb.lastPathIndex); + psr = metaDb.pPath.begin() + metaDb.lastPathIndex; + } + else + { + //psr = (PATHID*)bsearch(pszPath, metaDb.pPath.begin(), metaDb.pPath.size(), sizeof(PATHID), FileViewMeta_SearhPath); + psr = std::find_if(metaDb.pPath.begin(), metaDb.pPath.end(), + [&](PATHID &path) -> bool + { + return FileViewMeta_SearhPath(pszPath, &path) == 0; + } + ); + } + + //if (!psr) + if(psr == metaDb.pPath.end()) + { + PATHID pid; + pid.id = metaDb.maxPathId+1; + pid.pszPath = _wcsdup(pszPath); + metaDb.pPath.push_back(pid); + metaDb.maxPathId++; + //psr = &pid; + psr = metaDb.pPath.end() - 1; + + //qsort(metaDb.pPath.begin(), metaDb.pPath.size(), sizeof(PATHID), FileViewMeta_SortPathId); + std::sort(metaDb.pPath.begin(), metaDb.pPath.end(), + [&](const PATHID& lhs, const PATHID& rhs) -> bool + { + return FileViewMeta_SortPathId(&lhs, &rhs) == CSTR_LESS_THAN; + } + ); + } + metaDb.lastPathIndex = (size_t)(ULONG_PTR)(psr - metaDb.pPath.begin()); + return psr->id; +} + +static METAINFO *FileViewMeta_GetMeta(LPCWSTR pszPath, LPCWSTR pszFileName, LPCWSTR pszExt, BOOL bCreateIfNotFound) +{ + WCHAR szBuffer[MAX_PATH] = {0}; + METASEARCHKEY key; + key.pathId = FileViewMeta_GetPathId(pszPath); + key.pszFileName = pszFileName; + + if (pszExt == (pszFileName + lstrlenW(pszFileName) + 1)) + { + StringCchCopyW(szBuffer, sizeof(szBuffer)/sizeof(szBuffer[0]), pszFileName); + if (pszExt) + { + StringCchCatW(szBuffer, sizeof(szBuffer)/sizeof(szBuffer[0]), L"."); + StringCchCatW(szBuffer, sizeof(szBuffer)/sizeof(szBuffer[0]), pszExt); + } + key.pszFileName = szBuffer; + } + + //METAINFO **pr = (METAINFO**)bsearch(&key, metaDb.pRec.at(0), metaDb.pRec.size(), sizeof(METAINFO*), FileViewMeta_SearhMetaInfo); + auto it = std::find_if(metaDb.pRec.begin(), metaDb.pRec.end(), + [&](const METAINFO* info) -> bool + { + return FileViewMeta_SearhMetaInfo(&key, info) == 0; + } + ); + if (it != metaDb.pRec.end()) + { + return *it; + } + + //if (pr) + // return *pr; + + if (!bCreateIfNotFound) + return NULL; + + METAINFO *pInfo = (METAINFO*)calloc(1, sizeof(METAINFO)); + if (pInfo) + { + pInfo->pathId = key.pathId; + pInfo->pszFileName = _wcsdup(key.pszFileName); + metaDb.pRec.push_back(pInfo); + //qsort(metaDb.pRec.first(), metaDb.pRec.size(), sizeof(METAINFO*), FileViewMeta_SortMetaInfo); + std::sort(metaDb.pRec.begin(), metaDb.pRec.end(), FileViewMeta_SortMetaInfo_V2); + } + return pInfo; +} + +// sets part and parts to -1 or 0 on fail/missing (e.g. parts will be -1 on "1", but 0 on "1/") +static void ParseIntSlashInt(wchar_t *string, int *part, int *parts) +{ + *part = -1; + *parts = -1; + + if (string && string[0]) + { + *part = _wtoi(string); + while (*string && *string != '/') + { + string++; + } + if (*string == '/') + { + string++; + *parts = _wtoi(string); + } + } +} + +#define READFILEINFO(__fileName, __tag, __result, __pszBuffer, __cchBuffer)\ + (apiMetaData->GetExtendedFileInfo((__fileName), (__tag), (__pszBuffer), (__cchBuffer)) && L'\0' != *(__pszBuffer)) + +static void FileViewMeta_ReadAudioMeta(LPCWSTR pszFullPath, AUDIOMETA *pa) +{ + #define GETFILEINFO_STR(__tag, __result) { szBuffer[0] = L'\0';\ + if (READFILEINFO(pszFullPath, __tag, __result, szBuffer, sizeof(szBuffer)/sizeof(szBuffer[0])))\ + {(__result) = _wcsdup(szBuffer); }} + #define GETFILEINFO_INT(__tag, __result) { szBuffer[0] = L'\0';\ + if (READFILEINFO(pszFullPath, __tag, __result, szBuffer, sizeof(szBuffer)/sizeof(szBuffer[0])))\ + {(__result) = _wtoi(szBuffer); }} + #define GETFILEINFO_INTINT(__tag, __result1, __result2) { szBuffer[0] = L'\0';\ + if (READFILEINFO(pszFullPath, __tag, __result, szBuffer, sizeof(szBuffer)/sizeof(szBuffer[0])))\ + {ParseIntSlashInt(szBuffer, (__result1), (__result2)); }} + + if (AGAVE_API_MLDB) + { + itemRecordW *record = AGAVE_API_MLDB->GetFile(pszFullPath); + if (record) + { + if (record->artist) pa->pszArtist = _wcsdup(record->artist); + if (record->album) pa->pszAlbum = _wcsdup(record->album); + if (record->title) pa->pszTitle = _wcsdup(record->title); + if (record->albumartist) pa->pszAlbumArtist = _wcsdup(record->albumartist); + if (record->comment) pa->pszComment = _wcsdup(record->comment); + if (record->composer) pa->pszComposer = _wcsdup(record->composer); + if (record->genre) pa->pszGenre = _wcsdup(record->genre); + if (record->publisher) pa->pszPublisher = _wcsdup(record->publisher); + pa->nLength = record->length; + pa->nBitrate = record->bitrate; + pa->nYear = record->year; + pa->nDiscNum = record->disc; + pa->nDiscCount = record->discs; + pa->nTrackNum = record->track; + pa->nTrackCount = record->tracks; + pa->nSource = METADATA_SOURCE_MLDB; + AGAVE_API_MLDB->FreeRecord(record); + return; + } + } + + WCHAR szBuffer[2048] = {0}; + + GETFILEINFO_STR(L"artist", pa->pszArtist); + GETFILEINFO_STR(L"album", pa->pszAlbum); + GETFILEINFO_STR(L"title", pa->pszTitle); + GETFILEINFO_STR(L"albumartist", pa->pszAlbumArtist); + GETFILEINFO_STR(L"comment", pa->pszComment); + GETFILEINFO_STR(L"composer", pa->pszComposer); + GETFILEINFO_STR(L"genre", pa->pszGenre); + GETFILEINFO_STR(L"publisher", pa->pszPublisher); + + GETFILEINFO_INT(L"bitrate", pa->nBitrate); + GETFILEINFO_INT(L"year", pa->nYear); + GETFILEINFO_INT(L"length", pa->nLength); + pa->nLength = pa->nLength/1000; + + GETFILEINFO_INTINT(L"disc", &pa->nDiscNum, &pa->nDiscCount); + GETFILEINFO_INTINT(L"track",&pa->nTrackNum, &pa->nTrackCount); + + pa->nSource = METADATA_SOURCE_FILEINFO; +} + +static void FileViewMeta_ReadPlaylistMeta(LPCWSTR pszFullPath, PLAYLISTMETA *plm) +{ + if (!apiPlaylistManager) return; + PlMetaLoader plLoaderCb(plm); + apiPlaylistManager->Load(pszFullPath, &plLoaderCb); + plm->nLength = plm->nLength/1000; +} + +static FILEMETARECORD* FileViewMeta_ReadFileMeta(LPCWSTR pszFullPath, UINT uType) +{ + FILEMETARECORD *pmr = (FILEMETARECORD*)calloc(1, sizeof(FILEMETARECORD)); + if (!pmr) return NULL; + switch(uType) + { + case FVFT_AUDIO: + pmr->type = METATYPE_AUDIO; + FileViewMeta_ReadAudioMeta(pszFullPath, &pmr->audio); + break; + case FVFT_PLAYLIST: + pmr->type = METATYPE_PLAYLIST; + FileViewMeta_ReadPlaylistMeta(pszFullPath, &pmr->playlist); + break; + } + + return pmr; +} + +FILEMETARECORD *FileViewMeta_GetFromCache(LPCWSTR pszPath, FILERECORD *pfr) +{ + if ((FVFT_AUDIO != pfr->fileType && FVFT_VIDEO != pfr->fileType) || !pszPath) return NULL; + + METAINFO *pMeta = FileViewMeta_GetMeta(pszPath, pfr->Info.cFileName, &pfr->Info.cFileName[pfr->extOffset], FALSE); + if (pMeta && !pMeta->bBusy && pMeta->fileSizeLow == pfr->Info.nFileSizeLow && + pMeta->fileSizeHigh == pfr->Info.nFileSizeHigh && 0 == CompareFileTime(&pMeta->lastWriteTime, &pfr->Info.ftLastWriteTime)) + { + return pMeta->pFileMeta; + } + return NULL; +} + +BOOL FileViewMeta_Discover(LPCWSTR pszPath, FILERECORD *pfr, DISCOVERCALLBACK fnCallback, ULONG_PTR param, INT queueMax) +{ + if (!apiMetaData) return FALSE; + + if ((FVFT_AUDIO != pfr->fileType && + FVFT_VIDEO != pfr->fileType && + FVFT_PLAYLIST != pfr->fileType) || !pszPath) return FALSE; + + METAINFO *pMeta = FileViewMeta_GetMeta(pszPath, pfr->Info.cFileName, &pfr->Info.cFileName[pfr->extOffset], TRUE); + if (!pMeta || pMeta->bBusy) return FALSE; + if (0 == CompareFileTime(&pMeta->lastWriteTime, &pfr->Info.ftLastWriteTime) && + pMeta->fileSizeLow == pfr->Info.nFileSizeLow && pMeta->fileSizeHigh == pfr->Info.nFileSizeHigh) + { + pfr->pMeta = pMeta->pFileMeta; + return TRUE; + } + + FileViewMeta_FreeFileMeta(pMeta->pFileMeta); + + pMeta->lastWriteTime = pfr->Info.ftLastWriteTime; + pMeta->fileSizeLow = pfr->Info.nFileSizeLow; + pMeta->fileSizeHigh = pfr->Info.nFileSizeHigh; + pMeta->pFileMeta = NULL; + pMeta->bBusy = TRUE; + + if (!fnCallback && !param) + { + WCHAR szFullPath[MAX_PATH] = {0}; + FileViewMeta_TruncateQueue(0); + + PathCombineW(szFullPath, pszPath, pMeta->pszFileName); + pMeta->pFileMeta = FileViewMeta_ReadFileMeta(szFullPath, pfr->fileType); + pMeta->bBusy = FALSE; + pfr->pMeta = pMeta->pFileMeta; + return TRUE; + } + + if (queueMax > 0) + { + FileViewMeta_TruncateQueue(--queueMax); + } + if (!MetaDiscovery_InitializeThread(&metaDb) || + !MetaDiscovery_ScheduleJob(metaDb.hDiscoverWake, pszPath, pMeta, pfr->fileType, fnCallback, param)) + { + ZeroMemory(&pMeta->lastWriteTime, sizeof(FILETIME)); + pMeta->fileSizeLow = 0; + pMeta->bBusy = FALSE; + return FALSE; + } + + return FALSE; +} + +static BOOL MetaDiscovery_ScheduleJob(HANDLE hWakeEvent, LPCWSTR pszPath, METAINFO *pMeta, UINT fileType, DISCOVERCALLBACK fnCallback, ULONG_PTR param) +{ + if (!pszPath || !pMeta || !pMeta->pszFileName || !fnCallback || !hWakeEvent) + { + if (pMeta) + { + ZeroMemory(&pMeta->lastWriteTime, sizeof(FILETIME)); + pMeta->fileSizeLow = 0; + pMeta->bBusy = FALSE; + } + return FALSE; + } + + DISCOVERJOB *pJob = (DISCOVERJOB*)calloc(1, sizeof(DISCOVERJOB)); + if (pJob) + { + pJob->pMeta = pMeta; + + HANDLE hp = GetCurrentProcess(); + if (DuplicateHandle(hp, GetCurrentThread(), hp, &pJob->hCaller, 0, FALSE, DUPLICATE_SAME_ACCESS)) + { + pJob->fnCallback = fnCallback; + pJob->param = param; + pJob->fileType = fileType; + PathCombineW(pJob->szFileName, pszPath, pMeta->pszFileName); + EnterCriticalSection(&g_cs_discover); + discoverJobs.push_front(pJob); + LeaveCriticalSection(&g_cs_discover); + if (SetEvent(hWakeEvent)) return TRUE; + } + } + if (pJob) + { + if (pJob->hCaller) CloseHandle(pJob->hCaller); + free(pJob); + } + if (pMeta) + { + ZeroMemory(&pMeta->lastWriteTime, sizeof(FILETIME)); + pMeta->fileSizeLow = 0; + pMeta->bBusy = FALSE; + } + + return FALSE; +} + +static void CALLBACK MetaDiscovery_ApcCallback(ULONG_PTR param) +{ + DISCOVERJOB *pJob = (DISCOVERJOB*)param; + if (!pJob) return; + if (pJob->pMeta) pJob->pMeta->bBusy = FALSE; + if (pJob->fnCallback) pJob->fnCallback(pJob->szFileName, pJob->param); + if (pJob->hCaller) CloseHandle(pJob->hCaller); + free(pJob); +} + +static void MetaDiscovery_ExecuteJob(DISCOVERJOB *pJob) +{ + pJob->pMeta->pFileMeta = FileViewMeta_ReadFileMeta(pJob->szFileName, pJob->fileType); + if (pJob->hCaller) + { + QueueUserAPC(MetaDiscovery_ApcCallback, pJob->hCaller, (ULONG_PTR)pJob); + SleepEx(1, TRUE); + } + else MetaDiscovery_ApcCallback((ULONG_PTR)pJob); +} + +static DWORD CALLBACK MetaDiscovery_ThreadProc(LPVOID param) +{ + METADB *pMetaDb = (METADB*)param; + HANDLE hEvents[] = { pMetaDb->hDiscoverKill, pMetaDb->hDiscoverWake }; + SetThreadName(-1, "FileView meta discovery"); + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_IDLE); + SetEvent(hEvents[METATHREAD_WAKE]); + + for(;;) + { + switch (WaitForMultipleObjectsEx(2, hEvents, FALSE, INFINITE, TRUE)) + { + case WAIT_OBJECT_0: // kill switch + TRACE_FMT(TEXT("Kill Meta Discovery")); + return 0; + case WAIT_OBJECT_0+1: // job + EnterCriticalSection(&g_cs_discover); + if (discoverJobs.empty()) + { + ResetEvent(hEvents[METATHREAD_WAKE]); + LeaveCriticalSection(&g_cs_discover); + } + else + { + DISCOVERJOB *pj; + pj = discoverJobs.front(); + discoverJobs.pop_front(); + LeaveCriticalSection(&g_cs_discover); + MetaDiscovery_ExecuteJob(pj); + } + break; + } + } + return 0; +} + +static BOOL MetaDiscovery_InitializeThread(METADB *pMetaDb) +{ + if (!pMetaDb) return FALSE; + if (pMetaDb->hDiscoverThread) return TRUE; + + DWORD threadId; + if (!pMetaDb->hDiscoverWake) pMetaDb->hDiscoverWake = CreateEvent(0, TRUE, FALSE, 0); + if (!pMetaDb->hDiscoverKill) pMetaDb->hDiscoverKill = CreateEvent(0, TRUE, FALSE, 0); + if (pMetaDb->hDiscoverKill && pMetaDb->hDiscoverWake) + { + pMetaDb->hDiscoverThread = CreateThread(NULL, 0, MetaDiscovery_ThreadProc, (LPVOID)pMetaDb, 0, &threadId); + if (pMetaDb->hDiscoverThread) + { + WaitForSingleObject(pMetaDb->hDiscoverWake, INFINITE); + ResetEvent(pMetaDb->hDiscoverWake); + return TRUE; + } + } + + MetaDiscovery_KillThread(pMetaDb); + return FALSE; +} + +static void MetaDiscovery_KillThread(METADB *pMetaDb) +{ + if (!pMetaDb) return; + + if (pMetaDb->hDiscoverThread) + { + if (pMetaDb->hDiscoverKill) + { + SetEvent(metaDb.hDiscoverKill); + WaitForSingleObject(metaDb.hDiscoverThread, INFINITE); + } + CloseHandle(pMetaDb->hDiscoverThread); + pMetaDb->hDiscoverThread = NULL; + } + if (pMetaDb->hDiscoverKill) { CloseHandle(pMetaDb->hDiscoverKill); pMetaDb->hDiscoverKill = NULL; } + if (pMetaDb->hDiscoverWake) { CloseHandle(pMetaDb->hDiscoverWake); pMetaDb->hDiscoverWake = NULL; } +} + +static BOOL FileViewMeta_GetAudioString(AUDIOMETA *pam, UINT uMetaField, LPCWSTR *ppszOut) +{ + switch(uMetaField) + { + case MF_ARTIST: *ppszOut = pam->pszArtist; return TRUE; + case MF_ALBUM: *ppszOut = pam->pszAlbum; return TRUE; + case MF_TITLE: *ppszOut = pam->pszTitle; return TRUE; + case MF_GENRE: *ppszOut = pam->pszGenre; return TRUE; + case MF_COMMENT: *ppszOut = pam->pszComment; return TRUE; + case MF_PUBLISHER: *ppszOut = pam->pszPublisher; return TRUE; + case MF_COMPOSER: *ppszOut = pam->pszComposer; return TRUE; + case MF_ALBUMARTIST: *ppszOut = pam->pszAlbumArtist; return TRUE; + } + return FALSE; +} + +static BOOL FileViewMeta_GetAudioInt(AUDIOMETA *pam, UINT uMetaField, INT *pOut) +{ + switch(uMetaField) + { + case MF_BITRATE: *pOut = pam->nBitrate; return TRUE; + case MF_DISCCOUNT: *pOut = pam->nDiscCount; return TRUE; + case MF_DISCNUM: *pOut = pam->nDiscNum; return TRUE; + case MF_LENGTH: *pOut = pam->nLength; return TRUE; + case MF_SOURCE: *pOut = pam->nSource; return TRUE; + case MF_TRACKCOUNT: *pOut = pam->nTrackCount; return TRUE; + case MF_TRACKNUM: *pOut = pam->nTrackNum; return TRUE; + case MF_YEAR: *pOut = pam->nYear; return TRUE; + } + return FALSE; +} + +BOOL FileViewMeta_GetString(FILEMETARECORD *pMeta, UINT uMetaField, LPCWSTR *ppszOut) +{ + if (!pMeta) return FALSE; + + switch(pMeta->type) + { + case METATYPE_AUDIO: return FileViewMeta_GetAudioString(&pMeta->audio, uMetaField, ppszOut); + } + return FALSE; +} + +BOOL FileViewMeta_GetInt(FILEMETARECORD *pMeta, UINT uMetaField, INT *pOut) +{ + if (!pMeta) return FALSE; + + switch(pMeta->type) + { + case METATYPE_AUDIO: return FileViewMeta_GetAudioInt(&pMeta->audio, uMetaField, pOut); + } + return FALSE; +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/fileview_toolbar.cpp b/Src/Plugins/General/gen_ml/fileview_toolbar.cpp new file mode 100644 index 00000000..4bb2614a --- /dev/null +++ b/Src/Plugins/General/gen_ml/fileview_toolbar.cpp @@ -0,0 +1,338 @@ +#include "./fileview.h" +#include "./fileview_internal.h" +#include "./resource.h" +#include "../nu/menushortcuts.h" +#include +#include + +#define FVTOOLBAR_DATAW L"FVTOOLBAR" + +typedef struct _FVTOOLBAR +{ + HMENU hMenu; + +} FVTOOLBAR; + +#define GetToolbar(__hwnd) ((FVTOOLBAR*)GetPropW((__hwnd), FVTOOLBAR_DATAW)) + +static INT_PTR CALLBACK FileViewToolbar_DialogProc(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam); + +static HMLIMGLST hmlilModes = NULL; + +HWND FileViewToolbar_Create(HWND hwndParent) +{ + HWND hwnd = WASABI_API_CREATEDIALOGPARAMW(IDD_FILEVIEW_TOOLBAR, hwndParent, FileViewToolbar_DialogProc, 0L); + return hwnd; +} + +static LRESULT FileViewToolbar_NotifyParent(HWND hdlg, UINT uCode, NMHDR *phdr) +{ + HWND hParent = GetParent(hdlg); + if (!phdr || !hParent) return 0L; + phdr->code = uCode; + phdr->hwndFrom = hdlg; + phdr->idFrom = GetDlgCtrlID(hdlg); + return SendMessageW(hParent, WM_NOTIFY, (WPARAM)phdr->idFrom, (LPARAM)phdr); +} + +static void FileViewToolbar_LoadImages() +{ + MLIMAGESOURCE_I mlis; + + if (NULL != hmlilModes) return; + + ZeroMemory(&mlis, sizeof(MLIMAGESOURCE_I)); + mlis.type = SRC_TYPE_PNG; + mlis.hInst = plugin.hDllInstance; + + hmlilModes = MLImageListI_Create(18, 12, MLILC_COLOR32, 3, 2, 3, hmlifMngr); + if (NULL != hmlilModes) + { + INT imageList[] = { IDB_FILEVIEW_ICON, IDB_FILEVIEW_LIST, IDB_FILEVIEW_DETAIL }; + for(int i = 0; i < sizeof(imageList)/sizeof(imageList[0]); i++) + { + mlis.lpszName = MAKEINTRESOURCEW(imageList[i]); + MLImageListI_Add(hmlilModes, &mlis, MLIF_BUTTONBLENDPLUSCOLOR_UID, imageList[i]); + } + } +} + +static void FileViewToolbar_LayoutWindows(HWND hdlg, BOOL bRedraw) +{ + INT buttonList[] = { IDC_BTN_VIEW_ICON, IDC_BTN_VIEW_LIST, IDC_BTN_VIEW_DETAIL, IDC_BTN_ARRANGEBY, IDC_BTN_OPTIONS, }; + HDWP hdwp; + DWORD flags, size; + RECT rc; + + if (!GetClientRect(hdlg, &rc)) return; + + flags = SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOREDRAW; + + hdwp = BeginDeferWindowPos(sizeof(buttonList)/sizeof(buttonList[0])); + if (!hdwp) return; + + for(int i =0; i < sizeof(buttonList)/sizeof(buttonList[0]); i++) + { + HWND hctrl = GetDlgItem(hdlg, buttonList[i]); + if (NULL != hctrl) + { + size = MLSkinnedButton_GetIdealSize(hctrl, NULL); + INT width = LOWORD(size); + hdwp = DeferWindowPos(hdwp, hctrl, NULL, 0, 0, width, rc.bottom - rc.top, flags); + } + } + + EndDeferWindowPos(hdwp); + + SetWindowPos(hdlg, NULL, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | + SWP_NOZORDER | SWP_FRAMECHANGED | ((bRedraw) ? 0 : SWP_NOREDRAW)); +} + + +static BOOL FileViewToolbar_OnInitDialog(HWND hdlg, HWND hwndFocus, LPARAM lParam) +{ + FVTOOLBAR *ptb; + ptb = (FVTOOLBAR*)calloc(1, sizeof(FVTOOLBAR)); + if (ptb) + { + if (!SetPropW(hdlg, FVTOOLBAR_DATAW, ptb)) + { + free(ptb); + ptb = NULL; + } + } + + if (!ptb) + { + DestroyWindow(hdlg); + return 0; + } + + FileViewToolbar_LoadImages(); + + HWND hctrl; + SkinWindowEx(hdlg, SKINNEDWND_TYPE_AUTO, SWS_USESKINCOLORS | SWS_USESKINCURSORS | SWS_USESKINFONT); + + hctrl = GetDlgItem(hdlg, IDC_BTN_VIEW_ICON); + SkinWindowEx(hctrl, SKINNEDWND_TYPE_BUTTON, SWS_USESKINCOLORS | SWS_USESKINCURSORS | SWS_USESKINFONT | SWBS_TOOLBAR); + MLSkinnedButton_SetImageList(hctrl, hmlilModes, 0, 0, 0, 0); + + hctrl = GetDlgItem(hdlg, IDC_BTN_VIEW_LIST); + SkinWindowEx(hctrl, SKINNEDWND_TYPE_BUTTON, SWS_USESKINCOLORS | SWS_USESKINCURSORS | SWS_USESKINFONT | SWBS_TOOLBAR); + MLSkinnedButton_SetImageList(hctrl, hmlilModes, 1, 1, 1, 1); + + hctrl = GetDlgItem(hdlg, IDC_BTN_VIEW_DETAIL); + SkinWindowEx(hctrl, SKINNEDWND_TYPE_BUTTON, SWS_USESKINCOLORS | SWS_USESKINCURSORS | SWS_USESKINFONT | SWBS_TOOLBAR); + MLSkinnedButton_SetImageList(hctrl, hmlilModes, 2, 2, 2, 2); + + hctrl = GetDlgItem(hdlg, IDC_BTN_ARRANGEBY); + SkinWindowEx(hctrl, SKINNEDWND_TYPE_BUTTON, SWS_USESKINCOLORS | SWS_USESKINCURSORS | SWS_USESKINFONT | SWBS_DROPDOWNBUTTON | SWBS_TOOLBAR); + hctrl = GetDlgItem(hdlg, IDC_BTN_OPTIONS); + SkinWindowEx(hctrl, SKINNEDWND_TYPE_BUTTON, SWS_USESKINCOLORS | SWS_USESKINCURSORS | SWS_USESKINFONT | SWBS_DROPDOWNBUTTON | SWBS_TOOLBAR); + + FileViewToolbar_LayoutWindows(hdlg, FALSE); + + return FALSE; +} + +static void FileViewToolbar_OnDestroy(HWND hdlg) +{ + FVTOOLBAR *ptb = GetToolbar(hdlg); + if (ptb) + { + RemovePropW(hdlg, FVTOOLBAR_DATAW); + if (ptb->hMenu) DestroyMenu(ptb->hMenu); + free(ptb); + } +} + +static void FileViewToolbar_OnWindowPosChanged(HWND hdlg, WINDOWPOS *pwp) +{ + HWND hctrl; + RECT rc, rw; + UINT flags; + LONG right, left; + DWORD ws; + if (SWP_NOSIZE == ((SWP_NOSIZE | SWP_FRAMECHANGED) & pwp->flags)) return; + + GetClientRect(hdlg, &rc); + right = rc.right -2; + left = rc.left + 2; + rc.bottom -= 2; + + HDWP hdwp = BeginDeferWindowPos(4); + if (NULL == hdwp) return; + + flags = SWP_NOACTIVATE | SWP_NOZORDER | ((SWP_NOREDRAW | SWP_NOCOPYBITS) & pwp->flags); + + if (NULL != (hctrl = GetDlgItem(hdlg, IDC_BTN_VIEW_ICON)) && GetWindowRect(hctrl, &rw)) + { + hdwp = DeferWindowPos(hdwp, hctrl, NULL, left, rc.top, rw.right - rw.left, rc.bottom - rc.top, flags); + left += (rw.right - rw.left) + 2; + } + if (NULL != (hctrl = GetDlgItem(hdlg, IDC_BTN_VIEW_LIST)) && GetWindowRect(hctrl, &rw)) + { + hdwp = DeferWindowPos(hdwp, hctrl, NULL, left, rc.top, rw.right - rw.left, rc.bottom - rc.top, flags); + left += (rw.right - rw.left) + 2; + } + if (NULL != (hctrl = GetDlgItem(hdlg, IDC_BTN_VIEW_DETAIL)) && GetWindowRect(hctrl, &rw)) + { + hdwp = DeferWindowPos(hdwp, hctrl, NULL, left, rc.top, rw.right - rw.left, rc.bottom - rc.top, flags); + left += (rw.right - rw.left) + 2; + } + + if (NULL != (hctrl = GetDlgItem(hdlg, IDC_BTN_OPTIONS)) && GetWindowRect(hctrl, &rw)) + { + right -= (rw.right - rw.left); + ws = GetWindowLongPtrW(hctrl, GWL_STYLE); + if (right < (left + 8)) { if (WS_VISIBLE & ws) ShowWindow(hctrl, SW_HIDE);} + else { if (0 == (WS_VISIBLE & ws)) ShowWindow(hctrl, SW_SHOWNA); } + hdwp = DeferWindowPos(hdwp, hctrl, NULL, right, rc.top, rw.right - rw.left, rc.bottom - rc.top, flags); + right -= 4; + } + if (NULL != (hctrl = GetDlgItem(hdlg, IDC_BTN_ARRANGEBY)) && GetWindowRect(hctrl, &rw)) + { + right -= (rw.right - rw.left); + ws = GetWindowLongPtrW(hctrl, GWL_STYLE); + if (right < (left + 8)) { if (WS_VISIBLE & ws) ShowWindow(hctrl, SW_HIDE);} + else { if (0 == (WS_VISIBLE & ws)) ShowWindow(hctrl, SW_SHOWNA); } + hdwp = DeferWindowPos(hdwp, hctrl, NULL, right, rc.top, rw.right - rw.left, rc.bottom - rc.top, flags); + } + + EndDeferWindowPos(hdwp); +} + +static INT FileViewToolbar_OnGetBestHeight(HWND hdlg) +{ + INT height = 0; + HWND hctrl; + + if (NULL != (hctrl = GetDlgItem(hdlg, IDC_BTN_OPTIONS))) + { + DWORD sz = MLSkinnedButton_GetIdealSize(hctrl, NULL); + height = HIWORD(sz); + } + + if (NULL != (hctrl = GetDlgItem(hdlg, IDC_BTN_VIEW_ICON))) + { + DWORD sz = MLSkinnedButton_GetIdealSize(hctrl, NULL); + if (height < HIWORD(sz)) height = HIWORD(sz); + } + + height += 1; + if (height < 12) height = 12; + return height; +} + +static void FolderBrowserToolbar_NotifyViewSwitch(HWND hdlg) +{ + INT nCmd = 0; + if (BST_CHECKED == IsDlgButtonChecked(hdlg, IDC_BTN_VIEW_LIST)) nCmd = ID_FILEVIEW_SETMODE_LIST; + else if (BST_CHECKED == IsDlgButtonChecked(hdlg, IDC_BTN_VIEW_ICON)) nCmd = ID_FILEVIEW_SETMODE_ICON; + else if (BST_CHECKED == IsDlgButtonChecked(hdlg, IDC_BTN_VIEW_DETAIL)) nCmd = ID_FILEVIEW_SETMODE_DETAIL; + SendMessageW(GetParent(hdlg), WM_COMMAND, MAKEWPARAM(nCmd, 0), 0L); +} + +static void FileViewToolbar_InvertParentStyle(HWND hdlg, UINT style) +{ + HWND hParent = GetParent(hdlg); + if (hParent) + { + FileView_SetStyle(hParent, FileView_GetStyle(hParent) ^ style, style); + FileView_Refresh(hParent, FALSE); + } +} + +static void FileViewToolbar_DisplayOptionsMenu(HWND hdlg, HWND hButton) +{ + RECT r; + if (!hButton || !GetWindowRect(hButton, &r)) + { + GetCursorPos((POINT*)&r); + SetRect(&r, r.left, r.top, r.left, r.top); + } + + if (hButton) MLSkinnedButton_SetDropDownState(hButton, TRUE); + + FileView_DisplayPopupMenu(GetParent(hdlg), FVMENU_OPTIONS, + TPM_RIGHTBUTTON | TPM_LEFTBUTTON | TPM_TOPALIGN | TPM_RIGHTALIGN | TPM_VERPOSANIMATION, + *(((POINT*)&r) + 1)); + + if (hButton) MLSkinnedButton_SetDropDownState(hButton, FALSE); + +} + +static void FileViewToolbar_DisplayArrangeByMenu(HWND hdlg, HWND hButton) +{ + RECT r; + if (!hButton || !GetWindowRect(hButton, &r)) + { + GetCursorPos((POINT*)&r); + SetRect(&r, r.left, r.top, r.left, r.top); + } + + if (hButton) MLSkinnedButton_SetDropDownState(hButton, TRUE); + + FileView_DisplayPopupMenu(GetParent(hdlg), + FVMENU_ARRANGEBY, TPM_RIGHTBUTTON | TPM_LEFTBUTTON | TPM_TOPALIGN | TPM_RIGHTALIGN | TPM_VERPOSANIMATION, + *(((POINT*)&r) + 1)); + + if (hButton) MLSkinnedButton_SetDropDownState(hButton, FALSE); +} + +static void FileViewToolbar_OnCommand(HWND hdlg, INT eventId, INT ctrlId, HWND hwndCtrl) +{ + switch (ctrlId) + { + case IDC_BTN_VIEW_ICON: + case IDC_BTN_VIEW_LIST: + case IDC_BTN_VIEW_DETAIL: + switch(eventId) + { + case BN_CLICKED: FolderBrowserToolbar_NotifyViewSwitch(hdlg); break; + } + break; + case IDC_BTN_OPTIONS: + switch(eventId) + { + case MLBN_DROPDOWN: FileViewToolbar_DisplayOptionsMenu(hdlg, hwndCtrl); break; + } + break; + case IDC_BTN_ARRANGEBY: + switch(eventId) + { + case MLBN_DROPDOWN: FileViewToolbar_DisplayArrangeByMenu(hdlg, hwndCtrl); break; + } + break; + } +} + +static void FileViewToolbar_OnSetStyle(HWND hdlg, UINT uStyle, UINT uStyleMask) +{ + if (FVS_VIEWMASK & uStyleMask) + { + UINT idc = ((UINT)-1); + switch(FVS_VIEWMASK & uStyle) + { + case FVS_LISTVIEW: idc = IDC_BTN_VIEW_LIST; break; + case FVS_ICONVIEW: idc = IDC_BTN_VIEW_ICON; break; + case FVS_DETAILVIEW: idc = IDC_BTN_VIEW_DETAIL; break; + } + CheckRadioButton(hdlg, IDC_BTN_VIEW_ICON, IDC_BTN_VIEW_DETAIL, idc); + } +} + +static INT_PTR CALLBACK FileViewToolbar_DialogProc(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch(uMsg) + { + case WM_INITDIALOG: return FileViewToolbar_OnInitDialog(hdlg, (HWND)wParam, lParam); + case WM_DESTROY: FileViewToolbar_OnDestroy(hdlg); return TRUE; + case WM_WINDOWPOSCHANGED: FileViewToolbar_OnWindowPosChanged(hdlg, (WINDOWPOS*)lParam); return TRUE; + case WM_COMMAND: FileViewToolbar_OnCommand(hdlg, HIWORD(wParam), LOWORD(wParam), (HWND)lParam); return TRUE; + case FVM_GETIDEALHEIGHT: SetWindowLongPtrW(hdlg, DWLP_MSGRESULT, FileViewToolbar_OnGetBestHeight(hdlg)); return TRUE; + case WM_SETFONT: FileViewToolbar_LayoutWindows(hdlg, LOWORD(lParam)); return TRUE; + case FVM_SETSTYLE: FileViewToolbar_OnSetStyle(hdlg, (UINT)lParam, (UINT)wParam); return TRUE; + } + return 0; +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/flickerfix.cpp b/Src/Plugins/General/gen_ml/flickerfix.cpp new file mode 100644 index 00000000..0bed2bba --- /dev/null +++ b/Src/Plugins/General/gen_ml/flickerfix.cpp @@ -0,0 +1,101 @@ +#include +#include "ml.h" +#include "../nu/CGlobalAtom.h" + +#ifndef LONGX86 +#ifdef _WIN64 + #define LONGX86 LONG_PTR +#else /*_WIN64*/ + #define LONGX86 LONG +#endif /*_WIN64*/ +#endif // LONGX86 + +static CGlobalAtom PROP_FLIKERFIX(L"WAFFIX"); + +typedef struct _FFDATA +{ + WNDPROC oldProc; + DWORD mode; + BOOL unicode; + BOOL forward; +}FFDATA, *PFFDATA; + +static LRESULT WINAPI FlickerFixWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + PFFDATA pfd; + pfd = (PFFDATA)GetPropW(hwnd, PROP_FLIKERFIX); + if (!pfd) return (IsWindowUnicode(hwnd)) ? DefWindowProcW(hwnd, uMsg, wParam, lParam) : DefWindowProcA(hwnd, uMsg, wParam, lParam); + + switch(uMsg) + { + + case WM_ERASEBKGND: + if (pfd->forward) break; + + if (FFM_NOERASE & pfd->mode) return 1; + if (FFM_ERASEINPAINT & pfd->mode) return 0; + if (FFM_FORCEERASE & pfd->mode) + { + HBRUSH hb; + RECT rc; + hb = CreateSolidBrush(pfd->mode & 0x00FFFFFF); + GetClientRect(hwnd, &rc); + FillRect((HDC)wParam, &rc, hb); + DeleteObject(hb); + return 1; + } + // otherwise unsubclass.... + case WM_NCDESTROY: + + if (FlickerFixWndProc == (WNDPROC)(LONG_PTR)GetWindowLongPtrW(hwnd, GWLP_WNDPROC)) + { + RemovePropW(hwnd, PROP_FLIKERFIX); + SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (LONGX86)(LONG_PTR)pfd->oldProc); + (pfd->unicode) ? CallWindowProcW(pfd->oldProc, hwnd, uMsg, wParam, lParam) : + CallWindowProcA(pfd->oldProc, hwnd, uMsg, wParam, lParam); + + free(pfd); + return 0; + } + else pfd->forward = TRUE; + + break; + + } + + return (pfd->unicode) ? CallWindowProcW(pfd->oldProc, hwnd, uMsg, wParam, lParam) : + CallWindowProcA(pfd->oldProc, hwnd, uMsg, wParam, lParam); +} + + +BOOL FlickerFixWindow(HWND hwnd, INT mode) +{ + PFFDATA pfd; + + if (!hwnd || !IsWindow(hwnd)) return FALSE; + pfd = (PFFDATA)GetPropW(hwnd, PROP_FLIKERFIX); + if (pfd) pfd->mode = mode; + else + { + pfd = (PFFDATA)calloc(1, sizeof(FFDATA)); + if (pfd) + { + pfd->forward = FALSE; + pfd->mode = mode; + pfd->unicode = IsWindowUnicode(hwnd); + pfd->oldProc = (WNDPROC)(LONG_PTR) ((pfd->unicode) ? SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (LONGX86)(LONG_PTR)FlickerFixWndProc) : + SetWindowLongPtrA(hwnd, GWLP_WNDPROC, (LONGX86)(LONG_PTR)FlickerFixWndProc)); + if (!pfd->oldProc || !SetPropW(hwnd, PROP_FLIKERFIX, pfd)) + { + if (pfd->oldProc) + { + ((pfd->unicode) ? SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (LONGX86)(LONG_PTR)pfd->oldProc) : + SetWindowLongPtrA(hwnd, GWLP_WNDPROC, (LONGX86)(LONG_PTR)pfd->oldProc)); + } + free(pfd); + pfd = NULL; + } + } + } + return (NULL != pfd); +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/folderborwser_listbox.cpp b/Src/Plugins/General/gen_ml/folderborwser_listbox.cpp new file mode 100644 index 00000000..cb8d7dd0 --- /dev/null +++ b/Src/Plugins/General/gen_ml/folderborwser_listbox.cpp @@ -0,0 +1,388 @@ +#include "./folderbrowser.h" +#include "./folderbrowser_internal.h" + +#include "../nu/CGlobalAtom.h" + + +static CGlobalAtom NAVMGR_FBLISTBOXW(L"FBLISTBOX"); + +extern size_t FolderBrowser_GetListBoxColumn(HWND hwndList); +static LRESULT CALLBACK FBListBox_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + +typedef struct _FBMULTISELECT +{ + HWND hOwner; + INT nStart; + INT nTrack; + POINTS ptStart; +} FBMULTISELECT; + +static HWND g_hDragSource = NULL; +static FBMULTISELECT g_MultiSelect = {NULL, }; + +BOOL FolderBrowser_CustomizeListBox(HWND hwndListbox) +{ + LONG_PTR oldProc = GetWindowLongPtrW(hwndListbox, GWLP_WNDPROC); + if (!oldProc || !SetPropW(hwndListbox, NAVMGR_FBLISTBOXW, (HANDLE)oldProc)) return FALSE; + SetWindowLongPtrW(hwndListbox, GWLP_WNDPROC, (LONGX86)(LONG_PTR)FBListBox_WindowProc); + return TRUE; +} + +static void ResetMultiSelect(FBMULTISELECT *pms) +{ + pms->hOwner = NULL; + pms->nTrack = -1; + pms->nStart = -1; + pms->ptStart.x = -1; + pms->ptStart.y = -1; +} + +static void EnsureFocused(HWND hwnd) +{ + HWND hFocus = GetFocus(); + if (hwnd != hFocus) + { + SetFocus(hwnd); + HWND hParent = GetParent(hwnd); + if (NULL != hParent) + SendMessageW(hParent, WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(hwnd), LBN_SETFOCUS), (LPARAM)hwnd); + } +} + +static void EmulateSelectionChanged(HWND hwnd, INT caretIndex, INT ctrlId) +{ + HWND hParent = GetParent(hwnd); + SendMessageW(hwnd, LB_SETCARETINDEX, (WPARAM)caretIndex, TRUE); + if (NULL != hParent) + SendMessageW(hParent, WM_COMMAND, MAKEWPARAM(ctrlId, LBN_SELCHANGE), (LPARAM)hwnd); +} + +static BOOL FBListBox_OnLButtonDown(HWND hwnd, UINT uFlags, POINTS pts) +{ + g_hDragSource = NULL; + ResetMultiSelect(&g_MultiSelect); + + INT count = (INT)SendMessageW(hwnd, LB_GETCOUNT, 0, 0L); + if (count > 0) + { + DWORD item = (DWORD)SendMessageW(hwnd, LB_ITEMFROMPOINT, 0, *((LPARAM*)&pts)); + if (HIWORD(item)) + { + if (0 == (MK_SHIFT & uFlags)) + { + EnsureFocused(hwnd); + SendMessageW(hwnd, LB_SETSEL, FALSE, (LPARAM)-1); + EmulateSelectionChanged(hwnd, (INT)SendMessageW(hwnd, LB_GETCARETINDEX, 0, 0L), GetDlgCtrlID(hwnd)); + } + g_MultiSelect.hOwner = hwnd; + g_MultiSelect.nStart = LOWORD(item); + g_MultiSelect.nTrack = item; + g_MultiSelect.ptStart = pts; + SetCapture(hwnd); + + return TRUE; + } + item = LOWORD(item); + INT sel = (INT)SendMessageW(hwnd, LB_GETSEL, item, 0L); + if (sel > 0) + { + EnsureFocused(hwnd); + g_hDragSource = hwnd; + return TRUE; + } + + RECT rc; + if (SendMessageW(hwnd, LB_GETITEMRECT, (WPARAM)item, (LPARAM)&rc)) + { + MEASUREITEMSTRUCT mis; + ZeroMemory(&mis, sizeof(MEASUREITEMSTRUCT)); + mis.CtlID = GetDlgCtrlID(hwnd); + mis.CtlType = ODT_LISTBOX; + mis.itemID = item; + mis.itemData = (DWORD)SendMessageW(hwnd, LB_GETITEMDATA, (WPARAM)item, 0L); + if (SendMessageW(GetParent(hwnd), WM_MEASUREITEM, mis.CtlID, (LPARAM)&mis)) + { + rc.right = rc.left + mis.itemWidth; + POINT pt; + POINTSTOPOINT(pt, pts); + if (PtInRect(&rc, pt)) + { + EnsureFocused(hwnd); + if (0 == ((MK_CONTROL | MK_SHIFT) & uFlags)) SendMessageW(hwnd, LB_SETSEL, FALSE, (LPARAM)-1); + if (MK_SHIFT & uFlags) + { + INT anchor = (INT)SendMessageW(hwnd, LB_GETANCHORINDEX, 0, 0L); + INT start = (anchor < (INT)item) ? anchor : item; + INT stop = (start == anchor) ? item : anchor; + SendMessageW(hwnd, LB_SETSEL, FALSE, (LPARAM)-1); + SendMessageW(hwnd, LB_SELITEMRANGEEX, (WPARAM)start, (LPARAM)stop); + SendMessageW(hwnd, LB_SETANCHORINDEX, (WPARAM)anchor, 0L); + } + else + { + SendMessageW(hwnd, LB_SETSEL, (0 == (MK_CONTROL & uFlags) || 0 == sel), (LPARAM)item); + } + + EmulateSelectionChanged(hwnd, item, mis.CtlID); + return TRUE; + } + } + } + } + else + { + EnsureFocused(hwnd); + return TRUE; + } + + return FALSE; +} + +static BOOL FBListBox_OnLButtonUp(HWND hwnd, UINT uFlags, POINTS pts) +{ + ResetMultiSelect(&g_MultiSelect); + + if (hwnd == GetCapture()) + ReleaseCapture(); + + INT count = (INT)SendMessageW(hwnd, LB_GETCOUNT, 0, 0L); + if (count > 0) + { + DWORD item = (DWORD)SendMessageW(hwnd, LB_ITEMFROMPOINT, 0, *((LPARAM*)&pts)); + if (HIWORD(item)) return FALSE; + item = LOWORD(item); + INT sel = (INT)SendMessageW(hwnd, LB_GETSEL, item, 0L); + if (sel > 0 && hwnd == g_hDragSource) + { + if ((INT)SendMessageW(hwnd, LB_GETSELCOUNT, item, 0L) > 0) + { + SendMessageW(hwnd, LB_SETSEL, FALSE, (LPARAM)-1); + SendMessageW(hwnd, LB_SETSEL, TRUE, (LPARAM)item); + EmulateSelectionChanged(hwnd, item, GetDlgCtrlID(hwnd)); + } + } + } + + g_hDragSource = NULL; + + return FALSE; +} + +static BOOL FBListBox_OnMouseMove(HWND hwnd, UINT uFlags, POINTS pts) +{ + if (g_hDragSource == hwnd) return TRUE; + if (g_MultiSelect.hOwner == hwnd) + { + RECT rc; + if (GetClientRect(hwnd, &rc)) + pts.x = (SHORT)(rc.left + (rc.right - rc.left)/2); + + DWORD item = (DWORD)SendMessageW(hwnd, LB_ITEMFROMPOINT, 0, *((LPARAM*)&pts)); + BOOL bNotify = FALSE; + + if (item != g_MultiSelect.nTrack) + { + INT nCurrent = LOWORD(item); + + INT nTrack = LOWORD(g_MultiSelect.nTrack); + if (abs(nCurrent - g_MultiSelect.nStart) < abs(nTrack - g_MultiSelect.nStart)) + { + INT first = (nTrack < g_MultiSelect.nStart) ? g_MultiSelect.nStart : nTrack; + INT last = (first == nTrack) ? g_MultiSelect.nStart : nTrack; + LRESULT r = SendMessageW(hwnd, LB_SELITEMRANGEEX, (WPARAM)first, (LPARAM)last); + if (!bNotify) bNotify = (LB_ERR != r); + } + + if (nCurrent != g_MultiSelect.nStart) + { + INT first = (nCurrent > g_MultiSelect.nStart) ? g_MultiSelect.nStart : nCurrent; + INT last = (first == nCurrent) ? g_MultiSelect.nStart : nCurrent; + + LRESULT r = SendMessageW(hwnd, LB_SELITEMRANGEEX, (WPARAM)first, (LPARAM)last); + if (!bNotify) bNotify = (LB_ERR != r); + } + else if (0 == HIWORD(item)) + { + LRESULT r = SendMessageW(hwnd, LB_SETSEL, TRUE, (LPARAM)nCurrent); + if (!bNotify) bNotify = (LB_ERR != r); + } + g_MultiSelect.nTrack = (INT)item; + } + else if (0 != HIWORD(g_MultiSelect.nTrack) && 0 != HIWORD(item) && + LOWORD(item) == g_MultiSelect.nStart && + (INT)SendMessageW(hwnd, LB_GETSEL, (WPARAM)g_MultiSelect.nStart, 0L) > 0) + { + BOOL bReset = TRUE; + if (1 == (INT)SendMessageW(hwnd, LB_GETCOUNT, 0, 0L)) + { + RECT ri; + if (LB_ERR != SendMessageW(hwnd, LB_GETITEMRECT, (WPARAM)g_MultiSelect.nStart, (LPARAM)&ri)) + { + LONG t = (pts.y < g_MultiSelect.ptStart.y) ? pts.y : g_MultiSelect.ptStart.y; + LONG b = (t != pts.y) ? pts.y : g_MultiSelect.ptStart.y; + bReset = !(t < ri.bottom && b > ri.top); + } + } + if (bReset) + { + LRESULT r = SendMessageW(hwnd, LB_SETSEL, FALSE, (LPARAM)g_MultiSelect.nStart); + if (!bNotify) bNotify = (LB_ERR != r); + } + } + + if (bNotify) + { + HWND hParent = GetParent(hwnd); + if (NULL != hParent) + SendMessageW(hParent, WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(hwnd), LBN_SELCHANGE), (LPARAM)hwnd); + } + } + return FALSE; +} + +static BOOL FBListBox_OnMouseWheel(HWND hwnd, UINT vkCode, INT delta, POINTS pts) +{ + if (MK_CONTROL == vkCode) + { + PostMessageW(GetParent(hwnd), WM_MOUSEWHEEL, MAKEWPARAM(vkCode, delta), MAKELPARAM(pts.x, pts.y)); + return TRUE; + } + return FALSE; +} + +static BOOL FBListBox_OnKeyDown(HWND hwnd, UINT vkCode, UINT flags) +{ + UINT uiState = 0; + switch(vkCode) + { + case VK_CONTROL: + case VK_SHIFT: + return FALSE; + case VK_MENU: + uiState = (UINT)SendMessageW(hwnd, WM_QUERYUISTATE, 0, 0L); + if (UISF_HIDEACCEL & uiState) + SendMessageW(hwnd, WM_CHANGEUISTATE, MAKELONG(UIS_CLEAR, UISF_HIDEACCEL), 0L); + return FALSE; + } + + uiState = (UINT)SendMessageW(hwnd, WM_QUERYUISTATE, 0, 0L); + if (UISF_HIDEFOCUS & uiState) + { + SendMessageW(hwnd, WM_CHANGEUISTATE, MAKELONG(UIS_CLEAR, UISF_HIDEFOCUS), 0L); + } + + if (0 != (0x80000000 & GetAsyncKeyState(VK_CONTROL))) + { + switch(vkCode) + { + case VK_UP: + case VK_DOWN: + case VK_PRIOR: + case VK_NEXT: + case VK_END: + case VK_HOME: + { + INT count = (INT)SendMessageW(hwnd, LB_GETCOUNT, 0, 0L); + INT caret = (INT)SendMessageW(hwnd, LB_GETCARETINDEX, 0, 0L); + INT caretNew = caret; + INT page = 0; + if (count > 0) + { + RECT rc; + INT h = (INT)SendMessageW(hwnd, LB_GETITEMHEIGHT, 0, 0L); + if (h != -1 && GetClientRect(hwnd, &rc)) + { + page = (rc.bottom - rc.top)/(h) - 1; + if (page < 1) page = 1; + } + } + switch(vkCode) + { + case VK_UP: caretNew = caret - 1; break; + case VK_DOWN: caretNew = caret + 1; break; + case VK_PRIOR: caretNew = caret - page; break; + case VK_NEXT: caretNew = caret + page; break; + case VK_END: caretNew = count - 1; break; + case VK_HOME: caretNew = 0; break; + } + if (caretNew < 0) caretNew = 0; + if (caretNew >= count) caretNew = count - 1; + if (caret != caretNew && -1 != caretNew) + SendMessageW(hwnd, LB_SETCARETINDEX, (WPARAM)caretNew, FALSE); + } + return TRUE; + case VK_SPACE: + { + INT caret = (INT)SendMessageW(hwnd, LB_GETCARETINDEX, 0, 0L); + if (-1 != caret) + { + INT selected = (INT)SendMessageW(hwnd, LB_GETSEL, caret, 0L); + SendMessageW(hwnd, LB_SETSEL, (0 == selected), (LPARAM)caret); + HWND hParent = GetParent(hwnd); + if (NULL != hParent) + SendMessageW(hParent, WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(hwnd), LBN_SELCHANGE), (LPARAM)hwnd); + } + } + return TRUE; + } + } + return FALSE; +} + +static BOOL FBListBox_OnNcHitTest(HWND hwnd, POINTS pts, LRESULT *pResult) +{ + if (SIZER_OVERLAP_LEFT > 0 || SIZER_OVERLAP_RIGHT > 0) + { + RECT rw; + if (GetWindowRect(hwnd, &rw)) + { + if ((SIZER_OVERLAP_RIGHT > 0 && pts.x >= rw.left && pts.x <= (rw.left + SIZER_OVERLAP_RIGHT)) || + (SIZER_OVERLAP_LEFT > 0 && pts.x <= rw.right && pts.x >= (rw.right - SIZER_OVERLAP_LEFT))) + { + *pResult = HTTRANSPARENT; + return TRUE; + } + } + } + return FALSE; +} + +static LRESULT CALLBACK FBListBox_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + WNDPROC fnOriginalProc = (WNDPROC)GetPropW(hwnd, NAVMGR_FBLISTBOXW); + if (!fnOriginalProc) return DefWindowProcW(hwnd, uMsg, wParam, lParam); + + switch(uMsg) + { + case WM_NCDESTROY: + SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (LONGX86)(LONG_PTR)fnOriginalProc); + RemovePropW(hwnd, NAVMGR_FBLISTBOXW); + return CallWindowProcW(fnOriginalProc, hwnd, uMsg, wParam, lParam); + case WM_LBUTTONDOWN: + if (FBListBox_OnLButtonDown(hwnd, (UINT)wParam, MAKEPOINTS(lParam))) return 0; + break; + case WM_LBUTTONUP: + if (FBListBox_OnLButtonUp(hwnd, (UINT)wParam, MAKEPOINTS(lParam))) return 0; + break; + case WM_MOUSEWHEEL: + if (FBListBox_OnMouseWheel(hwnd, GET_KEYSTATE_WPARAM(wParam), GET_WHEEL_DELTA_WPARAM(wParam), MAKEPOINTS(lParam))) return 0; + break; + case WM_MOUSEMOVE: + if (FBListBox_OnMouseMove(hwnd, (UINT)wParam, MAKEPOINTS(lParam))) return 0; + break; + case WM_KEYDOWN: + if (FBListBox_OnKeyDown(hwnd, (UINT)wParam, (UINT)lParam)) return 0; + break; + case WM_UPDATEUISTATE: + InvalidateRect(hwnd, NULL, FALSE); + break; + case WM_NCHITTEST: + { + LRESULT lResult; + if (FBListBox_OnNcHitTest(hwnd, MAKEPOINTS(lParam), &lResult)) + return lResult; + } + break; + } + return CallWindowProcW(fnOriginalProc, hwnd, uMsg, wParam, lParam); +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/folderbrowser.cpp b/Src/Plugins/General/gen_ml/folderbrowser.cpp new file mode 100644 index 00000000..34fd8472 --- /dev/null +++ b/Src/Plugins/General/gen_ml/folderbrowser.cpp @@ -0,0 +1,2271 @@ +#include "./folderbrowser.h" +#include "./folderbrowser_internal.h" +#include "./stringvector.h" +#include +#include "../Winamp/wa_dlg.h" +#include "./skinnedlistbox.h" +#include "./colors.h" + +#include +#include + + + +static LRESULT CALLBACK FolderBrowser_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + + +typedef struct _FBITEM +{ + INT index; + DWORD styles; +} FBITEM; + +typedef struct _FBCOLUMN +{ + INT bufferOffset; + INT count; + INT firstVisible; + INT firstSelected; + INT width; + BOOL autoAdjust; + FBITEM *pItems; +} FBCOLUMN; + + + +typedef struct _FBDATA +{ + COLORREF rgbBk; + COLORREF rgbText; + std::vector *pColumns; + StringVector *pBuffer; + HWND hwndDraw; + HWND hwndActive; + LPWSTR pszRoot; + int focusedColumn; + // filesystem + FILESYSTEMINFO filesystem; +} FBDATA; + +static int clickoffs = 0; +static size_t hiddenActive = -1; +static size_t sizerActive = -1; +static size_t sizerHover = -1; +#define GetFolderBrowser(__hwnd) ((FBDATA*)(LONG_PTR)(LONGX86)GetWindowLongPtrW((__hwnd), 0)) + + +BOOL RegisterFolderBrowserControl(HINSTANCE hInstance) +{ + WNDCLASSW wc; + if (GetClassInfoW(hInstance, FOLDERBROWSER_NAME, &wc)) return TRUE; + ZeroMemory(&wc, sizeof(WNDCLASSW)); + + wc.hInstance = hInstance; + wc.lpszClassName = FOLDERBROWSER_NAME; + wc.lpfnWndProc = FolderBrowser_WindowProc; + wc.style = CS_DBLCLKS | CS_GLOBALCLASS; + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = NULL; + wc.cbWndExtra = sizeof(FBDATA*); + + return ( 0 != RegisterClassW(&wc)); + +} + + + +BOOL FolderBrowser_CustomizeListBox(HWND hwndListbox); + + +__inline size_t FolderBrowser_GetListBoxColumn(HWND hwndList) +{ + SetLastError(0); + size_t c = (size_t)GetWindowLongPtrW(hwndList, GWLP_USERDATA); + return (ERROR_SUCCESS == GetLastError()) ? c : ((size_t)-1); +} + +static BOOL FolderBrowser_GetAdjustedClientRect(HWND hwnd, RECT *prc) +{ + if (!GetClientRect(hwnd, prc)) + return FALSE; + + SCROLLINFO si; + si.cbSize = sizeof(SCROLLINFO); + si.fMask = SIF_POS; + if (!GetScrollInfo(hwnd, SB_HORZ, &si)) ZeroMemory(&si, sizeof(SCROLLINFO)); + prc->left -= si.nPos; + return TRUE; +} +static BOOL PrepareDrawingListBox(HWND hwndList, FBCOLUMN *pc, LONG height, size_t columnId) +{ + if (-1 != columnId && columnId == (size_t)GetWindowLongPtrW(hwndList, GWLP_USERDATA)) + return TRUE; + + + SetWindowLongPtrW(hwndList, GWLP_USERDATA, (LONGX86)columnId); + SetWindowPos(hwndList, NULL, 0, 0, pc->width, height, + SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE | SWP_NOREDRAW | SWP_NOSENDCHANGING); + if (pc->count) + { + SendMessageW(hwndList, LB_SETCOUNT, pc->count, 0L); + SendMessageW(hwndList, LB_SETTOPINDEX, pc->firstVisible, 0L); + } + else + { + SendMessageW(hwndList, LB_SETCOUNT, 1, 0L); // ** Keep this two messages + SendMessageW(hwndList, LB_SETTOPINDEX, 0, 0L); // ** for skinned scrollbars + } + MLSkinnedScrollWnd_UpdateBars(hwndList, FALSE); + + return TRUE; +} + +static void RefreshListBoxNC(HWND hHost, HWND hList, POINT ptViewport) +{ + UINT flags = DCX_PARENTCLIP | DCX_CACHE | DCX_WINDOW | DCX_CLIPSIBLINGS | + DCX_INTERSECTUPDATE | DCX_VALIDATE; + HDC hdc = GetDCEx(hHost, NULL, flags); + if (NULL != hdc) + { + POINT ptOrig; + SetViewportOrgEx(hdc, ptViewport.x, ptViewport.y, &ptOrig); + SendMessageW(hList, WM_PRINT, (WPARAM)hdc, (LPARAM)PRF_NONCLIENT); + SetViewportOrgEx(hdc, ptOrig.x, ptOrig.y, NULL); + ReleaseDC(hHost, hdc); + } +} + +static void FolderBrowser_UpdateScrollInfo(HWND hwnd) +{ + RECT rc; + + FBDATA *pfb = GetFolderBrowser(hwnd); + if (!pfb) return; + + GetClientRect(hwnd, &rc); + LONG totalWidth = 0; + for(size_t i = 0; i < pfb->pColumns->size(); i++) + { + totalWidth += (pfb->pColumns->at(i).width + SIZER_WIDTH); + } + SCROLLINFO si; + si.cbSize = sizeof(SCROLLINFO); + si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS; + if (GetScrollInfo(hwnd, SB_HORZ, &si) && si.nMax != totalWidth) + { + INT dx = 0; + si.fMask = SIF_RANGE | SIF_DISABLENOSCROLL; + si.nMin = 0; + si.nMax = totalWidth; + if (si.nPage != rc.right - rc.left) + { + si.nPage = rc.right - rc.left; + si.fMask |= SIF_PAGE; + } + if ((si.nPos + si.nPage) > (UINT)si.nMax && si.nPos > si.nMin) si.nMax = si.nPos + si.nPage; + SetScrollInfo(hwnd, SB_HORZ, &si, FALSE); + + RECT rw; + if (dx && pfb->hwndActive && + (WS_VISIBLE & GetWindowLongPtrW(pfb->hwndActive, GWL_STYLE)) + && GetWindowRect(pfb->hwndActive, &rw)) + { + MapWindowPoints(HWND_DESKTOP, hwnd, (POINT*)&rw, 2); + SetWindowPos(pfb->hwndActive, NULL, rw.left + dx, rw.top, 0, 0, + SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSENDCHANGING | SWP_NOSIZE); + } + } +} +static INT FolderBrowser_GetPreferredColumnWidth(HWND hwnd, size_t columnIndex) +{ + FBCOLUMN *pc; + INT nameWidth = 0, prevMaxLen = 0; + HDC hdc; + HFONT hf, hfo = NULL; + SIZE size; + size_t count; + + FBDATA *pfb = GetFolderBrowser(hwnd); + if (!pfb) return -1; + if (columnIndex >= pfb->pColumns->size()) return -1; + + pc = &pfb->pColumns->at(columnIndex); + count = pc->bufferOffset + pc->count; + if (count > pfb->pBuffer->Count()) count = pfb->pBuffer->Count(); + + hdc = GetDCEx(hwnd, NULL, DCX_CACHE); + if (!hdc) return -1; + + hf = (HFONT)SendMessageW((pfb->hwndDraw) ? pfb->hwndDraw : hwnd, WM_GETFONT, 0, 0L); + if (NULL == hf) hf = (HFONT)MlStockObjects_Get(DEFAULT_FONT); + if (NULL != hf) hfo = (HFONT)SelectObject(hdc, hf); + + for (size_t i = pc->bufferOffset; i < count; i++) + { + LPCWSTR pszText = pfb->pBuffer->GetString(i); + INT len = (pszText) ? lstrlenW(pszText) : 0; + + if (len > 0 && len > (prevMaxLen - 3) && + hdc && GetTextExtentPoint32W(hdc, pszText, len, &size) && + size.cx > nameWidth) + { + nameWidth = size.cx; + prevMaxLen = len; + } + } + + if (NULL != hfo) SelectObject(hdc, hfo); + ReleaseDC(hwnd, hdc); + + nameWidth += 20; + if (nameWidth < COLUMN_MIN_WIDTH) nameWidth = COLUMN_MIN_WIDTH; + if (nameWidth > COLUMN_MAX_WIDTH) nameWidth = COLUMN_MAX_WIDTH; + + return nameWidth; +} + +static StringVector *g_pCompareBuffer = NULL; +static INT g_szCompareOffset = 0; +static INT __cdecl FolderBrowser_CompareFolderNames(const void *elem1, const void *elem2) +{ + return (CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, + g_pCompareBuffer->GetString(g_szCompareOffset + ((FBITEM*)elem1)->index), -1, + g_pCompareBuffer->GetString(g_szCompareOffset + ((FBITEM*)elem2)->index), -1) - 2); +} + +static void FolderBrowser_SortColumn(HWND hwnd, size_t columnIndex) +{ + FBDATA *pfb = GetFolderBrowser(hwnd); + if (!pfb || columnIndex >= pfb->pColumns->size()) return; + + FBCOLUMN *pc = &pfb->pColumns->at(columnIndex); + if (pc->count == 0 || !pc->pItems) return; + + g_pCompareBuffer = pfb->pBuffer; + g_szCompareOffset = pc->bufferOffset; + qsort(pc->pItems, pc->count, sizeof(FBITEM), FolderBrowser_CompareFolderNames); + g_pCompareBuffer = NULL; +} + +typedef struct _FINDKEY +{ + LPCWSTR pszKey; + INT cchKey; + FBCOLUMN *pCol; + INT foundIndex; + StringVector *pBuffer; +} FINDKEY; + +static INT __cdecl FolderBrowser_FindKey(const void *key, const void *elem) +{ + FINDKEY *pfk = (FINDKEY*)key; + LPCWSTR pszTest = pfk->pBuffer->GetString(pfk->pCol->bufferOffset + ((FBITEM*)elem)->index); + return (CompareStringW(LOCALE_USER_DEFAULT, NORM_IGNORECASE, pfk->pszKey, pfk->cchKey, + pfk->pBuffer->GetString(pfk->pCol->bufferOffset + ((FBITEM*)elem)->index), -1) - 2); +} + +static INT FoderBrowser_FindFolder(StringVector *pBuffer, FBCOLUMN *pColumn, LPCWSTR pszFolder, INT cchFolder) +{ + FINDKEY fk; + fk.pCol = pColumn; + fk.pszKey = pszFolder; + fk.cchKey = cchFolder; + fk.pBuffer = pBuffer; + if (!pBuffer || !pszFolder) return -1; + + FBITEM *pi = (FBITEM*)bsearch(&fk, pColumn->pItems, pColumn->count, sizeof(FBITEM), FolderBrowser_FindKey); + if (!pi) return -1; + return (INT)(pi - pColumn->pItems); +} + +static INT FolderBrowser_AddColumn(HWND hwnd, LPCWSTR pszPath, INT cchPath, INT width, BOOL bAutoAdjust) +{ + HANDLE hFile; + WIN32_FIND_DATAW fd = {0}; + wchar_t szSearch[2 * MAX_PATH + 4] = {0}; + + FBDATA *pfb = GetFolderBrowser(hwnd); + if (!pfb || !pszPath) return -1; + if (cchPath < 0) cchPath = lstrlenW(pszPath); + if (S_OK != StringCchCopyNW(szSearch, sizeof(szSearch)/sizeof(szSearch[0]), pszPath, cchPath) || + S_OK != StringCchCatW(szSearch, sizeof(szSearch)/sizeof(szSearch[0]), L"\\*")) return -1; + + FBCOLUMN col; + ZeroMemory(&col, sizeof(FBCOLUMN)); + col.bufferOffset = (INT)pfb->pBuffer->Count(); + col.firstSelected = -1; + col.width = width; + col.autoAdjust = bAutoAdjust; + + DWORD ws = GetWindowLongPtrW(hwnd, GWL_STYLE); + + hFile = pfb->filesystem.fnFindFirstFile(szSearch, &fd); + if (INVALID_HANDLE_VALUE != hFile) + { + do + { + if (0 != (FILE_ATTRIBUTE_DIRECTORY & fd.dwFileAttributes) && + (0 == (FBS_IGNOREHIDDEN & ws) || 0 == (FILE_ATTRIBUTE_HIDDEN & fd.dwFileAttributes)) && + (0 == (FBS_IGNORESYSTEM & ws) || 0 == (FILE_ATTRIBUTE_SYSTEM & fd.dwFileAttributes)) && + !(L'.' == fd.cFileName[0] && (L'\0' == fd.cFileName[1] || (L'.' == fd.cFileName[1] && L'\0' == fd.cFileName[2])))) + { + pfb->pBuffer->Add(fd.cFileName); + col.count++; + } + } while (pfb->filesystem.fnFindNextFile(hFile, &fd)); + pfb->filesystem.fnFindClose(hFile); + } + + pfb->pColumns->push_back(col); + + if (pfb->pColumns->size() > 0) + { + FBCOLUMN *pc = &pfb->pColumns->back(); + if (pc->autoAdjust) + { + width = FolderBrowser_GetPreferredColumnWidth(hwnd, pfb->pColumns->size() - 1); + if (width < COLUMN_DEFAULT_WIDTH) width = COLUMN_DEFAULT_WIDTH; + } + else if (-1 == width) width = COLUMN_DEFAULT_WIDTH; + pc->width = width; + + if (pc->count) + { + FBITEM *pi = (FBITEM*)calloc(pc->count, sizeof(FBITEM)); + if (pi) + { + for (INT i = 0; i < pc->count; i++) + { + pi[i].index = i; + pi[i].styles = 0; + } + pc->pItems = pi; + } + } + FolderBrowser_SortColumn(hwnd, pfb->pColumns->size() - 1); + } + + return col.count; +} + +static INT FolderBrowser_CalculateListItemHeight(HWND hwndList) +{ + HFONT hf, hfo; + TEXTMETRIC tm; + HDC hdc = GetDCEx(hwndList, NULL, DCX_CACHE); + if (!hdc) return 20; + + hf = (HFONT)SendMessageW(hwndList, WM_GETFONT, 0, 0L); + if (NULL == hf) hf = (HFONT)MlStockObjects_Get(DEFAULT_FONT); + hfo = (NULL != hf) ? (HFONT)SelectObject(hdc, hf) : NULL; + if (!GetTextMetrics(hdc, &tm)) tm.tmHeight = 20; + if (hfo) SelectObject(hdc, hfo); + ReleaseDC(hwndList, hdc); + + return tm.tmHeight + 2; +} + +static INT FolderBrowser_OnGetCurrentPath(HWND hwnd, LPWSTR pszPath, INT cchMax) +{ + HRESULT hr; + FBCOLUMN *pc; + + if (!pszPath || cchMax < 1) return 0; + pszPath[0] = L'\0'; + + FBDATA *pfb = GetFolderBrowser(hwnd); + if (!pfb || !pfb->pszRoot) return 0; + size_t r = cchMax; + + for(size_t i = 0; i < pfb->pColumns->size(); i++) + { + pc = &pfb->pColumns->at(i); + if (-1 == pc->firstSelected) break; + + size_t textIndex = pc->firstSelected; + if (pc->pItems && textIndex < (size_t)pc->count) textIndex = pc->pItems[textIndex].index; + textIndex += pc->bufferOffset; + + LPCWSTR pszText = (textIndex < pfb->pBuffer->Count()) ? pfb->pBuffer->GetString(textIndex) : NULL; + if (!pszText) return 0; + + if (r < 2) hr= STRSAFE_E_INSUFFICIENT_BUFFER; + else + { + if (0 != i) { *pszPath = L'\\'; r--; pszPath++; } + hr = StringCchCopyExW(pszPath, r, pszText, &pszPath, &r, STRSAFE_IGNORE_NULLS); + } + if (S_OK != hr) return 0; + } + + return (cchMax - (INT)r); +} + +static BOOL FolderBrowser_HideActive(HWND hwnd) +{ + FBDATA *pfb = GetFolderBrowser(hwnd); + if (!pfb || -1 != hiddenActive || + 0 == (WS_VISIBLE & GetWindowLongPtrW(pfb->hwndActive, GWL_STYLE))) return FALSE; + hiddenActive = FolderBrowser_GetListBoxColumn(pfb->hwndActive); + + if (hiddenActive < pfb->pColumns->size()) + { + FBCOLUMN *pc = &pfb->pColumns->at(hiddenActive); + if (pc) pc->firstVisible = (INT)SendMessageW(pfb->hwndActive, LB_GETTOPINDEX, 0, 0L); + if (pfb->focusedColumn == hiddenActive) SetFocus(hwnd); + SetWindowLongPtrW(pfb->hwndActive, GWLP_USERDATA, (LONGX86)-1); + ShowWindow(pfb->hwndActive, SW_HIDE); + } + return TRUE; +} + +static BOOL FolderBrowser_RestoreActive(HWND hwnd) +{ + FBDATA *pfb = GetFolderBrowser(hwnd); + if (!pfb || -1 == hiddenActive) return FALSE; + if (hiddenActive >= pfb->pColumns->size()) + { + hiddenActive = -1; + return FALSE; + } + + RECT rw; + GetWindowRect(pfb->hwndActive, &rw); + MapWindowPoints(HWND_DESKTOP, hwnd, (POINT*)&rw, 2); + SetWindowLongPtrW(pfb->hwndActive, GWLP_USERDATA, (LONGX86)hiddenActive); + + SCROLLINFO si; + si.cbSize = sizeof(SCROLLINFO); + si.fMask = SIF_POS; + if(!GetScrollInfo(hwnd, SB_HORZ, &si)) si.nPos = 0; + LONG left = -si.nPos; + + for (size_t i=0; i < hiddenActive; i++) left += (pfb->pColumns->at(i).width + SIZER_WIDTH); + LONG width = pfb->pColumns->at(hiddenActive).width; + if (left != rw.left || width != (rw.right - rw.left)) + { + SetWindowPos(pfb->hwndActive, NULL, left, rw.top, width, rw.bottom - rw.top, + SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSENDCHANGING | + ((left == rw.left) ? SWP_NOMOVE : 0) | + ((width == (rw.right - rw.left)) ? SWP_NOSIZE : 0)); + } + + ShowWindow(pfb->hwndActive, SW_SHOWNA); + if (pfb->focusedColumn == hiddenActive) SetFocus(pfb->hwndActive); + hiddenActive = -1; + + return TRUE; +} + +static INT FolderBrowser_ScrollWindow(HWND hwnd, INT dx, UINT smoothTime, BOOL bRedraw) +{ + SCROLLINFO si; + size_t hiddenActive = -1; + FBDATA *pfb = GetFolderBrowser(hwnd); + if (!pfb) return 0; + + si.cbSize = sizeof(SCROLLINFO); + si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS; + if (!GetScrollInfo(hwnd, SB_HORZ, &si)) return 0; + + if ((si.nPos + dx) < si.nMin) dx = si.nMin - si.nPos; + else if ((si.nPos + dx) > (si.nMax - (INT)si.nPage)) + { + dx = si.nMax - si.nPos - si.nPage; + if (dx < 0) dx = 0; + } + if (dx == 0) return 0; + + SendMessageW(hwnd, WM_SETREDRAW, FALSE, 0L); + + if (pfb && (WS_VISIBLE & GetWindowLongPtrW(pfb->hwndActive, GWL_STYLE))) + { + hiddenActive = FolderBrowser_GetListBoxColumn(pfb->hwndActive); + if (-1 != hiddenActive) + { + pfb->pColumns->at(hiddenActive).firstVisible = (INT)SendMessageW(pfb->hwndActive, LB_GETTOPINDEX, 0, 0L); + + if (pfb->focusedColumn == hiddenActive) SetFocus(hwnd); + SetWindowLongPtrW(pfb->hwndActive, GWLP_USERDATA, (LONGX86)-1); + ShowWindow(pfb->hwndActive, SW_HIDE); + } + } + + si.fMask = SIF_POS; + si.nPos += dx; + SetScrollInfo(hwnd, SB_HORZ, &si, FALSE); + + if (bRedraw || smoothTime) SendMessageW(hwnd, WM_SETREDRAW, TRUE, 0L); + if (smoothTime) ScrollWindowEx(hwnd, -dx, 0, NULL, NULL, NULL, NULL, MAKELPARAM(SW_SMOOTHSCROLL, 150)); + else ScrollWindowEx(hwnd, -dx, 0, NULL, NULL, NULL, NULL, ((bRedraw) ? (SW_INVALIDATE | SW_ERASE) : 0)); + + if (-1 != hiddenActive) + { + RECT rw; + GetWindowRect(pfb->hwndActive, &rw); + MapWindowPoints(HWND_DESKTOP, hwnd, (POINT*)&rw, 1); + + SetWindowLongPtrW(pfb->hwndActive, GWLP_USERDATA, (LONGX86)hiddenActive); + SetWindowPos(pfb->hwndActive, NULL, rw.left - dx, rw.top, 0, 0, + SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE | SWP_NOSENDCHANGING | + ((bRedraw) ? 0 : SWP_NOREDRAW)); + + ShowWindow(pfb->hwndActive, SW_SHOWNA); + if (pfb->focusedColumn == hiddenActive) SetFocus(pfb->hwndActive); + } + if (!bRedraw && !smoothTime) SendMessageW(hwnd, WM_SETREDRAW, TRUE, 0L); + + return dx; +} + +static INT FolderBrowser_OnEnsureVisible(HWND hwnd, size_t column, UINT uFlags) +{ + RECT rc; + SCROLLINFO si; + FBDATA *pfb = GetFolderBrowser(hwnd); + if (!pfb) return 0; + LONG left; + FBCOLUMN *pc; + + size_t count = pfb->pColumns->size(); + if (column >= count) return 0; + + si.cbSize = sizeof(SCROLLINFO); + si.fMask = SIF_POS | SIF_RANGE | SIF_PAGE; + if (!GetScrollInfo(hwnd, SB_HORZ, &si) || !GetClientRect(hwnd, &rc)) return 0; + + pc = NULL; + left = -si.nPos; + for (size_t i = 0; i < column; i++) + { + pc = &pfb->pColumns->at(i); + left += (pc->width + SIZER_WIDTH); + } + + INT dx = 0; + if (left < rc.left) + { + dx = left - rc.left; + if (0 == (EVF_NOEXTRALSPACE & uFlags) && column != 0) dx -= COLUMN_EXTRALSPACE; + } + else + { + LONG ol = left; + if (0 == (EVF_NOEXTRALSPACE & uFlags) && column != 0) ol -= COLUMN_EXTRALSPACE; + left += (pfb->pColumns->at(column).width + SIZER_WIDTH); + if (0 == (EVF_NOEXTRARSPACE & uFlags) && column < (count - 1)) + left += (pfb->pColumns->at(column + 1).width + SIZER_WIDTH); + if (left > rc.right) dx = left - rc.right; + + if (ol - dx < rc.left) + { + dx = ol - rc.left; + } + if ((INT)si.nPage > si.nMax) si.nPage = si.nMax; + if ((si.nPos + dx) > (si.nMax - (INT)si.nPage)) + { + si.nMax = si.nPos + dx + si.nPage; + si.fMask = SIF_RANGE; + SetScrollInfo(hwnd, SB_HORZ, &si, FALSE); + } + } + if (dx == 0) return 0; + return FolderBrowser_ScrollWindow(hwnd, dx, 250, 0 == (EVF_NOREDRAW & uFlags)); +} + +static INT FolderBrowser_SaveActiveSelection(HWND hwnd) +{ + FBCOLUMN *pc; + FBDATA *pfb = GetFolderBrowser(hwnd); + if (!pfb) return -1; + + size_t activeColumn = FolderBrowser_GetListBoxColumn(pfb->hwndActive); + if (activeColumn >= pfb->pColumns->size()) return -1; + pc = &pfb->pColumns->at(activeColumn); + if (!pc) return -1; + + for (INT i = 0; i < pc->count; i++) + pc->pItems[i].styles &= ~FBIS_SELECTED; + + INT count = (INT)SendMessageW(pfb->hwndActive, LB_GETSELCOUNT, 0, 0L); + + INT *pSelection = NULL; + if (count > 0) + { + pSelection = (INT*)calloc((count + 1), sizeof(INT)); + if (LB_ERR == SendMessageW(pfb->hwndActive, LB_GETSELITEMS, count, (LPARAM)pSelection)) + count = 0; + } + + for (INT i = 0; i < count; i++) + { + INT k = pSelection[i]; + if (k < pc->count && k >= 0) + { + pc->pItems[k].styles |= FBIS_SELECTED; + } + } + + INT selectedItem = (1 == count && pSelection) ? pSelection[0] : -1; + if (pSelection) free(pSelection); + return selectedItem; +} + +static void FolderBrowser_OnSelectionChanged(HWND hwnd, BOOL bForceUpdate, BOOL bUpdateUI) +{ + FBCOLUMN *pc; + INT columnWidth = -1; + BOOL bAutoAdjust = TRUE; + size_t activeColumn, selectedItem; + + FBDATA *pfb = GetFolderBrowser(hwnd); + if (!pfb) return; + + activeColumn = FolderBrowser_GetListBoxColumn(pfb->hwndActive); + if (activeColumn >= pfb->pColumns->size()) return; + pc = &pfb->pColumns->at(activeColumn); + + selectedItem = FolderBrowser_SaveActiveSelection(hwnd); + + if (activeColumn < (pfb->pColumns->size() -1)) + { + columnWidth = pfb->pColumns->at(activeColumn + 1).width; + bAutoAdjust = pfb->pColumns->at(activeColumn + 1).autoAdjust; + } + pfb->pBuffer->TrimCount(pc->bufferOffset + pc->count); + while (pfb->pColumns->size() > (activeColumn + 1)) + { + FBCOLUMN *pctmp = &pfb->pColumns->back(); + + if (pctmp->pItems) + { + free(pctmp->pItems); + pctmp->pItems = NULL; + } + pfb->pColumns->pop_back(); + } + + //if (((size_t)-1) != selectedItem) + { + /* if (!bForceUpdate && (size_t)pc->firstSelected == selectedItem) + { + FolderBrowser_OnEnsureVisible(hwnd, activeColumn, 0); + return; + }*/ + + pc->firstSelected = (INT)selectedItem; + wchar_t szPath[2*MAX_PATH] = {0}; + INT cchPath = FolderBrowser_OnGetCurrentPath(hwnd, szPath, sizeof(szPath)/sizeof(szPath[0])); + if (0 != cchPath && ((size_t)-1) != selectedItem) FolderBrowser_AddColumn(hwnd, szPath, cchPath, columnWidth, bAutoAdjust); + } + + if (bUpdateUI) + { + RECT rc; + FolderBrowser_GetAdjustedClientRect(hwnd, &rc); + + for(size_t i = 0; i <= activeColumn; i++) rc.left += (pfb->pColumns->at(i).width + SIZER_WIDTH); + + FolderBrowser_OnEnsureVisible(hwnd, activeColumn, EVF_NOREDRAW); + FolderBrowser_UpdateScrollInfo(hwnd); + + InvalidateRect(hwnd, &rc, TRUE); + UpdateWindow(hwnd); + if (pfb->hwndActive) UpdateWindow(pfb->hwndActive); + } + + HWND hwndParent = GetParent(hwnd); + if (NULL != hwndParent) + { + NMHDR hdr; + hdr.code = FBN_SELCHANGED; + hdr.hwndFrom = hwnd; + hdr.idFrom = GetDlgCtrlID(hwnd); + SendMessageW(hwndParent, WM_NOTIFY, hdr.idFrom, (LPARAM)&hdr); + } +} + +// returns new active column index +static size_t FolderBrowser_UpdateActiveColumn(HWND hwnd, size_t newActiveColumn) +{ + FBDATA *pfb = GetFolderBrowser(hwnd); + if (!pfb) return ((size_t)-1); + + HRGN rgnInvalid = NULL; + FBCOLUMN *pc; + RECT rc, ri; + + size_t activeColumn = FolderBrowser_GetListBoxColumn(pfb->hwndActive); + + GetClientRect(hwnd, &rc); + + if (newActiveColumn >= pfb->pColumns->size()) newActiveColumn = ((size_t)-1); + if (newActiveColumn == -1 && pfb->focusedColumn != -1) + { + CopyRect(&ri, &rc); + for (size_t i = 0; i <= (size_t)pfb->focusedColumn; i++) + { + pc = &pfb->pColumns->at(i); + if (i < (size_t)pfb->focusedColumn) ri.left += (pc->width + SIZER_WIDTH); + else ri.right = ri.left + (pc->width + SIZER_WIDTH); + } + if (NULL == rgnInvalid) rgnInvalid = CreateRectRgnIndirect(&ri); + } + if (activeColumn == newActiveColumn) + { + if (rgnInvalid) + { + InvalidateRgn(hwnd, rgnInvalid, FALSE); + DeleteObject(rgnInvalid); + UpdateWindow(hwnd); + } + return activeColumn; + } + + SetRect(&ri, 0, 0, 0, 0); + if (activeColumn < pfb->pColumns->size()) + { + pc = &pfb->pColumns->at(activeColumn); + pc->firstVisible = (INT)SendMessageW(pfb->hwndActive, LB_GETTOPINDEX, 0, 0L); + + pc->firstSelected = FolderBrowser_SaveActiveSelection(hwnd); + INT selectedCount = (INT)SendMessageW(pfb->hwndActive, LB_GETSELCOUNT, 0, 0L); + + if (-1 == pc->firstSelected && selectedCount > 0) + { + pc->firstSelected = (INT)SendMessageW(pfb->hwndActive, LB_GETANCHORINDEX, 0, 0L); + } + + if (selectedCount > 0 || LB_ERR == SendMessageW(pfb->hwndActive, LB_GETITEMRECT, pc->firstSelected, (LPARAM)&ri)) + GetClientRect(pfb->hwndActive, &ri); + MapWindowPoints(pfb->hwndActive, hwnd, (POINT*)&ri, 2); + } + + if (ri.left != ri.right) + { + if (NULL == rgnInvalid) rgnInvalid = CreateRectRgnIndirect(&ri); + else + { + HRGN rgnTmp = CreateRectRgnIndirect(&ri); + CombineRgn(rgnInvalid, rgnInvalid, rgnTmp, RGN_OR); + DeleteObject(rgnTmp); + } + } + + SetWindowLongPtrW(pfb->hwndActive, GWLP_USERDATA, (LONGX86)newActiveColumn); + + if ((size_t)-1 == newActiveColumn) + { + pfb->focusedColumn = -1; + if ((WS_VISIBLE & GetWindowLongPtrW(pfb->hwndActive, GWL_STYLE))) + { + SendMessageW(pfb->hwndActive, WM_SETREDRAW, FALSE, 0L); + UpdateWindow(hwnd); + SendMessage(hwnd, WM_SETREDRAW, FALSE, 0L); + ShowWindow(pfb->hwndActive, SW_HIDE); + SendMessage(hwnd, WM_SETREDRAW, TRUE, 0L); + } + } + else + { + SendMessageW(pfb->hwndActive, WM_SETREDRAW, FALSE, 0L); + + UpdateWindow(hwnd); + SendMessage(hwnd, WM_SETREDRAW, FALSE, 0L); + + for (size_t i = 0; i < pfb->pColumns->size(); i++) + { + pc = &pfb->pColumns->at(i); + if (i == newActiveColumn) + { + rc.right = rc.left + pc->width; + + SendMessageW(pfb->hwndActive, LB_SETCOUNT, pc->count, 0L); + for (int k = 0; k < pc->count; k++) + { + if (FBIS_SELECTED & pc->pItems[k].styles) + SendMessageW(pfb->hwndActive, LB_SETSEL, TRUE, k); + } + if ((UINT)pc->firstSelected < (UINT)pc->count) + { + SendMessageW(pfb->hwndActive, LB_SETSEL, TRUE, pc->firstSelected); + } + SendMessageW(pfb->hwndActive, LB_SETTOPINDEX, pc->firstVisible, 0L); + + break; + } + else rc.left += (pc->width + SIZER_WIDTH); + } + + SCROLLINFO si; + si.cbSize = sizeof(SCROLLINFO); + si.fMask = SIF_POS; + if (!GetScrollInfo(hwnd, SB_HORZ, &si)) ZeroMemory(&si, sizeof(SCROLLINFO)); + + SetWindowPos(pfb->hwndActive, NULL, rc.left - si.nPos, rc.top, rc.right - rc.left, rc.bottom - rc.top, + SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSENDCHANGING | SWP_NOREDRAW); + + MLSkinnedScrollWnd_UpdateBars(pfb->hwndActive, FALSE); + if (0 == (WS_VISIBLE & GetWindowLongPtrW(pfb->hwndActive, GWL_STYLE))) + ShowWindow(pfb->hwndActive, SW_SHOWNA); + + SendMessage(hwnd, WM_SETREDRAW, TRUE, 0L); + SendMessageW(pfb->hwndActive, WM_CHANGEUISTATE, MAKEWPARAM(UIS_INITIALIZE, UISF_HIDEACCEL | UISF_HIDEFOCUS), 0L); + SendMessageW(pfb->hwndActive, WM_SETREDRAW, TRUE, 0L); + + UpdateWindow(pfb->hwndActive); + } + + if (rgnInvalid) + { + InvalidateRgn(hwnd, rgnInvalid, FALSE); + DeleteObject(rgnInvalid); + UpdateWindow(hwnd); + } + + return newActiveColumn; +} + +static BOOL FolderBrowser_OnSetRootFolder(HWND hwnd, LPCWSTR pszRoot) +{ + FBDATA *pfb = GetFolderBrowser(hwnd); + if (!pfb) return FALSE; + FolderBrowser_UpdateActiveColumn(hwnd, ((size_t)-1)); + pfb->pColumns->clear(); + pfb->pBuffer->Clear(); + pfb->pszRoot = NULL; + if (pszRoot) pfb->pszRoot = _wcsdup(pszRoot); + + FBCOLUMN col; + ZeroMemory(&col, sizeof(FBCOLUMN)); + col.width = COLUMN_DEFAULT_WIDTH; + col.count = 1; + col.pItems = (FBITEM*)calloc(1, sizeof(FBITEM)); + col.pItems[0].index = 0; + + pfb->pBuffer->Add(pszRoot); + pfb->pColumns->push_back(col); + + LRESULT result = FolderBrowser_AddColumn(hwnd, pszRoot, -1, -1, TRUE); + FolderBrowser_UpdateScrollInfo(hwnd); + return ( -1 != result); +} + +static INT FolderBrowser_OnGetRootFolder(HWND hwnd, LPWSTR pszBuffer, INT cchMax) +{ + FBDATA *pfb = GetFolderBrowser(hwnd); + if (!pfb || !pszBuffer) return -1; + + HRESULT hr = StringCchCopyExW(pszBuffer, cchMax, pfb->pszRoot, NULL, (size_t*)&cchMax, STRSAFE_IGNORE_NULLS); + return (S_OK == hr) ? cchMax : -1; +} + +static BOOL FolderBrowser_OnSetCurrentPath(HWND hwnd, LPCWSTR pszPath, BOOL bRedraw) +{ + FBDATA *pfb = GetFolderBrowser(hwnd); + if (!pfb || !pfb->pszRoot) return FALSE; + + BOOL updateLast = FALSE; + DWORD lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT); + size_t column = 0; + INT cchPart; + + LPCWSTR pszCursor, pszPart; + + pszCursor = pszPath; + pszPart = pszCursor; + + if (pszCursor) + { + for (; column < pfb->pColumns->size() && !updateLast; column++, pszCursor++) + { + pszPart = pszCursor; + FBCOLUMN *pc = &pfb->pColumns->at(column); + + if (pc->pItems) + { + for (int i = 0; i < pc->count; i++) + pc->pItems[i].styles = 0; + } + + while (L'\0' != *pszCursor && L'\\' != *pszCursor) pszCursor++; + cchPart = (int)(pszCursor - pszPart); + if (0 == cchPart) + { + pc->firstSelected = -1; + pc->firstVisible = 0; + break; + } + + if (-1 == pc->firstSelected || CSTR_EQUAL != CompareStringW(lcid, NORM_IGNORECASE, pszPart, cchPart, + pfb->pBuffer->GetString(pc->bufferOffset + pc->pItems[pc->firstSelected].index), -1)) + { + pc->firstSelected = FoderBrowser_FindFolder(pfb->pBuffer, pc, pszPart, cchPart); + if (-1 != pc->firstSelected) + { + pc->firstVisible = pc->firstSelected; + updateLast = TRUE; + } + else break; + } + if (-1 != pc->firstSelected) + { + if (pc->pItems && pc->firstSelected < pc->count) pc->pItems[pc->firstSelected].styles |= FBIS_SELECTED; + } + if (L'\0' == *pszCursor) break; + } + } + + if (0 == column) + { + FBCOLUMN *pc = &pfb->pColumns->at(0); + if (pc && pc->count > 0 && pc->pItems) + { + pc->firstSelected = 0; + pc->pItems[pc->firstSelected].styles |= FBIS_SELECTED; + } + if(CSTR_EQUAL == CompareStringW(lcid, NORM_IGNORECASE, pszPart, cchPart, pfb->pszRoot, cchPart)) + { + pszPath = pfb->pszRoot; + pszCursor = pfb->pszRoot + lstrlenW(pszPath); + updateLast = TRUE; + } + } + + if (column == pfb->pColumns->size()) updateLast = TRUE; + + if (column < pfb->pColumns->size()) + { + pfb->pBuffer->TrimCount(pfb->pColumns->at(column).bufferOffset + pfb->pColumns->at(column).count); + while (pfb->pColumns->size() > (column + 1)) + { + FBCOLUMN *pc = &pfb->pColumns->back(); + if (pc->pItems) free(pc->pItems); + pfb->pColumns->pop_back(); + } + } + + while (updateLast) + { + updateLast = FALSE; + INT cchPath = (INT)(pszCursor - pszPath); + if (0 != cchPath && FolderBrowser_AddColumn(hwnd, pszPath, cchPath, -1, TRUE) >= 0) + { + FBCOLUMN *pc = &pfb->pColumns->back(); + pszPart = pszCursor; + while (L'\0' != *pszCursor && L'\\' != *pszCursor) pszCursor++; + cchPart = (int)(pszCursor - pszPart); + if (0 != cchPart) + { + pc->firstSelected = FoderBrowser_FindFolder(pfb->pBuffer, pc, pszPart, cchPart); + if (-1 != pc->firstSelected) + { + pc->firstVisible = pc->firstSelected; + if (pc->pItems && pc->firstSelected < pc->count) pc->pItems[pc->firstSelected].styles |= FBIS_SELECTED; + updateLast = TRUE; + } + if (0x00 != *pszCursor) pszCursor++; + } + column++; + } + } + + FolderBrowser_HideActive(hwnd); + + if (bRedraw) + { + size_t active = column; + if (active >= pfb->pColumns->size()) active = pfb->pColumns->size() - 1; + while(0 != active && -1 == pfb->pColumns->at(active).firstSelected) active--; + FolderBrowser_OnEnsureVisible(hwnd, active, EVF_NOREDRAW); + FolderBrowser_UpdateScrollInfo(hwnd); + } + FolderBrowser_RestoreActive(hwnd); + if (bRedraw) InvalidateRect(hwnd, NULL, TRUE); + return TRUE; +} + +static LRESULT FolderBrowser_OnCreateWindow(HWND hwnd, CREATESTRUCT *pcs) +{ + FBDATA *pfb; + pfb = (FBDATA*)calloc(1, sizeof(FBDATA)); + if (!pfb) return -1; + + SetLastError(ERROR_SUCCESS); + if (!SetWindowLongPtrW(hwnd, 0, (LONGX86)(LONG_PTR)pfb) && ERROR_SUCCESS != GetLastError()) + { + free(pfb); + return -1; + } + + SetWindowLongPtrW(hwnd, GWL_STYLE, GetWindowLongPtrW(hwnd, GWL_STYLE) | WS_CLIPCHILDREN); + + pfb->rgbBk = GetSysColor(COLOR_WINDOW); + pfb->rgbText = GetSysColor(COLOR_WINDOWTEXT); + pfb->pColumns = new std::vector(); + pfb->pBuffer = new StringVector(8192, 4096); + pfb->focusedColumn = -1; + pfb->hwndActive = CreateWindowExW(WS_EX_NOACTIVATE /*| WS_EX_NOPARENTNOTIFY*/, L"ListBox", L"FolderView active listbox", + WS_CHILD | WS_VSCROLL | + LBS_NODATA | LBS_OWNERDRAWFIXED | LBS_DISABLENOSCROLL | LBS_NOREDRAW | LBS_NOTIFY | + LBS_EXTENDEDSEL | LBS_NOINTEGRALHEIGHT | LBS_WANTKEYBOARDINPUT, + 0, 0, 1, 1, hwnd, NULL, NULL, 0L); + pfb->hwndDraw = CreateWindowExW(WS_EX_NOACTIVATE | WS_EX_NOPARENTNOTIFY, L"ListBox", L"FolderView drawing listbox", + WS_CHILD | WS_VSCROLL | + LBS_NODATA | LBS_OWNERDRAWFIXED | LBS_DISABLENOSCROLL | LBS_NOREDRAW | + LBS_EXTENDEDSEL | LBS_NOINTEGRALHEIGHT, + 0, 0, 1, 1, hwnd, NULL, NULL, 0L); + HFONT hFont = (HFONT)SendMessageW(hwnd, WM_GETFONT, 0, 0L); + if (pfb->hwndActive) + { + SetWindowLongPtrW(pfb->hwndActive, GWLP_USERDATA, (LONGX86)-1); + SendMessageW(pfb->hwndActive, WM_SETFONT, (WPARAM)hFont, FALSE); + SendMessageW(pfb->hwndActive, LB_SETITEMHEIGHT, 0, (LPARAM)FolderBrowser_CalculateListItemHeight(pfb->hwndActive)); + FolderBrowser_CustomizeListBox(pfb->hwndActive); + } + if (pfb->hwndDraw) + { + SetWindowLongPtrW(pfb->hwndDraw, GWLP_USERDATA, (LONGX86)-1); + SendMessageW(pfb->hwndDraw, WM_SETFONT, (WPARAM)hFont, FALSE); + SendMessageW(pfb->hwndDraw, LB_SETITEMHEIGHT, 0, (LPARAM)FolderBrowser_CalculateListItemHeight(pfb->hwndDraw)); + } + + // this weird call need to be done to disable vertical scrollbar in skinned mode + SCROLLINFO si; + ZeroMemory(&si, sizeof(SCROLLINFO)); + si.cbSize = sizeof(SCROLLINFO); + si.fMask = SIF_RANGE | SIF_PAGE; + si.nMin = 0; + si.nMax = 0; + si.nPage = 100; + SetScrollInfo(hwnd, SB_VERT, &si, TRUE); + SendMessageW(hwnd, FBM_SETFILESYSTEMINFO, 0, 0L); + return 0; +} + +static void FolderBrowser_OnDestroyWindow(HWND hwnd) +{ + FBDATA *pfb = GetFolderBrowser(hwnd); + SetWindowLongPtrW(hwnd, 0, 0L); + if (!pfb) return; + + if (pfb->pBuffer) delete(pfb->pBuffer); + if (pfb->pColumns) + { + while (pfb->pColumns->size() > 1) + { + FBCOLUMN *pc = &pfb->pColumns->back(); + if (pc->pItems) free(pc->pItems); + pfb->pColumns->pop_back(); + } + + delete(pfb->pColumns); + } + if (pfb->hwndDraw) DestroyWindow(pfb->hwndDraw); + if (pfb->hwndActive) DestroyWindow(pfb->hwndActive); + free(pfb); +} + +static void FolderBrowser_Draw(HWND hwnd, PAINTSTRUCT *pps) +{ + FBDATA *pfb = GetFolderBrowser(hwnd); + HDC hdc; + RECT rc, rp, rs; + + hdc = pps->hdc; + + CopyRect(&rp, &pps->rcPaint); + + FolderBrowser_GetAdjustedClientRect(hwnd, &rc); + + size_t activeColumn = FolderBrowser_GetListBoxColumn(pfb->hwndActive); + int height = rc.bottom - rc.top; + for (size_t i = 0; i < pfb->pColumns->size(); i++) + { + FBCOLUMN *pc = &pfb->pColumns->at(i); + if (rc.left < rp.right && (rc.left + pc->width + SIZER_WIDTH) > rp.left) + { + SetViewportOrgEx(hdc, rc.left, rc.top, NULL); + + if (i != activeColumn && rp.left < (rc.left + pc->width)) + { + PrepareDrawingListBox(pfb->hwndDraw, pc, height, i); + SendMessageW(pfb->hwndDraw, WM_PRINT, (WPARAM)hdc, (LPARAM)(PRF_CLIENT | PRF_NONCLIENT | ((pps->fErase) ? PRF_ERASEBKGND : 0))); + } + + if (rp.right > (rc.left + pc->width) && (SIZER_WIDTH > 0)) + { + SetBkColor(hdc, pfb->rgbBk); + SetRect(&rs, pc->width, rp.top - rc.top, pc->width + SIZER_WIDTH, rp.bottom - rc.top); + ExtTextOutW(hdc, 0, 0, ETO_OPAQUE, &rs, NULL, 0, NULL); + if ((i == sizerActive || i == sizerHover) && 1 == (rs.right - rs.left)%2) + { + COLORREF clr; + HPEN hp, hpo; + + clr = BlendColors(pfb->rgbText, pfb->rgbBk, (i != sizerActive) ? 76 : 127); + + hp = (HPEN)GetStockObject(DC_PEN); + hpo = (hp) ? (HPEN)SelectObject(hdc, hp) : NULL; + SetDCPenColor(hdc, clr); + LONG l = rs.left + (rs.right - rs.left)/2; + MoveToEx(hdc, l, rs.top, NULL); + LineTo(hdc, l, rs.bottom); + if (hpo) SelectObject(hdc, hpo); + } + } + } + rc.left += (pc->width + SIZER_WIDTH); + if (rc.left > rc.right) break; + } + + if (pps->fErase && rc.left < rp.right) + { + if (rc.left < rp.left) rc.left = rp.left; + SetViewportOrgEx(hdc, 0, 0, NULL); + SetBkColor(hdc, pfb->rgbBk); + ExtTextOutW(hdc, 0, 0, ETO_OPAQUE, &rc, L"", 0, 0); + } + + SetWindowLongPtrW(pfb->hwndDraw, GWLP_USERDATA, (LONGX86)-1); +} + +static void FolderBrowser_OnPaint(HWND hwnd) +{ + PAINTSTRUCT ps; + if (BeginPaint(hwnd, &ps)) + { + if (ps.rcPaint.left != ps.rcPaint.right) FolderBrowser_Draw(hwnd, &ps); + EndPaint(hwnd, &ps); + } +} + +static COLORREF FolderBrowser_OnGetBkColor(HWND hwnd) +{ + FBDATA *pfb = GetFolderBrowser(hwnd); + return (pfb) ? pfb->rgbBk : 0; +} + +static BOOL FolderBrowser_OnSetBkColor(HWND hwnd, COLORREF rgbBk) +{ + FBDATA *pfb = GetFolderBrowser(hwnd); + if (!pfb) return FALSE; + pfb->rgbBk = rgbBk; + return TRUE; +} + +static COLORREF FolderBrowser_OnGetTextColor(HWND hwnd) +{ + FBDATA *pfb = GetFolderBrowser(hwnd); + return (pfb) ? pfb->rgbText : 0; +} + +static BOOL FolderBrowser_OnSetTextColor(HWND hwnd, COLORREF rgbText) +{ + FBDATA *pfb = GetFolderBrowser(hwnd); + if (!pfb) return FALSE; + pfb->rgbText = rgbText; + return TRUE; +} + +static BOOL FolderBrowser_OnGetFolderBrowserInfo(HWND hwnd, FOLDERBROWSERINFO *pInfo) +{ + FBDATA *pfb = GetFolderBrowser(hwnd); + if (!pfb || !pInfo || pInfo->cbSize < sizeof(FOLDERBROWSERINFO)) return FALSE; + pInfo->activeColumn = FolderBrowser_GetListBoxColumn(pfb->hwndActive); + pInfo->hwndActive = pfb->hwndActive; + pInfo->hwndDraw = pfb->hwndDraw; + return TRUE; +} + +static BOOL FolderBrowser_OnMeasureItem(HWND hwnd, INT ctrlId, MEASUREITEMSTRUCT *pmis) +{ + LPCWSTR pszText(NULL); + HWND hItem = GetDlgItem(hwnd, pmis->CtlID); + FBDATA *pfb = GetFolderBrowser(hwnd); + + if (!pfb || NULL == hItem) return FALSE; + + size_t c = FolderBrowser_GetListBoxColumn(hItem); + + if (c < pfb->pColumns->size()) + { + if (c == 0) // special case + { + pszText = pfb->pszRoot; + } + else + { + FBCOLUMN *pc = &pfb->pColumns->at(c); + size_t i = pmis->itemID; + if (pc->pItems && i < (UINT)pc->count) i = pc->pItems[i].index; + i += pc->bufferOffset; + if (i < pfb->pBuffer->Count()) pszText = pfb->pBuffer->GetString(i); + } + + INT cchText(0); + if (NULL != pszText) cchText = lstrlenW(pszText); + SkinnedListbox::MeasureItem(hItem, pszText, cchText, &pmis->itemWidth, &pmis->itemHeight); + } + + return TRUE; +} + +static BOOL FolderBrowser_OnDrawItem(HWND hwnd, INT ctrlId, DRAWITEMSTRUCT *pdis) +{ + LPCWSTR pszText(NULL); + FBDATA *pfb = GetFolderBrowser(hwnd); + if (!pfb) return FALSE; + + size_t c = FolderBrowser_GetListBoxColumn(pdis->hwndItem); + + if (c < pfb->pColumns->size()) + { + if (c == 0) // special case + { + pszText = pfb->pszRoot; + if (pdis->hwndItem != pfb->hwndActive && + pfb->pColumns->at(0).pItems && + (FBIS_SELECTED & pfb->pColumns->at(0).pItems[0].styles)) + { + pdis->itemState |= ODS_SELECTED; + } + } + else + { + FBCOLUMN *pc = &pfb->pColumns->at(c); + size_t i = pdis->itemID; + if (pc->pItems && i < (UINT)pc->count) i = pc->pItems[i].index; + i += pc->bufferOffset; + if (i < pfb->pBuffer->Count()) pszText = pfb->pBuffer->GetString(i); + + if (pdis->hwndItem != pfb->hwndActive && pc->pItems && + (FBIS_SELECTED & pc->pItems[pdis->itemID].styles)) + { + pdis->itemState |= ODS_SELECTED; + } + } + + INT cchText(0); + if (NULL != pszText) cchText = lstrlenW(pszText); + + if (c == pfb->focusedColumn && pdis->hwndItem != pfb->hwndActive) + { + pdis->hwndItem = hwnd; + if(pdis->itemState & ODS_SELECTED) + { + SkinnedListbox::DrawItem(pdis, pszText, cchText); + pdis->itemAction = ODA_FOCUS; + pdis->itemState &= ~0x0200/*ODS_NOFOCUSRECT*/; + if (UISF_HIDEFOCUS & SendMessageW(hwnd, WM_QUERYUISTATE, 0, 0L)) pdis->itemState |= 0x0200/*ODS_NOFOCUSRECT*/; + } + } + SkinnedListbox::DrawItem(pdis, pszText, cchText); + } + + return TRUE; +} + +static void FolderBrowser_OnSetFont(HWND hwnd, HFONT hFont, BOOL bRedraw) +{ + FBDATA *pfb = GetFolderBrowser(hwnd); + if (!pfb) return; + if (pfb->hwndActive) + { + SendMessageW(pfb->hwndActive, WM_SETFONT, (WPARAM)hFont, FALSE); + SendMessageW(pfb->hwndActive, LB_SETITEMHEIGHT, 0, (LPARAM)FolderBrowser_CalculateListItemHeight(pfb->hwndActive)); + } + if (pfb->hwndDraw) + { + SendMessageW(pfb->hwndDraw, WM_SETFONT, (WPARAM)hFont, FALSE); + SendMessageW(pfb->hwndDraw, LB_SETITEMHEIGHT, 0, (LPARAM)FolderBrowser_CalculateListItemHeight(pfb->hwndDraw)); + } +} + +static void FolderBrowser_OnSetFocus(HWND hwnd, HWND hwndLost) +{ + FBDATA *pfb = GetFolderBrowser(hwnd); + if (NULL == pfb) return; + + if (pfb && hwndLost != pfb->hwndActive && FolderBrowser_GetListBoxColumn(pfb->hwndActive) > pfb->pColumns->size()) + { + size_t last = 0; + for (size_t i= 0; i < pfb->pColumns->size(); i++) + { + if (-1 != pfb->pColumns->at(i).firstSelected) last = i; + else break; + } + last = FolderBrowser_UpdateActiveColumn(hwnd, last); + if (-1 != last) + { + //INT selCount = (INT)SendMessageW(pfb->hwndActive, LB_GETSELCOUNT, 0, 0L); + //if (selCount < 1) + //{ + // SendMessageW(pfb->hwndActive, LB_SETSEL, TRUE, 0L); + // FolderBrowser_OnSelectionChanged(hwnd, TRUE, TRUE); + //} + //else SendMessageW(pfb->hwndActive, LB_SETCARETINDEX, pfb->pColumns->at(last).firstSelected, FALSE); + //FolderBrowser_OnEnsureVisible(hwnd, last, 0); + } + if (IsWindowVisible(pfb->hwndActive) && IsWindowEnabled(pfb->hwndActive)) SetFocus(pfb->hwndActive); + } +} + +static void FolderBrowser_OnKillFocus(HWND hwnd, HWND hwndRecieve) +{ + FBDATA *pfb = GetFolderBrowser(hwnd); + if (pfb && hwndRecieve != pfb->hwndActive) + { + FolderBrowser_UpdateActiveColumn(hwnd, ((size_t)-1)); + } +} + +static LRESULT FolderBrowser_OnGetDlgCode(HWND hwnd, INT vKey, MSG *pMsg) +{ + return DLGC_WANTARROWS; +} + +static UINT FolderBrowser_HitTest(HWND hwnd, POINT pt, size_t *pColumn, size_t *pItem) +{ + RECT rc; + + size_t column = -1, item = -1; + + UINT hitTest = HTERROR; + + FBDATA *pfb = GetFolderBrowser(hwnd); + + if (!pfb || !FolderBrowser_GetAdjustedClientRect(hwnd, &rc)) + return hitTest; + + hitTest = HTCLIENT; + + size_t activeColumn = FolderBrowser_GetListBoxColumn(pfb->hwndActive); + + for(size_t i = 0; i < pfb->pColumns->size(); i++) + { + FBCOLUMN *pc = &pfb->pColumns->at(i); + rc.right = rc.left + pc->width; + if (PtInRect(&rc, pt)) + { + column = i; + if (pc->count > 0) + { + HWND hwndTest = NULL; + if (i != activeColumn) + { + hwndTest = pfb->hwndDraw; + SetWindowPos(hwndTest, NULL, 0, 0, pc->width, rc.bottom - rc.top, + SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE | SWP_NOREDRAW | SWP_NOSENDCHANGING); + SendMessageW(hwndTest, LB_SETCOUNT, pc->count, 0L); + if (-1 != pc->firstSelected) SendMessageW(pfb->hwndDraw, LB_SETSEL, TRUE, pc->firstSelected); + SendMessageW(hwndTest, LB_SETTOPINDEX, pc->firstVisible, 0L); + } + else + hwndTest = pfb->hwndActive; + + if (NULL != hwndTest) + { + RECT rw; + GetClientRect(hwndTest, &rw); + MapWindowPoints(hwndTest, hwnd, (POINT*)&rw, 2); + if (hwndTest != pfb->hwndActive) + OffsetRect(&rw, rc.left, rc.top); + if (PtInRect(&rw, pt)) + { + INT itemIndex = (INT)SendMessageW(hwndTest, LB_ITEMFROMPOINT, 0, MAKELPARAM(pt.x - rc.left, pt.y - rc.top)); + if (HIWORD(itemIndex)) itemIndex = -1; + if (itemIndex != -1) item = (size_t)itemIndex; + } + } + } + break; + } + + rc.right += SIZER_WIDTH; + rc.left = rc.right; + if (rc.left > pt.x) break; + } + if (pColumn) *pColumn = column; + if (pItem) *pItem = item; + return hitTest; +} + +static void FolderBrowser_UpdateScrollHovering(HWND hwnd, UINT uFlags, POINTS pts) +{ + RECT rc; + FBCOLUMN *pc; + POINT pt; + LONG left; + + static size_t hoveredColumn = -1; + static INT nHoveredPart = -1; + + FBDATA *pfb = GetFolderBrowser(hwnd); + if (!pfb || !FolderBrowser_GetAdjustedClientRect(hwnd, &rc)) + return; + + left = rc.left; + + POINTSTOPOINT(pt, pts); + size_t activeColumn = FolderBrowser_GetListBoxColumn(pfb->hwndActive); + + size_t nHovered = -1; + for(size_t i = 0; i < pfb->pColumns->size() && pt.x >= rc.left; i++) + { + pc = &pfb->pColumns->at(i); + rc.right = rc.left + pc->width; + if (i != activeColumn && PtInRect(&rc, pt)) + { + PrepareDrawingListBox(pfb->hwndDraw, pc, rc.bottom - rc.top, -1); + + SCROLLINFO si; + si.cbSize = sizeof(SCROLLINFO); + si.fMask = SIF_PAGE | SIF_RANGE; + if (GetScrollInfo(pfb->hwndDraw, SB_VERT, &si) && (INT)si.nPage <= si.nMax) + { + pt.x -= rc.left; + pt.y -= rc.top; + MapWindowPoints(hwnd, HWND_DESKTOP, &pt, 1); + INT ht = (INT) SendMessageW(pfb->hwndDraw, WM_NCHITTEST, 0, MAKELPARAM(pt.x, pt.y)); + if (HTVSCROLL == ht || HTHSCROLL == ht) + { + nHovered = i; + SBADJUSTHOVER hoverTest; + hoverTest.hitTest = ht; + hoverTest.ptMouse.x = (SHORT)pt.x; + hoverTest.ptMouse.y = (SHORT)pt.y; + if (SENDMLIPC(pfb->hwndDraw, IPC_ML_SKINNEDSCROLLWND_ADJUSTHOVER, (WPARAM)&hoverTest)) + { + if (nHoveredPart != hoverTest.nResult) + { + nHoveredPart = hoverTest.nResult; + RefreshListBoxNC(hwnd, pfb->hwndDraw, *(POINT*)&rc); + } + } + } + else + nHoveredPart = -1; + } + break; + } + + rc.right += SIZER_WIDTH; + rc.left = rc.right; + } + + if (hoveredColumn != nHovered && -1 != hoveredColumn) + { + rc.left = left; + for(size_t i = 0; i < hoveredColumn; i++) + rc.left += pfb->pColumns->at(i).width + SIZER_WIDTH; + + pc = &pfb->pColumns->at(hoveredColumn); + + if (PrepareDrawingListBox(pfb->hwndDraw, pc, rc.bottom - rc.top, -1)) + { + SendMessageW(pfb->hwndDraw, WM_NCMOUSELEAVE, HTNOWHERE, 0L); + RefreshListBoxNC(hwnd, pfb->hwndDraw, *(POINT*)&rc); + } + nHoveredPart = -1; + } + + hoveredColumn = nHovered; + if (-1 != hoveredColumn && -1 != nHoveredPart) + { + TRACKMOUSEEVENT tracker; + tracker.cbSize = sizeof(tracker); + tracker.hwndTrack = hwnd; + tracker.dwHoverTime = 0; + tracker.dwFlags = TME_LEAVE; + TrackMouseEvent(&tracker); + } +} + +static void FolderBrowser_OnMouseMove(HWND hwnd, UINT uFlags, POINTS pts) +{ + FBDATA *pfb = GetFolderBrowser(hwnd); + if (!pfb) return; + + if (GetCapture() == hwnd && sizerActive < pfb->pColumns->size()) + { + RECT rc, ri; + SCROLLINFO si; + FBCOLUMN *pc; + + GetClientRect(hwnd, &rc); + CopyRect(&ri, &rc); + si.cbSize = sizeof(SCROLLINFO); + si.fMask = SIF_POS; + if (!GetScrollInfo(hwnd, SB_HORZ, &si)) + ZeroMemory(&si, sizeof(SCROLLINFO)); + rc.right = rc.left - si.nPos; + for (size_t i = 0; i <= sizerActive; i++) + { + pc = &pfb->pColumns->at(i); + rc.right += (pc->width + SIZER_WIDTH); + } + + rc.left = rc.right - SIZER_WIDTH; + GetCursorPos(((LPPOINT)&rc) + 1); + MapWindowPoints(HWND_DESKTOP, hwnd, ((LPPOINT)&rc) + 1, 1); + rc.right -= clickoffs; + + if (rc.left != rc.right) + { + if (pc) + { + ri.left = rc.left - pc->width; + + INT w = pc->width + (rc.right - rc.left); + + pc->autoAdjust = FALSE; + if (w < COLUMN_MIN_WIDTH) w = COLUMN_MIN_WIDTH; + if (w > COLUMN_MAX_WIDTH) w = COLUMN_MAX_WIDTH; + if (pc->width != w) + { + pc->width = w; + FolderBrowser_UpdateScrollInfo(hwnd); + InvalidateRect(hwnd, &ri, TRUE); + UpdateWindow(hwnd); + } + } + } + } + else FolderBrowser_UpdateScrollHovering(hwnd, uFlags, pts); +} + +static void FolderBrowser_OnLButtonUp(HWND hwnd, UINT uFlags, POINTS pts) +{ + FBDATA *pfb = GetFolderBrowser(hwnd); + if (!pfb || pfb->pColumns->size() == 0) return; + + if (-1 != sizerActive) + { + RECT rc; + SCROLLINFO si; + + GetClientRect(hwnd, &rc); + si.cbSize = sizeof(SCROLLINFO); + si.fMask = SIF_POS; + if (!GetScrollInfo(hwnd, SB_HORZ, &si)) ZeroMemory(&si, sizeof(SCROLLINFO)); + rc.right = rc.left - si.nPos; + for (size_t i = 0; i <= sizerActive; i++) + { + rc.right += (pfb->pColumns->at(i).width + SIZER_WIDTH); + } + rc.left = rc.right - SIZER_WIDTH; + sizerActive = -1; + clickoffs = 0; + ReleaseCapture(); + FolderBrowser_RestoreActive(hwnd); + InvalidateRect(hwnd, &rc, FALSE); + } +} + +static void FolderBrowser_OnButtonDown(HWND hwnd, UINT uButtonMsg, UINT uFlags, POINTS pts) +{ + RECT rc; + POINT pt; + UINT ht; + size_t column, item; + + FBDATA *pfb = GetFolderBrowser(hwnd); + if (!pfb || pfb->pColumns->size() == 0) return; + + ht = HTERROR; + FolderBrowser_GetAdjustedClientRect(hwnd, &rc); + + POINTSTOPOINT(pt, pts); + + // check if sizer clicked + LONG cPoint = rc.left; + for(size_t i = 0; i < pfb->pColumns->size() && rc.left <= pt.x; i++) + { + FBCOLUMN *pc = &pfb->pColumns->at(i); + + cPoint += pc->width; + rc.left = cPoint - SIZER_OVERLAP_LEFT; + cPoint += SIZER_WIDTH; + rc.right = cPoint + SIZER_OVERLAP_RIGHT; + + if (WM_LBUTTONDOWN == uButtonMsg) + { + if (PtInRect(&rc, pt)) + { + sizerActive = i; + clickoffs = pts.x - (rc.left + SIZER_OVERLAP_LEFT); + UpdateWindow(hwnd); + FolderBrowser_HideActive(hwnd); + SetCapture(hwnd); + InvalidateRect(hwnd, &rc, FALSE); + return; + } + } + } + + ht = FolderBrowser_HitTest(hwnd, pt, &column, &item); + if (-1 != column && -1 == item) + { + while (-1 != column && 0 == pfb->pColumns->at(column).count) + { + column--; + /*item = */pfb->pColumns->at(column).firstSelected; + } + } + + if (-1 != column) + { + //if (WM_LBUTTONDOWN != uButtonMsg || (HTCLIENT == ht && -1 == item)) + //{ + // FolderBrowser_OnEnsureVisible(hwnd, column, 0); + // return; // prevent activation + //} + + if (pfb->hwndActive == GetFocus()) + { + UpdateWindow(pfb->hwndActive); + SendMessageW(pfb->hwndActive, WM_SETREDRAW, FALSE, 0L); + SetFocus(hwnd); + SendMessageW(pfb->hwndActive, WM_SETREDRAW, TRUE, 0L); + } + /*if (-1 != item) */pfb->focusedColumn = -1; + + FolderBrowser_UpdateActiveColumn(hwnd, column); + + if ((WS_VISIBLE & GetWindowLongPtrW(pfb->hwndActive, GWL_STYLE))) + { + MapWindowPoints(hwnd, HWND_DESKTOP, &pt, 1); + ht = (UINT)SendMessageW(pfb->hwndActive, WM_NCHITTEST, 0, MAKELPARAM(pt.x, pt.y)); + switch(ht) + { + case HTERROR: break; + case HTCLIENT: + MapWindowPoints(HWND_DESKTOP, pfb->hwndActive, &pt, 1); + + if (0 == (WS_EX_NOPARENTNOTIFY & (DWORD)GetWindowLongPtrW(pfb->hwndActive, GWL_EXSTYLE))) + { + HWND hParent = GetParent(pfb->hwndActive); + if (NULL != hParent) + { + POINT ptParent = pt; + MapWindowPoints(pfb->hwndActive, hParent, &ptParent, 1); + SendMessageW(hParent, WM_PARENTNOTIFY, MAKEWPARAM(uButtonMsg, 0), MAKELPARAM(ptParent.x, ptParent.y)); + } + } + SendMessageW(pfb->hwndActive, uButtonMsg, uFlags, MAKELPARAM(pt.x, pt.y)); + // maxRight = (LONG)SendMessageW(pfb->hwndActive, LB_ITEMFROMPOINT, uFlags, MAKELPARAM(pt.x, pt.y)); + // if (0 == HIWORD(maxRight)) + // { + // size_t a = FolderBrowser_GetListBoxColumn(pfb->hwndActive); + //// if (a < pfb->pColumns->size() && LOWORD(maxRight) == pfb->pColumns->at(a).firstSelected) + //// pfb->pColumns->at(a).firstSelected = -1; + // } + break; + default: + SendMessageW(pfb->hwndActive, (uButtonMsg - WM_MOUSEMOVE) + WM_NCMOUSEMOVE, ht, MAKELPARAM(pt.x, pt.y)); + break; + } + } + } + else + { + HWND hFocus = GetFocus(); + if (hFocus != hwnd && hFocus != pfb->hwndActive) + SetFocus(hwnd); + } +} + +static void FolderBrowser_OnLButtonDown(HWND hwnd, UINT uFlags, POINTS pts) +{ + FolderBrowser_OnButtonDown(hwnd, WM_LBUTTONDOWN, uFlags, pts); +} + +static void FolderBrowser_OnLButtonDblClick(HWND hwnd, UINT uFlags, POINTS pts) +{ + RECT rc; + LONG maxRight; + POINT pt; + + FBDATA *pfb = GetFolderBrowser(hwnd); + if (!pfb || pfb->pColumns->size() == 0) return; + + GetClientRect(hwnd, &rc); + + FolderBrowser_GetAdjustedClientRect(hwnd, &rc); + + maxRight = rc.right; + POINTSTOPOINT(pt, pts); + + LONG cPoint = rc.left; + for(size_t i = 0; i < pfb->pColumns->size() && rc.left <= pt.x; i++) + { + FBCOLUMN *pc = &pfb->pColumns->at(i); + + cPoint += pc->width; + rc.left = cPoint - SIZER_OVERLAP_LEFT; + cPoint += SIZER_WIDTH; + rc.right = cPoint + SIZER_OVERLAP_RIGHT; + + if (PtInRect(&rc, pt)) + { + pc->autoAdjust = TRUE; + INT width = FolderBrowser_GetPreferredColumnWidth(hwnd, i); + if ( -1 != width && pc->width != width) + { + rc.left = (rc.left + SIZER_OVERLAP_LEFT) - pc->width; + rc.right = maxRight; + + pc->width = width; + + UpdateWindow(hwnd); + SendMessageW(hwnd, WM_SETREDRAW, FALSE, 0L); + FolderBrowser_HideActive(hwnd); + FolderBrowser_UpdateScrollInfo(hwnd); + FolderBrowser_RestoreActive(hwnd); + SendMessageW(hwnd, WM_SETREDRAW, TRUE, 0L); + RedrawWindow(hwnd, &rc, NULL, RDW_INVALIDATE | RDW_ERASE | RDW_ALLCHILDREN | RDW_ERASENOW | RDW_UPDATENOW); + return; + } + } + } +} + +static void FolderBrowser_OnRButtonDown(HWND hwnd, UINT uFlags, POINTS pts) +{ + POINT pt; + size_t col, item; + POINTSTOPOINT(pt, pts); + UINT ht = FolderBrowser_HitTest(hwnd, pt, &col, &item); + if (HTCLIENT == ht && -1 != col) + { + INT ox = FolderBrowser_OnEnsureVisible(hwnd, col, EVF_NOEXTRALSPACE | EVF_NOEXTRARSPACE); + if (0 != ox) pts.x -= ox; + } + TRACE_FMT(TEXT("RDown at (%d,%d) [ht=%d, col=%d, raw=%d]\n"), pt.x, pt.y, ht, col, item); +} + +static void FolderBrowser_OnRButtonUp(HWND hwnd, UINT uFlags, POINTS pts) +{ + POINT pt; + size_t col, item; + POINTSTOPOINT(pt, pts); + UINT ht = FolderBrowser_HitTest(hwnd, pt, &col, &item); + TRACE_FMT(TEXT("RUp at (%d,%d) [ht=%d, col=%d, raw=%d]\n"), pt.x, pt.y, ht, col, item); + FolderBrowser_RestoreActive(hwnd); +} + +static void FolderBrowser_OnKeyDown(HWND hwnd, UINT vkCode, UINT flags) +{ + FBDATA *pfb = GetFolderBrowser(hwnd); + if (pfb) + { + switch(vkCode) + { + case VK_PRIOR: + case VK_NEXT: + case VK_END: + case VK_HOME: + case VK_DOWN: + case VK_UP: + case VK_RIGHT: + case VK_LEFT: + if (pfb->focusedColumn != -1) + { + FolderBrowser_UpdateActiveColumn(hwnd, pfb->focusedColumn); + if (IsWindowVisible(pfb->hwndActive) && IsWindowEnabled(pfb->hwndActive)) SetFocus(pfb->hwndActive); + } + if ((WS_VISIBLE & GetWindowLongPtrW(pfb->hwndActive, GWL_STYLE))) + { + SendMessageW(pfb->hwndActive, WM_KEYDOWN, (WPARAM)vkCode, (LPARAM)flags); + return; + } + break; + } + } + DefWindowProcW(hwnd, WM_KEYDOWN, (WPARAM)vkCode, (LPARAM)flags); +} + +static void FolderBorwser_OnCommand(HWND hwnd, UINT ctrlId, UINT eventId, HWND hwndCtrl) +{ + FBDATA *pfb = GetFolderBrowser(hwnd); + if (!pfb) return; + if (pfb->hwndActive == hwndCtrl) + { + switch(eventId) + { + //case LBN_SELCANCEL: + // break; + case LBN_SELCHANGE: + FolderBrowser_OnSelectionChanged(hwnd, FALSE, TRUE); + break; + case LBN_SETFOCUS: + pfb->focusedColumn = (int)FolderBrowser_GetListBoxColumn(hwndCtrl); + break; + case LBN_KILLFOCUS: + if (hwnd != GetFocus()) + { + FolderBrowser_UpdateActiveColumn(hwnd, ((size_t)-1)); + } + break; + } + } +} + +static LRESULT FolderBrowser_OnVKeyToItem(HWND hwnd, UINT vkCode, UINT caretPos, HWND hwndCtrl) +{ + + FBDATA *pfb = GetFolderBrowser(hwnd); + if (!pfb) return -1; + if (pfb->hwndActive == hwndCtrl) + { + size_t activeColumn = FolderBrowser_GetListBoxColumn(pfb->hwndActive); + + switch(vkCode) + { + case VK_LEFT: + if (0 != (0x80000000 & GetAsyncKeyState(VK_CONTROL)) || + 0 != (0x80000000 & GetAsyncKeyState(VK_SHIFT))) break; + + if (activeColumn > 0) + { + pfb->focusedColumn = -1; + FolderBrowser_UpdateActiveColumn(hwnd, activeColumn - 1); + pfb->focusedColumn = (int)FolderBrowser_GetListBoxColumn(pfb->hwndActive); + FolderBrowser_OnSelectionChanged(hwnd, TRUE, TRUE); + } + return -2; + + case VK_RIGHT: + if (0 != (0x80000000 & GetAsyncKeyState(VK_CONTROL)) || + 0 != (0x80000000 & GetAsyncKeyState(VK_SHIFT))) break; + + if (activeColumn < (pfb->pColumns->size() -1)) + { + FBCOLUMN *pc = &pfb->pColumns->at(activeColumn + 1); + if (pc->count > 0) + { + pc->firstSelected = 0; + pfb->focusedColumn = -1; + FolderBrowser_UpdateActiveColumn(hwnd, activeColumn + 1); + pfb->focusedColumn = (int)FolderBrowser_GetListBoxColumn(pfb->hwndActive); + FolderBrowser_OnSelectionChanged(hwnd, TRUE, TRUE); + } + } + return -2; + } + } + + return -1; +} + +static void FolderBrowser_OnWindowPosChanged(HWND hwnd, WINDOWPOS *pwp) +{ + FBDATA *pfb = GetFolderBrowser(hwnd); + if (!pfb) return; + + if (0 == (SWP_NOSIZE & pwp->flags) || (SWP_FRAMECHANGED & pwp->flags)) + { + RECT rc, rw; + GetClientRect(hwnd, &rc); + SCROLLINFO si; + INT dx = 0; + + LONG totalWidth = 0; + for(size_t i = 0; i < pfb->pColumns->size(); i++) totalWidth += (pfb->pColumns->at(i).width + SIZER_WIDTH); + + si.cbSize = sizeof(SCROLLINFO); + si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS; + if (!GetScrollInfo(hwnd, SB_HORZ, &si)) + { + ZeroMemory(&si, sizeof(SCROLLINFO)); + si.cbSize = sizeof(SCROLLINFO); + si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS; + si.nMax = 100; + si.nPage = 10; + SetScrollInfo(hwnd, SB_HORZ, &si, FALSE); + } + if ( (si.nPage != (rc.right - rc.left) || si.nMax < totalWidth)) + { + si.fMask = SIF_PAGE; + if (si.nMax != totalWidth) + { + si.nMax = totalWidth; + si.fMask |= SIF_RANGE; + } + + si.nPage = rc.right - rc.left; + if ((si.nPos + si.nPage) > (UINT)si.nMax && si.nPos > si.nMin) + { + dx = si.nPos; + si.nPos = si.nMax - si.nPage; + if (si.nPos < si.nMin) si.nPos = si.nMin; + dx -= si.nPos; + si.fMask |= SIF_POS; + InvalidateRect(hwnd, NULL, FALSE); + } + if (0 == si.nMax) + { + si.nMax = 100; + si.nPage = si.nMax; + } + + SetScrollInfo(hwnd, SB_HORZ, &si, FALSE); + } + + if (pfb->hwndActive && + (WS_VISIBLE & GetWindowLongPtrW(pfb->hwndActive, GWL_STYLE)) + && GetWindowRect(pfb->hwndActive, &rw)) + { + if (rw.bottom - rw.top != rc.bottom - rc.top || dx != 0) + { + if (dx) MapWindowPoints(HWND_DESKTOP, hwnd, (POINT*)&rw, 2); + SetWindowPos(pfb->hwndActive, NULL, rw.left + dx, rw.top, rw.right - rw.left, rc.bottom - rc.top, + SWP_NOACTIVATE | SWP_NOZORDER | + ((0 == dx) ? SWP_NOMOVE : 0) | + ((rw.bottom - rw.top == rc.bottom - rc.top) ? SWP_NOSIZE : 0)); + if (0 == (SWP_NOREDRAW & pwp->flags)) InvalidateRect(pfb->hwndActive, NULL, TRUE); + } + } + + if (0 == (SWP_NOREDRAW & pwp->flags)) + { + GetWindowRect(pfb->hwndDraw, &rw); + if (rw.bottom - rw.top != rc.bottom - rc.top) InvalidateRect(hwnd, NULL, FALSE); + } + } +} + +static void UpdateAfterScroll(HWND hwnd, INT scrollPos, BOOL fRedraw) +{ + FBDATA *pfb = GetFolderBrowser(hwnd); + if (pfb && (WS_VISIBLE & GetWindowLongPtrW(pfb->hwndActive, GWL_STYLE))) + { + RECT rc; + size_t a = FolderBrowser_GetListBoxColumn(pfb->hwndActive); + GetClientRect(hwnd, &rc); + rc.left -= scrollPos; + for (size_t i = 0; i < a; i++) rc.left += (pfb->pColumns->at(i).width + SIZER_WIDTH); + SetWindowPos(pfb->hwndActive, NULL, rc.left, rc.top, 0, 0, + SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSENDCHANGING | SWP_NOSIZE | + ((fRedraw) ? 0 : SWP_NOREDRAW)); + } + if (fRedraw) + { + InvalidateRect(hwnd, NULL, TRUE); + UpdateWindow(hwnd); + } +} + +static void FolderBrowser_OnHScroll(HWND hwnd, UINT uCode, UINT uPos) +{ + SCROLLINFO si; + FBDATA *pfb = GetFolderBrowser(hwnd); + if (!pfb) return; + + si.cbSize = sizeof(SCROLLINFO); + si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS; + if (!GetScrollInfo(hwnd, SB_HORZ, &si)) return; + INT line = si.nPage / 10; + if (line < 1) line = 1; + INT dx = 0; + + static INT startPos = 0; + static INT scrollCount = 0; + + switch(uCode) + { + case SB_LINELEFT: dx = -line; break; + case SB_LINERIGHT: dx = line; break; + case SB_PAGELEFT: dx = -((int)si.nPage); break; + case SB_PAGERIGHT: dx = ((int)si.nPage); break; + case SB_THUMBTRACK: + UpdateAfterScroll(hwnd, si.nPos, TRUE); + return; + case SB_LEFT: + si.fMask = SIF_POS; + si.nPos = 0; + SetScrollInfo(hwnd, SB_HORZ, &si, FALSE); + UpdateAfterScroll(hwnd, si.nPos, TRUE); + return; + case SB_RIGHT: + si.fMask = SIF_POS; + si.nPos = si.nMax - si.nPage; + SetScrollInfo(hwnd, SB_HORZ, &si, FALSE); + UpdateAfterScroll(hwnd, si.nPos, TRUE); + return; + case SB_ENDSCROLL: + if (-1 != hiddenActive) + { + RECT rw; + GetWindowRect(pfb->hwndActive, &rw); + MapWindowPoints(HWND_DESKTOP, hwnd, (POINT*)&rw, 1); + SetWindowLongPtrW(pfb->hwndActive, GWLP_USERDATA, (LONGX86)hiddenActive); + dx = startPos - si.nPos; + + SetWindowPos(pfb->hwndActive, NULL, rw.left + dx, rw.top, 0, 0, + SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE | SWP_NOSENDCHANGING); + ShowWindow(pfb->hwndActive, SW_SHOWNA); + if (pfb->focusedColumn == hiddenActive) SetFocus(pfb->hwndActive); + hiddenActive = -1; + startPos = si.nPos; + } + scrollCount = 0; + return; + default: + return; + } + + if (dx != 0) + { + if ((si.nPos + dx) < si.nMin) dx = si.nMin - si.nPos; + else if ((si.nPos + dx) > (si.nMax - (INT)si.nPage)) + { + dx = si.nMax - si.nPos - si.nPage; + if (dx < 0) dx = 0; + } + if (dx != 0) + { + SendMessageW(hwnd, WM_SETREDRAW, FALSE, 0L); + + if (pfb && -1 == hiddenActive && (WS_VISIBLE & GetWindowLongPtrW(pfb->hwndActive, GWL_STYLE))) + { + hiddenActive = FolderBrowser_GetListBoxColumn(pfb->hwndActive); + if (hiddenActive < pfb->pColumns->size()) + { + FBCOLUMN *pc = &pfb->pColumns->at(hiddenActive); + if (pc) pc->firstVisible = (INT)SendMessageW(pfb->hwndActive, LB_GETTOPINDEX, 0, 0L); + if (pfb->focusedColumn == hiddenActive) SetFocus(hwnd); + SetWindowLongPtrW(pfb->hwndActive, GWLP_USERDATA, (LONGX86)-1); + ShowWindow(pfb->hwndActive, SW_HIDE); + startPos = si.nPos; + } + } + + si.fMask = SIF_POS; + si.nPos += dx; + SetScrollInfo(hwnd, SB_HORZ, &si, FALSE); + SendMessageW(hwnd, WM_SETREDRAW, TRUE, 0L); + if (scrollCount) + { + ScrollWindowEx(hwnd, -dx, 0, NULL, NULL, NULL, NULL, SW_INVALIDATE | SW_ERASE); + // InvalidateRect(hwnd, NULL, TRUE); + } + else + { + ScrollWindowEx(hwnd, -dx, 0, NULL, NULL, NULL, NULL, MAKELPARAM(SW_SMOOTHSCROLL, 150)); + } + UpdateWindow(hwnd); + } + scrollCount++; + } +} + +static void FolderBroser_OnPrintClient(HWND hwnd, HDC hdc, UINT options) +{ + PAINTSTRUCT ps; + ZeroMemory(&ps, sizeof(PAINTSTRUCT)); + ps.hdc = hdc; + GetClientRect(hwnd, &ps.rcPaint); + ps.fErase = (0 != (PRF_ERASEBKGND & options)); + FolderBrowser_Draw(hwnd, &ps); +} + +static void FolderBrowser_OnMouseWheel(HWND hwnd, UINT vkCode, INT delta, POINTS pts) +{ + if (MK_CONTROL == vkCode) + { + SendMessageW(hwnd, WM_HSCROLL, MAKEWPARAM(((delta > 0) ? SB_LINELEFT : SB_LINERIGHT), 0), NULL); + SendMessageW(hwnd, WM_HSCROLL, MAKEWPARAM(SB_ENDSCROLL, 0), NULL); + } +} + +static BOOL FolderBrowser_OnSetCursor(HWND hwnd, HWND hwndCursor, UINT hitTest, UINT uMsg) +{ + FBDATA *pfb = GetFolderBrowser(hwnd); + SCROLLINFO si; + si.cbSize = sizeof(SCROLLINFO); + si.fMask = SIF_POS; + if(!GetScrollInfo(hwnd, SB_HORZ, &si)) si.nPos = 0; + + if(pfb && HTCLIENT == hitTest) + { + POINT pt; + GetCursorPos(&pt); + MapWindowPoints(HWND_DESKTOP, hwnd, &pt, 1); + + LONG left = -si.nPos; + + for (size_t i=0, count = pfb->pColumns->size(); i < count; i++) + { + left += pfb->pColumns->at(i).width; + if (pt.x >= (left - SIZER_OVERLAP_LEFT) && pt.x < (left + SIZER_WIDTH + SIZER_OVERLAP_RIGHT)) + { + if (sizerHover != i) + { + SetCursor(LoadCursor(NULL, IDC_SIZEWE)); + + if (IsChild(GetActiveWindow(), hwnd)) + { + RECT rc; + GetClientRect(hwnd, &rc); + rc.left = left; + rc.right = left + SIZER_WIDTH; + + sizerHover = i; + InvalidateRect(hwnd, &rc, FALSE); + + TRACKMOUSEEVENT tm; + tm.cbSize = sizeof(TRACKMOUSEEVENT); + tm.dwFlags = TME_LEAVE; + tm.hwndTrack = hwnd; + _TrackMouseEvent(&tm); + } + } + return TRUE; + } + left += SIZER_WIDTH; + if (left > pt.x) break; + } + } + if (sizerHover < pfb->pColumns->size()) + { + RECT rc; + GetClientRect(hwnd, &rc); + rc.right = rc.left - si.nPos; + for (size_t i = 0; i <= sizerHover; i++) rc.right += (pfb->pColumns->at(i).width + SIZER_WIDTH); + + rc.left = rc.right - SIZER_WIDTH; + sizerHover = -1; + InvalidateRect(hwnd, &rc, FALSE); + } + DefWindowProcW(hwnd, WM_SETCURSOR, (WPARAM)hwndCursor, MAKELPARAM(hitTest, uMsg)); + return TRUE; +} + +static BOOL FolderBrowser_OnSetFileSystemInfo(HWND hwnd, FILESYSTEMINFO *pfs) +{ + FBDATA *pfb = GetFolderBrowser(hwnd); + if (!pfb) return FALSE; + if (!pfs) + { + pfb->filesystem.fnFindFirstFile = FindFirstFileW; + pfb->filesystem.fnFindNextFile = FindNextFileW; + pfb->filesystem.fnFindClose = FindClose; + } + else + { + if (pfs->cbSize != sizeof(FILESYSTEMINFO) || + !pfs->fnFindFirstFile || !pfs->fnFindNextFile || !pfs->fnFindClose) return FALSE; + CopyMemory(&pfb->filesystem, pfs, sizeof(FILESYSTEMINFO)); + } + return TRUE; +} + +static BOOL FolderBrowser_OnGetFileSystemInfo(HWND hwnd, FILESYSTEMINFO *pfs) +{ + FBDATA *pfb = GetFolderBrowser(hwnd); + if (!pfb || !pfs || pfs->cbSize != sizeof(FILESYSTEMINFO)) return FALSE; + CopyMemory(pfs, &pfb->filesystem, sizeof(FILESYSTEMINFO)); + return TRUE; +} + +static void FolderBrowser_OnMouseLeave(HWND hwnd) +{ + FBDATA *pfb = GetFolderBrowser(hwnd); + + if (pfb && sizerHover < pfb->pColumns->size()) + { + RECT rc; + GetClientRect(hwnd, &rc); + + SCROLLINFO si; + si.cbSize = sizeof(SCROLLINFO); + si.fMask = SIF_POS; + if(!GetScrollInfo(hwnd, SB_HORZ, &si)) si.nPos = 0; + + rc.right = rc.left - si.nPos; + for (size_t i = 0; i <= sizerHover; i++) rc.right += (pfb->pColumns->at(i).width + SIZER_WIDTH); + + rc.left = rc.right - SIZER_WIDTH; + sizerHover = -1; + InvalidateRect(hwnd, &rc, FALSE); + } + POINTS pts = { -1, -1}; + FolderBrowser_UpdateScrollHovering(hwnd, 0, pts); +} + +static void FolderBrowser_OnParentNotify(HWND hwnd, UINT message, LPARAM lParam) +{ + switch(LOWORD(message)) + { + case WM_RBUTTONDOWN: + { + FolderBrowser_HideActive(hwnd); + DWORD flags = 0; + if (0 != (0x80000000 & GetAsyncKeyState(VK_CONTROL))) flags |= MK_CONTROL; + if (0 != (0x80000000 & GetAsyncKeyState(VK_SHIFT))) flags |= MK_SHIFT; + if (0 != (0x80000000 & GetAsyncKeyState(VK_LBUTTON))) flags |= MK_LBUTTON; + if (0 != (0x80000000 & GetAsyncKeyState(VK_RBUTTON))) flags |= MK_RBUTTON; + if (0 != (0x80000000 & GetAsyncKeyState(VK_MBUTTON))) flags |= MK_MBUTTON; + if (0 != (0x80000000 & GetAsyncKeyState(VK_XBUTTON1))) flags |= MK_XBUTTON1; + if (0 != (0x80000000 & GetAsyncKeyState(VK_XBUTTON2))) flags |= MK_XBUTTON1; + SendMessageW(hwnd, LOWORD(message), flags, lParam); + } + break; + } +} + +LRESULT CALLBACK FolderBrowser_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch(uMsg) + { + case WM_CREATE: return FolderBrowser_OnCreateWindow(hwnd, (CREATESTRUCT*)lParam); + case WM_DESTROY: FolderBrowser_OnDestroyWindow(hwnd); return 0; + case WM_PAINT: FolderBrowser_OnPaint(hwnd); return 0; + case WM_DRAWITEM: return (LRESULT)FolderBrowser_OnDrawItem(hwnd, (INT)wParam, (DRAWITEMSTRUCT*)lParam); + case WM_SETFONT: FolderBrowser_OnSetFont(hwnd, (HFONT)wParam, (BOOL)LOWORD(lParam)); break; + case WM_SETFOCUS: FolderBrowser_OnSetFocus(hwnd, (HWND)wParam); break; + case WM_KILLFOCUS: FolderBrowser_OnKillFocus(hwnd, (HWND)wParam); return 0; + case WM_GETDLGCODE: return FolderBrowser_OnGetDlgCode(hwnd, (INT)wParam, (MSG*)lParam); + case WM_LBUTTONDOWN: FolderBrowser_OnLButtonDown(hwnd, (UINT)wParam, MAKEPOINTS(lParam)); return 0; + case WM_LBUTTONUP: FolderBrowser_OnLButtonUp(hwnd, (UINT)wParam, MAKEPOINTS(lParam)); return 0; + case WM_LBUTTONDBLCLK: FolderBrowser_OnLButtonDblClick(hwnd, (UINT)wParam, MAKEPOINTS(lParam)); return 0; + case WM_RBUTTONDOWN: FolderBrowser_OnRButtonDown(hwnd, (UINT)wParam, MAKEPOINTS(lParam)); return 0; + case WM_RBUTTONUP: FolderBrowser_OnRButtonUp(hwnd, (UINT)wParam, MAKEPOINTS(lParam)); return 0; + case WM_MOUSEMOVE: FolderBrowser_OnMouseMove(hwnd, (UINT)wParam, MAKEPOINTS(lParam)); return 0; + case WM_WINDOWPOSCHANGED: FolderBrowser_OnWindowPosChanged(hwnd, (WINDOWPOS*)lParam); return 0; + case WM_KEYDOWN: FolderBrowser_OnKeyDown(hwnd, (UINT)wParam, (UINT)lParam); return 0; + case WM_COMMAND: FolderBorwser_OnCommand(hwnd, LOWORD(wParam), HIWORD(wParam), (HWND)lParam); return 0; + case WM_VKEYTOITEM: return FolderBrowser_OnVKeyToItem(hwnd, LOWORD(wParam), HIWORD(wParam), (HWND)lParam); + case WM_HSCROLL: FolderBrowser_OnHScroll(hwnd, LOWORD(wParam), HIWORD(wParam)); return 0; + case WM_PRINTCLIENT: FolderBroser_OnPrintClient(hwnd, (HDC)wParam, (UINT)lParam); return 0; + case WM_SETCURSOR: return (LRESULT)FolderBrowser_OnSetCursor(hwnd, (HWND)wParam, LOWORD(lParam), HIWORD(lParam)); + case WM_MOUSEWHEEL: FolderBrowser_OnMouseWheel(hwnd, GET_KEYSTATE_WPARAM(wParam), GET_WHEEL_DELTA_WPARAM(wParam), MAKEPOINTS(lParam)); return 0; + case WM_MOUSELEAVE: FolderBrowser_OnMouseLeave(hwnd); return 0; + + case WM_MOUSEACTIVATE: + if (HIWORD(lParam) == WM_MBUTTONDOWN) + { + return MA_NOACTIVATEANDEAT; + } + break; + + case WM_MEASUREITEM: return (LRESULT)FolderBrowser_OnMeasureItem(hwnd, (INT)wParam, (MEASUREITEMSTRUCT*)lParam); + case WM_PARENTNOTIFY: FolderBrowser_OnParentNotify(hwnd, (UINT)wParam, lParam); return 0; + + case FBM_GETBKCOLOR: return (LRESULT)FolderBrowser_OnGetBkColor(hwnd); + case FBM_SETBKCOLOR: return (LRESULT)FolderBrowser_OnSetBkColor(hwnd, (COLORREF)lParam); + case FBM_GETTEXTCOLOR: return (LRESULT)FolderBrowser_OnGetTextColor(hwnd); + case FBM_SETTEXTCOLOR: return (LRESULT)FolderBrowser_OnSetTextColor(hwnd, (COLORREF)lParam); + case FBM_GETFOLDERBROWSERINFO: return (LRESULT)FolderBrowser_OnGetFolderBrowserInfo(hwnd, (FOLDERBROWSERINFO*)lParam); + case FBM_SETROOT: return (LRESULT)FolderBrowser_OnSetRootFolder(hwnd, (LPCWSTR)lParam); + case FBM_GETROOT: return (LRESULT)FolderBrowser_OnGetRootFolder(hwnd, (LPWSTR)lParam, (INT)wParam); + case FBM_SETFILESYSTEMINFO: return (LRESULT)FolderBrowser_OnSetFileSystemInfo(hwnd, (FILESYSTEMINFO*)lParam); + case FBM_GETFILESYSTEMINFO: return (LRESULT)FolderBrowser_OnGetFileSystemInfo(hwnd, (FILESYSTEMINFO*)lParam); + case FBM_GETCURRENTPATH: return (LRESULT)FolderBrowser_OnGetCurrentPath(hwnd, (LPWSTR)lParam, (INT)wParam); + case FBM_SETCURRENTPATH: return (LRESULT)FolderBrowser_OnSetCurrentPath(hwnd, (LPWSTR)lParam, (BOOL)wParam); + case FBM_ENSUREVISIBLE: return (LRESULT)FolderBrowser_OnEnsureVisible(hwnd, LOWORD(wParam), HIWORD(wParam)); + } + return DefWindowProcW(hwnd, uMsg, wParam, lParam); +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/folderbrowser.h b/Src/Plugins/General/gen_ml/folderbrowser.h new file mode 100644 index 00000000..7f983dbf --- /dev/null +++ b/Src/Plugins/General/gen_ml/folderbrowser.h @@ -0,0 +1,22 @@ +#ifndef NULLOSFT_MEDIALIBRARY_FOLDERBROWSER_CONTROL_HEADER +#define NULLOSFT_MEDIALIBRARY_FOLDERBROWSER_CONTROL_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +BOOL RegisterFolderBrowserControl(HINSTANCE hInstance); + +#ifdef __cplusplus +} +#endif + + + +#endif // NULLOSFT_MEDIALIBRARY_FOLDERBROWSER_CONTROL_HEADER \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/folderbrowser_internal.h b/Src/Plugins/General/gen_ml/folderbrowser_internal.h new file mode 100644 index 00000000..91867830 --- /dev/null +++ b/Src/Plugins/General/gen_ml/folderbrowser_internal.h @@ -0,0 +1,34 @@ +#ifndef NULLOSFT_MEDIALIBRARY_FOLDERBROWSER_CONTROL_INTERNAL_HEADER +#define NULLOSFT_MEDIALIBRARY_FOLDERBROWSER_CONTROL_INTERNAL_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include +#include "./ml_ipc_0313.h" +#include "../nu/trace.h" + +#ifndef LONGX86 +#ifdef _WIN64 + #define LONGX86 LONG_PTR +#else /*_WIN64*/ + #define LONGX86 LONG +#endif /*_WIN64*/ +#endif // LONGX86 + +#define COLUMN_DEFAULT_WIDTH 120 +#define COLUMN_MIN_WIDTH 48 +#define COLUMN_MAX_WIDTH 640 +#define COLUMN_EXTRALSPACE 32 +#define SIZER_WIDTH 0 +#define SIZER_OVERLAP_LEFT 1 // how many pixels sizer steels from neighbors +#define SIZER_OVERLAP_RIGHT 3 // how many pixels sizer steels from neighbors + + +#define FBIS_SELECTED 0x00000001 +#define FBIS_HIGHLIGHTED 0x00000002 + + + +#endif //NULLOSFT_MEDIALIBRARY_FOLDERBROWSER_CONTROL_INTERNAL_HEADER \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/gaystring.cpp b/Src/Plugins/General/gen_ml/gaystring.cpp new file mode 100644 index 00000000..76c8d9ba --- /dev/null +++ b/Src/Plugins/General/gen_ml/gaystring.cpp @@ -0,0 +1,182 @@ +#include "gaystring.h" + +GayString::GayString(const char *initial) +{ + len = 0; + m_buf = NULL; + m_alloc = 0; + if (initial && *initial) Set(initial); +} + +GayString::~GayString() +{ + if ( m_buf ) + free( m_buf ); +} + +void GayString::Set(const char *value) +{ + if (!value) value=""; + len = strlen(value); + Grow(len + 1); + strncpy(m_buf, value, len); + m_buf[len] = 0; +} + +char *GayString::Get() { return m_buf ? m_buf : (char*)""; } + +void GayString::Append(const char *append) +{ + size_t oldsize = len; + len += strlen(append); + Grow(len + 1); + strncpy(m_buf + oldsize, append, len - oldsize); + m_buf[len] = 0; +} + +void GayString::Grow(size_t newsize) +{ + if (m_alloc < newsize) + { + size_t old_m_alloc = m_alloc; + m_alloc = newsize + 32; + char *new_m_buf = (char*)realloc(m_buf, m_alloc*sizeof(char)); + if (!new_m_buf) + { + new_m_buf = (char*)malloc(m_alloc*sizeof(char)); + if (!new_m_buf) + { + m_alloc = old_m_alloc; + } + else + { + memcpy(new_m_buf, m_buf, old_m_alloc*sizeof(char)); + free(m_buf); + m_buf = new_m_buf; + } + } + else m_buf = new_m_buf; + } +} + +void GayString::Compact() +{ + if (m_buf) + { + size_t old_m_alloc = m_alloc; + //m_alloc = strlen(m_buf) + 1; + m_alloc = len + 1; + char* new_m_buf = (char*)realloc(m_buf, m_alloc*sizeof(char)); + if (!new_m_buf) + { + new_m_buf = (char*)malloc(m_alloc*sizeof(char)); + if (!new_m_buf) + { + if (m_alloc > old_m_alloc) + m_alloc = old_m_alloc; + } + else + { + if (m_alloc > old_m_alloc) + m_alloc = old_m_alloc; + + memcpy(new_m_buf, m_buf, old_m_alloc*sizeof(char)); + free(m_buf); + m_buf = new_m_buf; + } + } + else m_buf = new_m_buf; + } +} + +size_t GayString::Length() { return len; } + +/* */ +GayStringW::GayStringW(const wchar_t *initial) +{ + len = 0; + m_buf = NULL; + m_alloc = 0; + if (initial && *initial) Set(initial); +} + +GayStringW::~GayStringW() +{ + free(m_buf); +} + +void GayStringW::Set(const wchar_t *value) +{ + if (!value) value=L""; + len = wcslen(value); + Grow(len + 1); + wcsncpy(m_buf, value, len); + m_buf[len] = 0; +} + +const wchar_t *GayStringW::Get() { return m_buf ? m_buf : L""; } + +void GayStringW::Append(const wchar_t *append) +{ + size_t oldsize = len; + len += wcslen(append); + Grow(len + 1); + wcsncpy(m_buf + oldsize, append, len - oldsize); + m_buf[len] = 0; +} + +void GayStringW::Grow(size_t newsize) +{ + if (m_alloc < newsize) + { + size_t old_m_alloc = m_alloc; + m_alloc = newsize + 32; + wchar_t *new_m_buf = (wchar_t*)realloc(m_buf, m_alloc*sizeof(wchar_t)); + if (!new_m_buf) + { + new_m_buf = (wchar_t*)malloc(m_alloc*sizeof(wchar_t)); + if (!new_m_buf) + { + m_alloc = old_m_alloc; + } + else + { + memcpy(new_m_buf, m_buf, old_m_alloc*sizeof(wchar_t)); + free(m_buf); + m_buf = new_m_buf; + } + } + else m_buf = new_m_buf; + } +} + +void GayStringW::Compact() +{ + if (m_buf) + { + size_t old_m_alloc = m_alloc; + m_alloc = len + 1; + wchar_t* new_m_buf = (wchar_t*)realloc(m_buf, m_alloc*sizeof(wchar_t)); + if (!new_m_buf) + { + new_m_buf = (wchar_t*)malloc(m_alloc*sizeof(wchar_t)); + if (!new_m_buf) + { + if (m_alloc > old_m_alloc) + m_alloc = old_m_alloc; + } + else + { + if (m_alloc > old_m_alloc) + m_alloc = old_m_alloc; + + memcpy(new_m_buf, m_buf, old_m_alloc*sizeof(wchar_t)); + free(m_buf); + m_buf = new_m_buf; + } + } + else m_buf = new_m_buf; + } +} + +size_t GayStringW::Length() { return len; } \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/gaystring.h b/Src/Plugins/General/gen_ml/gaystring.h new file mode 100644 index 00000000..54b39005 --- /dev/null +++ b/Src/Plugins/General/gen_ml/gaystring.h @@ -0,0 +1,44 @@ +#ifndef _GAYSTRING_H_ +#define _GAYSTRING_H_ + +#include + +class GayString +{ +public: + GayString(const char *initial=NULL); + ~GayString(); + void Set(const char *value); + char *Get(); + + void Append(const char *append); + void Grow(size_t newsize); + void Compact(); + size_t Length(); + +private: + char *m_buf; + size_t m_alloc; + size_t len; +}; + +class GayStringW +{ +public: + GayStringW(const wchar_t *initial=NULL); + ~GayStringW(); + void Set(const wchar_t *value); + const wchar_t *Get(); + + void Append(const wchar_t *append); + void Grow(size_t newsize); + void Compact(); + size_t Length(); + +private: + wchar_t *m_buf; + size_t m_alloc; + size_t len; +}; + +#endif//_GAYSTRING_H_ \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/gen_ml.rc b/Src/Plugins/General/gen_ml/gen_ml.rc new file mode 100644 index 00000000..cc07c1d8 --- /dev/null +++ b/Src/Plugins/General/gen_ml/gen_ml.rc @@ -0,0 +1,648 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "#include ""version.rc2""\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_MAIN DIALOGEX 0, 0, 384, 281 +STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN +EXSTYLE WS_EX_ACCEPTFILES | WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + CONTROL "",IDC_VDELIM,"Static",SS_BLACKFRAME,86,1,6,278 + PUSHBUTTON "Library",IDC_BTN_LIB,0,268,83,11 + CTEXT "\n\n\n\nUnable to display an appropriate library view.\n\nPlease select a view from one to the left or re-install Winamp with library plug-ins as applicable.",IDC_NO_VIEW,157,64,150,47,NOT WS_VISIBLE +END + +IDD_PREFS1 DIALOGEX 0, 0, 260, 226 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + GROUPBOX "Double Click or Enter Options",IDC_STATIC,4,3,256,57 + LTEXT "Double click or Enter in the views:",IDC_STATIC,10,17,112,9 + COMBOBOX IDC_COMBO1,122,15,132,68,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + CONTROL "Play all items (start with selected) on double click in playlists",IDC_PLPLAYLIST, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,32,202,10 + CONTROL "Play search results (start with selected) on double click in media views",IDC_VIEWPLAYMODE, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,45,239,10 + GROUPBOX "Column Resizing Behavior",IDC_STATIC,4,63,256,45 + LTEXT "Select the behavior to follow when resizing list columns:",IDC_STATIC,10,75,180,10 + COMBOBOX IDC_COMBO2,19,88,235,37,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + GROUPBOX "Advanced Library Preferences",IDC_STATIC,4,111,256,53 + CONTROL "Main Window lightning bolt opens Media Library instead of About Box",IDC_CHECK1, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,123,236,10 + CONTROL "Save ratings to file for compatible formats (default: off)",IDC_WRITE_RATINGS, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,136,194,10 + CONTROL "Group the 'Play' and 'Enqueue' buttons into a single button",IDC_GROUP_BUTTONS, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,149,203,10 + GROUPBOX "Send To Options",IDC_STATIC,4,167,256,40 + CONTROL "Show all playlists on the send to menu",IDC_PL_SEND_TO, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,179,137,10 + CONTROL "Group portable devices in a sub-menu",IDC_PMP_SEND_TO, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,192,137,10 +END + +IDD_VIEW_EMPTY DIALOGEX 0, 0, 194, 166 +STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN +FONT 8, "MS Shell Dlg", 0, 0, 0x0 +BEGIN +END + +IDD_PREFSFR DIALOGEX 0, 0, 272, 246 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + CONTROL "Tab1",IDC_TAB1,"SysTabControl32",WS_TABSTOP,0,0,271,246 +END + +IDD_PREFS2 DIALOGEX 0, 0, 260, 226 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + GROUPBOX "Internet Media Rating Preferences",IDC_STATIC,4,3,256,144 + LTEXT "Winamp can be configured to hide listings of the specified ratings below when accessing Internet Media.",IDC_STATIC,9,15,244,16 + LTEXT "Specify which content ratings you wish to include in your listings:",IDC_STATIC,9,36,212,8 + CONTROL "Everyone - Suitable for general audiences",IDC_CHECK_RATING0, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,49,147,10 + CONTROL "Teens - Suitable for teenage audiences",IDC_CHECK_RATING1, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,60,140,10 + CONTROL "Adult - Suitable for adult audiences.",IDC_CHECK_RATING2, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,72,127,10 + CONTROL "X-Rated - Sexually explicit content. Suitable for adults only.",IDC_CHECK_RATING3, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,84,208,10 + CONTROL "Not Rated - No rating specified, view at your own risk",IDC_CHECK_RATING4, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,96,183,10 + GROUPBOX "Security",IDC_STATIC,10,112,243,28 + CONTROL "Password required to change settings",IDC_CHECK_PASSPROMPT, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,124,134,10 + DEFPUSHBUTTON "Change Password",IDC_ITV_CHANGEPASS,181,122,65,13,WS_DISABLED + LTEXT " An incorrect password was entered or a password was not entered.\n\nYou will need to enter a valid password in order to change these options.",IDC_STATIC_INFO,12,99,235,28,WS_DISABLED +END + +IDD_MLPLUGINS DIALOGEX 0, 0, 272, 246 +STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + GROUPBOX "Media Library plug-ins",IDC_STATIC,0,0,272,246 + LTEXT "Installed Media Library plug-ins (loaded at startup):",IDC_STATIC,6,12,253,8 + LISTBOX IDC_GENLIB,5,26,259,198,LBS_SORT | LBS_USETABSTOPS | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP + PUSHBUTTON "&Configure selected plug-in",IDC_GENCONF,5,228,96,13,WS_DISABLED + PUSHBUTTON "Uninstall selected plug-in",IDC_UNINST,105,228,90,13,WS_DISABLED + CONTROL "Get plug-ins",IDC_PLUGINVERS,"Button",BS_OWNERDRAW | WS_TABSTOP,224,230,41,9 +END + +IDD_PREFS_ITV_ASSIGNPASS DIALOGEX 0, 0, 219, 94 +STYLE DS_SETFONT | DS_MODALFRAME | DS_SETFOREGROUND | DS_FIXEDSYS | WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN | WS_CAPTION | WS_SYSMENU +EXSTYLE WS_EX_CONTROLPARENT +CAPTION "Internet Ratings Security" +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + GROUPBOX "Please enter password to be used to access Internet Ratings",IDC_STATIC,6,6,207,62,0,WS_EX_TRANSPARENT + LTEXT "Password:",IDC_STATIC,12,30,54,8 + EDITTEXT IDC_EDIT_PASSWORD,74,28,126,12,ES_PASSWORD | ES_AUTOHSCROLL + LTEXT "Verify Password:",IDC_STATIC,12,46,54,8 + EDITTEXT IDC_EDIT_PASSWORD_VERIFY,74,44,126,12,ES_PASSWORD | ES_AUTOHSCROLL + DEFPUSHBUTTON "&Ok",IDOK,105,75,50,13 + PUSHBUTTON "&Cancel",IDCANCEL,163,75,50,13 +END + +IDD_PREFS_ITV_GETPASS DIALOGEX 0, 0, 161, 57 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION +EXSTYLE WS_EX_CONTROLPARENT +CAPTION "Internet Ratings Security" +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + GROUPBOX "Please Enter Password",-1,4,5,153,31 + LTEXT "Password:",-1,11,19,34,8 + EDITTEXT IDC_GPASS,51,17,100,12,ES_PASSWORD | ES_AUTOHSCROLL + DEFPUSHBUTTON "Ok",IDOK,53,40,50,13 + PUSHBUTTON "Cancel",IDCANCEL,107,40,50,13 +END + +IDD_RATINGTWEAK DIALOGEX 0, 0, 204, 197 +STYLE DS_SETFONT | DS_MODALFRAME | DS_SETFOREGROUND | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU +EXSTYLE WS_EX_CONTROLPARENT +CAPTION "Rating Column Appearance" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LTEXT "Track when:",IDC_STATIC,4,6,53,8 + COMBOBOX IDC_CMB_TRACKWHEN,72,4,128,64,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + LTEXT "Track what:",IDC_STATIC,4,22,53,8 + COMBOBOX IDC_CMB_TRACKWHAT,72,20,128,49,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + LTEXT "Alignment:",IDC_STATIC,4,38,53,8 + COMBOBOX IDC_CMB_ALIGNMENT,72,36,128,49,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + GROUPBOX "Show Empty",IDC_STATIC,4,52,196,27 + CONTROL "Normal",IDC_CHK_SHOWEMPTY_NORMAL,"Button",BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,10,65,38,8 + CONTROL "Hot",IDC_CHK_SHOWEMPTY_HOT,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,73,65,27,8 + CONTROL "Animation",IDC_CHK_SHOWEMPTY_ANIMATION,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,124,65,47,8 + GROUPBOX "Sizing",IDC_STATIC,4,83,196,27 + CONTROL "Allow Decrease",IDC_CHK_SIZEDECREASE,"Button",BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,10,96,65,8 + CONTROL "Allow Increase",IDC_CHK_SIZEINCREASE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,91,96,63,8 + GROUPBOX "Miscellaneous",IDC_STATIC,4,114,196,63 + CONTROL "Block clicking",IDC_CHK_BLOCKCLICK,"Button",BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,10,126,59,10 + CONTROL "Block unrate click",IDC_CHK_BLOCKUNRATE,"Button",BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,10,138,70,10 + CONTROL "Block dragging",IDC_CHK_BLOCKDRAG,"Button",BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,10,150,60,10 + CONTROL "Show inactive ratings as 'hot'",IDC_CHK_SHOWINACTIVE_HOT, + "Button",BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,10,162,110,10 + DEFPUSHBUTTON "Apply",IDOK,96,180,50,13 + PUSHBUTTON "Close",IDCANCEL,150,180,50,13 +END + +#if defined(APSTUDIO_INVOKED) || !defined(EXPERIMENTAL) +IDD_PREFS3 DIALOGEX 0, 0, 260, 226 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + GROUPBOX "Media Library Tree Options",IDC_ML_TREE_OPTS,4,3,256,156 + LTEXT "Here you can adjust the visual style of the Media Library tree.",-1,9,15,244,10 + CONTROL "Show icons next to the tree items",IDC_SHOW_ICONS, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,27,123,10 + CONTROL "Highlight full tree item",IDC_HIGHLIGHT_FULL_TREE_ITEM, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,39,140,10 + CONTROL "Allow right-click to make tree item the active view",IDC_RCLICK_TO_SELECT, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,51,243,10 + EDITTEXT IDC_ITEM_HEIGHT,10,64,22,12,ES_AUTOHSCROLL + LTEXT "Tree item height (default is 18)",-1,36,65,102,10,SS_CENTERIMAGE + GROUPBOX "CD/DVD items",IDC_CD_DVD_ITEMS,10,81,243,40 + CONTROL "Show eject icons next to the CD/DVD drives",IDC_SHOW_EJECT_ICONS, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,93,156,10 + CONTROL "Group CD/DVD drives under the 'Rip && Burn' item",IDC_GROUP_DRIVES, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,105,171,10 + GROUPBOX "'Downloads' item",IDC_DOWNLOADS_ITEMS,10,125,243,28 + CONTROL "Place 'Downloads' under the 'Podcast Directory' item",IDC_PARENT_PODCASTS, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,137,182,10 +END +#endif + +IDD_FILEVIEW DIALOGEX 0, 0, 233, 226 +STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN +EXSTYLE WS_EX_CLIENTEDGE | WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + EDITTEXT IDC_EDT_PATH,32,0,200,12,ES_AUTOHSCROLL | NOT WS_BORDER,WS_EX_STATICEDGE + CONTROL "Folder Browser",IDC_FOLDER_BROWSER,"MLFolderBrowser",WS_HSCROLL | WS_TABSTOP,0,14,232,112,WS_EX_NOPARENTNOTIFY + CONTROL "",IDC_HDELIM,"Static",SS_BLACKFRAME,0,126,231,2 + RTEXT "Address",IDC_LBL_ADDRESS,0,2,29,8,SS_CENTERIMAGE +END + +IDD_FILEVIEW_TOOLBAR DIALOGEX 0, 0, 326, 14 +STYLE DS_SETFONT | DS_3DLOOK | DS_FIXEDSYS | WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN +EXSTYLE WS_EX_NOPARENTNOTIFY +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + CONTROL "",IDC_BTN_VIEW_ICON,"Button",BS_AUTORADIOBUTTON | BS_PUSHLIKE | WS_GROUP,0,0,31,14 + CONTROL "",IDC_BTN_VIEW_LIST,"Button",BS_AUTORADIOBUTTON | BS_PUSHLIKE,35,0,28,14 + CONTROL "",IDC_BTN_VIEW_DETAIL,"Button",BS_AUTORADIOBUTTON | BS_PUSHLIKE,68,0,28,14 + PUSHBUTTON "Options",IDC_BTN_OPTIONS,268,0,58,14,NOT WS_TABSTOP + PUSHBUTTON "Arrange by:",IDC_BTN_ARRANGEBY,203,0,58,14,NOT WS_TABSTOP +END + +IDD_PREFS4 DIALOGEX 0, 0, 260, 226 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + GROUPBOX "Media Library Appearance",IDC_ML_TREE_OPTS,4,3,256,125 + LTEXT "Here you can adjust the visual style of the Media Library views\n(Requires Media Library plug-ins to support relevant features)",IDC_STATIC,9,15,244,16 + CONTROL "Media Library uses same font as Playlist Editor",IDC_CHECK_USEPLFONT, + "Button",BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP,9,35,160,10 + CONTROL "Use freeform scrollbars when available",IDC_FF_SCROLLBARS, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,9,48,140,8 + CONTROL "Use alternating row colors in list views when available",IDC_ALTERNATEITEMS, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,9,60,190,8 + CONTROL "Use skinned menus",IDC_SKINNED_MENUS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,9,72,80,8 + CONTROL "Match the OS style for file size units (e.g. GB) instead of displaying the technically correct value (e.g. GiB). [Note: this is applied after a restart]",IDC_GENO, + "Button",BS_AUTOCHECKBOX | BS_TOP | BS_MULTILINE | WS_TABSTOP,9,84,245,16 + LTEXT "Note: this is a very advanced option:",IDC_STATIC,9,111,120,8,WS_DISABLED + PUSHBUTTON "Rating Column Appearance...",IDC_RATING_COLUMN,133,108,120,14 +END + +#if defined(APSTUDIO_INVOKED) || defined(EXPERIMENTAL) +#if defined(APSTUDIO_INVOKED) +IDD_PREFS3$(EXPERIMENTAL) DIALOGEX 0, 0, 260, 226 +#else +IDD_PREFS3 DIALOGEX 0, 0, 260, 226 +#endif +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN + GROUPBOX "Media Library Tree Options",IDC_ML_TREE_OPTS,4,3,256,196 + LTEXT "Here you can adjust the visual style of the Media Library tree.",-1,9,15,244,10 + CONTROL "Show icons next to the tree items",IDC_SHOW_ICONS, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,27,123,10 + CONTROL "Highlight full tree item",IDC_HIGHLIGHT_FULL_TREE_ITEM, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,39,140,10 + CONTROL "Allow right-click to make tree item the active view",IDC_RCLICK_TO_SELECT, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,10,51,243,10 + EDITTEXT IDC_ITEM_HEIGHT,10,64,22,12,ES_AUTOHSCROLL + LTEXT "Tree item height (default is 18)",-1,36,65,102,10,SS_CENTERIMAGE + GROUPBOX "CD/DVD items",IDC_CD_DVD_ITEMS,10,81,243,40 + CONTROL "Show eject icons next to the CD/DVD drives",IDC_SHOW_EJECT_ICONS, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,93,156,10 + CONTROL "Group CD/DVD drives under the 'Rip && Burn' item",IDC_GROUP_DRIVES, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,105,171,10 + GROUPBOX "'Downloads' item",IDC_DOWNLOADS_ITEMS,10,125,243,28 + CONTROL "Place 'Downloads' under the 'Podcast Directory' item",IDC_PARENT_PODCASTS, + "Button",BS_AUTOCHECKBOX | WS_TABSTOP,16,137,182,10 + GROUPBOX "'Transfers' item",IDC_TRANSFERS_ITEMS,10,157,243,36 + LTEXT "Place the unified 'Transfers' item\n(Note: applied after a restart)",-1,16,169,104,16 + COMBOBOX IDC_TRANSFERS_COMBO,124,167,124,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP +END +#endif + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_MAIN, DIALOG + BEGIN + BOTTOMMARGIN, 280 + END + + IDD_VIEW_EMPTY, DIALOG + BEGIN + RIGHTMARGIN, 192 + END + + IDD_PREFSFR, DIALOG + BEGIN + RIGHTMARGIN, 191 + BOTTOMMARGIN, 191 + END + + IDD_MLPLUGINS, DIALOG + BEGIN + RIGHTMARGIN, 271 + END + + IDD_PREFS_ITV_ASSIGNPASS, DIALOG + BEGIN + LEFTMARGIN, 6 + RIGHTMARGIN, 213 + VERTGUIDE, 12 + VERTGUIDE, 66 + VERTGUIDE, 74 + VERTGUIDE, 200 + TOPMARGIN, 6 + BOTTOMMARGIN, 88 + END + + IDD_PREFS_ITV_GETPASS, DIALOG + BEGIN + LEFTMARGIN, 4 + RIGHTMARGIN, 157 + TOPMARGIN, 5 + BOTTOMMARGIN, 53 + END + + IDD_RATINGTWEAK, DIALOG + BEGIN + LEFTMARGIN, 4 + RIGHTMARGIN, 200 + TOPMARGIN, 4 + BOTTOMMARGIN, 193 + END + + IDD_FILEVIEW_TOOLBAR, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 179 + TOPMARGIN, 7 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDR_CONTEXTMENUS MENU +BEGIN + POPUP "DoShitMenu" + BEGIN + MENUITEM SEPARATOR + MENUITEM "Media Library &Preferences...\tCtrl+Shift+P", IDM_LIBRARY_CONFIG + MENUITEM SEPARATOR + MENUITEM "Help\tF1", IDM_LIBRARY_HELP + END +END + +IDR_MENU_FILEVIEW MENU +BEGIN + POPUP "View" + BEGIN + MENUITEM "Icons", ID_FILEVIEW_SETMODE_ICON + MENUITEM "List", ID_FILEVIEW_SETMODE_LIST + MENUITEM "Details", ID_FILEVIEW_SETMODE_DETAIL + END + POPUP "Options" + BEGIN + MENUITEM "Show audio files", IDM_FILEVIEW_SHOWAUDIO + MENUITEM "Show video files", IDM_FILEVIEW_SHOWVIDEO + MENUITEM "Show playlist files", IDM_FILEVIEW_SHOWPLAYLIST + MENUITEM "Show unknown files", IDM_FILEVIEW_SHOWUNKNOWN + MENUITEM SEPARATOR + MENUITEM "Ignore hidden files and folders", IDM_FILEVIEW_IGNOREHIDDEN + MENUITEM "Hide known extensions ", IDM_FILEVIEW_HIDEEXTENSION + END + POPUP "Arrange by" + BEGIN + MENUITEM SEPARATOR + MENUITEM "Sort Ascending", IDM_FILEVIEW_SORTASCENDING + END + POPUP "Select Columns" + BEGIN + MENUITEM SEPARATOR + MENUITEM "Customize...", ID_SELECTCOLUMNS_CUSTOMIZE, INACTIVE + END + POPUP "FileList" + BEGIN + POPUP "Play" + BEGIN + MENUITEM "Play", ID_FILEVIEW_PLAYSELECTION + MENUITEM "Enqueue", ID_FILEVIEW_PLAYSELECTION_SHIFT + END + POPUP "ViewContext" + BEGIN + MENUITEM SEPARATOR + END + POPUP "OpContext" + BEGIN + MENUITEM SEPARATOR + END + END + POPUP "Common" + BEGIN + MENUITEM "Refresh", ID_FILEVIEW_REFRESH + MENUITEM "Select All", ID_FILEVIEW_SELECT_ALL + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// Cursor +// + +ML_IDC_DRAGDROP CURSOR ".\\resources\\dragdrop.cur" + +///////////////////////////////////////////////////////////////////////////// +// +// Bitmap +// + +IDB_TREEITEM_COLLAPSED BITMAP "resources\\tree_closed_16x16x16.bmp" +IDB_TREEITEM_NOCHILD BITMAP "resources\\tree_closed_disabled_16x16x16.bmp" +IDB_TREEITEM_EXPANDED BITMAP "resources\\tree_open_16x16x16.bmp" +IDB_TREEITEM_DEFAULT BITMAP "resources\\ti_default_16x16x16.bmp" +#if defined(APSTUDIO_INVOKED) || defined(DISABLED) +#if defined(APSTUDIO_INVOKED) +IDB_TREEITEM_LABS$(DISABLED) BITMAP "resources\\ti_labs_16x16x16.bmp" +#else +IDB_TREEITEM_LABS BITMAP "resources\\ti_labs_16x16x16.bmp" +#endif +#endif + +///////////////////////////////////////////////////////////////////////////// +// +// Accelerator +// + +IDR_ACCELERATOR_GLOBAL ACCELERATORS +BEGIN + "L", ID_TOGGLE_LIBRARY, VIRTKEY, ALT, NOINVERT +END + +IDR_ACCELERATOR_MAIN ACCELERATORS +BEGIN + "P", IDM_LIBRARY_CONFIG, VIRTKEY, SHIFT, CONTROL, NOINVERT + "R", ID_SHOW_RATINGTWEAK, VIRTKEY, CONTROL, ALT, NOINVERT + VK_F1, ID_SHOW_HELP, VIRTKEY, NOINVERT + VK_F4, ID_WINDOW_CLOSE, VIRTKEY, CONTROL, NOINVERT + VK_F6, ID_GO_TO_VIEW_SEARCHBAR, VIRTKEY, NOINVERT + VK_INSERT, ID_NEW_PLAYLIST, VIRTKEY, SHIFT, NOINVERT + VK_F8, ID_REFRESH_SEARCH, VIRTKEY, NOINVERT +END + +IDR_ACCELERATOR_FILEVIEW ACCELERATORS +BEGIN + VK_F5, ID_FILEVIEW_REFRESH, VIRTKEY, NOINVERT + "3", ID_FILEVIEW_SETMODE_DETAIL, VIRTKEY, CONTROL, NOINVERT + "1", ID_FILEVIEW_SETMODE_ICON, VIRTKEY, CONTROL, NOINVERT + "2", ID_FILEVIEW_SETMODE_LIST, VIRTKEY, CONTROL, NOINVERT + VK_RETURN, ID_FILEVIEW_PLAYSELECTION, VIRTKEY, NOINVERT + VK_RETURN, ID_FILEVIEW_PLAYSELECTION_SHIFT, VIRTKEY, SHIFT, NOINVERT + "A", ID_FILEVIEW_SELECT_ALL, VIRTKEY, CONTROL, NOINVERT +END + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + IDS_ENQUEUE_IN_WINAMP "Enqueue in Winamp" + IDS_LIBRARY_OPTIONS "Options" + IDS_ONLINE_MEDIA "Online Media" + IDS_RATINGS_PASSWORD_MISSING + "Ratings Password Enabled, but password is missing!" + IDS_SERCURITY_ALERT "Security Alert!" + IDS_MUST_ENTER_PASSWORD "You must enter a password!" + IDS_INTERNET_ACCESS "Internet Access" + IDS_PASSWORD_NO_MATCH "Passwords do not match!" + IDS_INVALID_PASSWORD "Invalid Password!" + IDS_PLAY_SELECTED "Plays selected item(s)" + IDS_ENQUEUE_SELECTED "Enqueues selected item(s)" + IDS_NO_CONFIG_PRESENT "This plug-in has no configuration implemented" + IDS_ML_PLUGIN_INFO "Media Library plug-in info" + IDS_BAD_ITEM "Bad Item" + IDS_ML_GHK_STR "ML: Show/Hide Media Library" +END + +STRINGTABLE +BEGIN + IDS_NULLSOFT_ML_STR "Nullsoft Media Library v%X.%02X" + 65535 "{3D968813-F245-40ad-8589-5599C754B924}" +END + +STRINGTABLE +BEGIN + IDS_MEDIA_LIBRARY "Media Library" + IDS_ML_ALT_L_SHORTCUT "Media Library\tAlt+L" + IDS_WINAMP_LIBRARY "Winamp Library" + IDS_ONLINE_SERVICES_NOT_PRESENT "Online Services module not found. " + IDS_ERROR_SWITCHING_TO_VIEW "Error switching to view" + IDS_CDDB_CONNECTING "Connecting" + IDS_CDDB_SENDING "Sending" + IDS_CDDB_RECEIVING "Receiving" + IDS_CDDB_COMPLETE "Complete" + IDS_TOGGLE_LIBRARY "Toggle Library" + IDS_UNINSTALL_PROMPT "Permanently uninstall this plug-in?\n(This may require a restart of Winamp)" + IDS_UINSTALL_CONFIRMATION "Confirmation" + IDS_DOWNLOADING "Downloading..." + IDS_WEBINFO_NAVIGATE_ERROR "Unable to get information." + IDS_WEBINFO_MESSAGEBOX_TITLE "Winamp Browser" +END + +STRINGTABLE +BEGIN + IDS_LOADING_IN_PROGRESS "Loading..." + IDS_UNABLE_TO_APPLY_NEW_STYLE "Unable to apply new style." + IDS_TWEAK_ERROR "Tweak Error" + IDS_ALWAYS "Always" + IDS_PROCESS_ACTIVE "Process Active" + IDS_ANCESTOR_ACTIVE "Ancestor Active" + IDS_WINDOW_FOCUSED "Window Focused" + IDS_NEVER "Never" + IDS_ALL "All" + IDS_SELECTED "Selected" + IDS_FOCUSED "Focused" + IDS_LEFT "Left" + IDS_CENTER "Center" + IDS_RIGHT "Right" + IDS_DO_YOU_WANT_TO_SAVE_CHANGES_FIRST "Do you want to save changes first?" + IDS_CONFIRM_CLOSE "Confirm Close" +END + +STRINGTABLE +BEGIN + IDS_TREE_OPTIONS "Tree Options" + IDS_FILE_ATTRIBUTES "RHSACE" + IDS_FILETYPE_AUDIO "Audio" + IDS_FILETYPE_VIDEO "Video" + IDS_FILETYPE_PLAYLIST "Playlist" + IDS_FILETYPE_UNKNOWN "Unknown" + IDS_FILEVIEW_COL_NAME "Name" + IDS_FILEVIEW_COL_TYPE "Type" + IDS_FILEVIEW_COL_SIZE "Size" + IDS_FILEVIEW_COL_MODIFIED "Date Modified" + IDS_FILEVIEW_COL_CREATED "Date Created" + IDS_FILEVIEW_COL_EXTENSION "Extension" + IDS_FILEVIEW_COL_ATTRIBUTES "Attributes" + IDS_FILEVIEW_COL_ARTIST "Artist" + IDS_FILEVIEW_COL_ALBUM "Album" + IDS_FILEVIEW_COL_TITLE "Title" +END + +STRINGTABLE +BEGIN + IDS_FILEVIEW_COL_INMLDB "Media Library" + IDS_FILEVIEW_COL_GENRE "Genre" + IDS_FILEVIEW_COL_YEAR "Year" + IDS_FILEVIEW_COL_LENGTH "Length" + IDS_FILEVIEW_COL_BITRATE "Bitrate" + IDS_FILEVIEW_COL_TRACK "Track#" + IDS_FILEVIEW_COL_DISC "Disc" + IDS_FILEVIEW_COL_COMMENT "Comment" + IDS_FILEVIEW_COL_PUBLISHER "Publisher" + IDS_FILEVIEW_COL_COMPOSER "Composer" + IDS_FILEVIEW_COL_ALBUMARTIST "Album Artist" + IDS_FILEVIEW_COL_ENTRYCOUNT "Entries Count" + IDS_FILEVIEW_COL_TOTALLENGTH "Total Length" +END + +STRINGTABLE +BEGIN + IDS_KBPS "kbps" + IDS_APPEARANCE "Appearance" +END + +STRINGTABLE +BEGIN + IDS_YES "Yes" + IDS_NO "No" + IDS_FILEVIEW_EMPTYFOLDER "Empty Folder" + IDS_FILEVIEWSTATUS_GROUPTEMPLATE "%d files, %s" + IDS_FILEVIEWSTATUS_SELECTEDGROUPTEMPLATE "%d files selected, %s" + IDS_RESTART "Restart Winamp?" + IDS_RESTART_MESSAGE "Would you like to restart Winamp now to apply this settings change?\nChoose 'No' to apply this setting the next time Winamp is restarted." + IDS_COL_NORMAL "Resize only the selected column (default Windows behavior)" + IDS_COL_SELONLY "Resize column and adjust adjacent column" + IDS_COL_PROP "Resize column and adjust all columns proportionately" + IDS_PLAY "Play" + IDS_ENQUEUE "Enqueue" +END + +STRINGTABLE +BEGIN + IDS_NOT_LOADED "NOT LOADED" +END + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// +#include "version.rc2" + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/Src/Plugins/General/gen_ml/gen_ml.sln b/Src/Plugins/General/gen_ml/gen_ml.sln new file mode 100644 index 00000000..6a985d49 --- /dev/null +++ b/Src/Plugins/General/gen_ml/gen_ml.sln @@ -0,0 +1,92 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29424.173 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gen_ml", "gen_ml.vcxproj", "{9B212232-4908-49D2-8D6D-96555B1F701E}" + ProjectSection(ProjectDependencies) = postProject + {A929EC04-302E-4B4F-B2A3-65AF63BB088F} = {A929EC04-302E-4B4F-B2A3-65AF63BB088F} + {DABE6307-F8DD-416D-9DAC-673E2DECB73F} = {DABE6307-F8DD-416D-9DAC-673E2DECB73F} + {D0EC862E-DDDD-4F4F-934F-B75DC9062DC1} = {D0EC862E-DDDD-4F4F-934F-B75DC9062DC1} + {977153BF-8420-4C8D-AA25-592FAEDB6CBA} = {977153BF-8420-4C8D-AA25-592FAEDB6CBA} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tataki", "..\tataki\tataki.vcxproj", "{255B68B5-7EF8-45EF-A675-2D6B88147909}" + ProjectSection(ProjectDependencies) = postProject + {DABE6307-F8DD-416D-9DAC-673E2DECB73F} = {DABE6307-F8DD-416D-9DAC-673E2DECB73F} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "bfc", "..\Wasabi\bfc\bfc.vcxproj", "{D0EC862E-DDDD-4F4F-934F-B75DC9062DC1}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "nsutil", "..\nsutil\nsutil.vcxproj", "{DABE6307-F8DD-416D-9DAC-673E2DECB73F}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "elevator", "..\Elevator\elevator.vcxproj", "{977153BF-8420-4C8D-AA25-592FAEDB6CBA}" + ProjectSection(ProjectDependencies) = postProject + {A929EC04-302E-4B4F-B2A3-65AF63BB088F} = {A929EC04-302E-4B4F-B2A3-65AF63BB088F} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ElevatorPS", "..\Elevator\ElevatorPS.vcxproj", "{A929EC04-302E-4B4F-B2A3-65AF63BB088F}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {9B212232-4908-49D2-8D6D-96555B1F701E}.Debug|Win32.ActiveCfg = Debug|Win32 + {9B212232-4908-49D2-8D6D-96555B1F701E}.Debug|Win32.Build.0 = Debug|Win32 + {9B212232-4908-49D2-8D6D-96555B1F701E}.Debug|x64.ActiveCfg = Debug|x64 + {9B212232-4908-49D2-8D6D-96555B1F701E}.Debug|x64.Build.0 = Debug|x64 + {9B212232-4908-49D2-8D6D-96555B1F701E}.Release|Win32.ActiveCfg = Release|Win32 + {9B212232-4908-49D2-8D6D-96555B1F701E}.Release|Win32.Build.0 = Release|Win32 + {9B212232-4908-49D2-8D6D-96555B1F701E}.Release|x64.ActiveCfg = Release|x64 + {9B212232-4908-49D2-8D6D-96555B1F701E}.Release|x64.Build.0 = Release|x64 + {255B68B5-7EF8-45EF-A675-2D6B88147909}.Debug|Win32.ActiveCfg = Debug|Win32 + {255B68B5-7EF8-45EF-A675-2D6B88147909}.Debug|Win32.Build.0 = Debug|Win32 + {255B68B5-7EF8-45EF-A675-2D6B88147909}.Debug|x64.ActiveCfg = Debug|x64 + {255B68B5-7EF8-45EF-A675-2D6B88147909}.Debug|x64.Build.0 = Debug|x64 + {255B68B5-7EF8-45EF-A675-2D6B88147909}.Release|Win32.ActiveCfg = Release|Win32 + {255B68B5-7EF8-45EF-A675-2D6B88147909}.Release|Win32.Build.0 = Release|Win32 + {255B68B5-7EF8-45EF-A675-2D6B88147909}.Release|x64.ActiveCfg = Release|x64 + {255B68B5-7EF8-45EF-A675-2D6B88147909}.Release|x64.Build.0 = Release|x64 + {D0EC862E-DDDD-4F4F-934F-B75DC9062DC1}.Debug|Win32.ActiveCfg = Debug|Win32 + {D0EC862E-DDDD-4F4F-934F-B75DC9062DC1}.Debug|Win32.Build.0 = Debug|Win32 + {D0EC862E-DDDD-4F4F-934F-B75DC9062DC1}.Debug|x64.ActiveCfg = Debug|x64 + {D0EC862E-DDDD-4F4F-934F-B75DC9062DC1}.Debug|x64.Build.0 = Debug|x64 + {D0EC862E-DDDD-4F4F-934F-B75DC9062DC1}.Release|Win32.ActiveCfg = Release|Win32 + {D0EC862E-DDDD-4F4F-934F-B75DC9062DC1}.Release|Win32.Build.0 = Release|Win32 + {D0EC862E-DDDD-4F4F-934F-B75DC9062DC1}.Release|x64.ActiveCfg = Release|x64 + {D0EC862E-DDDD-4F4F-934F-B75DC9062DC1}.Release|x64.Build.0 = Release|x64 + {DABE6307-F8DD-416D-9DAC-673E2DECB73F}.Debug|Win32.ActiveCfg = Debug|Win32 + {DABE6307-F8DD-416D-9DAC-673E2DECB73F}.Debug|Win32.Build.0 = Debug|Win32 + {DABE6307-F8DD-416D-9DAC-673E2DECB73F}.Debug|x64.ActiveCfg = Debug|x64 + {DABE6307-F8DD-416D-9DAC-673E2DECB73F}.Debug|x64.Build.0 = Debug|x64 + {DABE6307-F8DD-416D-9DAC-673E2DECB73F}.Release|Win32.ActiveCfg = Release|Win32 + {DABE6307-F8DD-416D-9DAC-673E2DECB73F}.Release|Win32.Build.0 = Release|Win32 + {DABE6307-F8DD-416D-9DAC-673E2DECB73F}.Release|x64.ActiveCfg = Release|x64 + {DABE6307-F8DD-416D-9DAC-673E2DECB73F}.Release|x64.Build.0 = Release|x64 + {977153BF-8420-4C8D-AA25-592FAEDB6CBA}.Debug|Win32.ActiveCfg = Debug|Win32 + {977153BF-8420-4C8D-AA25-592FAEDB6CBA}.Debug|Win32.Build.0 = Debug|Win32 + {977153BF-8420-4C8D-AA25-592FAEDB6CBA}.Debug|x64.ActiveCfg = Debug|x64 + {977153BF-8420-4C8D-AA25-592FAEDB6CBA}.Debug|x64.Build.0 = Debug|x64 + {977153BF-8420-4C8D-AA25-592FAEDB6CBA}.Release|Win32.ActiveCfg = Release|Win32 + {977153BF-8420-4C8D-AA25-592FAEDB6CBA}.Release|Win32.Build.0 = Release|Win32 + {977153BF-8420-4C8D-AA25-592FAEDB6CBA}.Release|x64.ActiveCfg = Release|x64 + {977153BF-8420-4C8D-AA25-592FAEDB6CBA}.Release|x64.Build.0 = Release|x64 + {A929EC04-302E-4B4F-B2A3-65AF63BB088F}.Debug|Win32.ActiveCfg = Debug|Win32 + {A929EC04-302E-4B4F-B2A3-65AF63BB088F}.Debug|Win32.Build.0 = Debug|Win32 + {A929EC04-302E-4B4F-B2A3-65AF63BB088F}.Debug|x64.ActiveCfg = Debug|x64 + {A929EC04-302E-4B4F-B2A3-65AF63BB088F}.Debug|x64.Build.0 = Debug|x64 + {A929EC04-302E-4B4F-B2A3-65AF63BB088F}.Release|Win32.ActiveCfg = Release|Win32 + {A929EC04-302E-4B4F-B2A3-65AF63BB088F}.Release|Win32.Build.0 = Release|Win32 + {A929EC04-302E-4B4F-B2A3-65AF63BB088F}.Release|x64.ActiveCfg = Release|x64 + {A929EC04-302E-4B4F-B2A3-65AF63BB088F}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {C138FA71-FE1F-4840-B678-526B21CE935D} + EndGlobalSection +EndGlobal diff --git a/Src/Plugins/General/gen_ml/gen_ml.vcxproj b/Src/Plugins/General/gen_ml/gen_ml.vcxproj new file mode 100644 index 00000000..bb833fd8 --- /dev/null +++ b/Src/Plugins/General/gen_ml/gen_ml.vcxproj @@ -0,0 +1,526 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {9B212232-4908-49D2-8D6D-96555B1F701E} + gen_ml + 10.0.19041.0 + + + + DynamicLibrary + v142 + Unicode + + + DynamicLibrary + v142 + Unicode + + + DynamicLibrary + v142 + Unicode + + + DynamicLibrary + v142 + Unicode + + + + + + + + + + + + + + + + + + + false + $(PlatformShortName)_$(Configuration)\ + $(PlatformShortName)_$(Configuration)\ + $(IncludePath) + $(LibraryPath) + + + false + $(PlatformShortName)_$(Configuration)\ + $(PlatformShortName)_$(Configuration)\ + + + false + $(PlatformShortName)_$(Configuration)\ + $(PlatformShortName)_$(Configuration)\ + $(IncludePath) + $(LibraryPath) + true + + + false + $(PlatformShortName)_$(Configuration)\ + $(PlatformShortName)_$(Configuration)\ + + + false + + + + + false + Debug + x86-windows-static-md + + + + + false + x86-windows-static-md + + + + + true + x86-windows-static-md + Debug + + + + + true + x86-windows-static-md + + + + Disabled + .;..;..\..\..;..\..\..\WAT;..\..\..\Wasabi;%(AdditionalIncludeDirectories) + _WIN32_WINNT=0x0601;WINVER=0x0601;_WINDOWS;_USRDLL;GEN_ML_EXPORTS;_WIN32_IE=0x0A00;IGNORE_API_GRACENOTE;WIN32;_DEBUG;%(PreprocessorDefinitions) + false + true + Default + MultiThreadedDebugDLL + true + $(IntDir) + $(IntDir) + $(IntDir)$(TargetName).pdb + true + Level3 + ProgramDatabase + 4302;4996;%(DisableSpecificWarnings) + false + false + false + + + %(PreprocessorDefinitions);WIN32;_DEBUG;_WINDOWS;__STDC_CONSTANT_MACROS;__STDC_FORMAT_MACROS;_WIN32;UNICODE;_UNICODE;WINVER=0x0601;_WIN32_WINNT=0x601;NOMINMAX;WIN32_LEAN_AND_MEAN;_HAS_EXCEPTIONS=0;CEF_USE_ATL;CMAKE_INTDIR=\"Debug\" + 0x0409 + ..\..\..\external_dependencies\CEF;%(AdditionalIncludeDirectories) + + + comctl32.lib;shlwapi.lib;rpcrt4.lib;fmtd.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)$(TargetExt) + tataki.dll;%(DelayLoadDLLs) + true + $(IntDir)$(TargetName).pdb + false + + + $(IntDir)$(TargetName).lib + MachineX86 + false + ..\..\..\external_dependencies\vcpkg\buildtrees\fmt\$(PlatformShortName)-windows-dbg;%(AdditionalLibraryDirectories) + Windows + true + true + + + + + 0x8000 + true + + + xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ +xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ + Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\' + + + + + Disabled + .;..;..\..\..;..\..\..\WAT;..\..\..\Wasabi;%(AdditionalIncludeDirectories) + _WIN32_WINNT=0x0601;WINVER=0x0601;_WINDOWS;_USRDLL;GEN_ML_EXPORTS;_WIN32_IE=0x0A00;IGNORE_API_GRACENOTE;WIN64;_DEBUG;%(PreprocessorDefinitions) + false + true + Default + MultiThreadedDebugDLL + true + $(IntDir) + $(IntDir) + $(IntDir)$(TargetName).pdb + true + Level3 + ProgramDatabase + 4244;4267;4302;4996;%(DisableSpecificWarnings) + true + true + true + + + _WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions) + 0x0409 + + + comctl32.lib;shlwapi.lib;rpcrt4.lib;fmtd.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)$(TargetExt) + tataki.dll;%(DelayLoadDLLs) + true + $(IntDir)$(TargetName).pdb + false + + + $(IntDir)$(TargetName).lib + false + ..\..\..\external_dependencies\vcpkg\buildtrees\fmt\$(PlatformShortName)-windows-dbg;%(AdditionalLibraryDirectories) + Windows + true + true + + + + + + + xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ +xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ + Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\' + + + + + MinSpace + OnlyExplicitInline + Size + .;..;..\..\..;..\..\..\WAT;..\..\..\Wasabi;%(AdditionalIncludeDirectories) + _WIN32_WINNT=0x0601;WINVER=0x0601;_WINDOWS;_USRDLL;GEN_ML_EXPORTS;_WIN32_IE=0x0A00;IGNORE_API_GRACENOTE;WIN32;NDEBUG;%(PreprocessorDefinitions) + true + true + MultiThreadedDLL + true + $(IntDir) + $(IntDir) + $(IntDir)$(TargetName).pdb + Level3 + ProgramDatabase + 4244;4267;4302;4996;%(DisableSpecificWarnings) + true + + + %(PreprocessorDefinitions);WIN32;_WINDOWS;NDEBUG;__STDC_CONSTANT_MACROS;__STDC_FORMAT_MACROS;_WIN32;UNICODE;_UNICODE;WINVER=0x0601;_WIN32_WINNT=0x601;NOMINMAX;WIN32_LEAN_AND_MEAN;_HAS_EXCEPTIONS=0;CEF_USE_ATL;_NDEBUG;CMAKE_INTDIR=\"Release\" + 0x0409 + ..\..\..\external_dependencies\CEF;%(AdditionalIncludeDirectories) + + + comctl32.lib;shlwapi.lib;rpcrt4.lib;fmt.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)$(TargetExt) + tataki.dll;%(DelayLoadDLLs) + false + $(IntDir)$(TargetName).pdb + true + true + false + $(IntDir)$(TargetName).lib + MachineX86 + false + ..\..\..\external_dependencies\vcpkg\buildtrees\fmt\$(PlatformShortName)-windows-rel;%(AdditionalLibraryDirectories) + Windows + 0x8000 + true + + + xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ + Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\' + + + + + MinSpace + OnlyExplicitInline + Size + .;..;..\..\..;..\..\..\WAT;..\..\..\Wasabi;%(AdditionalIncludeDirectories) + _WIN32_WINNT=0x0601;WINVER=0x0601;_WINDOWS;_USRDLL;GEN_ML_EXPORTS;_WIN32_IE=0x0A00;IGNORE_API_GRACENOTE;WIN64;NDEBUG;%(PreprocessorDefinitions) + true + true + MultiThreadedDLL + true + $(IntDir) + $(IntDir) + $(IntDir)$(TargetName).pdb + Level3 + None + 4244;4267;4302;4996;%(DisableSpecificWarnings) + true + + + _WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions) + 0x0409 + + + comctl32.lib;shlwapi.lib;rpcrt4.lib;fmt.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)$(TargetExt) + tataki.dll;%(DelayLoadDLLs) + false + $(IntDir)$(TargetName).pdb + true + true + false + $(IntDir)$(TargetName).lib + false + ..\..\..\external_dependencies\vcpkg\buildtrees\fmt\$(PlatformShortName)-windows-rel;%(AdditionalLibraryDirectories) + Windows + + + xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ + Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\' + + + + + {255b68b5-7ef8-45ef-a675-2d6b88147909} + true + true + + + {3e0bfa8a-b86a-42e9-a33f-ec294f823f7f} + + + {c5714908-a71f-4644-bd95-aad8ee7914da} + + + + + + + + + + + + WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_ML_EXPORTS + + + + WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_ML_EXPORTS + + + WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_ML_EXPORTS + + + + + + + + + + + + + + WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_ML_EXPORTS + + + + + + + + + WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_ML_EXPORTS + + + + + + + + + WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_ML_EXPORTS + + + + + + + WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_ML_EXPORTS + + + EnableFastChecks + + + + WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_ML_EXPORTS + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/gen_ml.vcxproj.filters b/Src/Plugins/General/gen_ml/gen_ml.vcxproj.filters new file mode 100644 index 00000000..0c97abd4 --- /dev/null +++ b/Src/Plugins/General/gen_ml/gen_ml.vcxproj.filters @@ -0,0 +1,540 @@ + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Ressource Files + + + Ressource Files + + + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + + + {0a05bd80-ec56-40ac-a633-3a3c9aa13bf6} + + + {5216efe6-f772-4af6-b4c9-3b7b25b0153c} + + + {2807c32e-739b-42a2-85e4-a5b2b4fc36be} + + + {c5319216-3757-4121-90c8-052366765304} + + + + + Image Files + + + \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/graphics.cpp b/Src/Plugins/General/gen_ml/graphics.cpp new file mode 100644 index 00000000..6fcded96 --- /dev/null +++ b/Src/Plugins/General/gen_ml/graphics.cpp @@ -0,0 +1,130 @@ +#include ".\graphics.h" +#include "ml.h" +#include + +HBITMAP CreateBitmapMask(HBITMAP originalBmp, int cx, int cy) +{ + return CreateBitmapMask(originalBmp, NULL, cx, cy); +} + +HBITMAP CreateBitmapMask(HBITMAP originalBmp, COLORREF transColor) +{ + return CreateBitmapMask(originalBmp, transColor, 0, 0); +} + +HBITMAP CreateBitmapMask(HBITMAP originalBmp, COLORREF transColor, int cx, int cy) +{ + HDC hdcMem, hdcMem2; + HBITMAP hbmMask; + BITMAP bm; + + GetObjectW(originalBmp, sizeof(BITMAP), &bm); + hbmMask = CreateBitmap(bm.bmWidth, bm.bmHeight, 1, 1, NULL); + + hdcMem = CreateCompatibleDC(0); + hdcMem2 = CreateCompatibleDC(0); + + HBITMAP obmp = (HBITMAP)SelectObject(hdcMem, originalBmp); + HBITMAP omsk = (HBITMAP)SelectObject(hdcMem2, hbmMask); + + if (transColor == NULL) transColor = GetPixel(hdcMem, cx, cy); + + COLORREF oldColorBK = SetBkColor(hdcMem, transColor); + + BitBlt(hdcMem2, 0, 0, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCCOPY); + + BitBlt(hdcMem, 0, 0, bm.bmWidth, bm.bmHeight, hdcMem2, 0, 0, SRCINVERT); + + SetBkColor(hdcMem, oldColorBK); + + SelectObject(hdcMem, obmp); + SelectObject(hdcMem2, omsk); + + DeleteDC(hdcMem); + DeleteDC(hdcMem2); + + return hbmMask; +} + +HBITMAP ConvertTo24bpp(HBITMAP bmp, int bpp) +{ + HDC hdcMem, hdcMem2; + HBITMAP hbm24; + BITMAP bm; + + GetObjectW(bmp, sizeof(BITMAP), &bm); + + hdcMem = CreateCompatibleDC(0); + hdcMem2 = CreateCompatibleDC(0); + + void *bits; + BITMAPINFOHEADER bi; + + ZeroMemory (&bi, sizeof (bi)); + bi.biSize = sizeof (bi); + bi.biWidth= bm.bmWidth; + bi.biHeight = -bm.bmHeight; + bi.biPlanes = 1; + bi.biBitCount= (WORD)(0xFF & bpp); + + hbm24 = CreateDIBSection(hdcMem2, (BITMAPINFO *)&bi, DIB_RGB_COLORS, &bits, NULL, NULL); + + HBITMAP oBmp = (HBITMAP)SelectObject(hdcMem, bmp); + HBITMAP oBmp24 = (HBITMAP)SelectObject(hdcMem2, hbm24); + + BitBlt(hdcMem2, 0, 0, bm.bmWidth, bm.bmHeight, hdcMem, 0, 0, SRCCOPY); + + SelectObject(hdcMem, oBmp); + SelectObject(hdcMem2, oBmp24); + + DeleteDC(hdcMem); + DeleteDC(hdcMem2); + + return hbm24; +} + + +// works only with DIB +HBITMAP PatchBitmapColors24(HBITMAP bitmap, COLORREF color1, COLORREF color2, BMPFILTERPROC filterProc) +{ + BITMAP bm; + + COLOR24 clrBG, clrFG; + INT x, y; + + if (!filterProc) return NULL; + + if (!GetObjectW(bitmap, sizeof(BITMAP), &bm) || !bm.bmBits || 24 != bm.bmBitsPixel) return NULL; + + clrBG.rgbRed = (BYTE)(0xFF & color1); + clrFG.rgbRed = (BYTE)(0xFF & color2); + clrBG.rgbGreen = (BYTE)(0xFF & (color1>>8)); + clrFG.rgbGreen = (BYTE)(0xFF & (color2>>8)); + clrBG.rgbBlue = (BYTE)(0xFF & (color1>>16)); + clrFG.rgbBlue = (BYTE)(0xFF & (color2>>16)); + + for (y = 0; y < bm.bmHeight; y++) + { + LONG width = (bm.bmWidthBytes%4)?bm.bmWidth*4:bm.bmWidthBytes; + // bm.bmWidthBytes can lie so is safer to go with bm.bmWidth if the dword alignment cbeck fails + // http://blogs.msdn.com/oldnewthing/archive/2004/10/26/247918.aspx#248529 + COLOR24 *cursor = (COLOR24*)(((BYTE*)bm.bmBits) + (width*y)); + for (x = 0; x < bm.bmWidth; x++, cursor++) filterProc(&clrBG, &clrFG, cursor); + } + return bitmap; +} + +void Filter1(const COLOR24 *color1, const COLOR24 *color2, COLOR24 *pixel) +{ + pixel->rgbBlue = (BYTE)(color1->rgbBlue - (int)((1.f - (pixel->rgbBlue /255.f))* (color1->rgbBlue - color2->rgbBlue))); + pixel->rgbGreen = (BYTE)(color1->rgbGreen - (int)((1.f - (pixel->rgbGreen /255.f))* (color1->rgbGreen - color2->rgbGreen))); + pixel->rgbRed = (BYTE)(color1->rgbRed - (int)((1.f - (pixel->rgbRed /255.f))* (color1->rgbRed - color2->rgbRed))); +} + +void Filter2(const COLOR24 *color1, const COLOR24 *color2, COLOR24 *pixel) +{ + float chrom = (float)pixel->rgbBlue / 255.f; + pixel->rgbBlue = (BYTE)(color1->rgbBlue * (1.f - chrom) + color2->rgbBlue * chrom); + pixel->rgbGreen = (BYTE)(color1->rgbGreen * (1.f - chrom) + color2->rgbGreen * chrom); + pixel->rgbRed = (BYTE)(color1->rgbRed * (1.f - chrom) + color2->rgbRed * chrom); +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/graphics.h b/Src/Plugins/General/gen_ml/graphics.h new file mode 100644 index 00000000..a7a00d6f --- /dev/null +++ b/Src/Plugins/General/gen_ml/graphics.h @@ -0,0 +1,26 @@ +#ifndef NULLSOFT_MEIDALIBRARY_GRAPHICS_HEADER +#define NULLSOFT_MEIDALIBRARY_GRAPHICS_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include + +typedef struct _COLOR24 COLOR24; + +typedef void (*BMPFILTERPROC)(const COLOR24*, const COLOR24*, COLOR24*); + +HBITMAP CreateBitmapMask(HBITMAP originalBmp, COLORREF transColor); // Creates Mask Bitmap using specified color (helper) +HBITMAP CreateBitmapMask(HBITMAP originalBmp, int cx, int cy); // Creates Mask Bitmap using color from specified location (helper) +HBITMAP CreateBitmapMask(HBITMAP originalBmp, COLORREF transColor, int cx, int cy); // Creates Mask Bitmap if transColor is NULL uses color from specified position + +HBITMAP ConvertTo24bpp(HBITMAP bmp, int bpp=24); // creates a new bitmap with 24bpp. You responsible for destroying both + +HBITMAP PatchBitmapColors24(HBITMAP bitmap, COLORREF color1, COLORREF color2, BMPFILTERPROC filterProc); + +void Filter1(const COLOR24 *color1, const COLOR24 *color2, COLOR24 *pixel); // default filter 1 +void Filter2(const COLOR24 *color1, const COLOR24 *color2, COLOR24 *pixel); // default filter 2 + +#endif //NULLSOFT_MEIDALIBRARY_GRAPHICS_HEADER + diff --git a/Src/Plugins/General/gen_ml/imagefilters.cpp b/Src/Plugins/General/gen_ml/imagefilters.cpp new file mode 100644 index 00000000..e143c1d0 --- /dev/null +++ b/Src/Plugins/General/gen_ml/imagefilters.cpp @@ -0,0 +1,349 @@ +#include "main.h" +#include "./imagefilters.h" + +#define FILTER_REMOVE_ALPHA 0 +#define FILTER_PRESERVE_ALPHA 1 + + +#define MLIF_FILTER1_TITLE L"Default filter #1 (removes alpha)" +#define MLIF_FILTER2_TITLE L"Default filter #2" +#define MLIF_FILTER3_TITLE L"Grayscale + filter#1" +#define MLIF_GRAYSCALE_TITLE L"Grayscale filtes" +#define MLIF_BLENDONBK_TITLE L"AlphaBlend filter" +#define MLIF_FILTER1_PRESERVE_ALPHA_TITLE L"Default filter #1 (preserves alpha)" + +static BOOL CALLBACK MLIF_FILTER1_PROC(LPBYTE pData, LONG cx, LONG cy, INT bpp, COLORREF rgbBk, COLORREF rgbFg, INT_PTR imageTag, LPARAM lParam) +{ + LONG pitch, x, y; + INT step; + BYTE rBk, gBk, bBk, rFg, gFg, bFg; + LPBYTE cursor, line; + + if (bpp < 24 || cx < 0) + return FALSE; + + if (cy < 0) + cy = -cy; + + step = (bpp>>3); + pitch = cx*step; + while (pitch%4) pitch++; + + rFg = GetRValue(rgbFg); gFg = GetGValue(rgbFg); bFg = GetBValue(rgbFg); + rBk = GetRValue(rgbBk); gBk = GetGValue(rgbBk); bBk = GetBValue(rgbBk); + + if (24 == bpp) + { + for (y = 0, line = pData; y < cy; y++, line += pitch) + { + for (x = 0, cursor = line; x < cx; x++, cursor += 3) + { + cursor[0] = bFg - ((bFg - bBk)*(255 - cursor[0])>>8); + cursor[1] = gFg - ((gFg - gBk)*(255 - cursor[1])>>8); + cursor[2] = rFg - ((rFg - rBk)*(255 - cursor[2])>>8); + } + } + } + else if (32 == bpp) + { + for (y = 0, line = pData; y < cy; y++, line += pitch ) + { + for (x = 0, cursor = line; x < cx; x++, cursor += 4) + { + if (0x00 == cursor[3]) + { + cursor[0] = bBk; + cursor[1] = gBk; + cursor[2] = rBk; + } + else if (0xFF == cursor[3]) + { + cursor[0] = bFg - ((bFg - bBk)*(255 - cursor[0])>>8); + cursor[1] = gFg - ((gFg - gBk)*(255 - cursor[1])>>8); + cursor[2] = rFg - ((rFg - rBk)*(255 - cursor[2])>>8); + } + else + { + cursor[0] = ((bFg - ((bFg - bBk)*(255 - cursor[0])>>8))*cursor[3] + (((255 - cursor[3])*255 + 127)/255)*bBk + 127)/255; + cursor[1] = ((gFg - ((gFg - gBk)*(255 - cursor[1])>>8))*cursor[3] + (((255 - cursor[3])*255 + 127)/255)*gBk + 127)/255; + cursor[2] = ((rFg - ((rFg - rBk)*(255 - cursor[2])>>8))*cursor[3] + (((255 - cursor[3])*255 + 127)/255)*rBk + 127)/255; + } + } + } + + if (FILTER_REMOVE_ALPHA == lParam) + { + for (y = 0, line = pData; y < cy; y++, line += pitch) + { + for (x = 0, cursor = line; x < cx; x++, cursor += 4) + { + cursor[3] = 0xFF; + } + } + } + } + return TRUE; +} + +static BOOL CALLBACK MLIF_FILTER2_PROC(LPBYTE pData, LONG cx, LONG cy, INT bpp, COLORREF rgbBk, COLORREF rgbFg, INT_PTR imageTag, LPARAM lParam) +{ + LONG pitch, x; + INT step; + LPBYTE cursor, line; + BYTE chrom; + BYTE rBk, gBk, bBk, rFg, gFg, bFg; + + if (bpp < 24) return FALSE; + + step = (bpp>>3); + pitch = cx*step; + while (pitch%4) pitch++; + + rFg = GetRValue(rgbFg); gFg = GetGValue(rgbFg); bFg = GetBValue(rgbFg); + rBk = GetRValue(rgbBk); gBk = GetGValue(rgbBk); bBk = GetBValue(rgbBk); + + if (24 == bpp) + { + for (line = pData; cy-- != 0; line += pitch ) + { + for (x = cx, cursor = line; x-- != 0; cursor += step) + { + chrom = cursor[0]; + cursor[0] = (bBk * (255 - chrom) + bFg * chrom)>>8; + cursor[1] = (gBk * (255 - chrom) + gFg * chrom)>>8; + cursor[2] = (rBk * (255 - chrom) + rFg * chrom)>>8; + } + } + } + else if (32 == bpp) + { + for (line = pData; cy-- != 0; line += pitch ) + { + for (x = cx, cursor = line; x-- != 0; cursor += 4) + { + chrom = cursor[0]; + if (0x00 == cursor[3]) + { + cursor[0] = bBk; + cursor[1] = gBk; + cursor[2] = rBk; + cursor[3] = 0xFF; + } + else if (0xFF == cursor[3]) + { + cursor[0] = (bBk * (255 - chrom) + bFg * chrom)>>8; + cursor[1] = (gBk * (255 - chrom) + gFg * chrom)>>8; + cursor[2] = (rBk * (255 - chrom) + rFg * chrom)>>8; + } + else + { + cursor[0] = (((bBk * (255 - chrom) + bFg * chrom)>>8)*cursor[3] + (((255 - cursor[3])*255 + 127)/255)*bBk + 127)/255; + cursor[1] = (((gBk * (255 - chrom) + gFg * chrom)>>8)*cursor[3] + (((255 - cursor[3])*255 + 127)/255)*gBk + 127)/255; + cursor[2] = (((rBk * (255 - chrom) + rFg * chrom)>>8)*cursor[3] + (((255 - cursor[3])*255 + 127)/255)*rBk + 127)/255; + cursor[3] = 0xFF; + } + } + } + } + return TRUE; +} + + +static BOOL CALLBACK MLIF_FILTER3_PROC(LPBYTE pData, LONG cx, LONG cy, INT bpp, COLORREF rgbBk, COLORREF rgbFg, INT_PTR imageTag, LPARAM lParam) +{ + LONG pitch, x; + INT step, r, g, b; + BYTE rBk, gBk, bBk, rFg, gFg, bFg, px; + + LPBYTE cursor, line; + + if (bpp < 24) return FALSE; + + step = (bpp>>3); + pitch = cx*step; + while (pitch%4) pitch++; + + rFg = GetRValue(rgbFg); gFg = GetGValue(rgbFg); bFg = GetBValue(rgbFg); + rBk = GetRValue(rgbBk); gBk = GetGValue(rgbBk); bBk = GetBValue(rgbBk); + + if (24 == bpp) + { + for (line = pData; cy-- != 0; line += pitch ) + { + for (x = cx, cursor = line; x-- != 0; cursor += 3) + { + r = cursor[2]; + g = cursor[1]; + b = cursor[0]; + px = (r*299 + g*587 + b*114)/1000; + + cursor[0] = bFg - ((bFg - bBk)*(255 - px)>>8); + cursor[1] = gFg - ((gFg - gBk)*(255 - px)>>8); + cursor[2] = rFg - ((rFg - rBk)*(255 - px)>>8); + } + } + } + else if (32 == bpp) + { + for (line = pData; cy-- != 0; line += pitch ) + { + for (x = cx, cursor = line; x-- != 0; cursor += 4) + { + if (0x00 == cursor[3]) + { + cursor[0] = bBk; + cursor[1] = gBk; + cursor[2] = rBk; + cursor[3] = 0xFF; + } + else + { + r = cursor[2]; + g = cursor[1]; + b = cursor[0]; + px = (r*299 + g*587 + b*114)/1000; + + if (0xFF == cursor[3]) + { + cursor[0] = bFg - ((bFg - bBk)*(255 - px)>>8); + cursor[1] = gFg - ((gFg - gBk)*(255 - px)>>8); + cursor[2] = rFg - ((rFg - rBk)*(255 - px)>>8); + } + else + { + cursor[0] = ((bFg - ((bFg - bBk)*(255 - px)>>8))*px + (((255 - px)*255 + 127)/255)*bBk + 127)/255; + cursor[1] = ((gFg - ((gFg - gBk)*(255 - px)>>8))*px + (((255 - px)*255 + 127)/255)*gBk + 127)/255; + cursor[2] = ((rFg - ((rFg - rBk)*(255 - px)>>8))*px + (((255 - px)*255 + 127)/255)*rBk + 127)/255; + cursor[3] = 0xFF; + } + } + } + } + } + return TRUE; +} + +static BOOL CALLBACK MLIF_GRAYSCALE_PROC(LPBYTE pData, LONG cx, LONG cy, INT bpp, COLORREF rgbBk, COLORREF rgbFg, INT_PTR imageTag, LPARAM lParam) +{ + LONG pitch, x; + INT step; + LPBYTE cursor, line; + BYTE luma; + + if (bpp < 24) return FALSE; + + step = (bpp>>3); + pitch = cx*step; + while (pitch%4) pitch++; + + for (line = pData; cy-- != 0; line += pitch ) + { + for (x = cx, cursor = line; x-- != 0; cursor += step) + { + luma = (BYTE)((cursor[2]*30 + cursor[1]*59 + cursor[0]*11)/100); + cursor[0] = luma; + cursor[1] = luma; + cursor[2] = luma; + } + } + return TRUE; +} + + +static BOOL CALLBACK MLIF_BLENDONBK_PROC(LPBYTE pData, LONG cx, LONG cy, INT bpp, COLORREF rgbBk, COLORREF rgbFg, INT_PTR imageTag, LPARAM lParam) +{ + LONG pitch, x; + LPBYTE cursor, line; + + if (32 != bpp) return FALSE; + + pitch = cx*4; + + for (line = pData; cy-- != 0; line += pitch ) + { + for (x = cx, cursor = line; x-- != 0; cursor += 4) + { + if (0x00 == cursor[3]) + { + cursor[0] = GetBValue(rgbBk); + cursor[1] = GetGValue(rgbBk); + cursor[2] = GetRValue(rgbBk); + cursor[3] = 0xFF; + } + else if (cursor[3] != 0xFF) + { + cursor[0] = (cursor[0]*cursor[3] + (((255 - cursor[3])*255 + 127)/255)*GetBValue(rgbBk) + 127)/255; + cursor[1] = (cursor[1]*cursor[3] + (((255 - cursor[3])*255 + 127)/255)*GetGValue(rgbBk) + 127)/255; + cursor[2] = (cursor[2]*cursor[3] + (((255 - cursor[3])*255 + 127)/255)*GetRValue(rgbBk) + 127)/255; + cursor[3] = 0xFF; + } + } + } + return TRUE; +} + +static BOOL CALLBACK MLIF_NULL_PROC(LPBYTE pData, LONG cx, LONG cy, INT bpp, COLORREF rgbBk, COLORREF rgbFg, INT_PTR imageTag, LPARAM lParam) +{ + return TRUE; +} + +BOOL RegisterImageFilters(HMLIMGFLTRMNGR hmlifMngr) +{ + MLIMAGEFILTERINFO_I mlif; + BOOL fResult; + ZeroMemory(&mlif, sizeof(MLIMAGEFILTERINFO_I)); + + fResult = TRUE; + mlif.mask = MLIFF_TITLE_I | MLIFF_FLAGS_I | MLIFF_PROC_I | MLIFF_PARAM_I; + + mlif.uid = GUID_NULL; // so nobdy can take it + mlif.fnProc = MLIF_NULL_PROC; + mlif.pszTitle = NULL; + mlif.fFlags = MLIFF_IGNORE_BKCOLOR_I | MLIFF_IGNORE_FGCOLOR_I; + mlif.lParam = FILTER_REMOVE_ALPHA; + if (!MLImageFilterI_Register(hmlifMngr, &mlif)) fResult = FALSE; + + mlif.uid = MLIF_FILTER1_UID; + mlif.fnProc = MLIF_FILTER1_PROC; + mlif.pszTitle = MLIF_FILTER1_TITLE; + mlif.fFlags = 0; + mlif.lParam = 0; + if (!MLImageFilterI_Register(hmlifMngr, &mlif)) fResult = FALSE; + + mlif.uid = MLIF_FILTER2_UID; + mlif.fnProc = MLIF_FILTER2_PROC; + mlif.pszTitle = MLIF_FILTER2_TITLE; + mlif.fFlags = 0; + mlif.lParam = 0; + if (!MLImageFilterI_Register(hmlifMngr, &mlif)) fResult = FALSE; + + mlif.uid = MLIF_FILTER3_UID; + mlif.fnProc = MLIF_FILTER3_PROC; + mlif.pszTitle = MLIF_FILTER3_TITLE; + mlif.fFlags = 0; + mlif.lParam = 0; + if (!MLImageFilterI_Register(hmlifMngr, &mlif)) fResult = FALSE; + + mlif.uid = MLIF_GRAYSCALE_UID; + mlif.fnProc = MLIF_GRAYSCALE_PROC; + mlif.pszTitle = MLIF_GRAYSCALE_TITLE; + mlif.fFlags = MLIFF_IGNORE_BKCOLOR_I | MLIFF_IGNORE_FGCOLOR_I; + mlif.lParam = 0; + if (!MLImageFilterI_Register(hmlifMngr, &mlif)) fResult = FALSE; + + mlif.uid = MLIF_BLENDONBK_UID; + mlif.fnProc = MLIF_BLENDONBK_PROC; + mlif.pszTitle = MLIF_BLENDONBK_TITLE; + mlif.fFlags = MLIFF_IGNORE_FGCOLOR_I; + mlif.lParam = 0; + if (!MLImageFilterI_Register(hmlifMngr, &mlif)) fResult = FALSE; + + mlif.uid = MLIF_FILTER1_PRESERVE_ALPHA_UID; + mlif.fnProc = MLIF_FILTER1_PROC; + mlif.pszTitle = MLIF_FILTER1_PRESERVE_ALPHA_TITLE; + mlif.fFlags = 0; + mlif.lParam = FILTER_PRESERVE_ALPHA; + if (!MLImageFilterI_Register(hmlifMngr, &mlif)) fResult = FALSE; + + return fResult; +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/imagefilters.h b/Src/Plugins/General/gen_ml/imagefilters.h new file mode 100644 index 00000000..4fc36d8d --- /dev/null +++ b/Src/Plugins/General/gen_ml/imagefilters.h @@ -0,0 +1,29 @@ +#ifndef NULLSOFT_MEIDALIBRARY_DEFAULT_IMAGE_FILTERS_HEADER +#define NULLSOFT_MEIDALIBRARY_DEFAULT_IMAGE_FILTERS_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include "./ml_imagefilter.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + + +extern const GUID MLIF_FILTER1_UID; // {8A054D1F-E38E-4cc0-A78A-F216F059F57E} +extern const GUID MLIF_FILTER2_UID; // {BE1A6A40-39D1-4cfb-8C33-D8988E8DD2F8} +extern const GUID MLIF_FILTER3_UID; // {721E9E62-CC6D-4fd7-A6ED-DD4CD2B2612E} +extern const GUID MLIF_GRAYSCALE_UID; // {B6310C20-E731-44dd-83BD-FBC3349798F2} +extern const GUID MLIF_BLENDONBK_UID; // {526C6F4A-C979-4d6a-B8ED-1A90F5A26F7B} +extern const GUID MLIF_FILTER1_PRESERVE_ALPHA_UID; // {F1DD3228-7DA8-4524-B7D3-46F651BFB680} + + +BOOL RegisterImageFilters(HMLIMGFLTRMNGR hmlifMngr); + +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif // NULLSOFT_MEIDALIBRARY_DEFAULT_IMAGE_FILTERS_HEADER \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/itemlist.cpp b/Src/Plugins/General/gen_ml/itemlist.cpp new file mode 100644 index 00000000..7658d07f --- /dev/null +++ b/Src/Plugins/General/gen_ml/itemlist.cpp @@ -0,0 +1,147 @@ +/* +** Copyright (C) 2003-2006 Nullsoft, Inc. +** +** This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held +** liable for any damages arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, including commercial applications, and to +** alter it and redistribute it freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. +** If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +** +** 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +** +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#include +#include "itemlist.h" + +C_ItemList::C_ItemList() +{ +} + +C_ItemList::~C_ItemList() +{ + if (m_list) ::free(m_list); +} + +void *C_ItemList::Add(void *i) +{ + // check if we have enough space to add an element + if (!m_list || alloc_size == 0 || current_index >= alloc_size - 1)//|| !(m_size&31)) + { + alloc_size += MEMORY_STEP; + void **new_m_list=(void**)::realloc(m_list,sizeof(void*)*(alloc_size)); + if (new_m_list) + m_list = new_m_list; + else + { + m_list=NULL; + return NULL; + } + } + // add the element and increase the index + m_list[current_index]=i; + current_index++; + return i; +} + +void C_ItemList::Set(int w, void *newv) +{ + if (w >= 0 && w < current_index) + { + m_list[w]=newv; + } +} + +void *C_ItemList::Get(int w) const +{ + if (w >= 0 && w < current_index) + { + return m_list[w]; + } + return NULL; +} + +void C_ItemList::Del(int idx) +{ + if (m_list && idx >= 0 && idx < current_index) + { + current_index--; + if (idx != current_index) + { + ::memcpy(m_list + idx, m_list + idx + 1, sizeof(void*) * (current_index - idx)); + } + if (!(current_index &31)&& current_index) // resize down + { + void** new_m_list=(void**)::realloc(m_list,sizeof(void*)* current_index); + if (new_m_list) m_list = new_m_list; + else + { + new_m_list=(void**)::malloc(sizeof(void*)* current_index); + if (new_m_list) + { + memcpy(new_m_list, m_list, sizeof(void*)* current_index); + free(m_list); + m_list = new_m_list; + } + } + } + } +} + +void *C_ItemList::Insert(void *i, int pos) +{ + if (!m_list || !(current_index &31)) + { + void** new_m_list=(void**)::realloc(m_list,sizeof(void*)*(current_index +32)); + if (new_m_list) + m_list = new_m_list; + else + { + new_m_list=(void**)::malloc(sizeof(void*)*(current_index +32)); + if (new_m_list) + { + memcpy(new_m_list, m_list, sizeof(void*)* current_index); + free(m_list); + m_list = new_m_list; + } + else + return i; + } + + } + current_index++; + + for ( int j = current_index - 1; j > pos; j-- ) + m_list[ j ] = m_list[ j - 1 ]; + + m_list[ pos ] = i; + + return i; +} + +void C_ItemList::for_all(void (*for_all_func)(void *)) +{ + if (m_list) + { + for (int i=0;i< current_index;i++) + { + for_all_func(m_list[i]); + } + } +} + +void C_ItemList::for_all_ctx(void (*for_all_func)(void *, void *), void *ctx) +{ + if (m_list) + { + for (int i=0;i< current_index;i++) + { + for_all_func(m_list[i], ctx); + } + } +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/itemlist.h b/Src/Plugins/General/gen_ml/itemlist.h new file mode 100644 index 00000000..4a2000f6 --- /dev/null +++ b/Src/Plugins/General/gen_ml/itemlist.h @@ -0,0 +1,54 @@ +/* +** Copyright (C) 2003-2006 Nullsoft, Inc. +** +** This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held +** liable for any damages arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, including commercial applications, and to +** alter it and redistribute it freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. +** If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +** +** 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +** +** 3. This notice may not be removed or altered from any source distribution. +** +*/ +#ifndef _C_ITEMLIST_H_ +#define _C_ITEMLIST_H_ + +#define MEMORY_STEP 32 + +class C_ItemList +{ +public: + C_ItemList(); + ~C_ItemList(); + + void *Add( void *i ); + + void Set( int w, void *newv ); + + void *Get( int w ) const; + + void Del( int idx ); + + void *Insert( void *i, int pos ); + + int GetSize( void ) const { return current_index; } + + void **GetAll() { return m_list; } + + void for_all( void ( *for_all_func )( void * ) ); + + void for_all_ctx( void ( *for_all_func )( void *, void * ), void * ); + +protected: + void **m_list = NULL; + int current_index = 0; + int alloc_size = 0; + +}; + +#endif //_C_ITEMLIST_H_ diff --git a/Src/Plugins/General/gen_ml/klib/khash.h b/Src/Plugins/General/gen_ml/klib/khash.h new file mode 100644 index 00000000..7d0e3c9f --- /dev/null +++ b/Src/Plugins/General/gen_ml/klib/khash.h @@ -0,0 +1,528 @@ +/* The MIT License + + Copyright (c) 2008, 2009, 2011 by Attractive Chaos + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +*/ + +/* + An example: + +#include "khash.h" +KHASH_MAP_INIT_INT(32, char) +int main() { + int ret, is_missing; + khiter_t k; + khash_t(32) *h = kh_init(32); + k = kh_put(32, h, 5, &ret); + if (!ret) kh_del(32, h, k); + kh_value(h, k) = 10; + k = kh_get(32, h, 10); + is_missing = (k == kh_end(h)); + k = kh_get(32, h, 5); + kh_del(32, h, k); + for (k = kh_begin(h); k != kh_end(h); ++k) + if (kh_exist(h, k)) kh_value(h, k) = 1; + kh_destroy(32, h); + return 0; +} +*/ + +/* + 2011-02-14 (0.2.5): + + * Allow to declare global functions. + + 2009-09-26 (0.2.4): + + * Improve portability + + 2008-09-19 (0.2.3): + + * Corrected the example + * Improved interfaces + + 2008-09-11 (0.2.2): + + * Improved speed a little in kh_put() + + 2008-09-10 (0.2.1): + + * Added kh_clear() + * Fixed a compiling error + + 2008-09-02 (0.2.0): + + * Changed to token concatenation which increases flexibility. + + 2008-08-31 (0.1.2): + + * Fixed a bug in kh_get(), which has not been tested previously. + + 2008-08-31 (0.1.1): + + * Added destructor +*/ + + +#ifndef __AC_KHASH_H +#define __AC_KHASH_H + +/*! + @header + + Generic hash table library. + + @copyright Heng Li + */ + +#define AC_VERSION_KHASH_H "0.2.5" + +#include +#include +#include + +/* compipler specific configuration */ + +#if UINT_MAX == 0xffffffffu +typedef unsigned int khint32_t; +#elif ULONG_MAX == 0xffffffffu +typedef unsigned long khint32_t; +#endif + +#if ULONG_MAX == ULLONG_MAX +typedef unsigned long khint64_t; +#else +typedef unsigned long long khint64_t; +#endif + +//#ifdef _MSC_VER +//#define inline __inline +//#endif + +typedef khint32_t khint_t; +typedef khint_t khiter_t; + +#define __ac_HASH_PRIME_SIZE 32 +static const khint32_t __ac_prime_list[__ac_HASH_PRIME_SIZE] = +{ + 0ul, 3ul, 11ul, 23ul, 53ul, + 97ul, 193ul, 389ul, 769ul, 1543ul, + 3079ul, 6151ul, 12289ul, 24593ul, 49157ul, + 98317ul, 196613ul, 393241ul, 786433ul, 1572869ul, + 3145739ul, 6291469ul, 12582917ul, 25165843ul, 50331653ul, + 100663319ul, 201326611ul, 402653189ul, 805306457ul, 1610612741ul, + 3221225473ul, 4294967291ul +}; + +#define __ac_isempty(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&2) +#define __ac_isdel(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&1) +#define __ac_iseither(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&3) +#define __ac_set_isdel_false(flag, i) (flag[i>>4]&=~(1ul<<((i&0xfU)<<1))) +#define __ac_set_isempty_false(flag, i) (flag[i>>4]&=~(2ul<<((i&0xfU)<<1))) +#define __ac_set_isboth_false(flag, i) (flag[i>>4]&=~(3ul<<((i&0xfU)<<1))) +#define __ac_set_isdel_true(flag, i) (flag[i>>4]|=1ul<<((i&0xfU)<<1)) + +static const double __ac_HASH_UPPER = 0.77; + +#define KHASH_DECLARE(name, khkey_t, khval_t) \ + typedef struct { \ + khint_t n_buckets, size, n_occupied, upper_bound; \ + khint32_t *flags; \ + khkey_t *keys; \ + khval_t *vals; \ + } kh_##name##_t; \ + extern kh_##name##_t *kh_init_##name(); \ + extern void kh_destroy_##name(kh_##name##_t *h); \ + extern void kh_clear_##name(kh_##name##_t *h); \ + extern khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key); \ + extern void kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets); \ + extern khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret); \ + extern void kh_del_##name(kh_##name##_t *h, khint_t x); + +#define KHASH_INIT2(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \ + typedef struct { \ + khint_t n_buckets, size, n_occupied, upper_bound; \ + khint32_t *flags; \ + khkey_t *keys; \ + khval_t *vals; \ + } kh_##name##_t; \ + SCOPE kh_##name##_t *kh_init_##name() { \ + return (kh_##name##_t*)calloc(1, sizeof(kh_##name##_t)); \ + } \ + SCOPE void kh_destroy_##name(kh_##name##_t *h) \ + { \ + if (h) { \ + free(h->keys); free(h->flags); \ + free(h->vals); \ + free(h); \ + } \ + } \ + SCOPE void kh_clear_##name(kh_##name##_t *h) \ + { \ + if (h && h->flags) { \ + memset(h->flags, 0xaa, ((h->n_buckets>>4) + 1) * sizeof(khint32_t)); \ + h->size = h->n_occupied = 0; \ + } \ + } \ + SCOPE khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key) \ + { \ + if (h->n_buckets) { \ + khint_t inc, k, i, last; \ + k = __hash_func(key); i = k % h->n_buckets; \ + inc = 1 + k % (h->n_buckets - 1); last = i; \ + while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \ + if (i + inc >= h->n_buckets) i = i + inc - h->n_buckets; \ + else i += inc; \ + if (i == last) return h->n_buckets; \ + } \ + return __ac_iseither(h->flags, i)? h->n_buckets : i; \ + } else return 0; \ + } \ + SCOPE void kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets) \ + { \ + khint32_t *new_flags = 0; \ + khint_t j = 1; \ + { \ + khint_t t = __ac_HASH_PRIME_SIZE - 1; \ + while (__ac_prime_list[t] > new_n_buckets) --t; \ + new_n_buckets = __ac_prime_list[t+1]; \ + if (h->size >= (khint_t)(new_n_buckets * __ac_HASH_UPPER + 0.5)) j = 0; \ + else { \ + new_flags = (khint32_t*)malloc(((new_n_buckets>>4) + 1) * sizeof(khint32_t)); \ + memset(new_flags, 0xaa, ((new_n_buckets>>4) + 1) * sizeof(khint32_t)); \ + if (h->n_buckets < new_n_buckets) { \ + h->keys = (khkey_t*)realloc(h->keys, new_n_buckets * sizeof(khkey_t)); \ + if (kh_is_map) \ + h->vals = (khval_t*)realloc(h->vals, new_n_buckets * sizeof(khval_t)); \ + } \ + } \ + } \ + if (j) { \ + for (j = 0; j != h->n_buckets; ++j) { \ + if (__ac_iseither(h->flags, j) == 0) { \ + khkey_t key = h->keys[j]; \ + khval_t val; \ + if (kh_is_map) val = h->vals[j]; \ + __ac_set_isdel_true(h->flags, j); \ + while (1) { \ + khint_t inc, k, i; \ + k = __hash_func(key); \ + i = k % new_n_buckets; \ + inc = 1 + k % (new_n_buckets - 1); \ + while (!__ac_isempty(new_flags, i)) { \ + if (i + inc >= new_n_buckets) i = i + inc - new_n_buckets; \ + else i += inc; \ + } \ + __ac_set_isempty_false(new_flags, i); \ + if (i < h->n_buckets && __ac_iseither(h->flags, i) == 0) { \ + { khkey_t tmp = h->keys[i]; h->keys[i] = key; key = tmp; } \ + if (kh_is_map) { khval_t tmp = h->vals[i]; h->vals[i] = val; val = tmp; } \ + __ac_set_isdel_true(h->flags, i); \ + } else { \ + h->keys[i] = key; \ + if (kh_is_map) h->vals[i] = val; \ + break; \ + } \ + } \ + } \ + } \ + if (h->n_buckets > new_n_buckets) { \ + h->keys = (khkey_t*)realloc(h->keys, new_n_buckets * sizeof(khkey_t)); \ + if (kh_is_map) \ + h->vals = (khval_t*)realloc(h->vals, new_n_buckets * sizeof(khval_t)); \ + } \ + free(h->flags); \ + h->flags = new_flags; \ + h->n_buckets = new_n_buckets; \ + h->n_occupied = h->size; \ + h->upper_bound = (khint_t)(h->n_buckets * __ac_HASH_UPPER + 0.5); \ + } \ + } \ + SCOPE khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret) \ + { \ + khint_t x; \ + if (h->n_occupied >= h->upper_bound) { \ + if (h->n_buckets > (h->size<<1)) kh_resize_##name(h, h->n_buckets - 1); \ + else kh_resize_##name(h, h->n_buckets + 1); \ + } \ + { \ + khint_t inc, k, i, site, last; \ + x = site = h->n_buckets; k = __hash_func(key); i = k % h->n_buckets; \ + if (__ac_isempty(h->flags, i)) x = i; \ + else { \ + inc = 1 + k % (h->n_buckets - 1); last = i; \ + while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \ + if (__ac_isdel(h->flags, i)) site = i; \ + if (i + inc >= h->n_buckets) i = i + inc - h->n_buckets; \ + else i += inc; \ + if (i == last) { x = site; break; } \ + } \ + if (x == h->n_buckets) { \ + if (__ac_isempty(h->flags, i) && site != h->n_buckets) x = site; \ + else x = i; \ + } \ + } \ + } \ + if (__ac_isempty(h->flags, x)) { \ + h->keys[x] = key; \ + __ac_set_isboth_false(h->flags, x); \ + ++h->size; ++h->n_occupied; \ + *ret = 1; \ + } else if (__ac_isdel(h->flags, x)) { \ + h->keys[x] = key; \ + __ac_set_isboth_false(h->flags, x); \ + ++h->size; \ + *ret = 2; \ + } else *ret = 0; \ + return x; \ + } \ + SCOPE void kh_del_##name(kh_##name##_t *h, khint_t x) \ + { \ + if (x != h->n_buckets && !__ac_iseither(h->flags, x)) { \ + __ac_set_isdel_true(h->flags, x); \ + --h->size; \ + } \ + } + +#define KHASH_INIT(name, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \ + KHASH_INIT2(name, static inline, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) + +/* --- BEGIN OF HASH FUNCTIONS --- */ + +/*! @function + @abstract Integer hash function + @param key The integer [khint32_t] + @return The hash value [khint_t] + */ +#define kh_int_hash_func(key) (khint32_t)(key) +/*! @function + @abstract Integer comparison function + */ +#define kh_int_hash_equal(a, b) ((a) == (b)) +/*! @function + @abstract 64-bit integer hash function + @param key The integer [khint64_t] + @return The hash value [khint_t] + */ +#define kh_int64_hash_func(key) (khint32_t)((key)>>33^(key)^(key)<<11) +/*! @function + @abstract 64-bit integer comparison function + */ +#define kh_int64_hash_equal(a, b) ((a) == (b)) +/*! @function + @abstract const char* hash function + @param s Pointer to a null terminated string + @return The hash value + */ +static inline khint_t __ac_X31_hash_string(const char *s) +{ + khint_t h = *s; + if (h) for (++s ; *s; ++s) h = (h << 5) - h + *s; + return h; +} +/*! @function + @abstract Another interface to const char* hash function + @param key Pointer to a null terminated string [const char*] + @return The hash value [khint_t] + */ +#define kh_str_hash_func(key) __ac_X31_hash_string(key) +/*! @function + @abstract Const char* comparison function + */ +#define kh_str_hash_equal(a, b) (strcmp(a, b) == 0) + +/* --- END OF HASH FUNCTIONS --- */ + +/* Other necessary macros... */ + +/*! + @abstract Type of the hash table. + @param name Name of the hash table [symbol] + */ +#define khash_t(name) kh_##name##_t + +/*! @function + @abstract Initiate a hash table. + @param name Name of the hash table [symbol] + @return Pointer to the hash table [khash_t(name)*] + */ +#define kh_init(name) kh_init_##name() + +/*! @function + @abstract Destroy a hash table. + @param name Name of the hash table [symbol] + @param h Pointer to the hash table [khash_t(name)*] + */ +#define kh_destroy(name, h) kh_destroy_##name(h) + +/*! @function + @abstract Reset a hash table without deallocating memory. + @param name Name of the hash table [symbol] + @param h Pointer to the hash table [khash_t(name)*] + */ +#define kh_clear(name, h) kh_clear_##name(h) + +/*! @function + @abstract Resize a hash table. + @param name Name of the hash table [symbol] + @param h Pointer to the hash table [khash_t(name)*] + @param s New size [khint_t] + */ +#define kh_resize(name, h, s) kh_resize_##name(h, s) + +/*! @function + @abstract Insert a key to the hash table. + @param name Name of the hash table [symbol] + @param h Pointer to the hash table [khash_t(name)*] + @param k Key [type of keys] + @param r Extra return code: 0 if the key is present in the hash table; + 1 if the bucket is empty (never used); 2 if the element in + the bucket has been deleted [int*] + @return Iterator to the inserted element [khint_t] + */ +#define kh_put(name, h, k, r) kh_put_##name(h, k, r) + +/*! @function + @abstract Retrieve a key from the hash table. + @param name Name of the hash table [symbol] + @param h Pointer to the hash table [khash_t(name)*] + @param k Key [type of keys] + @return Iterator to the found element, or kh_end(h) is the element is absent [khint_t] + */ +#define kh_get(name, h, k) kh_get_##name(h, k) + +/*! @function + @abstract Remove a key from the hash table. + @param name Name of the hash table [symbol] + @param h Pointer to the hash table [khash_t(name)*] + @param k Iterator to the element to be deleted [khint_t] + */ +#define kh_del(name, h, k) kh_del_##name(h, k) + + +/*! @function + @abstract Test whether a bucket contains data. + @param h Pointer to the hash table [khash_t(name)*] + @param x Iterator to the bucket [khint_t] + @return 1 if containing data; 0 otherwise [int] + */ +#define kh_exist(h, x) (!__ac_iseither((h)->flags, (x))) + +/*! @function + @abstract Get key given an iterator + @param h Pointer to the hash table [khash_t(name)*] + @param x Iterator to the bucket [khint_t] + @return Key [type of keys] + */ +#define kh_key(h, x) ((h)->keys[x]) + +/*! @function + @abstract Get value given an iterator + @param h Pointer to the hash table [khash_t(name)*] + @param x Iterator to the bucket [khint_t] + @return Value [type of values] + @discussion For hash sets, calling this results in segfault. + */ +#define kh_val(h, x) ((h)->vals[x]) + +/*! @function + @abstract Alias of kh_val() + */ +#define kh_value(h, x) ((h)->vals[x]) + +/*! @function + @abstract Get the start iterator + @param h Pointer to the hash table [khash_t(name)*] + @return The start iterator [khint_t] + */ +#define kh_begin(h) (khint_t)(0) + +/*! @function + @abstract Get the end iterator + @param h Pointer to the hash table [khash_t(name)*] + @return The end iterator [khint_t] + */ +#define kh_end(h) ((h)->n_buckets) + +/*! @function + @abstract Get the number of elements in the hash table + @param h Pointer to the hash table [khash_t(name)*] + @return Number of elements in the hash table [khint_t] + */ +#define kh_size(h) ((h)->size) + +/*! @function + @abstract Get the number of buckets in the hash table + @param h Pointer to the hash table [khash_t(name)*] + @return Number of buckets in the hash table [khint_t] + */ +#define kh_n_buckets(h) ((h)->n_buckets) + +/* More conenient interfaces */ + +/*! @function + @abstract Instantiate a hash set containing integer keys + @param name Name of the hash table [symbol] + */ +#define KHASH_SET_INIT_INT(name) \ + KHASH_INIT(name, khint32_t, char, 0, kh_int_hash_func, kh_int_hash_equal) + +/*! @function + @abstract Instantiate a hash map containing integer keys + @param name Name of the hash table [symbol] + @param khval_t Type of values [type] + */ +#define KHASH_MAP_INIT_INT(name, khval_t) \ + KHASH_INIT(name, khint32_t, khval_t, 1, kh_int_hash_func, kh_int_hash_equal) + +/*! @function + @abstract Instantiate a hash map containing 64-bit integer keys + @param name Name of the hash table [symbol] + */ +#define KHASH_SET_INIT_INT64(name) \ + KHASH_INIT(name, khint64_t, char, 0, kh_int64_hash_func, kh_int64_hash_equal) + +/*! @function + @abstract Instantiate a hash map containing 64-bit integer keys + @param name Name of the hash table [symbol] + @param khval_t Type of values [type] + */ +#define KHASH_MAP_INIT_INT64(name, khval_t) \ + KHASH_INIT(name, khint64_t, khval_t, 1, kh_int64_hash_func, kh_int64_hash_equal) + +typedef const char *kh_cstr_t; +/*! @function + @abstract Instantiate a hash map containing const char* keys + @param name Name of the hash table [symbol] + */ +#define KHASH_SET_INIT_STR(name) \ + KHASH_INIT(name, kh_cstr_t, char, 0, kh_str_hash_func, kh_str_hash_equal) + +/*! @function + @abstract Instantiate a hash map containing const char* keys + @param name Name of the hash table [symbol] + @param khval_t Type of values [type] + */ +#define KHASH_MAP_INIT_STR(name, khval_t) \ + KHASH_INIT(name, kh_cstr_t, khval_t, 1, kh_str_hash_func, kh_str_hash_equal) + +#endif /* __AC_KHASH_H */ diff --git a/Src/Plugins/General/gen_ml/listheader.cpp b/Src/Plugins/General/gen_ml/listheader.cpp new file mode 100644 index 00000000..07bf536d --- /dev/null +++ b/Src/Plugins/General/gen_ml/listheader.cpp @@ -0,0 +1,295 @@ +#include "main.h" +#include +#include "config.h" +#include "../winamp/wa_dlg.h" + +static int m_column_resize=0; //0=normal, 1=selected item only, 2=proportional + + +static int m_origsizes[32], m_origwidth; +static float m_origperc[32]; + +static void columnTrackStart(HWND hwnd, int item) +{ + int l=Header_GetItemCount(hwnd); + m_origwidth=0; + int i; + for(i=item+1;ihdr.code == NM_CUSTOMDRAW && lpnmcd->hdr.hwndFrom == headerWnd) { + switch (lpnmcd->dwDrawStage) { + // prior to painting + case CDDS_PREPAINT: + return CDRF_NOTIFYITEMDRAW; // tell windows we want individual notification of each item being drawn + // notification of each item being drawn + case CDDS_ITEMPREPAINT: + { + LOGBRUSH lb={BS_SOLID,WADlg_getColor(WADLG_LISTHEADER_BGCOLOR)}; + HBRUSH brush; + brush=CreateBrushIndirect(&lb); + HDC hdc=lpnmcd->hdc; + RECT *rc=&lpnmcd->rc; + FillRect(hdc,rc,brush); + DeleteObject(brush); + + int selected=(lpnmcd->uItemState&CDIS_SELECTED)?1:0; + + HPEN pen; + + if (!selected) pen=CreatePen(PS_SOLID,1,WADlg_getColor(WADLG_LISTHEADER_FRAME_TOPCOLOR)); + else pen=CreatePen(PS_SOLID,1,WADlg_getColor(WADLG_LISTHEADER_FRAME_BOTTOMCOLOR)); + + HGDIOBJ oldobj=SelectObject(hdc,pen); + //SelectPen(hdc,pen); + MoveToEx(hdc,rc->left,rc->top,NULL); + LineTo(hdc,rc->right,rc->top); + MoveToEx(hdc,rc->left,rc->top,NULL); + LineTo(hdc,rc->left,rc->bottom); + + if (!selected) + { + SelectObject(hdc,oldobj); + DeleteObject(pen); + pen=CreatePen(PS_SOLID,1,WADlg_getColor(WADLG_LISTHEADER_FRAME_BOTTOMCOLOR)); + oldobj=SelectObject(hdc,pen); + } + + MoveToEx(hdc,rc->right-1,rc->top,NULL); + LineTo(hdc,rc->right-1,rc->bottom); + MoveToEx(hdc,rc->right-1,rc->bottom-1,NULL); + LineTo(hdc,rc->left-1,rc->bottom-1); + + SelectObject(hdc,oldobj); + DeleteObject(pen); + + if(!selected) { + pen=CreatePen(PS_SOLID,1,WADlg_getColor(WADLG_LISTHEADER_FRAME_MIDDLECOLOR)); + oldobj=SelectObject(hdc,pen); + MoveToEx(hdc,rc->right-2,rc->top+1,NULL); + LineTo(hdc,rc->right-2,rc->bottom-2); + MoveToEx(hdc,rc->right-2,rc->bottom-2,NULL); + LineTo(hdc,rc->left,rc->bottom-2); + SelectObject(hdc,oldobj); + DeleteObject(pen); + } + + DWORD_PTR i=lpnmcd->dwItemSpec; + char txt[128]; + LVCOLUMN lv={LVCF_TEXT,0,0,txt,sizeof(txt)-1,}; + ListView_GetColumn(listWnd,i,&lv); + SetBkMode(hdc,TRANSPARENT); + SetTextColor(hdc,WADlg_getColor(WADLG_LISTHEADER_FONTCOLOR)); + RECT rc2=*rc; + rc2.left+=5; rc2.right-=3; + rc2.top+=2; rc2.bottom-=2; + if(selected) { + rc2.left++; + rc2.top++; + } + + if (sortShow && (i == sortIndex) && ((rc->right - rc->left) > 40) ) + { + rc2.right -= 14; + HPEN penDark; + pen=CreatePen(PS_SOLID,1,WADlg_getColor(WADLG_LISTHEADER_FRAME_TOPCOLOR)); + penDark=CreatePen(PS_SOLID,1,WADlg_getColor(WADLG_LISTHEADER_EMPTY_BGCOLOR)); + oldobj=SelectObject(hdc,pen); + if (sortAscending) + { + //strcpy(txt, " /\\ "); + // Draw triangle pointing upwards + MoveToEx(hdc, rc->right - 10, rc->top + 5, NULL); + LineTo(hdc, rc->right - 6, rc->bottom - 6); + LineTo(hdc, rc->right - 15, rc->bottom - 6 ); + MoveToEx(hdc, rc->right - 14, rc->bottom - 7, NULL ); + + SelectObject(hdc, penDark); + LineTo(hdc, rc->right - 10, rc->top + 5); + } + else + { + // Draw triangle pointing downwords + MoveToEx(hdc, rc->right - 7, rc->top + 7, NULL); + LineTo(hdc, rc->right - 11, rc->bottom - 6 ); + MoveToEx(hdc, rc->right - 11, rc->bottom - 6, NULL); + + SelectObject(hdc, penDark); + LineTo(hdc, rc->right - 15, rc->top + 6 ); + LineTo(hdc, rc->right - 6, rc->top + 6); + } + SelectObject(hdc,oldobj); + DeleteObject(pen); + DeleteObject(penDark); + } + DrawText(hdc,txt,-1,&rc2,DT_VCENTER|DT_SINGLELINE|DT_LEFT); + + } + return CDRF_SKIPDEFAULT; + } + } + HD_NOTIFY *pHDN = (HD_NOTIFY*)lParam; + if(pHDN->hdr.code == HDN_BEGINTRACKW || pHDN->hdr.code == HDN_BEGINTRACKA) + { + m_column_resize=g_config->ReadInt("column_resize_mode",m_column_resize); + if(m_column_resize) columnTrackStart(pHDN->hdr.hwndFrom, pHDN->iItem); + } + + if(pHDN->hdr.code==HDN_ENDTRACKW || pHDN->hdr.code==HDN_ENDTRACKA || pHDN->hdr.code==HDN_ITEMCHANGINGW || pHDN->hdr.code==HDN_ITEMCHANGINGA) { + static int disable=0; + if(disable) return FALSE; + disable=1; + if(m_column_resize==1) columnAutoResizeItem(pHDN->hdr.hwndFrom, pHDN->iItem); + if(m_column_resize==2) columnAutoResizeProp(pHDN->hdr.hwndFrom, pHDN->iItem); + disable=0; + SendMessage(hwndDlg,WM_USER+0x3443,0,0); //update ScrollWnd + } + } + /*if(uMsg==WM_WINDOWPOSCHANGING) { + HWND h=ListView_GetHeader(hwndDlg); + if(h) size_autoResizeStart(h); + } + if(uMsg==WM_SIZE) { + HWND h=ListView_GetHeader(hwndDlg); + if(h) size_autoResizeProp(h); + }*/ + return 0; +} + +static int RectInRect(RECT *rect1, RECT *rect2) +{ + // this has a bias towards true + + // this could probably be optimized a lot + return ((rect1->top >= rect2->top && rect1->top <= rect2->bottom) || + (rect1->bottom >= rect2->top && rect1->bottom <= rect2->bottom) || + (rect2->top >= rect1->top && rect2->top <= rect1->bottom) || + (rect2->bottom >= rect1->top && rect2->bottom <= rect1->bottom)) // vertical intersect + && + ((rect1->left >= rect2->left && rect1->left <= rect2->right) || + (rect1->right >= rect2->left && rect1->right <= rect2->right) || + (rect2->left >= rect1->left && rect2->left <= rect1->right) || + (rect2->right >= rect1->left && rect2->right <= rect1->right)) // horiz intersect + ; +} + +INT_PTR handleListViewHeaderPaintMsgs(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { + if(uMsg==WM_ERASEBKGND) + { + return 1; //we erase the background below + } + if(uMsg==WM_PAINT) + { + //process the grey area with our color + RECT r; + GetClientRect(hwndDlg,&r); + int n=Header_GetItemCount(hwndDlg); + if(n) + { + RECT r2; + Header_GetItemRect(hwndDlg,n-1,&r2); + r.left=r2.right; + } + RECT ur; + GetUpdateRect(hwndDlg,&ur,FALSE); + if(RectInRect(&r,&ur)) + { + HDC hdc=GetDC(hwndDlg); + HBRUSH b=CreateSolidBrush(WADlg_getColor(WADLG_LISTHEADER_EMPTY_BGCOLOR)); + FillRect(hdc,&r,b); + DeleteObject(b); + ReleaseDC(hwndDlg,hdc); + ValidateRect(hwndDlg,&r); + } + } + return 0; +} diff --git a/Src/Plugins/General/gen_ml/listskin.cpp b/Src/Plugins/General/gen_ml/listskin.cpp new file mode 100644 index 00000000..f7dbf3e0 --- /dev/null +++ b/Src/Plugins/General/gen_ml/listskin.cpp @@ -0,0 +1,219 @@ +#include "main.h" +#include "listskin.h" +#include "scrollwnd.h" +#include "../nu/CCVersion.h" + +#ifndef LVS_EX_DOUBLEBUFFER //this will work XP only +#define LVS_EX_DOUBLEBUFFER 0x00010000 +#endif + +COLORREF Blender(COLORREF fg, COLORREF bg){ + return RGB((GetRValue(fg)+GetRValue(bg))/2,(GetGValue(fg)+GetGValue(bg))/2,(GetBValue(fg)+GetBValue(bg))/2); +} + +//listview's wndproc +static INT_PTR CALLBACK wndproc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam) +{ + ListSkin *ls=(ListSkin *)(LONG_PTR)GetWindowLongPtr(hwndDlg,GWLP_USERDATA); + INT_PTR r=handleListViewHeaderMsgs(hwndDlg, + ls->m_headerwnd, + ls->m_listwnd, + uMsg, + wParam, + lParam, + ls->sortShow, + ls->sortAscending, + ls->sortIndex); + if(r) return r; + if (uMsg == WM_ENABLE) + { + // custom handling of this allows us to handle disabled listview controls correctly + // so we don't have the part skinned / part OS colouring which happened before + InvalidateRect(hwndDlg,0,1); + ls->m_enabled = wParam; + return 1; + } + if (uMsg == WM_ERASEBKGND) + { + //fg> removes the header's region from the erasebackground's hdc clipping region + // so that they do not flicker when scrolling the list with the horizontal scrollbar + // in transparency mode + HDC dc = (HDC)wParam; + RECT r; + RECT hr; + GetClientRect(hwndDlg, &r); + GetClientRect(ListView_GetHeader(hwndDlg), &hr); + RECT lr; + SubtractRect(&lr, &r, &hr); + HRGN rgn = CreateRectRgnIndirect(&lr); + int rt = GetClipRgn(dc, rgn); + if (rt == 0) { + SelectClipRgn(dc, rgn); + } else if (rt > 0) { + HRGN trg = CreateRectRgnIndirect(&lr); + HRGN res = CreateRectRgn(0,0,0,0); + IntersectRgn(res, rgn, trg); + SelectClipRgn(dc, res); + DeleteRgn(res); + DeleteRgn(trg); + } + DeleteRgn(rgn); + } + if (uMsg == WM_KEYUP && wParam == VK_RETURN) + { + NMHDR nmh= + { + ls->m_listwnd, + GetDlgCtrlID(ls->m_listwnd), + NM_RETURN, + }; + SendMessage(ls->m_hwnd,WM_NOTIFY,0,(LPARAM)&nmh); + return 0; + } + return CallWindowProc(ls->m_old_wndproc,hwndDlg,uMsg,wParam,lParam); +} + +//dialog's wndproc +static INT_PTR CALLBACK mainwndproc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam) +{ + ListSkin *ls=(ListSkin *)(LONG_PTR)GetWindowLongPtr(hwndDlg,GWLP_USERDATA); + + if(uMsg==WM_USER+0x411) return 0;//ls->m_changing_item_sel; + + if(uMsg==WM_NOTIFY) + { + LPNMCUSTOMDRAW lpnmcd = (NMCUSTOMDRAW *)lParam; + //HWND listwnd=ls->m_listwnd; + HWND listwnd=lpnmcd->hdr.hwndFrom; + if(lpnmcd->hdr.code == NM_CUSTOMDRAW && lpnmcd->hdr.hwndFrom == listwnd) + { + LPNMLVCUSTOMDRAW lplvcd = (LPNMLVCUSTOMDRAW)lParam; + NMCUSTOMDRAW &nmcd = lplvcd->nmcd; + //static bool bHighlighted = false; + + switch(lplvcd->nmcd.dwDrawStage) + { + case CDDS_PREPAINT : + return CDRF_NOTIFYITEMDRAW; + + // Modify item text and or background + case CDDS_ITEMPREPAINT: + { + int iRow = (int)nmcd.dwItemSpec; + + bool bHighlighted = (ListView_GetItemState(listwnd, iRow, LVIS_SELECTED) != 0); + + if (bHighlighted) + { + if(GetFocus()==listwnd && ls->m_enabled) + { + lplvcd->clrText = WADlg_getColor(WADLG_SELBAR_FGCOLOR); + lplvcd->clrTextBk = WADlg_getColor(WADLG_SELBAR_BGCOLOR); + } else { + lplvcd->clrText = (!ls->m_enabled?Blender(WADlg_getColor(WADLG_INACT_SELBAR_FGCOLOR),WADlg_getColor(WADLG_WNDBG)):WADlg_getColor(WADLG_INACT_SELBAR_FGCOLOR)); + if(ls->m_enabled) + lplvcd->clrTextBk = WADlg_getColor(WADLG_INACT_SELBAR_BGCOLOR); + else + lplvcd->clrTextBk = WADlg_getColor(WADLG_ITEMBG); + } + } + else + { + if(!ls->m_enabled) + { + lplvcd->clrText = Blender(WADlg_getColor(WADLG_INACT_SELBAR_FGCOLOR),WADlg_getColor(WADLG_WNDBG)); + lplvcd->clrTextBk = WADlg_getColor(WADLG_ITEMBG); + } + } + + // Turn off listview highlight otherwise it uses the system colors! + //ls->m_changing_item_sel=1; + //ListView_SetItemState(listwnd, iRow, 0, LVIS_SELECTED); + lplvcd->nmcd.uItemState &= ~CDIS_SELECTED; + + return CDRF_DODEFAULT | CDRF_NOTIFYPOSTPAINT; + } + + // Modify sub item text and/or background + case CDDS_ITEMPOSTPAINT: + { + //if(bHighlighted) + //{ + // int iRow = (int)nmcd.dwItemSpec; + // Turn listview control's highlighting back on now that we have + // drawn the row in the colors we want. + //LockWindowUpdate(listwnd); + // lplvcd->nmcd.uItemState |= CDIS_SELECTED; + // ListView_SetItemState(listwnd, iRow, 0xff, LVIS_SELECTED); + //ls->m_changing_item_sel=0; + //LockWindowUpdate(NULL); + //CT> now validate the window so it doesn't flicker + //RECT r; + //ListView_GetItemRect(listwnd,iRow,&r,LVIR_BOUNDS); + //ValidateRect(listwnd,&r); + //} + } + default: + return CDRF_DODEFAULT; + } + } + } + + return CallWindowProc(ls->m_old_mainwndproc,hwndDlg,uMsg,wParam,lParam); +} + +//list header's wndproc +static INT_PTR CALLBACK header_wndproc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam) +{ + ListSkin *ls=(ListSkin *)(LONG_PTR)GetWindowLongPtr(hwndDlg,GWLP_USERDATA); + INT_PTR a=handleListViewHeaderPaintMsgs(ls->m_headerwnd,uMsg,wParam,lParam); + if(a) return a; + return CallWindowProc(ls->m_old_header_wndproc,hwndDlg,uMsg,wParam,lParam); +} + +ListSkin::ListSkin(HWND hwnd) +{ + m_hwnd=GetParent(hwnd); + m_listwnd=hwnd; + + sortShow = FALSE; + sortAscending = TRUE; + sortIndex = 0; + + if (comctlVersion >= PACKVERSION(6,0)) + ListView_SetExtendedListViewStyleEx(m_listwnd, LVS_EX_DOUBLEBUFFER, LVS_EX_DOUBLEBUFFER); + m_headerwnd=ListView_GetHeader(hwnd); + SetWindowLongPtr(hwnd,GWLP_USERDATA, (LONGX86)(LONG_PTR)this); + m_old_wndproc=(WNDPROC)(LONG_PTR)SetWindowLongPtr(hwnd,GWLP_WNDPROC, (LONGX86)(LONG_PTR)wndproc); + SetWindowLongPtr(m_headerwnd,GWLP_USERDATA, (LONGX86)(LONG_PTR)this); + m_old_header_wndproc=(WNDPROC)(LONG_PTR)SetWindowLongPtr(m_headerwnd,GWLP_WNDPROC, (LONGX86)(LONG_PTR)header_wndproc); + m_scrollwnd=new ScrollWnd(hwnd, SCROLLBAR_LISTVIEW); + m_old_mainwndproc=NULL; + m_enabled = IsWindowEnabled(hwnd); + //m_changing_item_sel=0; + if(!GetWindowLongPtr(m_hwnd,GWLP_USERDATA)) + { + SetWindowLongPtr(m_hwnd,GWLP_USERDATA, (LONGX86)(LONG_PTR)this); + m_old_mainwndproc=(WNDPROC)(LONG_PTR)SetWindowLongPtr(m_hwnd,GWLP_WNDPROC, (LONGX86)(LONG_PTR)mainwndproc); + } + +} + +ListSkin::~ListSkin() +{ + delete m_scrollwnd; + m_scrollwnd=0; + SetWindowLongPtr(m_listwnd,GWLP_WNDPROC, (LONGX86)(LONG_PTR)m_old_wndproc); + SetWindowLongPtr(m_headerwnd,GWLP_WNDPROC, (LONGX86)(LONG_PTR)m_old_header_wndproc); + if(m_old_mainwndproc) SetWindowLongPtr(m_hwnd,GWLP_WNDPROC, (LONGX86)(LONG_PTR)m_old_mainwndproc); +} + +void ListSkin::updateScrollWnd() +{ + m_scrollwnd->update(); +} + +void ListSkin::disableHorzScroll() +{ + m_scrollwnd->disableHorzScroll(); +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/listskin.h b/Src/Plugins/General/gen_ml/listskin.h new file mode 100644 index 00000000..63bfb42e --- /dev/null +++ b/Src/Plugins/General/gen_ml/listskin.h @@ -0,0 +1,36 @@ +#ifndef _LISTSKIN_H +#define _LISTSKIN_H + +#include + +class ScrollWnd; + +class ListSkin +{ +public: + ListSkin(HWND hwnd); + ~ListSkin(); + + void updateScrollWnd(); + void disableHorzScroll(); + + + HWND m_hwnd; + HWND m_listwnd; + HWND m_headerwnd; + ScrollWnd *m_scrollwnd; + WNDPROC m_old_wndproc; + WNDPROC m_old_header_wndproc; + WNDPROC m_old_mainwndproc; + + // sort + BOOL sortShow; + BOOL sortAscending; + int sortIndex; + + // enabled/disabled handling + int m_enabled; +// int m_changing_item_sel; +}; + +#endif \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/listview.cpp b/Src/Plugins/General/gen_ml/listview.cpp new file mode 100644 index 00000000..a3ceb22c --- /dev/null +++ b/Src/Plugins/General/gen_ml/listview.cpp @@ -0,0 +1,128 @@ +/* +** Copyright (C) 2003 Nullsoft, Inc. +** +** This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held +** liable for any damages arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, including commercial applications, and to +** alter it and redistribute it freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. +** If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +** +** 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +** +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#include +#include +#include "listview.h" + +#ifdef GEN_ML_EXPORTS +#include "main.h" // for getting the font +#include "config.h" +#endif +// bp Comment: all the calls beginning "ListView_" are +// MACROs defined in commctrl.h + +void W_ListView :: AddCol (char *text, int w) +{ + LVCOLUMN lvc={0,}; + lvc.mask = LVCF_TEXT|LVCF_WIDTH; + lvc.pszText = text; + if (w) lvc.cx=w; + ListView_InsertColumn (m_hwnd, m_col, &lvc); + m_col++; +} + +int W_ListView::GetColumnWidth (int col) +{ + if (col < 0 || col >= m_col) return 0; + return ListView_GetColumnWidth (m_hwnd, col); +} + + +int W_ListView::GetParam (int p) +{ + LVITEM lvi={0,}; + lvi.mask = LVIF_PARAM; + lvi.iItem = p; + ListView_GetItem (m_hwnd, &lvi); + return lvi.lParam; +} + +int W_ListView::InsertItem (int p, char *text, int param) +{ + LVITEM lvi={0,}; + lvi.mask = LVIF_TEXT | LVIF_PARAM; + lvi.iItem = p; + lvi.pszText = text; + lvi.cchTextMax=strlen (text); + lvi.lParam = param; + return ListView_InsertItem (m_hwnd, &lvi); +} + + +void W_ListView::SetItemText (int p, int si, char *text) +{ + LVITEM lvi={0,}; + lvi.iItem = p; + lvi.iSubItem = si; + lvi.mask = LVIF_TEXT; + lvi.pszText = text; + lvi.cchTextMax = strlen (text); + ListView_SetItem (m_hwnd, &lvi); +} + +void W_ListView::SetItemParam (int p, int param) +{ + LVITEM lvi={0,}; + lvi.iItem = p; + lvi.mask=LVIF_PARAM; + lvi.lParam=param; + ListView_SetItem (m_hwnd, &lvi); +} + +void W_ListView::refreshFont () +{ + if (m_font) + { + DeleteFont (m_font); + SetWindowFont (m_hwnd, NULL, FALSE); + } + m_font = NULL; + + HWND h; +#ifdef GEN_ML_EXPORTS + h=g_hwnd; +#else + h=m_libraryparent; +#endif + if (h && m_allowfonts) + { + int a=SendMessage (h, WM_USER+0x1000 /*WM_ML_IPC*/,66, 0x0600 /*ML_IPC_SKIN_WADLG_GETFUNC*/); + if (a) + { + m_font= (HFONT)a; + SetWindowFont (m_hwnd, m_font, FALSE); + } + } + InvalidateRect (m_hwnd, NULL, TRUE); +} + +void W_ListView::setallowfonts (int allow) +{ + m_allowfonts=allow; +} + +void W_ListView::setwnd (HWND hwnd) +{ + m_hwnd = hwnd; + if (hwnd) + { + ListView_SetExtendedListViewStyle (hwnd, LVS_EX_FULLROWSELECT|LVS_EX_UNDERLINEHOT ); + refreshFont (); + } +} diff --git a/Src/Plugins/General/gen_ml/listview.h b/Src/Plugins/General/gen_ml/listview.h new file mode 100644 index 00000000..1a3a463e --- /dev/null +++ b/Src/Plugins/General/gen_ml/listview.h @@ -0,0 +1,143 @@ +/* +** Copyright (C) 2003 Nullsoft, Inc. +** +** This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held +** liable for any damages arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, including commercial applications, and to +** alter it and redistribute it freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. +** If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +** +** 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +** +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#ifndef _LISTVIEW_H_ +#define _LISTVIEW_H_ + +#include +#include +#include + +class W_ListView +{ +public: + W_ListView() + { + m_hwnd=NULL; + m_col=0; + m_allowfonts=1; + m_font=NULL; +#ifndef GEN_ML_EXPORTS + m_libraryparent=NULL; +#endif + } + W_ListView(HWND hwnd) + { + m_hwnd=NULL; + m_col=0; + m_allowfonts=1; + m_font=NULL; +#ifndef GEN_ML_EXPORTS + m_libraryparent=NULL; +#endif + setwnd(hwnd); + } + ~W_ListView() + { + if (m_font) DeleteFont(m_font); + m_font=0; + } + + void refreshFont(); + +#ifndef GEN_ML_EXPORTS + void setLibraryParentWnd(HWND hwndParent) + { + m_libraryparent=hwndParent; + }// for Winamp Font getting stuff +#endif + void setallowfonts(int allow=1); + void setwnd(HWND hwnd); + void AddCol(char *text, int w); + int GetCount(void) + { + return ListView_GetItemCount(m_hwnd); + } + int GetParam(int p); + void DeleteItem(int n) + { + ListView_DeleteItem(m_hwnd,n); + } + void Clear(void) + { + ListView_DeleteAllItems(m_hwnd); + } + int GetSelected(int x) + { + return(ListView_GetItemState(m_hwnd, x, LVIS_SELECTED) & LVIS_SELECTED)?1:0; + } + + int GetSelectedCount() + { + return ListView_GetSelectedCount(m_hwnd); + } + + int GetSelectionMark() + { + return ListView_GetSelectionMark(m_hwnd); + } + void SetSelected(int x) + { + ListView_SetItemState(m_hwnd,x,LVIS_SELECTED,LVIS_SELECTED); + } + int InsertItem(int p, char *text, int param); + void GetItemRect(int i, RECT *r) + { + ListView_GetItemRect(m_hwnd, i, r, LVIR_BOUNDS); + } + void SetItemText(int p, int si, char *text); + void SetItemParam(int p, int param); + + void GetText(int p, int si, char *text, int maxlen) + { + ListView_GetItemText(m_hwnd, p, si, text, maxlen); + } + int FindItemByParam(int param) + { + LVFINDINFO fi={LVFI_PARAM,0,param}; + return ListView_FindItem(m_hwnd,-1,&fi); + } + int FindItemByPoint(int x, int y) + { + int l=GetCount(); + for (int i=0;i=x && r.top<=y && r.bottom>=y) return i; + } + return -1; + } + int GetColumnWidth(int col); + HWND getwnd(void) + { + return m_hwnd; + } + +protected: + HWND m_hwnd; + HFONT m_font; + int m_col; + int m_allowfonts; +#ifndef GEN_ML_EXPORTS + HWND m_libraryparent; +#endif +}; + +#endif//_LISTVIEW_H_ + diff --git a/Src/Plugins/General/gen_ml/main.cpp b/Src/Plugins/General/gen_ml/main.cpp new file mode 100644 index 00000000..8fc9069c --- /dev/null +++ b/Src/Plugins/General/gen_ml/main.cpp @@ -0,0 +1,1172 @@ +#include "main.h" +#include +#include +#include +#include "../winamp/gen.h" +#include "resource.h" +#include "childwnd.h" +#include "config.h" +#include "../winamp/ipc_pe.h" +#include "../winamp/wa_dlg.h" +#include "../winamp/strutil.h" +#include "ml.h" +#include "ml_ipc.h" +#include "./folderbrowser.h" +#include "./mldwm.h" + +#ifndef _ML_HEADER_IMPMLEMENT +#define _ML_HEADER_IMPMLEMENT +#endif // _ML_HEADER_IMPMLEMENT +#include "ml_ipc_0313.h" +#undef _ML_HEADER_IMPMLEMENT + +#include "sendto.h" +#include "../gen_hotkeys/wa_hotkeys.h" +#include "MediaLibraryCOM.h" +#include "../nu/CCVersion.h" +#include "../nu/AutoWideFn.h" +#include "../nu/htmlcontainer2.h" +#include + +#include "api__gen_ml.h" +#include +#include "./navigation.h" +//#include "./skinnedwnd.h" +#include "./skinning.h" +#include "../nu/ServiceWatcher.h" +#include "MusicID.h" +#include +#include +#include "../Winamp/wasabicfg.h" + +// {6B0EDF80-C9A5-11d3-9F26-00C04F39FFC6} +static const GUID library_guid = +{ 0x6b0edf80, 0xc9a5, 0x11d3, { 0x9f, 0x26, 0x0, 0xc0, 0x4f, 0x39, 0xff, 0xc6 } }; + +int m_calling_getfileinfo; +int IPC_GETMLWINDOW, IPC_LIBRARY_SENDTOMENU, IPC_GET_ML_HMENU; +int config_use_ff_scrollbars=1, config_use_alternate_colors=0; +LARGE_INTEGER freq; +C_Config *g_config; + +embedWindowState myWindowState; +prefsDlgRecW myPrefsItem, myPrefsItemPlug; + +DEFINE_EXTERNAL_SERVICE(api_service, WASABI_API_SVC); +DEFINE_EXTERNAL_SERVICE(api_application, WASABI_API_APP); +DEFINE_EXTERNAL_SERVICE(api_language, WASABI_API_LNG); +DEFINE_EXTERNAL_SERVICE(obj_ombrowser, AGAVE_OBJ_BROWSER); +DEFINE_EXTERNAL_SERVICE(api_mldb, AGAVE_API_MLDB); +DEFINE_EXTERNAL_SERVICE(api_syscb, WASABI_API_SYSCB); +DEFINE_EXTERNAL_SERVICE(api_threadpool, AGAVE_API_THREADPOOL); +DEFINE_EXTERNAL_SERVICE(api_decodefile, AGAVE_API_DECODE); +DEFINE_EXTERNAL_SERVICE(wnd_api, WASABI_API_WND); +DEFINE_EXTERNAL_SERVICE(api_skin, WASABI_API_SKIN); +DEFINE_EXTERNAL_SERVICE(api_config, AGAVE_API_CONFIG); +DEFINE_EXTERNAL_SERVICE(api_palette, WASABI_API_PALETTE); +#ifndef IGNORE_API_GRACENOTE +DEFINE_EXTERNAL_SERVICE(api_gracenote, AGAVE_API_GRACENOTE); +#endif +DEFINE_EXTERNAL_SERVICE(JSAPI2::api_security, AGAVE_API_JSAPI2_SECURITY); + +ifc_configitem *ieDisableSEH = 0; + +// wasabi based services for localisation support +HINSTANCE WASABI_API_LNG_HINST = 0, WASABI_API_ORIG_HINST = 0; + +static ServiceWatcher serviceWatcher; +#ifndef IGNORE_API_GRACENOTE +MusicIDCOM musicIDCOM; +#endif + +void config(); +void quit(); +int init(); + +BOOL init2(void); + +extern "C" +{ + HWND g_hwnd, g_ownerwnd; + + extern winampGeneralPurposePlugin plugin = + { + GPPHDR_VER_U, + "nullsoft(gen_ml.dll)", + init, + config, + quit, + }; +}; +HWND g_PEWindow; + +HMENU wa_main_menu = NULL; +HMENU wa_windows_menu = NULL; +HMENU wa_playlists_cmdmenu = NULL; +HMENU last_playlistsmenu = NULL; +HMENU last_viewmenu = NULL; +int last_viewmenu_insert = 0; +int g_safeMode = 0, sneak = 0; + +HCURSOR hDragNDropCursor; +int profile = 0; + +wchar_t pluginPath[MAX_PATH] = {0}; +static wchar_t preferencesName[128]; + + +HMENU g_context_menus; + +extern C_ItemList m_plugins; +extern HNAVCTRL hNavigation; + +//xp theme disabling shit +static HMODULE m_uxdll; +HRESULT (__stdcall *SetWindowTheme)(HWND hwnd, LPCWSTR pszSubAppName, LPCWSTR pszSubIdList); +BOOL (__stdcall *IsAppThemed)(void); + +template +void ServiceBuild(api_T *&api_t, GUID factoryGUID_t) +{ + if (WASABI_API_SVC) + { + waServiceFactory *factory = WASABI_API_SVC->service_getServiceByGuid(factoryGUID_t); + if (factory) + api_t = reinterpret_cast( factory->getInterface() ); + } +} + +template +void ServiceRelease(api_T *api_t, GUID factoryGUID_t) +{ + if (WASABI_API_SVC && api_t) + { + waServiceFactory *factory = WASABI_API_SVC->service_getServiceByGuid(factoryGUID_t); + if (factory) + factory->releaseInterface(api_t); + } + api_t = NULL; +} + +bool IsVisible() +{ + return g_hwnd && IsWindowVisible(g_ownerwnd); +} + +void MLVisibleChanged(BOOL fVisible) +{ + static BOOL visible = FALSE; + if (fVisible != visible) + { + visible = fVisible; + plugin_SendMessage(ML_MSG_MLVISIBLE, visible, 0, 0); + } +} + +BOOL MlWindow_SetMinimizedMode(BOOL fMinimized) +{ + if (FALSE != fMinimized) + return SetPropW(g_ownerwnd, L"MLWindow_MinimizedMode", (HANDLE)1); + + RemovePropW(g_ownerwnd, L"MLWindow_MinimizedMode"); + return TRUE; +} + +BOOL MlWindow_IsMinimizedMode(void) +{ + return (0 != GetPropW(g_ownerwnd, L"MLWindow_MinimizedMode")); +} + +void toggleVisible(int closecb) +{ + BOOL fVisible, fMinimized; + HWND rootWindow; + fVisible = (0 != (WS_VISIBLE & GetWindowLongPtrW(g_ownerwnd, GWL_STYLE)));//IsWindowVisible(g_ownerwnd); + + rootWindow = GetAncestor(g_ownerwnd, GA_ROOT); + if (NULL == rootWindow || rootWindow == g_ownerwnd) + { + rootWindow = (HWND)(HWND)SENDWAIPC(plugin.hwndParent, IPC_GETDIALOGBOXPARENT, 0); + if (NULL == rootWindow) + rootWindow = plugin.hwndParent; + } + + fMinimized = IsIconic(rootWindow); + + if (FALSE != fVisible || 1 == closecb) + { + if (FALSE == fMinimized && FALSE != fVisible) + { + HWND hwndFocus = GetFocus(); + + if (hwndFocus == g_ownerwnd || IsChild(g_ownerwnd, hwndFocus)) + SendMessageW(plugin.hwndParent, WM_COMMAND, WINAMP_NEXT_WINDOW, 0); + + ShowWindow(g_ownerwnd, SW_HIDE); + } + } + else + { + if (init2() && FALSE == fMinimized && FALSE == fVisible) + { + ShowWindow(g_ownerwnd, SW_SHOWNORMAL); + // make sure that we focus the tree to work around some modern skin quirks + if(closecb != 2) + { + SetFocus(NavCtrlI_GetHWND(hNavigation)); + } + else + { + // delay the focusing on loading as some machines are too fast and + // may cause the wrong view to the selected (root instead of child) + PostMessage(g_ownerwnd,WM_ML_IPC,0,ML_IPC_FOCUS_TREE); + } + } + } + + if (FALSE != fMinimized && 1 != closecb) + { + MlWindow_SetMinimizedMode(TRUE); + + if (NULL != g_config) + g_config->WriteInt(L"visible", (FALSE == fVisible)); + + UINT menuFlags = (FALSE == fVisible) ? MF_CHECKED : MF_UNCHECKED; + menuFlags |= MF_BYCOMMAND; + + INT szMenu[] = { 0, 4, }; + for (INT i = 0; i < ARRAYSIZE(szMenu); i++) + { + HMENU hMenu = (HMENU)SendMessage(plugin.hwndParent, WM_WA_IPC, szMenu[i], IPC_GET_HMENU); + if (NULL != hMenu) + CheckMenuItem(hMenu, WA_MENUITEM_ID, menuFlags); + } + } +} + + +static WNDPROC wa_oldWndProc; + +static BOOL Winamp_OnIPC(HWND hwnd, UINT uMsg, INT_PTR param, LRESULT *pResult) +{ + if (IPC_GETMLWINDOW == uMsg && IPC_GETMLWINDOW > 65536) + { + if (param == -1 && !g_hwnd) init2(); + *pResult = (LRESULT)g_hwnd; + return TRUE; + } + else if (IPC_LIBRARY_SENDTOMENU == uMsg && IPC_LIBRARY_SENDTOMENU > 65536) + { + librarySendToMenuStruct *s = (librarySendToMenuStruct*)param; + if (!s || s->mode == 0) + { + *pResult = 0xFFFFFFFF; + return TRUE; + } + if (s->mode == 1) + { + if (!s->ctx[0]) + { + if (!g_hwnd) init2(); + SendToMenu *stm = new SendToMenu(); + if (s->build_start_id && s->build_end_id) + { + stm->buildmenu(s->build_hMenu, s->data_type, s->ctx[1], s->ctx[2], s->build_start_id, s->build_end_id); + } + else + { + stm->buildmenu(s->build_hMenu, s->data_type, s->ctx[1], s->ctx[2]); + } + s->ctx[0] = (intptr_t)stm; + *pResult = 0xFFFFFFFF; + return TRUE; + } + } + else if (s->mode == 2) + { + SendToMenu *stm = (SendToMenu *)s->ctx[0]; + if (stm && stm->isourcmd(s->menu_id)) + { + *pResult = 0xFFFFFFFF; + return TRUE; + } + } + else if (s->mode == 3) + { + SendToMenu *stm = (SendToMenu *)s->ctx[0]; + if (stm) + { + *pResult = stm->handlecmd(s->hwnd, s->menu_id, s->data_type, s->data); + return TRUE; + } + } + else if (s->mode == 4) + { + delete (SendToMenu *)s->ctx[0]; + s->ctx[0] = 0; + } + *pResult = TRUE; + return TRUE; + } + else if (IPC_GET_ML_HMENU == uMsg && IPC_GET_ML_HMENU > 65536) + { + *pResult = (LRESULT)g_context_menus; + return TRUE; + } + + switch(uMsg) + { + case IPC_CB_RESETFONT: + PostMessageW(g_hwnd, WM_DISPLAYCHANGE, 0, 0); + break; + + case IPC_CB_GETTOOLTIPW: + if (param == 16 && g_config->ReadInt(L"attachlbolt", 0)) + { + static wchar_t tlStr[64]; + *pResult = (LRESULT)WASABI_API_LNGSTRINGW_BUF(IDS_TOGGLE_LIBRARY,tlStr,64); + return TRUE; + } + break; + + case IPC_GET_EXTENDED_FILE_INFO_HOOKABLE: + if (!m_calling_getfileinfo) + { + extendedFileInfoStruct *extendedInfo; + extendedInfo = (extendedFileInfoStruct*)param; + if (NULL != extendedInfo && + NULL != extendedInfo->filename && + NULL != extendedInfo->metadata) + { + if (plugin_SendMessage(ML_IPC_HOOKEXTINFO, param, 0, 0)) + { + *pResult = 1; + return TRUE; + } + } + } + break; + + case IPC_GET_EXTENDED_FILE_INFOW_HOOKABLE: + if (!m_calling_getfileinfo) + { + extendedFileInfoStructW *extendedInfo; + extendedInfo = (extendedFileInfoStructW*)param; + if (NULL != extendedInfo && + NULL != extendedInfo->filename && + NULL != extendedInfo->metadata) + { + if (plugin_SendMessage(ML_IPC_HOOKEXTINFOW, param, 0, 0)) + { + *pResult = 1; + return TRUE; + } + } + } + break; + + case IPC_HOOK_TITLES: + if (NULL != param) + { + waHookTitleStruct *hookTitle; + hookTitle = (waHookTitleStruct*)param; + if (NULL != hookTitle->filename && + plugin_SendMessage(ML_IPC_HOOKTITLE, param, 0, 0)) + { + *pResult = 1; + return TRUE; + } + } + break; + + case IPC_HOOK_TITLESW: + if (NULL != param) + { + waHookTitleStructW *hookTitle; + hookTitle = (waHookTitleStructW*)param; + if (NULL != hookTitle->filename && + plugin_SendMessage(ML_IPC_HOOKTITLEW, param, 0, 0)) + { + *pResult = 1; + return TRUE; + } + } + break; + + case IPC_ADD_PREFS_DLG: + case IPC_ADD_PREFS_DLGW: + if (param && !((prefsDlgRec*)param)->where) + { + prefsDlgRec *p = (prefsDlgRec *)param; + // we use the dialog proc for the preferences to determine the hinstance of the module and + // use that to then determine if we set it as a child of the media library preference node + // it also handles localised versions of the preference pages as the dialog proceedure is + // going to be in the true plug-in dll and so can be matched to the main ml plugins list! + MEMORY_BASIC_INFORMATION mbi = {0}; + if(VirtualQuery(p->proc, &mbi, sizeof(mbi))) + { + int i = m_plugins.GetSize(); + while (i-- > 0) + { + winampMediaLibraryPlugin *mlplugin = (winampMediaLibraryPlugin *)m_plugins.Get(i); + if (mlplugin->hDllInstance == (HINSTANCE)mbi.AllocationBase) + { + p->where = (intptr_t)(INT_PTR)&myPrefsItem; + break; + } + } + } + } + break; + + case IPC_CB_ONSHOWWND: + if ((HWND)param == g_ownerwnd) MLVisibleChanged(TRUE); + break; + + case IPC_HOOK_OKTOQUIT: + { + if (plugin_SendMessage(ML_MSG_NOTOKTOQUIT, 0, 0, 0)) + { + *pResult = 0; + return TRUE; + } + } + break; + + case IPC_PLAYING_FILEW: + plugin_SendMessage(ML_MSG_PLAYING_FILE, param, 0, 0); + break; + + case IPC_WRITECONFIG: + plugin_SendMessage(ML_MSG_WRITE_CONFIG, param, 0, 0); + break; + } + return FALSE; +} + +static LRESULT WINAPI wa_newWndProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch(uMsg) + { + // far from ideal fix but deals with differing plugin load orders (mainly from FAT32 drives) + // and not being able to unload/clean up properly the scrollbar bitmaps used - DRO 29/09/07 + case WM_CLOSE: + SkinnedScrollWnd_Quit(); + break; + case WM_WA_IPC: + { + LRESULT result = 0; + if (Winamp_OnIPC(hwndDlg, (UINT)lParam, (INT_PTR)wParam, &result)) return result; + break; + } + case WM_SIZE: + if (wParam == SIZE_RESTORED) + { + if (FALSE != MlWindow_IsMinimizedMode()) + { + MlWindow_SetMinimizedMode(FALSE); + int showCommand = (0 != g_config->ReadInt(L"visible", 1)) ? SW_SHOWNA : SW_HIDE; + ShowWindow(g_ownerwnd, showCommand); + } + } + break; + case WM_COMMAND: + case WM_SYSCOMMAND: + { + WORD lowP = LOWORD(wParam); + if (lowP == WA_MENUITEM_ID || lowP == WINAMP_LIGHTNING_CLICK) + { + if (lowP != WINAMP_LIGHTNING_CLICK || g_config->ReadInt(L"attachlbolt", 0)) + { + toggleVisible(); + return 0; + } + } + #if 0 // no radio - don't delete yet - tag will need to do this in ml_online + else if (lowP == WINAMP_VIDEO_TVBUTTON) // && g_config->ReadInt("attachtv",1)) + { + if (!g_hwnd || !IsWindowVisible(g_ownerwnd)) toggleVisible(); + PostMessage(g_ownerwnd, WM_NEXTDLGCTL, (WPARAM)g_hwnd, TRUE); + HWND hwndTree = GetTreeHWND(g_hwnd); + HTREEITEM hti = findByParam(hwndTree, TREE_INTERNET_VIDEO, TVI_ROOT); + if (hti) + { + TreeView_SelectItem(hwndTree, hti); + return 0; + } + } + #endif + // done like this since ml_online can't really subclass winamp to get the notification + else if (lowP == WINAMP_VIDEO_TVBUTTON) + { + if (!g_hwnd || !IsWindowVisible(g_ownerwnd)) toggleVisible(); + HNAVITEM hDefItem = NavCtrlI_FindItemByName(hNavigation, LOCALE_USER_DEFAULT, NICF_INVARIANT_I | NICF_DISPLAY_I | NICF_IGNORECASE_I, L"Shoutcast TV", -1); + + if(!hDefItem) + { + // work with the localised version of the Online Services root (if there...) + wchar_t OSName[64] = {L"Online Services"}; + WASABI_API_LNG->GetStringFromGUIDW(MlOnlineLangGUID,WASABI_API_ORIG_HINST,1,OSName,64); + + // just incase the localised dll was there but the file was missing the translation + if(!lstrcmpiW(OSName,L"Error loading string")) + lstrcpynW(OSName,L"Online Services",64); + + hDefItem = NavCtrlI_FindItemByName(hNavigation, LOCALE_USER_DEFAULT, NICF_INVARIANT_I | NICF_DISPLAY_I | NICF_IGNORECASE_I, OSName, -1); + } + if (hDefItem) + { + NavItemI_Select(hDefItem); + NavCtrlI_Show(hNavigation, SW_SHOWNA); + } + else + { + wchar_t titleStr[128] = {0}; + MessageBoxW(hwndDlg,WASABI_API_LNGSTRINGW(IDS_ONLINE_SERVICES_NOT_PRESENT), + WASABI_API_LNGSTRINGW_BUF(IDS_ERROR_SWITCHING_TO_VIEW,titleStr,128),0); + } + return 0; + } + if (lowP == WINAMP_SHOWLIBRARY) + { + if (!g_hwnd || !IsWindowVisible(g_hwnd)) + toggleVisible((2 == HIWORD(wParam) ? 2 : 0)); + } + else if (lowP == WINAMP_CLOSELIBRARY) + { + if (g_hwnd && IsWindowVisible(g_ownerwnd)) toggleVisible(); + } + } + break; + case WM_DWMCOMPOSITIONCHANGED: + if (IsWindow(g_hwnd)) PostMessageW(g_hwnd, WM_DWMCOMPOSITIONCHANGED, 0, 0L); + break; + } + + return CallWindowProcW(wa_oldWndProc, hwndDlg, uMsg, wParam, lParam); +} + +INT_PTR CALLBACK dialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); + +BOOL init2(void) +{ + if (!g_hwnd) + { + WADlg_init(plugin.hwndParent); + + //xp theme disabling shit + m_uxdll = LoadLibraryA("uxtheme.dll"); + if (m_uxdll) + { + IsAppThemed = (BOOL (__stdcall *)(void))GetProcAddress(m_uxdll, "IsAppThemed"); + SetWindowTheme = (HRESULT (__stdcall *)(struct HWND__ *, LPCWSTR , LPCWSTR ))GetProcAddress(m_uxdll, "SetWindowTheme"); + } + else + { + IsAppThemed = NULL; + SetWindowTheme = NULL; + } + + g_context_menus = WASABI_API_LOADMENU(IDR_CONTEXTMENUS); + + // 02/11/08 DrO + // defaults were 100,100,500,400 and not visible but these now make it align when opened under + // a clean install starting with a classic skin and is also visible on start now as with modern + myWindowState.r.left = g_config->ReadInt(L"mw_xpos", 301); + myWindowState.r.top = g_config->ReadInt(L"mw_ypos", 29); + myWindowState.r.right = myWindowState.r.left + g_config->ReadInt(L"mw_width", 500); + myWindowState.r.bottom = myWindowState.r.top + g_config->ReadInt(L"mw_height", 348); + SET_EMBED_GUID((&myWindowState), library_guid); + + g_ownerwnd = (HWND)SendMessage(plugin.hwndParent, WM_WA_IPC, (LPARAM) & myWindowState, IPC_GET_EMBEDIF); + if (!g_ownerwnd) return FALSE; + + if (NULL != WASABI_API_APP) WASABI_API_APP->app_registerGlobalWindow(g_ownerwnd); + + SetWindowTextW(g_ownerwnd, WASABI_API_LNGSTRINGW(IDS_WINAMP_LIBRARY)); + g_hwnd = WASABI_API_CREATEDIALOGW(IDD_MAIN, g_ownerwnd, dialogProc); + if (!g_hwnd) + { + DestroyWindow(g_ownerwnd); + g_ownerwnd = NULL; + return FALSE; + } + } + return TRUE; +} + +wchar_t WINAMP_INI[MAX_PATH] = {0}, WINAMP_INI_DIR[MAX_PATH] = {0}; +MediaLibraryCOM mediaLibraryCOM; +IDispatch *winampExternal = 0; + +void TAG_FMT_EXT(const wchar_t *filename, void *f, void *ff, void *p, wchar_t *out, int out_len, int extended) +{ + waFormatTitleExtended fmt; + fmt.filename=filename; + fmt.useExtendedInfo=extended; + fmt.out = out; + fmt.out_len = out_len; + fmt.p = p; + fmt.spec = 0; + *(void **)&fmt.TAGFUNC = f; + *(void **)&fmt.TAGFREEFUNC = ff; + *out = 0; + + int oldCallingGetFileInfo=m_calling_getfileinfo; + m_calling_getfileinfo=1; + SendMessage(plugin.hwndParent, WM_WA_IPC, (WPARAM)&fmt, IPC_FORMAT_TITLE_EXTENDED); + m_calling_getfileinfo=oldCallingGetFileInfo; +} + +wchar_t *itemrecordTagFunc(wchar_t *tag, void * p) //return 0 if not found +{ + itemRecord *t = (itemRecord *)p; + char buf[128] = {0}; + char *value = NULL; + + if (!_wcsicmp(tag, L"artist")) value = t->artist; + else if (!_wcsicmp(tag, L"album")) value = t->album; + else if (!_wcsicmp(tag, L"filename")) value = t->filename; + else if (!_wcsicmp(tag, L"title")) value = t->title; + else if ( !_wcsicmp( tag, L"ext" ) ) value = t->ext; + else if (!_wcsicmp(tag, L"year")) + { + if (t->year > 0) + { + StringCchPrintfA(buf, 128, "%04d", t->year); + value = buf; + } + } + else if (!_wcsicmp(tag, L"genre")) value = t->genre; + else if (!_wcsicmp(tag, L"comment")) value = t->comment; + else if (!_wcsicmp(tag, L"tracknumber") || !_wcsicmp(tag, L"track")) + { + if (t->track > 0) + { + StringCchPrintfA(buf, 128, "%02d", t->track); + value = buf; + } + } + else if (!_wcsicmp(tag, L"rating")) value = getRecordExtendedItem(t, "RATING"); + else if (!_wcsicmp(tag, L"playcount")) value = getRecordExtendedItem(t, "PLAYCOUNT"); + else if (!_wcsicmp(tag, L"bitrate")) value = getRecordExtendedItem(t, "BITRATE"); + else + return 0; + + if (!value) return reinterpret_cast(-1); + else return AutoWideDup(value); +} + +wchar_t *itemrecordWTagFunc(wchar_t *tag, void * p) //return 0 if not found +{ + itemRecordW *t = (itemRecordW *)p; + wchar_t buf[128] = {0}; + wchar_t *value = NULL; + + // TODO: more fields + if (!_wcsicmp(tag, L"artist")) value = t->artist; + else if (!_wcsicmp(tag, L"album")) value = t->album; + else if (!_wcsicmp(tag, L"albumartist")) value = t->albumartist; + else if (!_wcsicmp(tag, L"category")) value = t->category; + else if (!_wcsicmp(tag, L"comment")) value = t->comment; + else if (!_wcsicmp(tag, L"composer")) value = t->composer; + else if (!_wcsicmp(tag, L"publisher")) value = t->publisher; + else if (!_wcsicmp(tag, L"filename")) value = t->filename; + else if (!_wcsicmp(tag, L"title")) value = t->title; + else if (!_wcsicmp(tag, L"year")) + { + if (t->year > 0) + { + StringCchPrintfW(buf, 128, L"%04d", t->year); + value = buf; + } + } + else if (!_wcsicmp(tag, L"genre")) value = t->genre; + else if (!_wcsicmp(tag, L"comment")) value = t->comment; + else if (!_wcsicmp(tag, L"tracknumber") || !_wcsicmp(tag, L"track")) + { + if (t->track > 0) + { + StringCchPrintfW(buf, 128, L"%02d", t->track); + value = buf; + } + } + else if (!_wcsicmp(tag, L"rating")) + { + if (t->rating > 0) + { + StringCchPrintfW(buf, 128, L"%d", t->rating); + value = buf; + } + } + else if (!_wcsicmp(tag, L"playcount")) + { + if (t->playcount > 0) + { + StringCchPrintfW(buf, 128, L"%d", t->playcount); + value = buf; + } + } + else if (!_wcsicmp(tag, L"bitrate")) + { + if (t->bitrate > 0) + { + StringCchPrintfW(buf, 128, L"%d", t->bitrate); + value = buf; + } + } + else + return 0; + + if (!value) return reinterpret_cast(-1); + else return _wcsdup(value); +} + + +void fieldTagFuncFree(wchar_t * tag, void * p) +{ + free(tag); +} + +void main_playItemRecordList(itemRecordList *obj, int enqueue, int startplaying) +{ + if (obj->Size && !enqueue) SendMessage(plugin.hwndParent, WM_WA_IPC, 0, IPC_DELETE); + + int x; + wchar_t title[2048]=L""; + + for (x = 0; x < obj->Size; x ++) + { + if (obj->Items[x].filename && *obj->Items[x].filename) + { + AutoWideFn wfn( obj->Items[ x ].filename ); + + TAG_FMT_EXT(wfn, itemrecordTagFunc, fieldTagFuncFree, (void*)&obj->Items[x], title, 2048, 1); + + { + enqueueFileWithMetaStructW s; + s.filename = wfn; + s.title = title; + s.ext = NULL; + s.length = obj->Items[x].length; + SendMessage(plugin.hwndParent, WM_WA_IPC, (WPARAM)&s, IPC_PLAYFILEW); + } + } + } + + if (obj->Size && !enqueue && startplaying) SendMessage(plugin.hwndParent, WM_WA_IPC, 0, IPC_STARTPLAY); +} + +void main_playItemRecordListW(itemRecordListW *obj, int enqueue, int startplaying) +{ + if (obj->Size && !enqueue) SendMessage(plugin.hwndParent, WM_WA_IPC, 0, IPC_DELETE); + + int x; + wchar_t title[2048]=L""; + + for (x = 0; x < obj->Size; x ++) + { + if (obj->Items[x].filename && *obj->Items[x].filename) + { + TAG_FMT_EXT(obj->Items[x].filename, itemrecordWTagFunc, fieldTagFuncFree, (void*)&obj->Items[x], title, 2048, 1); + { + enqueueFileWithMetaStructW s; + s.filename = obj->Items[x].filename; + s.title = title; + s.ext = NULL; + s.length = obj->Items[x].length; + SendMessage(plugin.hwndParent, WM_WA_IPC, (WPARAM)&s, IPC_PLAYFILEW); + } + } + } + + if (obj->Size && !enqueue && startplaying) SendMessage(plugin.hwndParent, WM_WA_IPC, 0, IPC_STARTPLAY); +} + +void OpenMediaLibraryPreferences() +{ + SendMessage(plugin.hwndParent, WM_WA_IPC, (WPARAM)&myPrefsItem, IPC_OPENPREFSTOPAGE); +} + +int AddTreeImageBmp(int resourceId) +{ + HMLIMGLST hmlilNavigation = MLNavCtrl_GetImageList(g_hwnd); + MLIMAGESOURCE mlis = {sizeof(MLIMAGESOURCE),0}; + MLIMAGELISTITEM item = {0}; + item.cbSize = sizeof(MLIMAGELISTITEM); + item.hmlil = hmlilNavigation; + item.filterUID = MLIF_FILTER1_UID; + item.pmlImgSource = &mlis; + + mlis.hInst = WASABI_API_ORIG_HINST; + mlis.bpp = 24; + mlis.lpszName = MAKEINTRESOURCEW(resourceId); + mlis.type = SRC_TYPE_BMP; + mlis.flags = ISF_FORCE_BPP; + return MLImageList_Add(g_hwnd, &item); +} + +void SkinnedScrollWnd_Init(); +void SkinnedScrollWnd_Quit(); +int init() +{ + wchar_t g_path[MAX_PATH] = {0}; + QueryPerformanceFrequency(&freq); + + WASABI_API_SVC = (api_service*)SendMessage(plugin.hwndParent, WM_WA_IPC, 0, IPC_GET_API_SERVICE); + if (!WASABI_API_SVC || WASABI_API_SVC == (api_service *)1) + return GEN_INIT_FAILURE; + + HTMLContainer2_Initialize(); + Tataki::Init(WASABI_API_SVC); + + // loader so that we can get the localisation service api for use + ServiceBuild(WASABI_API_LNG, languageApiGUID); + ServiceBuild(WASABI_API_SYSCB, syscbApiServiceGuid); + ServiceBuild(AGAVE_API_DECODE, decodeFileGUID); + ServiceBuild(AGAVE_API_JSAPI2_SECURITY, JSAPI2::api_securityGUID); + ServiceBuild(AGAVE_OBJ_BROWSER, OBJ_OmBrowser); + #ifndef IGNORE_API_GRACENOTE + ServiceBuild(AGAVE_API_GRACENOTE, gracenoteApiGUID); + #endif + ServiceBuild(WASABI_API_APP, applicationApiServiceGuid); + ServiceBuild(AGAVE_API_CONFIG, AgaveConfigGUID); + ServiceBuild(WASABI_API_PALETTE, PaletteManagerGUID); + ServiceBuild(AGAVE_API_THREADPOOL, ThreadPoolGUID); + // no guarantee that AGAVE_API_MLDB will be available yet, so we'll start a watcher for it + serviceWatcher.WatchWith(WASABI_API_SVC); + serviceWatcher.WatchFor(&AGAVE_API_MLDB, mldbApiGuid); + serviceWatcher.WatchFor(&WASABI_API_SKIN, skinApiServiceGuid); + serviceWatcher.WatchFor(&WASABI_API_WND,wndApiServiceGuid); + WASABI_API_SYSCB->syscb_registerCallback(&serviceWatcher); + SkinnedScrollWnd_Init(); + + // need to have this initialised before we try to do anything with localisation features + WASABI_API_START_LANG(plugin.hDllInstance,GenMlLangGUID); + + // Build plugin description string... + static wchar_t szDescription[256]; + StringCchPrintfW(szDescription, ARRAYSIZE(szDescription), + WASABI_API_LNGSTRINGW(IDS_NULLSOFT_ML_STR), + LOWORD(PLUGIN_VERSION) >> 8, + PLUGIN_VERSION & 0xFF); + plugin.description = (char*)szDescription; + + DispatchInfo dispatchInfo; + dispatchInfo.name = L"MediaLibrary"; + dispatchInfo.dispatch = &mediaLibraryCOM; + SendMessage(plugin.hwndParent, WM_WA_IPC, (WPARAM)&dispatchInfo, IPC_ADD_DISPATCH_OBJECT); + + #ifndef IGNORE_API_GRACENOTE + dispatchInfo.name = L"MusicID"; + dispatchInfo.dispatch = &musicIDCOM; + SendMessage(plugin.hwndParent, WM_WA_IPC, (WPARAM)&dispatchInfo, IPC_ADD_DISPATCH_OBJECT); + #endif + + IPC_LIBRARY_SENDTOMENU = (INT)SendMessage(plugin.hwndParent, WM_WA_IPC, (WPARAM)&"LibrarySendToMenu", IPC_REGISTER_WINAMP_IPCMESSAGE); + IPC_GETMLWINDOW = (INT)SendMessage(plugin.hwndParent, WM_WA_IPC, (WPARAM)&"LibraryGetWnd", IPC_REGISTER_WINAMP_IPCMESSAGE); + IPC_GET_ML_HMENU = (INT)SendMessage(plugin.hwndParent, WM_WA_IPC, (WPARAM)&"LibraryGetHmenu", IPC_REGISTER_WINAMP_IPCMESSAGE); + + lstrcpynW(WINAMP_INI, (wchar_t*)SendMessage(plugin.hwndParent, WM_WA_IPC, 0, IPC_GETINIFILEW), MAX_PATH); + lstrcpynW(WINAMP_INI_DIR, (wchar_t*)SendMessage(plugin.hwndParent, WM_WA_IPC, 0, IPC_GETINIDIRECTORYW), MAX_PATH); + + PathCombineW(g_path, WINAMP_INI_DIR, L"Plugins"); + CreateDirectoryW(g_path, NULL); + + wchar_t *dir = (wchar_t*)SendMessage(plugin.hwndParent, WM_WA_IPC, 0, IPC_GETPLUGINDIRECTORYW); + if (dir == (wchar_t *)1 || dir == 0) + lstrcpynW(pluginPath, g_path, MAX_PATH); + else + lstrcpynW(pluginPath, dir, MAX_PATH); + + hDragNDropCursor = LoadCursor(plugin.hDllInstance, MAKEINTRESOURCE(ML_IDC_DRAGDROP)); + profile = GetPrivateProfileIntW(L"winamp", L"profile", 0, WINAMP_INI); + + wchar_t configName[1024 + 32] = {0}; + StringCchPrintfW(configName, 1024 + 32, L"%s\\gen_ml.ini", g_path); + g_config = new C_Config(configName); + config_use_ff_scrollbars = g_config->ReadInt(L"ffsb", 1); + config_use_alternate_colors = g_config->ReadInt(L"alternate_items", 1); + + int vis = g_config->ReadInt(L"visible", 1); + wa_main_menu = (HMENU)SendMessage(plugin.hwndParent, WM_WA_IPC, 0, IPC_GET_HMENU); + wa_windows_menu = (HMENU)SendMessage(plugin.hwndParent, WM_WA_IPC, 4, IPC_GET_HMENU); + wa_playlists_cmdmenu = NULL; + if (wa_main_menu || wa_windows_menu) + { + if (wa_main_menu) + { + MENUITEMINFOW i = {sizeof(i), MIIM_ID | MIIM_STATE | MIIM_TYPE, MFT_STRING, vis ? (UINT)MFS_CHECKED : 0, WA_MENUITEM_ID}; + int prior_item = GetMenuItemID(wa_main_menu,9); + if(prior_item <= 0) prior_item = GetMenuItemID(wa_main_menu,8); + i.dwTypeData = WASABI_API_LNGSTRINGW(IDS_ML_ALT_L_SHORTCUT); + + // append before the video menu entry (more reliable than inserting into position '9' in the menu + InsertMenuItemW(wa_main_menu, prior_item, FALSE, &i); + SendMessage(plugin.hwndParent, WM_WA_IPC, 1, IPC_ADJUST_OPTIONSMENUPOS); + } + + if (wa_windows_menu) + { + MENUITEMINFOW i = {sizeof(i), MIIM_ID | MIIM_STATE | MIIM_TYPE, MFT_STRING, vis ? (UINT)MFS_CHECKED : 0, WA_MENUITEM_ID}; + int prior_item = GetMenuItemID(wa_windows_menu,3); + if(prior_item <= 0) prior_item = GetMenuItemID(wa_windows_menu,2); + i.dwTypeData = WASABI_API_LNGSTRINGW(IDS_ML_ALT_L_SHORTCUT); + InsertMenuItemW(wa_windows_menu, prior_item, FALSE, &i); + SendMessage(plugin.hwndParent, WM_WA_IPC, 1, IPC_ADJUST_FFWINDOWSMENUPOS); + } + } + + // subclass the winamp window to get our leet menu item to work + wa_oldWndProc = (WNDPROC)(LONG_PTR)SetWindowLongPtrW(plugin.hwndParent, GWLP_WNDPROC, (LONGX86)(LONG_PTR)wa_newWndProc); + + myPrefsItem.dlgID = IDD_PREFSFR; + myPrefsItem.name = WASABI_API_LNGSTRINGW_BUF(IDS_MEDIA_LIBRARY,preferencesName,128); + myPrefsItem.proc = (void*)PrefsProc; + myPrefsItem.hInst = WASABI_API_LNG_HINST; + myPrefsItem.where = -6; // to become root based item + SendMessage(plugin.hwndParent, WM_WA_IPC, (WPARAM)&myPrefsItem, IPC_ADD_PREFS_DLGW); + + myPrefsItemPlug.dlgID = IDD_MLPLUGINS; + myPrefsItemPlug.name = preferencesName; + myPrefsItemPlug.proc = (void*)PluginsProc; + myPrefsItemPlug.hInst = WASABI_API_LNG_HINST; + myPrefsItemPlug.where = 1; + SendMessage(plugin.hwndParent, WM_WA_IPC, (WPARAM)&myPrefsItemPlug, IPC_ADD_PREFS_DLGW); + + g_PEWindow = (HWND)SendMessage(plugin.hwndParent, WM_WA_IPC, IPC_GETWND_PE, IPC_GETWND); + g_safeMode = SendMessage(plugin.hwndParent, WM_WA_IPC, 0, IPC_IS_SAFEMODE); + + // we're gonna go ahead and make this directory just to be safe. + // if a plugin tries to use it as an INI directory but it doesn't exist, things go wrong + wchar_t mldir[MAX_PATH] = {0}; + PathCombineW(mldir, g_path, L"ml"); + CreateDirectoryW(mldir, NULL); + PathCombineW(mldir, mldir, L"views"); + CreateDirectoryW(mldir, NULL); + + //add general hotkey + int m_genhotkeys_add_ipc = (INT)SendMessage(plugin.hwndParent, WM_WA_IPC, (WPARAM)&"GenHotkeysAdd", IPC_REGISTER_WINAMP_IPCMESSAGE); + + static genHotkeysAddStruct ghas = { + (char*)_wcsdup(WASABI_API_LNGSTRINGW(IDS_ML_GHK_STR)), + HKF_BRING_TO_FRONT|HKF_UNICODE_NAME, + WM_COMMAND, + WA_MENUITEM_ID, + 0, + // specifically set the id str now so that it'll work correctly with whatever lngpack is in use + "ML: Show/Hide Media Library" + }; + if (m_genhotkeys_add_ipc > 65536) PostMessage(plugin.hwndParent, WM_WA_IPC, (WPARAM)&ghas, m_genhotkeys_add_ipc); //post so gen_hotkeys will catch it if not inited yet + + init2(); + + // register the art view window classes + { + extern void InitSmoothScrollList(); + extern void InitHeaderIconList(); + InitSmoothScrollList(); + InitHeaderIconList(); + RegisterFolderBrowserControl(plugin.hDllInstance); + } + + NavCtrlI_BeginUpdate(hNavigation, NUF_LOCK_NONE_I); + loadMlPlugins(); + +#if 0 + #ifdef _DEBUG + #define BETA + #endif + #ifdef BETA + sneak = GetPrivateProfileIntW(L"winamp", L"sneak", 0, WINAMP_INI); + if (!(sneak & 1)) + { + NAVINSERTSTRUCT nis = {0}; + nis.item.cbSize = sizeof(NAVITEM); + nis.item.pszText = L"Winamp Labs"; + nis.item.pszInvariant = L"winamp_labs"; + nis.item.mask = NIMF_TEXT | NIMF_TEXTINVARIANT | NIMF_IMAGE | NIMF_IMAGESEL | NIMF_STYLE; + nis.item.iImage = nis.item.iSelectedImage = AddTreeImageBmp(IDB_TREEITEM_LABS); + nis.item.style = NIS_BOLD; + nis.hInsertAfter = NCI_FIRST; + NAVITEM nvItem = {sizeof(NAVITEM),0,NIMF_ITEMID,}; + nvItem.hItem = MLNavCtrl_InsertItem(g_hwnd, &nis); + } + #endif +#endif + + NavCtrlI_EndUpdate(hNavigation); + + if (SW_SHOWMINIMIZED == SENDWAIPC(plugin.hwndParent, IPC_INITIAL_SHOW_STATE, 0)) + { + MlWindow_SetMinimizedMode(TRUE); + } + else if (0 != vis) + { + PostMessageW(plugin.hwndParent, WM_COMMAND, MAKEWPARAM(WINAMP_SHOWLIBRARY, 2), 0L); + } + + return GEN_INIT_SUCCESS; +} + + +void quit() +{ + serviceWatcher.StopWatching(); + serviceWatcher.Clear(); + + MlWindow_SetMinimizedMode(FALSE); + + if (g_ownerwnd) + { + g_config->WriteInt(L"mw_xpos", myWindowState.r.left); + g_config->WriteInt(L"mw_ypos", myWindowState.r.top); + g_config->WriteInt(L"mw_width", myWindowState.r.right - myWindowState.r.left); + g_config->WriteInt(L"mw_height", myWindowState.r.bottom - myWindowState.r.top); + + if (NULL != WASABI_API_APP) WASABI_API_APP->app_unregisterGlobalWindow(g_ownerwnd); + DestroyWindow(g_ownerwnd); + g_ownerwnd = NULL; + } + + // unload any services from ml_ plugins before unloading the plugins + ServiceRelease(AGAVE_API_MLDB, mldbApiGuid); + + #ifndef IGNORE_API_GRACENOTE + musicIDCOM.Quit(); + #endif + + unloadMlPlugins(); + + WADlg_close(); + + if (g_config) + { + delete g_config; + g_config = NULL; + } + + if (m_uxdll) + { + FreeLibrary(m_uxdll); + m_uxdll = NULL; + } + SkinnedScrollWnd_Quit(); + #ifndef IGNORE_API_GRACENOTE + ServiceRelease(AGAVE_API_GRACENOTE, gracenoteApiGUID); + #endif + ServiceRelease(WASABI_API_SYSCB, syscbApiServiceGuid); + ServiceRelease(AGAVE_API_DECODE, decodeFileGUID); + ServiceRelease(AGAVE_API_JSAPI2_SECURITY, JSAPI2::api_securityGUID); + ServiceRelease(AGAVE_OBJ_BROWSER, OBJ_OmBrowser); + ServiceRelease(WASABI_API_LNG, languageApiGUID); + ServiceRelease(WASABI_API_WND, wndApiServiceGuid); + ServiceRelease(WASABI_API_SKIN, skinApiServiceGuid); + ServiceRelease(WASABI_API_APP, applicationApiServiceGuid); + ServiceRelease(AGAVE_API_CONFIG, AgaveConfigGUID); + ServiceRelease(WASABI_API_PALETTE, PaletteManagerGUID); + + Tataki::Quit(); + + HTMLContainer2_Uninitialize(); + + ServiceRelease(AGAVE_API_THREADPOOL, ThreadPoolGUID); +} + +void config() +{ + OpenMediaLibraryPreferences(); +} + +INT MediaLibrary_TrackPopupEx(HMENU hMenu, UINT fuFlags, INT x, INT y, HWND hwnd, LPTPMPARAMS lptpm, HMLIMGLST hmlil, + INT width, UINT skinStyle, MENUCUSTOMIZEPROC customProc, ULONG_PTR customParam) +{ + if (NULL == hMenu) + return NULL; + + return IsSkinnedPopupEnabled(FALSE) ? + TrackSkinnedPopupMenuEx(hMenu, fuFlags, x, y, hwnd, lptpm, hmlil, width, skinStyle, customProc, customParam) : + TrackPopupMenuEx(hMenu, fuFlags, x, y, hwnd, lptpm); +} + + +INT MediaLibrary_TrackPopup(HMENU hMenu, UINT fuFlags, INT x, INT y, HWND hwnd) +{ + return MediaLibrary_TrackPopupEx(hMenu, fuFlags, x, y, hwnd, NULL, NULL, 0, SMS_USESKINFONT, NULL, NULL); +} + + HANDLE MediaLibrary_InitSkinnedPopupHook(HWND hwnd, HMLIMGLST hmlil, INT width, UINT skinStyle, MENUCUSTOMIZEPROC customProc, ULONG_PTR customParam) + { + if (FALSE == IsSkinnedPopupEnabled(FALSE)) + return FALSE; + + return InitSkinnedPopupHook(hwnd, hmlil, width, skinStyle, customProc, customParam); + } + + + +BOOL +MediaLibrary_OpenUrl(HWND ownerWindow, const wchar_t *url, BOOL forceExternal) +{ + BOOL result; + HCURSOR cursor; + + cursor = LoadCursor(NULL, IDC_APPSTARTING); + if (NULL != cursor) + cursor = SetCursor(cursor); + + if (FALSE != forceExternal) + { + HINSTANCE instance; + + if (NULL == ownerWindow) + ownerWindow = (HWND)SENDWAIPC(plugin.hwndParent, IPC_GETDIALOGBOXPARENT, 0); + + instance = ShellExecuteW(ownerWindow, L"open", url, NULL, NULL, SW_SHOWNORMAL); + result = ((INT_PTR)instance > 32) ? TRUE: FALSE; + } + else + { + SENDWAIPC(plugin.hwndParent, IPC_OPEN_URL, url); + result = TRUE; + } + + if (NULL != cursor) + SetCursor(cursor); + + return result; +} + +BOOL +MediaLibrary_OpenHelpUrl(const wchar_t *helpUrl) +{ + HWND ownerWindow; + + ownerWindow = (HWND)SENDWAIPC(plugin.hwndParent, IPC_GETDIALOGBOXPARENT, 0); + + return MediaLibrary_OpenUrl(ownerWindow, helpUrl, FALSE); +} + +extern "C" +{ + int getFileInfo(const char *filename, const char *metadata, char *dest, int len) + { + m_calling_getfileinfo = 1; + dest[0] = 0; + extendedFileInfoStruct efis = { + filename, + metadata, + dest, + (size_t)len, + }; + int r = (INT)SendMessage(plugin.hwndParent, WM_WA_IPC, (WPARAM) & efis, IPC_GET_EXTENDED_FILE_INFO); //will return 1 if wa2 supports this IPC call + m_calling_getfileinfo = 0; + return r; + } + + __declspec(dllexport) winampGeneralPurposePlugin *winampGetGeneralPurposePlugin() + { + return &plugin; + } +}; + diff --git a/Src/Plugins/General/gen_ml/main.h b/Src/Plugins/General/gen_ml/main.h new file mode 100644 index 00000000..f8c415f8 --- /dev/null +++ b/Src/Plugins/General/gen_ml/main.h @@ -0,0 +1,142 @@ +#ifndef _MAIN_H +#define _MAIN_H + +#include +#include +#include + +#include "../winamp/wa_dlg.h" +#include "../winamp/wa_ipc.h" + + +#include "./itemlist.h" +#include "./config.h" +#include "../winamp/gen.h" +#include "../Agave/Config/ifc_configitem.h" + +#define PLUGIN_VERSION 0x0378 + +#include "./ml.h" +#include "./skinning.h" +#include "../nu/trace.h" + +#define WA_MENUITEM_ID 23123 +#define WINAMP_VIDEO_TVBUTTON 40338 // we hook this =) +#define WINAMP_LIGHTNING_CLICK 40339 // this three +#define WINAMP_NEXT_WINDOW 40063 +#define WINAMP_SHOWLIBRARY 40379 +#define WINAMP_CLOSELIBRARY 40380 + + +#define CSTR_INVARIANT MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT) + +#define MSGRESULT(__hwnd, __result) { SetWindowLongPtrW((__hwnd), DWLP_MSGRESULT, ((LONGX86)(LONG_PTR)(__result))); return TRUE; } + +#ifndef LONGX86 +#ifdef _WIN64 + #define LONGX86 LONG_PTR +#else /*_WIN64*/ + #define LONGX86 LONG +#endif /*_WIN64*/ +#endif // LONGX86 + +BOOL FlickerFixWindow(HWND hwnd, INT mode); + +extern "C" winampGeneralPurposePlugin plugin; +extern "C" int getFileInfo(const char *filename, const char *metadata, char *dest, int len); + +extern LARGE_INTEGER freq; +extern int profile; +extern HCURSOR hDragNDropCursor; +extern wchar_t WINAMP_INI[MAX_PATH], WINAMP_INI_DIR[MAX_PATH]; +extern wchar_t pluginPath[]; +extern C_Config *g_config; + +extern HMENU g_context_menus; +extern HWND g_PEWindow, g_hwnd, prefsWnd; +extern int g_safeMode, sneak; +extern int config_use_ff_scrollbars; +extern int config_use_alternate_colors; +extern C_Config *g_view_metaconf; + +void main_playItemRecordList (itemRecordList *obj, int enqueue, int startplaying=1); +void main_playItemRecordListW(itemRecordListW *obj, int enqueue, int startplaying=1); + +int handleDragDropMove(HWND hwndDlg, int type, POINT p, int do_cursors); +void MLVisibleChanged(BOOL fVisible); + +INT MediaLibrary_TrackPopupEx(HMENU hMenu, UINT fuFlags, INT x, INT y, HWND hwnd, LPTPMPARAMS lptpm, HMLIMGLST hmlil, + INT width, UINT skinStyle, MENUCUSTOMIZEPROC customProc, ULONG_PTR customParam); + +INT MediaLibrary_TrackPopup(HMENU hMenu, UINT fuFlags, INT x, INT y, HWND hwnd); + +HANDLE MediaLibrary_InitSkinnedPopupHook(HWND hwnd, HMLIMGLST hmlil, INT width, UINT skinStyle, + MENUCUSTOMIZEPROC customProc, ULONG_PTR customParam); + +BOOL +MediaLibrary_OpenUrl(HWND ownerWindow, const wchar_t *url, BOOL forceExternal); + +BOOL +MediaLibrary_OpenHelpUrl(const wchar_t *helpUrl); + +/* +//gracenote.cpp +void gracenoteInit(); +int gracenoteQueryFile(const char *filename); +void gracenoteCancelRequest(); +int gracenoteDoTimerStuff(); +void gracenoteSetValues(char *artist, char *album, char *title); +char *gracenoteGetTuid(); +int gracenoteIsWorking(); +*/ +//listheader.cpp +INT_PTR handleListViewHeaderMsgs(HWND hwndDlg, + HWND headerWnd, + HWND listWnd, + UINT uMsg, + WPARAM wParam, + LPARAM lParam, + BOOL sortShow, + BOOL sortAscending, + int sortIndex); + +INT_PTR handleListViewHeaderPaintMsgs(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); + +//plugin.cpp +void loadMlPlugins(); +void unloadMlPlugins(); +INT_PTR pluginHandleIpcMessage(HWND hwndML, int msg, INT_PTR param); +INT_PTR plugin_SendMessage(int message_type, INT_PTR param1, INT_PTR param2, INT_PTR param3); +INT_PTR CALLBACK PluginsProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam); + + +//prefs.cpp +INT_PTR CALLBACK PrefsProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam); + +void refreshPrefs(INT_PTR screen); +void openPrefs(INT_PTR screen); //-1 for default + +void FixAmps(char *str, size_t len); +LPWSTR FixAmpsW(LPWSTR pszText, INT cchMaxText); + +//view_devices.cpp +INT_PTR CALLBACK view_devicesDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam); +// webinfo_dlg +HWND CreateWebInfoWindow(HWND hwndParent, UINT uMsgQuery, INT x, INT y, INT cx, INT cy, INT ctrlId); + +extern int winampVersion; + +void toggleVisible(int closecb = 0); +bool IsVisible(); +void myOpenURLWithFallback(HWND hwnd, wchar_t *loc, wchar_t *fallbackLoc); + +bool IsVista(); + +void SkinnedScrollWnd_Init(); +void SkinnedScrollWnd_Quit(); + +class ifc_configitem; + +extern ifc_configitem *ieDisableSEH; + +#endif \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/menu.cpp b/Src/Plugins/General/gen_ml/menu.cpp new file mode 100644 index 00000000..0c3db24a --- /dev/null +++ b/Src/Plugins/General/gen_ml/menu.cpp @@ -0,0 +1,20 @@ +#include "menu.h" + +INT Menu_TrackPopupParam(HWND library, HMENU hMenu, UINT fuFlags, INT x, INT y, HWND hwnd, LPTPMPARAMS lptpm, ULONG_PTR param) +{ + if ( hMenu == NULL ) + return NULL; + + MLSKINNEDPOPUP popup = {0}; + popup.cbSize = sizeof(MLSKINNEDPOPUP); + popup.hmenu = hMenu; + popup.fuFlags = fuFlags; + popup.x = x; + popup.y = y; + popup.hwnd = hwnd; + popup.lptpm = lptpm; + popup.skinStyle = SMS_USESKINFONT; + popup.customParam = param; + + return (INT)SENDMLIPC( library, ML_IPC_TRACKSKINNEDPOPUPEX, &popup ); +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/menu.h b/Src/Plugins/General/gen_ml/menu.h new file mode 100644 index 00000000..6506bd84 --- /dev/null +++ b/Src/Plugins/General/gen_ml/menu.h @@ -0,0 +1,17 @@ +#ifndef NULLOSFT_SKINNED_MENU_HEADER +#define NULLOSFT_SKINNED_MENU_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include +#include "./ml.h" +#include "./ml_ipc_0313.h" + +#define Menu_TrackPopup(library, hMenu, fuFlags, x, y, hwnd, lptpm) \ + Menu_TrackPopupParam(library, hMenu, fuFlags, x, y, hwnd, lptpm, 0) + +INT Menu_TrackPopupParam(HWND library, HMENU hMenu, UINT fuFlags, INT x, INT y, HWND hwnd, LPTPMPARAMS lptpm, ULONG_PTR param); + +#endif //NULLOSFT_SKINNED_MENU_HEADER \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/menufucker.h b/Src/Plugins/General/gen_ml/menufucker.h new file mode 100644 index 00000000..df215a81 --- /dev/null +++ b/Src/Plugins/General/gen_ml/menufucker.h @@ -0,0 +1,43 @@ +#ifndef _NULLSOFT_GEN_ML_MENUFUCKER_H_ +#define _NULLSOFT_GEN_ML_MENUFUCKER_H_ + +#include "ml.h" +#include "../playlist/ifc_playlist.h" + +/* +there are two IPC messages, both sent to your ml plugins messageproc. Get the message IDs by doing: +ML_IPC_MENUFUCKER_BUILD = SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&"menufucker_build", IPC_REGISTER_WINAMP_IPCMESSAGE); +ML_IPC_MENUFUCKER_RESULT = SendMessage(plugin.hwndWinampParent, WM_WA_IPC, (WPARAM)&"menufucker_result", IPC_REGISTER_WINAMP_IPCMESSAGE); + +ML_IPC_MENUFUCKER_BUILD: +This is sent just before the menu is shown. param1 is a pointer to a menufucker_t struct. +Do what you like to the menu, if you add anything, give it the id nextidx, and increment nextidx + +ML_IPC_MENUFUCKER_RESULT: +param1 is a pointer to a menufucker_t struct, param2 is the id of the menu item selected +*/ + +#define MENU_MEDIAVIEW 0 +#define MENU_MLPLAYLIST 1 +#define MENU_PLAYLIST 2 +#define MENU_SONGTICKER 3 + +typedef struct { + size_t size; + int type; + HMENU menu; + int nextidx; + int maxidx; + union { + struct { + HWND list; + itemRecordListW *items; + } mediaview; // valid if type==MENU_MEDIAVIEW + struct { + HWND list; + ifc_playlist * pl; + } mlplaylist; // valid if type==MENU_MLPLAYLIST + } extinf; +} menufucker_t; + +#endif // _NULLSOFT_GEN_ML_MENUFUCKER_H_ \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/ml.h b/Src/Plugins/General/gen_ml/ml.h new file mode 100644 index 00000000..68d7c07f --- /dev/null +++ b/Src/Plugins/General/gen_ml/ml.h @@ -0,0 +1,915 @@ +/* +** Copyright (C) 2003-2014 Winamp SA +** +** This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held +** liable for any damages arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, including commercial applications, and to +** alter it and redistribute it freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. +** If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +** +** 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +** +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#ifndef _ML_H_ +#define _ML_H_ + +#define MLHDR_VER 0x17 // used from Winamp v5.66 +#define MLHDR_VER_U 0x16 // used from Winamp v5.64 to v5.65 (is loaded by v5.66+) +#define MLHDR_VER_OLD 0x15 // used up to Winamp v5.63 (is loaded by v5.64+) + +#include +#include +#include +#include + +typedef struct +{ + int version; // changed 5.64+ - MLHDR_VER will now require a unicode (wchar_t*) description and only work correctly on 5.64+ + // MLHDR_VER_OLD will still be loaded but will use the original (char*) description as before + // note: we are using the fact that sizeof(char*) == sizeof(wchar_t*) to be able to allow this + // so when using MLHDR_VER = 0x16 or higher then you will need to cast description to be + // a (char*) to be able to set it + const char *description; + int (__cdecl *init)(); // return 0 on success, non-zero for failure (quit WON'T be called) + void (__cdecl *quit)(); + + // return NONZERO if you accept this message as yours, otherwise 0 to pass it to other plugins + INT_PTR (__cdecl *MessageProc)(int message_type, INT_PTR param1, INT_PTR param2, INT_PTR param3); + // Note: INT_PTR becomes 64bit on 64bit compiles... + // if you don't like windows types or caps, you can #include and use intptr_t + + // all the following data is filled in by the library + HWND hwndWinampParent; + HWND hwndLibraryParent; // send this any of the WM_ML_IPC messages + HINSTANCE hDllInstance; + + // filled in by Winamp (added 5.66+ to replace need to call IPC_GET_API_SERVICE on loading) + #ifdef __cplusplus + api_service *service; + #else + void * service; + #endif +} winampMediaLibraryPlugin; + +// return values from the init(..) which determines if Winamp will continue loading +// and handling the plugin or if it will disregard the load attempt. If ML_INIT_FAILURE +// is returned then the plugin will be listed as [NOT LOADED] on the plug-in prefs page. +#define ML_INIT_SUCCESS 0 +#define ML_INIT_FAILURE 1 + +// These are the return values to be used with the uninstall plugin export function: +// __declspec(dllexport) int __cdecl winampUninstallPlugin(HINSTANCE hDllInst, HWND hwndDlg, int param) +// which determines if Winamp can uninstall the plugin immediately or on winamp restart. +// If this is not exported then Winamp will assume an uninstall with reboot is the only way. + +#define ML_PLUGIN_UNINSTALL_NOW 0x1 +#define ML_PLUGIN_UNINSTALL_REBOOT 0x0 + +// +// Uninstall support was added from 5.0+ and uninstall now support from 5.5+ though note +// that it is down to you to ensure that if uninstall now is returned that it will not +// cause a crash i.e. don't use if you've been subclassing the main or library windows. +// +// The HWND passed in the calling of winampUninstallPlugin(..) is the preference page HWND. +// + +// For a Media Library plugin to be correctly detected by Winamp you need to ensure that +// the exported winampMediaLibraryPlugin(..) is exported as an undecorated function +// e.g. +// #ifdef __cplusplus +// extern "C" { +// #endif +// __declspec(dllexport) winampMediaLibraryPlugin * __cdecl winampGetMediaLibraryPlugin(){ return &plugin; } +// #ifdef __cplusplus +// } +// #endif +// + +// messages your plugin may receive on MessageProc() + +#define ML_MSG_TREE_BEGIN 0x100 +#define ML_MSG_TREE_ONCREATEVIEW 0x100 // param1 = param of tree item, param2 is HWND of parent. return HWND if it is us + +#define ML_MSG_TREE_ONCLICK 0x101 // param1 = param of tree item, param2 = action type (below), param3 = HWND of main window +#define ML_ACTION_RCLICK 0 // return value should be nonzero if ours +#define ML_ACTION_DBLCLICK 1 +#define ML_ACTION_ENTER 2 +#define ML_ACTION_LCLICK 3 + +#define ML_MSG_TREE_ONDROPTARGET 0x102 // param1 = param of tree item, param2 = type of drop (ML_TYPE_*), param3 = pointer to data (or NULL if querying). + // return -1 if not allowed, 1 if allowed, or 0 if not our tree item + +#define ML_MSG_TREE_ONDRAG 0x103 // param1 = param of tree item, param2 = POINT * to the mouse position, and param3 = (int *) to the type + // set *(int*)param3 to the ML_TYPE you want and return 1, if you support drag&drop, or -1 to prevent d&d. + +#define ML_MSG_TREE_ONDROP 0x104 // param1 = param of tree item, param2 = POINT * to the mouse position + // if you support dropping, send the appropriate ML_IPC_HANDLEDROP using SendMessageW() and return 1, otherwise return -1. + +#define ML_MSG_TREE_ONKEYDOWN 0x105 // Send when key pressed + // param1 = param of tree item; + // param2 = pointer to NMTVKEYDOWN + // param3 = tree hwnd + // return 0 if it's not yours, 1 if it is + +#define ML_MSG_TREE_END 0x1FF // end of tree specific messages + +#define ML_MSG_ONSENDTOBUILD 0x300 // you get sent this when the sendto menu gets built +// param1 = type of source, param2 param to pass as context to ML_IPC_ADDTOSENDTO +// be sure to return 0 to allow other plugins to add their context menus + +// if your sendto item is selected, you will get this with your param3 == your user32 (preferably some +// unique identifier (like your plugin message proc). See ML_IPC_ADDTOSENDTO +#define ML_MSG_ONSENDTOSELECT 0x301 +// param1 = type of source, param2 = data, param3 = user32 + +// param1 = write type - added 5.7 +// this relates to IPC_WRITECONFIG (see wa_ipc.h) for values sent via 'param1' +#define ML_MSG_WRITE_CONFIG 0x397 + +// param1 = wchar_t* of the file filepath of the file when playback begins - added 5.7 +// this relates to IPC_PLAYING_FILEW (see wa_ipc.h) without the need to subclass Winamp +#define ML_MSG_PLAYING_FILE 0x398 + +// return TRUE to block the closing of Winamp (relates to IPC_HOOK_OKTOQUIT) - added 5.64+ +#define ML_MSG_NOTOKTOQUIT 0x399 + +// return TRUE and do a config dialog using param1 as a HWND parent for this one +#define ML_MSG_CONFIG 0x400 + +// return TRUE to allow the plug-in config button to be disabled as applicable +#define ML_MSG_NO_CONFIG 0x401 + +// added from 5.64+ (internal usage, do not re-use this message range unless allowed) +#define ML_MSG_CLOUD_MIN 0x402 +#define ML_MSG_CLOUD_MAX 0x410 + +// added from 5.66+ (used for some easier plug-in hooking of the views +#define ML_MSG_VIEW_BUTTON_HOOK 0x420 // param1 = hwndDlg, param2 = MAKELONG(button_id_to_use, button_id_to_follow), param3 = plug-in id e.g. "ml_history". return text to show on the button +#define ML_MSG_VIEW_BUTTON_HOOK_IN_USE 0x421 // param1 = current 'play' / 'enqueue' mode +#define ML_MSG_VIEW_PLAY_ENQUEUE_CHANGE 0x422 // param1 = new state of 'play' / 'enqueue' mode, param2 = new state of 'button grouping mode' + // these can be also read from gen_ml.ini and the [gen_ml_config] using 'enqueuedef' and 'groupbtn' respectively + // note: you can use IPC_GETMLINIFILEW from wa_ipc.h to get the gen_ml.ini path +typedef struct +{ + wchar_t* play; + wchar_t* enqueue; +} viewButtons; + +#define ML_IPC_GET_VIEW_BUTTON_TEXT 0x423 // pass viewButtons as the param and gen_ml will fill in appropriate 'play' and 'enqueue' button text to use (saves duplicating translations) + +#define ML_MSG_DOWNLOADS_VIEW_LOADED 0x424 // returns TRUE by ml_downloads.dll if loaded and it's view is enabled (used internally) +#define ML_MSG_DOWNLOADS_VIEW_POSITION 0x425 // param1 = parent param2 = 1 (always ensure param2 is set as 1) +// end of those added for 5.66 + +// types for drag and drop +#define ML_TYPE_UNKNOWN -1 +#define ML_TYPE_ITEMRECORDLIST 0 // if this, cast obj to itemRecordList +#define ML_TYPE_FILENAMES 1 // double NULL terminated char * to files, URLS, or playlists +#define ML_TYPE_STREAMNAMES 2 // double NULL terminated char * to URLS, or playlists ( effectively the same as ML_TYPE_FILENAMES, but not for files) +#define ML_TYPE_CDTRACKS 3 // if this, cast obj to itemRecordList (CD tracks) -- filenames should be cda://,. artist/album/title might be valid (CDDB) +#define ML_TYPE_QUERYSTRING 4 // char * to a query string +#define ML_TYPE_PLAYLIST 5 // mlPlaylist * +#define ML_TYPE_ITEMRECORDLISTW 6 // if this, cast obj to itemRecordListW +// added from 5.36+ +#define ML_TYPE_FILENAMESW 7 // double NULL terminated wchar_t * to files, URLS, or playlists +#define ML_TYPE_STREAMNAMESW 8 // double NULL terminated wchar_t * to URLS, or playlists ( effectively the same as ML_TYPE_FILENAMESW, but not for files) +#define ML_TYPE_PLAYLISTS 9 // mlPlaylist **, null terminated +#define ML_TYPE_TREEITEM 69 // uhh? + +typedef struct +{ + const wchar_t *filename; + const wchar_t *title; + // only fill in the following two if already know. don't calculate it just for the struct. + // put -1 for "don't know" + int numItems; + int length; // in seconds. +} mlPlaylist; + +// if you wish to put your tree items under "devices", use this constant for ML_IPC_ADDTREEITEM +#define ML_TREEVIEW_ID_DEVICES 10000 + +// children communicate back to the media library by SendMessageW(plugin.hwndLibraryParent,WM_ML_IPC,param,ML_IPC_X); +#define WM_ML_IPC WM_USER+0x1000 + +#define ML_IPC_GETCURRENTVIEW 0x090 // Returns HWND to the currently selected view or NULL if nothing selected or error. (WA 5.22 and higher) + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Old Tree Item API (deprecated) +// +#define ML_IPC_ADDTREEITEM 0x0101 // pass mlAddTreeItemStruct as the param +#define ML_IPC_SETTREEITEM 0x0102 // pass mlAddTreeItemStruct with id valid +#define ML_IPC_DELTREEITEM 0x0103 // pass param of tree item to remove +#define ML_IPC_GETCURTREEITEM 0x0104 // returns current tree item param or 0 if none +#define ML_IPC_SETCURTREEITEM 0x0105 // selects the tree item passed, returns 1 if found, 0 if not +#define ML_IPC_GETTREE 0x0106 // returns a HMENU with all the tree items. the caller needs to delete the returned handle! pass mlGetTreeStruct as the param +/* +** This will produce a menu of every item in the media library left pane. +** mlGetTreeStruct mgts = { 0, 45000, -1 }; +** HMENU media_library_menu = (HMENU)SendMessageW(plugin.hwndLibraryParent,WM_ML_IPC,(WPARAM)&mgts,ML_IPC_GETTREE); +** if(media_library_menu){ +** // show or insert the menu as applicable +** } +** +** Whereas this will produce a menu just of the available media library playlists stored. +** mlGetTreeStruct mgts = { TREE_PLAYLISTS, 45000, -1 }; +** HMENU playlists_menu = (HMENU)SendMessageW(plugin.hwndLibraryParent,WM_ML_IPC,(WPARAM)&mgts,ML_IPC_GETTREE); +** if(playlists_menu){ +** // show or insert the menu as applicable +** } +*/ + +/* deprecated. Use MLTREEITEM instead */ +typedef struct +{ + INT_PTR parent_id; //0=root, or ML_TREEVIEW_ID_* + char *title; + int has_children; + INT_PTR this_id; //filled in by the library on ML_IPC_ADDTREEITEM +} mlAddTreeItemStruct; + +#define TREE_LOCALMEDIA 1000 +#define TREE_PLAYLISTS 3001 +#define TREE_QUERIES 1000 +typedef struct +{ + int item_start; // TREE_PLAYLISTS,... + int cmd_offset; // menu command offset if you need to make a command proxy, 0 otherwise + int max_numitems; // maximum number of items you wish to insert or -1 for no limit +} mlGetTreeStruct; + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +/// For Predixis, with Love +/// deprecated (never use!!!) +/// +#define ML_IPC_ADDTREEITEM_EX 0x0111 // pass mlAddTreeItemStructEx as the param +#define ML_IPC_SETTREEITEM_EX 0x0112 // pass mlAddTreeItemStructEx with this_id valid + +typedef struct { + INT_PTR parent_id; //0=root, or ML_TREEVIEW_ID_* + char *title; + int has_children; + INT_PTR this_id; //filled in by the library on ML_IPC_ADDTREEITEM + int imageIndex; // index of the image you want to be associated with your item +} mlAddTreeItemStructEx; + +/// deprecatded (never use!!!) +#define ML_IPC_ADDTREEIMAGE 0x110 // adds tree image to the ml. Use mlAddTreeImageStruct as the param. +/// end of predixis special + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Tree Item API (starting from 5.3) +// +#define ML_IPC_TREEITEM_GETHANDLE 0x120 // Gives you HANDLE to the item with specified ID in the param +#define ML_IPC_TREEITEM_GETCHILD 0x121 // Returns HANDLE to the child item for the item HANDLE specified as a param. +#define ML_IPC_TREEITEM_GETNEXT 0x122 // Returns HANDLE to the next item for the item HANDLE specified as a param. +#define ML_IPC_TREEITEM_GETSELECTED 0x123 // Returns HANDLE to selected item. +#define ML_IPC_TREEITEM_GETINFO 0x124 // Pass MLTREEITEMINFO as a param. return TRUE - if ok +#define ML_IPC_TREEITEM_SETINFO 0x125 // Pass MLTREEITEMINFO as a param. return TRUE - if ok +#define ML_IPC_TREEITEM_ADD 0x126 // Adds new item using MLTREEITEM passed as a param +#define ML_IPC_TREEITEM_DELETE 0x127 // Deletes tree item. Pass HANDLE as a param. +#define ML_IPC_TREEITEM_SELECT 0x128 // Selects tree item. Pass HANDLE as a param. +#define ML_IPC_TREEITEM_GETROOT 0x129 // Gets first item. +#define ML_IPC_TREEITEM_INSERT 0x130 // like ML_IPC_TREEITEM_ADD, but id becomes an "insert after" ID +#define ML_IPC_TREEITEM_GETCHILD_ID 0x131 // Returns ID to the child item for the item ID specified as a param. +#define ML_IPC_TREEITEM_GETNEXT_ID 0x132 // Returns ID to the next item for the item ID specified as a param. +#define ML_IPC_TREEITEM_ADDW 0x133 // Adds new item using MLTREEITEMW passed as a param +#define ML_IPC_TREEITEM_INSERTW 0x134 // like ML_IPC_TREEITEM_ADDW, but id becomes an "insert after" ID +#define ML_IPC_TREEITEM_SETINFOW 0x135 // Pass MLTREEITEMINFOW as a param. return TRUE - if ok +#define ML_IPC_TREEITEM_GETINFOW 0x136 // Pass MLTREEITEMINFO as a param. return TRUE - if ok +#define MLTI_ROOT (INT_PTR)TVI_ROOT // can be used in ML_IPC_TREEITEM_GETCHILD + +typedef struct { + size_t size; // size of this struct + UINT_PTR id; // depends on contxext + UINT_PTR parentId; // 0 = root, or ML_TREEVIEW_ID_* + char *title; // pointer to the buffer contained item name. + size_t titleLen; // used for GetInfo + BOOL hasChildren; // TRUE - has children + int imageIndex; // index of the associated image +} MLTREEITEM; + +typedef struct { + MLTREEITEM item; // item data + UINT mask; // one or more of MLTI_* flags + UINT_PTR handle; // Handle to the item. If handle is NULL item->id will be used +} MLTREEITEMINFO; + +typedef struct { + size_t size; // size of this struct + UINT_PTR id; // depends on context + UINT_PTR parentId; // 0 = root, or ML_TREEVIEW_ID_* + wchar_t *title; // pointer to the buffer contained item name. + size_t titleLen; // used for GetInfo + BOOL hasChildren; // TRUE - has children + int imageIndex; // index of the associated image +} MLTREEITEMW; + +typedef struct { + MLTREEITEMW item; // item data + UINT mask; // one or more of MLTI_* flags + UINT_PTR handle; // Handle to the item. If handle is NULL item->id will be used +} MLTREEITEMINFOW; + +// Flags that used in the MLTREEITEMINFO struct +#define MLTI_CHILDREN TVIF_CHILDREN +#define MLTI_IMAGE TVIF_IMAGE +#define MLTI_TEXT TVIF_TEXT +#define MLTI_ID TVIF_PARAM + +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// Tree image (starting from 5.3) +// +#define ML_IPC_TREEIMAGE_ADD 0x140 // adds tree image to the ml. Use MLTREEIMAGE as the param. + +typedef struct _COLOR24 +{ + unsigned char rgbBlue; + unsigned char rgbGreen; + unsigned char rgbRed; +}COLOR24; // color struct + +typedef void (*BMPFILTERPROC)(const COLOR24*, const COLOR24*, COLOR24*); // color filter procedure +// you got two colors: color1 and color2 (usualy BG and FG colors) also you as a third parameter +// you get a pixel color value that you need (can) modify + +#define FILTER_NO ((BMPFILTERPROC)NULL) +#define FILTER_DEFAULT1 ((BMPFILTERPROC)1) +#define FILTER_DEFAULT2 ((BMPFILTERPROC)2) + +#define MLTREEIMAGE_NONE 0 +#define MLTREEIMAGE_DEFAULT 1 +#define MLTREEIMAGE_BRANCH 2 // calculates at the time +#define MLTREEIMAGE_BRANCH_EXPANDED 3 +#define MLTREEIMAGE_BRANCH_COLLAPSED 4 +#define MLTREEIMAGE_BRANCH_NOCHILD 5 + +typedef struct { + HINSTANCE hinst; // hInstance + int resourceId; // resource id + int imageIndex; // set image to specified index (specify -1 to get a new index back) + BMPFILTERPROC filterProc; // pointer to the filter proc to use or one of the FILTER_* + int width; // reserved + int height; // reserved +} MLTREEIMAGE; // basicly ml will read your reosurce when it will need to create your image + +#define ML_IPC_NEWPLAYLIST 0x107 // pass hwnd for dialog parent as param +#define ML_IPC_IMPORTPLAYLIST 0x108 // pass hwnd for dialog parent as param +#define ML_IPC_IMPORTCURRENTPLAYLIST 0x109 +#define ML_IPC_GETPLAYLISTWND 0x10A +#define ML_IPC_SAVEPLAYLIST 0x10B // pass hwnd for dialog parent as param +#define ML_IPC_OPENPREFS 0x10C + +#define ML_IPC_PLAY_PLAYLIST 0x010D // plays the playlist pointed to by the tree item passed, returns 1 if found, 0 if not +#define ML_IPC_LOAD_PLAYLIST 0x010E // loads the playlist pointed to by the tree item passed into the playlist editor, returns 1 if found, 0 if not + +#define ML_IPC_REFRESH_PREFS 0x10F // this doesn't belong in here + +/** ------------------ + ** ml_playlists + ** ------------------ */ + +#define PL_FLAG_SHOW 1 +#define PL_FLAG_SWITCH 2 +#define PL_FLAGS_IMPORT 4 // set to have ml_playlists make a copy (only valid for mlAddPlaylist) +#define PL_FLAG_FILL_FILENAME 8 // only valid for mlMakePlaylist + +typedef struct +{ + size_t size; // size of this struct + const wchar_t *playlistName; // set to NULL (or empty string) to prompt the user for a name + const wchar_t *filename; + int flags; // see PL_FLAG_* above + // the following two items can be optionally filled in (set to -1 otherwise) + // if they aren't set, the playlist file will have to be opened and parsed + // so prepopulating is faster (assuming if you already know the data) + int numItems; // set to -1 if you don't know. + int length; // in seconds, set to -1 if you don't know +} mlAddPlaylist; + +#define ML_IPC_PLAYLIST_ADD 0x180 // call to add a new playlist file to the Playlists treeview. pass an mlAddPlaylist * + +typedef struct +{ + size_t size; // size of this struct + const wchar_t *playlistName; // set to NULL (or empty string) to prompt the user for a name + int type; //ML_TYPE_ITEMRECORDLIST, etc + void *data; // object to load + int flags; // see PL_FLAG_* above + wchar_t filename[MAX_PATH]; // this will get populated if PL_FLAG_FILL_NAME is set +} mlMakePlaylistV2; + +// old structure, here to make it easy to do a sizeof() check +typedef struct +{ + size_t size; // size of this struct + const wchar_t *playlistName; // set to NULL (or empty string) to prompt the user for a name + int type; //ML_TYPE_ITEMRECORDLIST, etc + void *data; // object to load + int flags; // see PL_FLAG_* above +} mlMakePlaylist; + +/* Call to add a new playlist to the Playlists treeview. + It will be automatically created based on the data you pass + type & data follow the same specifications as send-to, drag-and-drop, etc. +*/ +#define ML_IPC_PLAYLIST_MAKE 0x181 // call to create a new playlist in the treeview based on passed information. pass an mlMakePlaylist * +#define ML_IPC_PLAYLIST_COUNT 0x182 +#define ML_IPC_PLAYLIST_INFO 0x183 // pass in the struct below. returns "1" on success and "0" on failure + +typedef struct +{ + // you fill this in + size_t size; // size of this struct + size_t playlistNum; // number of the playlist you want to retrieve (0 index) + // ml_playlists fills these in + wchar_t playlistName[128]; + wchar_t filename[MAX_PATH]; + int numItems; + int length; // in seconds +} mlPlaylistInfo; + +/** ------------------ + ** + ** ------------------ */ + +#define ML_IPC_GETFILEINFO 0x0200 // pass it a &itemRecord with a valid filename (and all other fields NULL), and it will try to fill in the rest +#define ML_IPC_FREEFILEINFO 0x0201 // pass it a &itemRecord that was filled by getfileinfo, it will free the strings it allocated +// added for 5.58+ +#define ML_IPC_GETFILEINFOW 0x0202 // pass it a &itemRecordW with a valid filename (and all other fields NULL), and it will try to fill in the rest +#define ML_IPC_FREEFILEINFOW 0x0203 // pass it a &itemRecordW that was filled by getfileinfo, it will free the strings it allocated + +#define ML_IPC_HANDLEDRAG 0x0300 // pass it a &mlDropItemStruct it will handle cursors etc (unless flags has the lowest bit set), and it will set result appropriately: +#define ML_IPC_HANDLEDROP 0x0301 // pass it a &mlDropItemStruct with data on drop: + +#define ML_IPC_SENDTOWINAMP 0x0302 // send with a mlSendToWinampStruct: +typedef struct { + int type; //ML_TYPE_ITEMRECORDLIST, etc + void *data; // object to play + + int enqueue; // low bit set specifies enqueuing, and second bit NOT set specifies that + // the media library should use its default behavior as the user configured it (if + // enqueue is the default, the low bit will be flipped by the library) +} mlSendToWinampStruct; + +typedef struct { + char *desc; // str (addition 5.64+ - set as L"-" to create a separator and begin with a # to show as grayed) + intptr_t context; // context passed by ML_MSG_ONSENDTOBUILD + intptr_t user32; // use some unique ptr in memory, you will get it back in ML_MSG_ONSENDTOSELECT... +} mlAddToSendToStruct; +#define ML_IPC_ADDTOSENDTO 0x0400 + +typedef struct { + wchar_t *desc; // str (addition 5.64+ - set as L"-" to create a separator and begin with a # to show as grayed) + intptr_t context; // context passed by ML_MSG_ONSENDTOBUILD + intptr_t user32; // use some unique ptr in memory, you will get it back in ML_MSG_ONSENDTOSELECT... +} mlAddToSendToStructW; +#define ML_IPC_ADDTOSENDTOW 0x0401 // pass mlAddToSendToStructW + +// used to make a submenu in sendto +// pass mlAddToSendToStructW, set desc to 0 to start, set valid desc to end +// user32 is unused +// note: the 5.64+ additions for separators and greyed items do not apply to the branch node +#define ML_IPC_BRANCHSENDTO 0x0402 + +// follow same rules as ML_IPC_ADDTOSENDTOW, but adds to branch instead of main send-to menu +#define ML_IPC_ADDTOBRANCH 0x0403 // pass mlAddToSendToStructW + +#define ML_IPC_HOOKTITLE 0x0440 // this is like winamp's IPC_HOOK_TITLES... :) param1 is waHookTitleStruct +#define ML_IPC_HOOKEXTINFO 0x0441 // called on IPC_GET_EXTENDED_FILE_INFO_HOOKABLE, param1 is extendedFileInfoStruct +#define ML_IPC_HOOKEXTINFOW 0x0442 // called on IPC_GET_EXTENDED_FILE_INFO_HOOKABLEW, param1 is extendedFileInfoStructW +#define ML_IPC_HOOKTITLEW 0x0443 // this is like winamp's IPC_HOOK_TITLESW... :) param1 is waHookTitleStructW + +#define ML_HANDLEDRAG_FLAG_NOCURSOR 1 +#define ML_HANDLEDRAG_FLAG_NAME 2 + +typedef struct { + int type; //ML_TYPE_ITEMRECORDLIST, etc + void *data; // NULL if just querying + + int result; // filled in by client: -1 if dont allow, 0 if dont know, 1 if allow. + + POINT p; // cursor pos in screen coordinates + int flags; + + char *name; // only valid if ML_HANDLEDRAG_FLAG_NAME +} mlDropItemStruct; + +#define ML_IPC_SKIN_LISTVIEW 0x0500 // pass the hwnd of your listview. returns a handle to use with ML_IPC_UNSKIN_LISTVIEW +#define ML_IPC_UNSKIN_LISTVIEW 0x0501 // pass the handle you got from ML_IPC_SKIN_LISTVIEW +#define ML_IPC_LISTVIEW_UPDATE 0x0502 // pass the handle you got from ML_IPC_SKIN_LISTVIEW +#define ML_IPC_LISTVIEW_DISABLEHSCROLL 0x0503 // pass the handle you got from ML_IPC_SKIN_LISTVIEW +#define ML_IPC_LISTVIEW_DISABLEVSCROLL 0x050A // pass the handle you got from ML_IPC_SKIN_LISTVIEW +#define ML_IPC_LISTVIEW_SHOWSORT 0x0504 // use LV_SKIN_SHOWSORT +#define ML_IPC_LISTVIEW_SORT 0x0505 // use LV_SKIN_SORT + +typedef struct +{ + INT_PTR listView; + BOOL showSort; +} LV_SKIN_SHOWSORT; + +typedef struct +{ + INT_PTR listView; + int columnIndex; + BOOL ascending; +} LV_SKIN_SORT; + + +#define ML_IPC_SKIN_COMBOBOX 0x0508 // pass the hwnd of your combobox to skin, returns a ahndle to use with ML_IPC_UNSKIN_COMBOBOX +#define ML_IPC_UNSKIN_COMBOBOX 0x0509 // pass the handle from ML_IPC_SKIN_COMBOBOX + +#define ML_IPC_SKIN_WADLG_GETFUNC 0x0600 + // 1: return int (*WADlg_getColor)(int idx); // pass this an index, returns a RGB value (passing 0 or > 3 returns NULL) + // 2: return int (*WADlg_handleDialogMsgs)(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); + // 3: return void (*WADlg_DrawChildWindowBorders)(HWND hwndDlg, int *tab, int tabsize); // each entry in tab would be the id | DCW_* + // 4: return HBITMAP (*WADlg_getBitmap)(); // a copy of the current skin's genex.bmp + // 32: return void (*childresize_init)(HWND hwndDlg, ChildWndResizeItem *list, int num); + // 33: return void (*childresize_resize)(HWND hwndDlg, ChildWndResizeItem *list, int num); + // 66: return (HFONT) font to use for dialog elements, if desired (0 otherwise) + +// itemRecord type for use with ML_TYPE_ITEMRECORDLIST, as well as many other functions +typedef struct +{ + char *filename; + char *title; + char *ext; + char *album; + char *artist; + char *comment; + char *genre; + int year; + int track; + int length; + char **extended_info; + // currently defined extended columns (while they are stored internally as integers + // they are passed using extended_info as strings): + // use getRecordExtendedItem and setRecordExtendedItem to get/set. + // for your own internal use, you can set other things, but the following values + // are what we use at the moment. Note that setting other things will be ignored + // by ML_IPC_DB*. + // + //"RATING" file rating. can be 1-5, or 0 or empty for undefined + //"PLAYCOUNT" number of file plays. + //"LASTPLAY" last time played, in standard time_t format + //"LASTUPD" last time updated in library, in standard time_t format + //"FILETIME" last known file time of file, in standard time_t format + //"FILESIZE" last known file size, in kilobytes. + //"BITRATE" file bitrate, in kbps + //"TYPE" - "0" for audio, "1" for video + // TODO list all of the other values available for completeness between this and itemRecordW + //"ISPODCAST", + //"PODCASTCHANNEL", + //"PODCASTPUBDATE", + //"LOSSLESS", + //"CODEC", + //"DIRECTOR", + //"PRODUCER", + //"WIDTH", + //"HEIGHT", + //"MIME", + // TODO END + //"REALSIZE" last known file size, in bytes (provides a 64-bit integer as a string due to 'filesize being limited to 32-bit). + //"DATEADDED" time the file was added to the library, in standard time_t format +} itemRecord; + +typedef struct +{ + itemRecord *Items; + int Size; + int Alloc; +} itemRecordList; + +#include + +typedef struct +{ + wchar_t *key; + wchar_t *value; +} extendedInfoW; + +/* sizeof(itemRecordW) should be 128 (on x86). If you use a compiler other than +Microsoft Visual Studio, it's worth checking to make sure it's the same */ +typedef struct +{ + wchar_t *filename; + wchar_t *title; + wchar_t *ext; + wchar_t *album; + wchar_t *artist; + wchar_t *comment; + wchar_t *genre; + wchar_t *albumartist; + wchar_t *replaygain_album_gain; // these are strings rather than float's to differentiate between '0 gain' and 'not defined' + wchar_t *replaygain_track_gain; // these are strings rather than float's to differentiate between '0 gain' and 'not defined' + wchar_t *publisher; + wchar_t *composer; + int year; + int track; + int tracks; + int length; + int rating; // file rating. can be 1-5, or 0 for undefined + int playcount; // number of file plays. + __time64_t lastplay; // last time played, in standard time_t format + __time64_t lastupd; // last time updated in library, in standard time_t format + __time64_t filetime; // last known file time of file, in standard time_t format + int filesize; // last known file size, in kilobytes. + int bitrate; // file bitrate, in kbps + int type; // 0 for audio, 1 for video + int disc; // disc number + int discs; // number of discs + int bpm; + extendedInfoW *extended_info; + // currently defined extended columns (while they are stored internally as integers + // they are passed using extended_info as strings): + // use getRecordExtendedItem and setRecordExtendedItem to get/set. + // for your own internal use, you can set other things, but the following values + // are what we use at the moment. Note that setting other things will be ignored + // by ML_IPC_DB*. + // + wchar_t *category; // 5.61+: we were able to sneak this in because of padding. it'll be zero on older versions because the structs get memset +} itemRecordW; + +typedef struct +{ + itemRecordW *Items; + int Size; + int Alloc; +} itemRecordListW; + +// +// all return 1 on success, -1 on error. or 0 if not supported, maybe? + +// pass these a mlQueryStruct +// results should be zeroed out before running a query, but if you wish you can run multiple queries and +// have it concatenate the results. tho it would be more efficient to just make one query that contains both, +// as running multiple queries might have duplicates etc. +// in general, though, if you need to treat "results" as if they are native, you should use +// copyRecordList to save a copy, then free the results using ML_IPC_DB_FREEQUERYRESULTS. +// if you need to keep an exact copy that you will only read (and will not modify), then you can +// use the one in the mlQueryStruct. + +#define ML_IPC_DB_RUNQUERY 0x0700 +#define ML_IPC_DB_RUNQUERY_SEARCH 0x0701 // "query" should be interpreted as keyword search instead of query string +#define ML_IPC_DB_RUNQUERY_FILENAME 0x0702 // searches for one exact filename match of "query" +#define ML_IPC_DB_RUNQUERY_INDEX 0x0703 // retrieves item #(int)query [deprecated and doesn't work] + +#define ML_IPC_DB_FREEQUERYRESULTS 0x0705 // frees memory allocated by ML_IPC_RUNQUERY (empties results) +typedef struct +{ + char *query; + int max_results; // can be 0 for unlimited + itemRecordList results; +} mlQueryStruct; + +/* Unicode versions of the above */ + +#define ML_IPC_DB_RUNQUERYW 0x1700 +#define ML_IPC_DB_RUNQUERY_SEARCHW 0x1701 // "query" should be interpreted as keyword search instead of query string +#define ML_IPC_DB_RUNQUERY_FILENAMEW 0x1702 // searches for one exact filename match of "query" +#define ML_IPC_DB_RUNQUERY_INDEXW 0x1703 // retrieves item #(int)query [deprecated and doesn't work] +#define ML_IPC_DB_FREEQUERYRESULTSW 0x1705 // frees memory allocated by ML_IPC_RUNQUERYW (empties results) +typedef struct +{ + wchar_t *query; + int max_results; // can be 0 for unlimited + itemRecordListW results; +} mlQueryStructW; + +/* ----------------------------- */ + +// pass these an (itemRecord *) to add/update. +// note that any NULL fields in the itemRecord won't be updated, +// and year, track, or length of -1 prevents updating as well. +#define ML_IPC_DB_UPDATEITEM 0x0706 // returns -2 if item not found in db +#define ML_IPC_DB_ADDORUPDATEITEM 0x0707 + +#define ML_IPC_DB_REMOVEITEM 0x0708 // pass a char * to the filename to remove. returns -2 if file not found in db. +// added in v5.58 +#define ML_IPC_DB_REMOVEITEMW 0x1708 // pass a wchar_t * to the filename to remove. returns -2 if file not found in db. + +typedef struct +{ + char* fileName; // file name to add + int meta_mode; // metadata get mode (0 - don't use metadata, 1 - use metadata; -1 - read from user settings (ini file) + int gues_mode; // metadata guessing mode (0 - smart, 1 - simple; 2 - no, -1 - read from user settings (ini file) +} LMDB_FILE_ADD_INFO; + +#define ML_IPC_DB_UPDATEFILE 0x0710 // Update File in the Local Media Data Base (return -2 if file record not found) +#define ML_IPC_DB_ADDORUPDATEFILE 0x0711 // Adds or Updates File in the Local Media Data Base. + +/* Unicode versions of the above */ + +// pass these an (itemRecordW *) to add/update. +// note that any NULL fields in the itemRecordW won't be updated, +// and year, track, or length of -1 prevents updating as well. +#define ML_IPC_DB_UPDATEITEMW 0x1706 // returns -2 if item not found in db +#define ML_IPC_DB_ADDORUPDATEITEMW 0x1707 + +typedef struct +{ + wchar_t* fileName; // file name to add + int meta_mode; // metadata get mode (0 - don't use metadata, 1 - use metadata; -1 - read from user settings (ini file) + int gues_mode; // metadata guessing mode (0 - smart, 1 - simple; 2 - no, -1 - read from user settings (ini file) +} LMDB_FILE_ADD_INFOW; + +#define ML_IPC_DB_UPDATEFILEW 0x1710 // Update File in the Local Media Data Base (return -2 if file record not found) NOTE that this call is broken on 5.33. Only use on 5.34+ +#define ML_IPC_DB_ADDORUPDATEFILEW 0x1711 // Adds or Updates File in the Local Media Data Base. + +/* ----------------------------- */ + +#define ML_IPC_DB_SYNCDB 0x0709 // sync db if dirty flags are set. good to do after a batch of updates. + +// these return 0 if unsupported, -1 if failed, 1 if succeeded + +// pass a winampMediaLibraryPlugin *. Will not call plugin's init() func. +// YOU MUST set winampMediaLibraryPlugin->hDllInstance to NULL, and version to MLHDR_VER +// 5.25+: You can set hDllInstance to valid value. +// This IPC will return -1 on failure, so a good check against old verions +// is to try with hDllInstance set, if it returns -1, try again with hDllInstance=0 +#define ML_IPC_ADD_PLUGIN 0x0750 +#define ML_IPC_REMOVE_PLUGIN 0x0751 // winampMediaLibraryPlugin * of plugin to remove. Will not call plugin's quit() func + +#define ML_IPC_SEND_PLUGIN_MESSAGE 0x0752 // sends message to plugins (wParam = 0, lParam = pointer to the pluginMessage struct) +// pluginMessage struct +typedef struct { + int messageType; + INT_PTR param1; + INT_PTR param2; + INT_PTR param3; +} pluginMessage; + +#define ML_IPC_ENSURE_VISIBLE 0x0753 // ensures that the media library is visible +#define ML_IPC_IS_VISIBLE 0x0754 // queries the current visibility status + +#define ML_IPC_GET_PARENTAL_RATING 0x0755 + +#define ML_IPC_TOGGLE_VISIBLE 0x0756 + +// 5.58+ to fix modern skin focusing issue +#define ML_IPC_FOCUS_TREE 0x0757 + +/* start of ML_CHILDIPC_* section */ + +// this gets sent to any child windows of the library windows, and then (if not handled) the library window itself +#define WM_ML_CHILDIPC WM_APP+ 0x800 // avoids conflicts with any windows controls + +// lParam = 0x100, wParam = &mlDropItemStruct +#define ML_CHILDIPC_DROPITEM 0x100 + +// notification only message sent via WM_ML_CHILDIPC when F6 (or the accelerator defined against id 40157) +// is pressed - use this to focus to the searchbar in the view if you have one (usability change for 5.55+) +// lParam == 0x200, wParam = 0 +#define ML_CHILDIPC_GO_TO_SEARCHBAR 0x200 + +// added 5.666+ +// notification only message sent via WM_ML_CHILDIPC when F8 (or the accelerator defined against id 40165) +// is pressed - use this to re-run the current search or to act as a way to refresh the current view results +// lParam == 0x300, wParam = 0 +#define ML_CHILDIPC_REFRESH_SEARCH 0x300 + +/* end of ML_CHILDIPC_* section */ + + +// current item ratings +#define ML_IPC_SETRATING 0x0900 // lParam = 0 to 5, rates current track -- inserts it in the db if it's not in it yet +#define ML_IPC_GETRATING 0x0901 // return the current track's rating or 0 if not in db/no rating + +// playlist entry rating +typedef struct { + int plentry; + int rating; +} pl_set_rating; + +#define ML_IPC_PL_SETRATING 0x0902 // lParam = pointer to pl_set_rating struct +#define ML_IPC_PL_GETRATING 0x0903 // lParam = playlist entry, returns the rating or 0 if not in db/no rating + +typedef struct { + HWND dialog_parent; // Use this window as a parent for the query editor dialog + const char *query; // The query to edit, or "" / null for new query +} ml_editquery; + +#define ML_IPC_EDITQUERY 0x0904 // lParam = pointer to ml_editquery struct, returns 0 if edition was canceled and 1 on success + // After returning, and if ok was clicked, the struct contains a pointer to the edited query. this pointer is static : + // - do *not* free it + // - if you need to keep it around, _strdup it, as it may be changed later by other plugins calling ML_IPC_EDITQUERY. + +typedef struct { + HWND dialog_parent; // Use this window as a parent for the view editor dialog + const char *query; // The query to edit, or "" / null for new views + const char *name; // Name of the view (ignored for new views) + int mode; // View mode (0=simple view, 1=artist/album view, -1=hide mode radio boxes) +} ml_editview; + +#define ML_IPC_EDITVIEW 0x0905 // lParam = pointer to ml_editview struct, returns 0 if edition was canceled and 1 on success + // After returning, and if ok was clicked, the struct contains the edited values. String pointers are static: + // - do *not* free them + // - if you need to keep them around, _strdup them, as they may be changed later by other plugins calling ML_IPC_EDITQUERY. + +#define ML_IPC_SET_FILE_RATING 0x0906 // lParam = 0 to 5, rates current track -- inserts it in the db if it's not in it yet +#define ML_IPC_GET_FILE_RATING 0x0907 // return the current track's rating or 0 if not in db/no rating + +// playlist entry rating +typedef struct { + const char* fileName; + int newRating; +} file_set_rating; + +#define ML_IPC_SMARTVIEW_COUNT 0x0908 // returns the number of smartviews. no parameter required +#define ML_IPC_SMARTVIEW_INFO 0x0909 // pass a mlSmartViewInfo*. returns 1 on success and 0 on failure +#define ML_IPC_SMARTVIEW_ADD 0x0910 // pass a mlSmartViewInfo* with filled in size, name, query, mode, iconImgIndex. treeitemid gets filled in. returns 1 on success and 0 on failure + +typedef struct +{ + // you fill these in + size_t size; // set to sizeof(mlSmartViewInfo) + size_t smartViewNum; + // ml_local fills these in + wchar_t smartViewName[128]; + wchar_t smartViewQuery[512]; + int mode; + int iconImgIndex; + int treeItemId; +} mlSmartViewInfo; + + +#define ML_IPC_SET_FILE_RATINGW 0x0911 // lParam = 0 to 5, rates current track -- inserts it in the db if it's not in it yet +#define ML_IPC_GET_FILE_RATINGW 0x0912 // return the current track's rating or 0 if not in db/no rating + +// playlist entry rating +typedef struct { + const wchar_t *fileName; + int newRating; +} file_set_ratingW; + + +// utility functions in ml_lib.cpp +void freeRecordList(itemRecordList *obj); +void emptyRecordList(itemRecordList *obj); // does not free Items +void freeRecord(itemRecord *p); + +// if out has items in it copyRecordList will append "in" to "out". +void copyRecordList(itemRecordList *out, const itemRecordList *in); +//copies a record +void copyRecord(itemRecord *out, const itemRecord *in); + +void allocRecordList(itemRecordList *obj, int newsize, int granularity +#ifdef __cplusplus +=1024 +#endif +); + +char *getRecordExtendedItem(const itemRecord *item, const char *name); +void setRecordExtendedItem(itemRecord *item, const char *name, char *value); + +#ifdef __cplusplus +// utility functions for itemRecordListW +void freeRecordList(itemRecordListW *obj); +void emptyRecordList(itemRecordListW *obj); // does not free Items +void freeRecord(itemRecordW *p); + +wchar_t *getRecordExtendedItem(const itemRecordW *item, const wchar_t *name); +void setRecordExtendedItem(itemRecordW *item, const wchar_t *name, const wchar_t *value); +void copyRecordList(itemRecordListW *out, const itemRecordListW *in); +void copyRecord(itemRecordW *out, const itemRecordW *in); +void allocRecordList(itemRecordListW *obj, int newsize, int granularity=1024); + +void convertRecord(itemRecord *output, const itemRecordW *input); +void convertRecord(itemRecordW *output, const itemRecord *input); +void convertRecordList(itemRecordList *output, const itemRecordListW *input); +void convertRecordList(itemRecordListW *output, const itemRecordList *input); +#endif + +#define ML_IPC_GRACENOTE 0x1000 +#define GRACENOTE_TUID 1 +#define GRACENOTE_IS_WORKING 2 +#define GRACENOTE_DO_TIMER_STUFF 3 +#define GRACENOTE_CANCEL_REQUEST 4 + +#define ML_IPC_FLICKERFIX 0x1002 /// param = (FLICKERFIX*)ffix; Returns 1 if succesfull. if window already treated will update flag data + +#define FFM_ERASE 0x00000000 // flicker treatment will be removed +#define FFM_FORCEERASE 0x01000000 // erase backgrund with speicfied color (WM_ERASEBKGND will fill hdc with color and return 1). Use mode = FFM_MODE | RGB(0,0,0) +#define FFM_NOERASE 0x02000000 // block erase operation ( WM_ERASEBKGND will return 1); +#define FFM_ERASEINPAINT 0x04000000 // forward erase operation to WM_PAINT ( WM_ERASEBKGND will return 0); + +typedef struct _FLICKERFIX +{ + HWND hwnd; // target hwnd + DWORD mode; // FFM_XXX; +}FLICKERFIX, *PFLICKERFIX; + +// resource id of the drag & drop cursor used by the ml plugins +// you can use the following to get the cursor rather than rebundling the same icon +// hDragNDropCursor = LoadCursor(GetModuleHandle("gen_ml.dll"), MAKEINTRESOURCE(ML_IDC_DRAGDROP)); +#define ML_IDC_DRAGDROP 107 + + +#endif//_ML_H_ \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/ml_cloud.cpp b/Src/Plugins/General/gen_ml/ml_cloud.cpp new file mode 100644 index 00000000..e8aee869 --- /dev/null +++ b/Src/Plugins/General/gen_ml/ml_cloud.cpp @@ -0,0 +1,66 @@ +#include "./ml_cloud.h" +#include + +BOOL MLCloudI_Draw( HDC hdc, INT value, HMLIMGLST hmlil, INT index, RECT *prc ) +{ + if ( !hdc || !hmlil || !prc ) + return FALSE; + + INT ilIndex = MLImageListI_GetRealIndex(hmlil, index, GetBkColor(hdc), GetTextColor(hdc)); + + if ( -1 != ilIndex ) + { + INT val = ( value < 0 ) ? 0 : value; + static IMAGELISTDRAWPARAMS ildp = { 56/*sizeof(IMAGELISTDRAWPARAMS)*/, 0, }; + + ildp.hdcDst = hdc; + ildp.himl = MLImageListI_GetRealList( hmlil ); + ildp.i = ilIndex; + ildp.x = prc->left; + ildp.y = prc->top; + ildp.rgbBk = CLR_DEFAULT; + ildp.rgbFg = CLR_DEFAULT; + ildp.fStyle = ILD_NORMAL; + ildp.dwRop = SRCCOPY; + + MLImageListI_GetImageSize( hmlil, &ildp.cx, &ildp.cy ); + + ildp.xBitmap = 0; + + if ( ildp.y < prc->top ) + { + ildp.yBitmap = prc->top - ildp.y; + ildp.y = prc->top; + } + else + ildp.yBitmap = 0; + + if ( ildp.cy > ( prc->bottom - ildp.y ) ) + ildp.cy = prc->bottom - ildp.y; + + if ( !val ) + ildp.xBitmap -= ildp.cx; + + if ( !( ildp.x < ( prc->left - ildp.cx ) ) ) + { + if ( prc->right < ( ildp.x + ildp.cx ) ) + ildp.cx = prc->right - ildp.x; + + ImageList_DrawIndirect( &ildp ); + } + } + + return TRUE; +} + +BOOL MLCloudI_CalcMinRect( HMLIMGLST hmlil, RECT *prc ) +{ + INT imageCX, imageCY; + + if ( !hmlil || !prc || !MLImageListI_GetImageSize( hmlil, &imageCX, &imageCY ) ) + return FALSE; + + SetRect( prc, 0, 0, imageCX + 2, imageCY ); + + return TRUE; +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/ml_cloud.h b/Src/Plugins/General/gen_ml/ml_cloud.h new file mode 100644 index 00000000..06384d10 --- /dev/null +++ b/Src/Plugins/General/gen_ml/ml_cloud.h @@ -0,0 +1,15 @@ +#ifndef NULLOSFT_MEDIALIBRARY_CLOUD_HEADER +#define NULLOSFT_MEDIALIBRARY_CLOUD_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include +#include "./ml_imagelist.h" + +// Draws Cloud Status based on CLOUDDRAWPARAMS +BOOL MLCloudI_Draw(HDC hdc, INT value, HMLIMGLST hmlil, INT index, RECT *prc); +BOOL MLCloudI_CalcMinRect(HMLIMGLST hmlil, RECT *prc); + +#endif //NULLOSFT_MEDIALIBRARY_CLOUD_HEADER \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/ml_cloudcolumn.cpp b/Src/Plugins/General/gen_ml/ml_cloudcolumn.cpp new file mode 100644 index 00000000..d8173ad4 --- /dev/null +++ b/Src/Plugins/General/gen_ml/ml_cloudcolumn.cpp @@ -0,0 +1,80 @@ +#include "main.h" +#include "./ml_cloudcolumn.h" +#include "./ml_cloud.h" +#include "api__gen_ml.h" +#include "./ml.h" +#include "./ml_IPC_0313.h" +#include "./resource.h" +#include "../winamp/gen.h" +#include "./stockobjects.h" +#include +#include + +extern HMLIMGLST hmlilCloud; + +#define CLOUD_IMAGELIST hmlilCloud + +#define CLOUD_LEFTPADDING 5 +#define CLOUD_RIGHTPADDING 4 + +static INT cloudMinWidth = 27; + +BOOL MLCloudColumnI_Initialize(void) +{ + RECT rc; + cloudMinWidth = ((MLCloudI_CalcMinRect(CLOUD_IMAGELIST, &rc)) ? (rc.right - rc.left) : 0); + return TRUE; +} + +INT MLCloudColumnI_GetMinWidth(void) +{ + return cloudMinWidth + CLOUD_LEFTPADDING + CLOUD_RIGHTPADDING; +} + +BOOL MLCloudColumnI_Paint(CLOUDCOLUMNPAINT_I *pRCPaint) +{ + RECT rc; + + rc.left = LVIR_BOUNDS; + rc.top = pRCPaint->iSubItem; + if (SendMessageW(pRCPaint->hwndList, LVM_GETSUBITEMRECT, pRCPaint->iItem, (LPARAM)&rc)) + { + if ((rc.right - rc.left - CLOUD_LEFTPADDING - CLOUD_RIGHTPADDING) >= cloudMinWidth && + (rc.left + CLOUD_LEFTPADDING) < (rc.right - CLOUD_RIGHTPADDING) && rc.top < rc.bottom) + { + INT left; + COLORREF rgbBkOld; + + if (rc.right <= pRCPaint->prcView->left || rc.left >= pRCPaint->prcView->right) return TRUE; + + rgbBkOld = SetBkColor(pRCPaint->hdc, pRCPaint->rgbBk); + + left = rc.left; + if (0 == rc.left) rc.left = 3; + ExtTextOutW(pRCPaint->hdc, 0, 0, ETO_OPAQUE, &rc, L"", 0, 0); + + INT value = pRCPaint->value; + if (value) + { + COLORREF rgbFgOld = SetTextColor(pRCPaint->hdc, pRCPaint->rgbFg); + rc.left = left + CLOUD_LEFTPADDING; + rc.right -= CLOUD_RIGHTPADDING; + MLCloudI_Draw(pRCPaint->hdc, value, CLOUD_IMAGELIST, (value - 1), &rc); + if (pRCPaint->rgbFg != rgbFgOld) SetTextColor(pRCPaint->hdc, rgbFgOld); + } + + if (pRCPaint->rgbBk != rgbBkOld) SetBkColor(pRCPaint->hdc, rgbBkOld); + return TRUE; + } + } + + return FALSE; +} + +INT MLCloudColumnI_GetWidth(INT width) +{ + INT minWidth = MLCloudColumnI_GetMinWidth(); + if (width < minWidth) width = minWidth; + if (width > minWidth) width = minWidth; + return width; +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/ml_cloudcolumn.h b/Src/Plugins/General/gen_ml/ml_cloudcolumn.h new file mode 100644 index 00000000..46bcc3c2 --- /dev/null +++ b/Src/Plugins/General/gen_ml/ml_cloudcolumn.h @@ -0,0 +1,34 @@ +#ifndef NULLOSFT_MEDIALIBRARY_CLOUD_COLUMN_HEADER +#define NULLOSFT_MEDIALIBRARY_CLOUD_COLUMN_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif +#include + +typedef struct _CLOUDCOLUMNPAINT_I +{ + HWND hwndList; // hwnd of the listview + HDC hdc; // hdc + UINT iItem; // item index + UINT iSubItem; // subitem index + INT value; // database cloud status (1=full,2=partial,3=unavail) + RECT *prcItem; // whole item rect (plvcd->nmcd.rc) + RECT *prcView; // client area size (you can get it at CDDS_PREPAINT in plvcd->nmcd.rc) + COLORREF rgbBk; // color to use as background (plvcd->clrTextBk) + COLORREF rgbFg; // color to use as foreground (plvcd->clrText) +} CLOUDCOLUMNPAINT_I; + +typedef struct _CLOUDBACKTEXT_I +{ + LPWSTR pszText; + INT cchTextMax; + INT nColumnWidth; // used if style is RCS_ALLIGN_CENTER or RCS_ALLIGN_RIGHT +} CLOUDBACKTEXT_I; + +BOOL MLCloudColumnI_Initialize(void); // call it before any other. You can call it any time something changed +BOOL MLCloudColumnI_Paint(CLOUDCOLUMNPAINT_I *pRCPaint); +INT MLCloudColumnI_GetMinWidth(void); +INT MLCloudColumnI_GetWidth(INT width); + +#endif // NULLOSFT_MEDIALIBRARY_CLOUD_COLUMN_HEADER \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/ml_ex/ex.rc b/Src/Plugins/General/gen_ml/ml_ex/ex.rc new file mode 100644 index 00000000..b5fa580c --- /dev/null +++ b/Src/Plugins/General/gen_ml/ml_ex/ex.rc @@ -0,0 +1,144 @@ +//Microsoft Developer Studio generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_VIEW_EX DIALOG DISCARDABLE 0, 0, 291, 274 +STYLE DS_CONTROL | WS_CHILD | WS_CLIPSIBLINGS | WS_CLIPCHILDREN +FONT 8, "MS Sans Serif" +BEGIN + CONTROL "List4",IDC_LIST,"SysListView32",LVS_REPORT | + LVS_SHOWSELALWAYS | LVS_OWNERDATA | WS_TABSTOP,0,13,289, + 246 + LTEXT "",IDC_STATUS,41,262,248,8 + LTEXT "Search:",IDC_STATIC,0,1,26,8 + EDITTEXT IDC_QUICKSEARCH,26,1,263,10,ES_AUTOHSCROLL | NOT + WS_BORDER + CONTROL "Config",IDC_BUTTON_CONFIG,"Button",BS_OWNERDRAW | + WS_TABSTOP,2,261,35,11 +END + +IDD_CONFIG DIALOG DISCARDABLE 0, 0, 200, 97 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Ex Configuration" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,75,76,50,14 + PUSHBUTTON "Cancel",IDCANCEL,143,76,50,14 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO DISCARDABLE +BEGIN + IDD_VIEW_EX, DIALOG + BEGIN + RIGHTMARGIN, 289 + END + + IDD_CONFIG, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 193 + TOPMARGIN, 7 + BOTTOMMARGIN, 90 + END +END +#endif // APSTUDIO_INVOKED + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////// +// French (France) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_FRA) +#ifdef _WIN32 +LANGUAGE LANG_FRENCH, SUBLANG_FRENCH +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE DISCARDABLE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE DISCARDABLE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE DISCARDABLE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Menu +// + +IDR_CONTEXTMENUS MENU DISCARDABLE +BEGIN + POPUP "TreeItemContext" + BEGIN + MENUITEM "About Ex..", ID_ABOUT + MENUITEM "Ex Configuration...", ID_CONFIG + END +END + +#endif // French (France) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/Src/Plugins/General/gen_ml/ml_ex/ml_ex.dsp b/Src/Plugins/General/gen_ml/ml_ex/ml_ex.dsp new file mode 100644 index 00000000..ed360f15 --- /dev/null +++ b/Src/Plugins/General/gen_ml/ml_ex/ml_ex.dsp @@ -0,0 +1,152 @@ +# Microsoft Developer Studio Project File - Name="ml_ex" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102 + +CFG=ml_ex - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "ml_ex.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "ml_ex.mak" CFG="ml_ex - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "ml_ex - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE "ml_ex - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +MTL=midl.exe +RSC=rc.exe + +!IF "$(CFG)" == "ml_ex - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "Release" +# PROP Intermediate_Dir "Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "ML_ex_EXPORTS" /YX /FD /c +# ADD CPP /nologo /MD /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "ML_ex_EXPORTS" /YX /FD /c +# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "NDEBUG" +# ADD RSC /l 0x40c /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib /nologo /dll /machine:I386 /out:"c:\progra~1\winamp\plugins\ml_ex.dll" /opt:nowin98 +# SUBTRACT LINK32 /pdb:none + +!ELSEIF "$(CFG)" == "ml_ex - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "Debug" +# PROP Intermediate_Dir "Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "ML_ex_EXPORTS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "ML_ex_EXPORTS" /YX /FD /GZ /c +# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32 +# ADD BASE RSC /l 0x40c /d "_DEBUG" +# ADD RSC /l 0x40c /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib /nologo /dll /debug /machine:I386 /out:"c:\progra~1\winamp\plugins\ml_ex.dll" /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "ml_ex - Win32 Release" +# Name "ml_ex - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\itemlist.cpp +# End Source File +# Begin Source File + +SOURCE=..\listview.cpp +# End Source File +# Begin Source File + +SOURCE=..\ml_lib.cpp +# End Source File +# Begin Source File + +SOURCE=.\view_ex.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=..\childwnd.h +# End Source File +# Begin Source File + +SOURCE=..\..\winamp\ipc_pe.h +# End Source File +# Begin Source File + +SOURCE=..\itemlist.h +# End Source File +# Begin Source File + +SOURCE=..\listview.h +# End Source File +# Begin Source File + +SOURCE=..\ml.h +# End Source File +# Begin Source File + +SOURCE=..\..\winamp\wa_dlg.h +# End Source File +# Begin Source File + +SOURCE=..\..\winamp\wa_ipc.h +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# Begin Source File + +SOURCE=.\ex.rc +# End Source File +# End Group +# End Target +# End Project diff --git a/Src/Plugins/General/gen_ml/ml_ex/ml_ex.dsw b/Src/Plugins/General/gen_ml/ml_ex/ml_ex.dsw new file mode 100644 index 00000000..19b4e015 --- /dev/null +++ b/Src/Plugins/General/gen_ml/ml_ex/ml_ex.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "ml_ex"=.\ml_ex.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### + diff --git a/Src/Plugins/General/gen_ml/ml_ex/resource.h b/Src/Plugins/General/gen_ml/ml_ex/resource.h new file mode 100644 index 00000000..12c9eb5c --- /dev/null +++ b/Src/Plugins/General/gen_ml/ml_ex/resource.h @@ -0,0 +1,62 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Developer Studio generated include file. +// Used by ex.rc +// +#define IDD_DIALOG1 101 +#define IDR_CONTEXTMENUS 102 +#define IDD_SEND_FILES 103 +#define IDD_CONFIG 104 +#define IDD_EDIT_INFO 105 +#define IDD_VIEW_IPOD 221 +#define IDD_VIEW_NJB 221 +#define IDD_VIEW_EX 221 +#define IDD_INIT 222 +#define IDC_PROGRESS1 1000 +#define IDC_STATUS 1001 +#define IDC_EDIT_ARTIST 1002 +#define IDC_PROGRESS2 1002 +#define IDC_CHECK_ARTIST 1003 +#define IDC_CHECK_TITLE 1004 +#define IDC_CHECK_ALBUM 1005 +#define IDC_QUICKSEARCH 1006 +#define IDC_CHECK_TRACK 1006 +#define IDC_CHECK_GENRE 1007 +#define IDC_CHECK_YEAR 1008 +#define IDC_EDIT_TITLE 1009 +#define IDC_EDIT_ALBUM 1010 +#define IDC_BUTTON_AUTODETECT 1010 +#define IDC_EDIT_TRACK 1011 +#define IDC_CHECK1 1011 +#define IDC_EDIT_GENRE 1012 +#define IDC_DETECTDUPS 1012 +#define IDC_EDIT_YEAR 1013 +#define IDC_CURTRACKNAME 1013 +#define IDC_BUTTON_PLAY 1016 +#define IDC_BUTTON_ENQUEUE 1017 +#define IDC_BUTTON_REFRESH 1018 +#define IDC_BUTTON_CONFIG 1018 +#define IDC_IPODSTATUS 1030 +#define IDC_LIST 1031 +#define IDC_COMBO1 1032 +#define IDC_COMBO2 1033 +#define IDC_TEXT_GENRE 1039 +#define IDC_TEXT_BITRATE 1040 +#define ID_IPODWND_REMOVEFROMIPOD 40001 +#define ID_IPODTREEITEMCONTEXT_ABOUTIPOD 40002 +#define ID_ABOUT 40002 +#define ID_IPODTREEITEMCONTEXT_IPODCONFIGURATION 40003 +#define ID_CONFIG 40003 +#define ID_IPODWND_EDITITEMSINFORMATIONS 40004 +#define ID_IPODWND_PLAYSELECTEDFILES 40013 +#define ID_IPODWND_ENQUEUESELECTEDFILES 40014 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 106 +#define _APS_NEXT_COMMAND_VALUE 40005 +#define _APS_NEXT_CONTROL_VALUE 1014 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/Src/Plugins/General/gen_ml/ml_ex/view_ex.cpp b/Src/Plugins/General/gen_ml/ml_ex/view_ex.cpp new file mode 100644 index 00000000..7a58971a --- /dev/null +++ b/Src/Plugins/General/gen_ml/ml_ex/view_ex.cpp @@ -0,0 +1,888 @@ +/* +** Copyright (C) 2003 Nullsoft, Inc. +** +** This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held +** liable for any damages arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, including commercial applications, and to +** alter it and redistribute it freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. +** If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +** +** 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +** +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#include +#include +#include +#include "../ml.h" +#include "resource.h" +#include "../listview.h" + +#include "../childwnd.h" +#include "../../winamp/wa_dlg.h" + +#include "../itemlist.h" + + +// configuration section in winamp.ini +#define CONFIG_SEC "ml_ex" + + +// columns in our big treeview +#define COL_ARTIST 0 +#define COL_TITLE 1 +#define COL_ALBUM 2 +#define COL_LENGTH 3 +#define COL_TRACK 4 +#define COL_GENRE 5 +#define COL_YEAR 6 +#define COL_FILENAME 7 + +// makes a NULL char * an empty string +#define MAKESAFE(x) ((x)?(x):"") + + +// yes, we could easily use an itemRecord / itemRecordList here instead of 'Song's, but the point of this example +// is to show how to integrate with some other native structure +typedef struct +{ + char *artist; + char *title; + char *album; + int songlen; // seconds? + int track_nr; + char *genre; + int year; + char *filename; +} Song; + + +// our leading crap reduction agent for use with sorting/etc +#define SKIP_THE_AND_WHITESPACE(x) { while (!isalnum(*x) && *x) x++; if (!_strnicmp(x,"the ",4)) x+=4; while (*x == ' ') x++; } + + +extern winampMediaLibraryPlugin plugin; +static int myParam; // param of our tree item +static C_ItemList m_songs, *m_songs_sorted; +static W_ListView m_list; +static HWND m_hwnd; +static HMENU m_context_menus; +static int m_skinlistview_handle; + + +void config(HWND parent); +void sortResults(); + +static void deleteSongPtr(Song *song) +{ + free(song->album); + free(song->artist); + free(song->title); + free(song->genre); + free(song->filename); + free(song); +} + +static void clearSongList() +{ + int i=m_songs.GetSize(); + while (i>0) + { + Song *song=(Song *)m_songs.Get(--i); + deleteSongPtr(song); + m_songs.Del(i); + } +} + +// this doesnt actually alloc the memory for all the strings, just references them (so it is only temporarily valid at best) +void SongsToItemList(itemRecordList *p, int all) +{ + if (!m_hwnd) all=1; + + p->Alloc=p->Size=0; + p->Items=0; + + C_ItemList *list=(C_ItemList *)m_songs_sorted; + if (!list) { list=&m_songs; all=1; } + + int x,l=list->GetSize(); + for (x = 0 ; x < l; x ++) + { + if (!all && !m_list.GetSelected(x)) continue; + + allocRecordList(p,p->Size+1,256); + if (!p->Items) break; + + Song *s=(Song *)list->Get(x); + + memset(&p->Items[p->Size],0,sizeof(itemRecord)); + p->Items[p->Size].album=s->album; + p->Items[p->Size].artist=s->artist; + p->Items[p->Size].title=s->title; + p->Items[p->Size].genre=s->genre; + p->Items[p->Size].filename=s->filename; + p->Items[p->Size].track=s->track_nr; + p->Items[p->Size].year=s->year; + p->Items[p->Size].length=s->songlen; + p->Size++; + } +} + + +static void playFiles(int enqueue, int all) +{ + if (!m_songs_sorted) return; + if (!m_hwnd && !all) return; + + itemRecordList obj={0,}; + SongsToItemList(&obj,all); + if (obj.Size) + { + mlSendToWinampStruct s={ML_TYPE_ITEMRECORDLIST,(void*)&obj,!!enqueue}; + SendMessage(plugin.hwndLibraryParent,WM_ML_IPC,(WPARAM)&s,ML_IPC_SENDTOWINAMP); + } + free(obj.Items); +} + + +void addItemListToSongs(itemRecordList *p) +{ + if (p) for (int x = 0 ; x < p->Size; x ++) + { + Song *s=(Song *)calloc(1,sizeof(Song)); + if (p->Items[x].album) s->album=_strdup(p->Items[x].album); + if (p->Items[x].artist) s->artist=_strdup(p->Items[x].artist); + if (p->Items[x].title) s->title=_strdup(p->Items[x].title); + if (p->Items[x].genre) s->genre=_strdup(p->Items[x].genre); + if (p->Items[x].filename) s->filename=_strdup(p->Items[x].filename); + s->track_nr=p->Items[x].track; + s->year=p->Items[x].year; + s->songlen=p->Items[x].length; + m_songs.Add((void*)s); + } +} + +char *conf_file; +int init() { + mlAddTreeItemStruct mla={ + 0, // if you used 0, it would put it on top level, or ML_TREEVIEW_ID_DEVICES + "Item Cache Example", + 1, + }; + conf_file=(char*)SendMessage(plugin.hwndWinampParent,WM_WA_IPC,0,IPC_GETINIFILE); // get winamp.ini name :) + + SendMessage(plugin.hwndLibraryParent,WM_ML_IPC,(WPARAM)&mla,ML_IPC_ADDTREEITEM); + myParam=mla.this_id; + + m_context_menus=LoadMenu(plugin.hDllInstance,MAKEINTRESOURCE(IDR_CONTEXTMENUS)); + return 0; +} + +void quit() +{ + clearSongList(); +} + +void loadSongList() +{ + clearSongList(); + + // populate m_songs from whatever source we have + Song *p=(Song *)calloc(sizeof(Song),1); + p->filename = _strdup("http://www.firehose.net/~deadbeef/media/Misc/music/030223%20-%20pervert-in-a-satellite.mp3"); + p->album=_strdup("SEP"); + p->artist=_strdup("Nullsoft Band"); + p->genre=_strdup("Shit"); + p->songlen = 666; + p->track_nr=1; + p->title=_strdup("Pervert In A Satellite"); + p->year=2003; + + m_songs.Add((void*)p); +} + + +// this is uberadvancedsearchtechnology[tm] +static void parsequicksearch(char *out, char *in) // parses a list into a list of terms that we are searching for +{ + int inquotes=0, neednull=0; + while (*in) + { + char c=*in++; + if (c != ' ' && c != '\t' && c != '\"') + { + neednull=1; + *out++=c; + } + else if (c == '\"') + { + inquotes=!inquotes; + if (!inquotes) + { + *out++=0; + neednull=0; + } + } + else + { + if (inquotes) *out++=c; + else if (neednull) + { + *out++=0; + neednull=0; + } + } + } + *out++=0; + *out++=0; +} + +static int in_string(char *string, char *substring) +{ + if (!string) return 0; + if (!*substring) return 1; + int l=strlen(substring); + while (string[0]) if (!_strnicmp(string++,substring,l)) return 1; + return 0; +} + + +static void updateList() +{ + if(!m_hwnd) return; + + char filterstr[256],filteritems[300]; + GetDlgItemText(m_hwnd,IDC_QUICKSEARCH,filterstr,sizeof(filterstr)-1); + parsequicksearch(filteritems,filterstr); + + delete m_songs_sorted; + m_songs_sorted=new C_ItemList; + unsigned int totallen=0,filterlen=0,filterval=0; + for(int i=0;isonglen; + char year[32]=""; + if (s->year < 5000 && s->year > 0) sprintf(year,"%d",s->year); + char *p=filteritems; + if (*p) + { + while (*p) + { + // search for 'p' in the song + if (!in_string(s->album,p) && !in_string(s->artist,p) && !in_string(s->title,p) && !in_string(s->genre,p) && !in_string(year,p)) + break; + + p+=strlen(p)+1; + } + if (*p) continue; + } + filterval++; + filterlen+=s->songlen; + m_songs_sorted->Add((void *)s); + } + + sortResults(); + + char tmp[512]; + if (m_songs.GetSize() != m_songs_sorted->GetSize()) + wsprintf(tmp,"Found: %d items [%d:%02d:%02d]", + m_songs_sorted->GetSize(),filterval, + filterlen/3600,(filterlen/60)%60,filterlen%60); + else + wsprintf(tmp,"%d items [%d:%02d:%02d]",m_songs.GetSize(),totallen/3600,(totallen/60)%60,totallen%60); + + SetDlgItemText(m_hwnd,IDC_STATUS,tmp); +} + + +static ChildWndResizeItem resize_rlist[]={ + {IDC_QUICKSEARCH,0x0010}, + {IDC_LIST,0x0011}, + {IDC_BUTTON_CONFIG,0x0101}, + {IDC_STATUS,0x0111} +}; + + + +int g_sortcol, g_sortdir; +static int STRCMP_NULLOK(const char *pa, const char *pb) +{ + if (!pa) pa=""; + else SKIP_THE_AND_WHITESPACE(pa) + + if (!pb) pb=""; + else SKIP_THE_AND_WHITESPACE(pb) + + return _stricmp(pa,pb); +} + +static int sortFunc(const void *elem1, const void *elem2) +{ + Song *a=(Song *)*(void **)elem1; + Song *b=(Song *)*(void **)elem2; + + int use_by=g_sortcol; + int use_dir=g_sortdir; + +#define RETIFNZ(v) if ((v)<0) return use_dir?1:-1; if ((v)>0) return use_dir?-1:1; + + // this might be too slow, but it'd be nice + int x; + for (x = 0; x < 4; x ++) + { + if (use_by == COL_YEAR) // year -> artist -> album -> track + { + int v1=a->year; + int v2=b->year; + if (v1<0)v1=0; + if (v2<0)v2=0; + RETIFNZ(v1-v2) + use_by=COL_ARTIST; + } + else if (use_by == COL_TITLE) // title -> artist -> album -> track + { + int v=STRCMP_NULLOK(a->title,b->title); + RETIFNZ(v) + use_by=COL_ARTIST; + } + else if (use_by == COL_ARTIST) // artist -> album -> track -> title + { + int v=STRCMP_NULLOK(a->artist,b->artist); + RETIFNZ(v) + use_by=COL_ALBUM; + } + else if (use_by == COL_ALBUM) // album -> track -> title -> artist + { + int v=STRCMP_NULLOK(a->album,b->album); + RETIFNZ(v) + use_dir=0; + use_by=COL_TRACK; + } + else if (use_by == COL_GENRE) // genre -> artist -> album -> track + { + int v=STRCMP_NULLOK(a->genre,b->genre); + RETIFNZ(v) + use_by=COL_ARTIST; + } + else if (use_by == COL_TRACK) // track -> title -> artist -> album + { + int v1=a->track_nr; + int v2=b->track_nr; + if (v1<0)v1=0; + if (v2<0)v2=0; + RETIFNZ(v1-v2) + use_by=COL_TITLE; + } + else if (use_by == COL_LENGTH) // length -> artist -> album -> track + { + int v1=a->songlen; + int v2=b->songlen; + if (v1<0)v1=0; + if (v2<0)v2=0; + RETIFNZ(v1-v2) + use_by=COL_ARTIST; + } + else break; // no sort order? + } +#undef RETIFNZ + return 0; +} + + + +void sortResults() +{ + if (!m_songs_sorted) return; + qsort(m_songs_sorted->GetAll(),m_songs_sorted->GetSize(),sizeof(void*),sortFunc); + + ListView_SetItemCount(m_list.getwnd(),0); + ListView_SetItemCount(m_list.getwnd(),m_songs_sorted->GetSize()); + ListView_RedrawItems(m_list.getwnd(),0,m_songs_sorted->GetSize()-1); +} + +int (*wad_getColor)(int idx); +int (*wad_handleDialogMsgs)(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); +void (*wad_DrawChildWindowBorders)(HWND hwndDlg, int *tab, int tabsize); +void (*cr_init)(HWND hwndDlg, ChildWndResizeItem *list, int num); +void (*cr_resize)(HWND hwndDlg, ChildWndResizeItem *list, int num); + + +static BOOL CALLBACK dlgproc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam) +{ + if (wad_handleDialogMsgs) + { + BOOL a=wad_handleDialogMsgs(hwndDlg,uMsg,wParam,lParam); if (a) return a; + } + switch (uMsg) + { + case WM_DISPLAYCHANGE: + ListView_SetTextColor(m_list.getwnd(),wad_getColor?wad_getColor(WADLG_ITEMFG):RGB(0xff,0xff,0xff)); + ListView_SetBkColor(m_list.getwnd(),wad_getColor?wad_getColor(WADLG_ITEMBG):RGB(0x00,0x00,0x00)); + ListView_SetTextBkColor(m_list.getwnd(),wad_getColor?wad_getColor(WADLG_ITEMBG):RGB(0x00,0x00,0x00)); + m_list.refreshFont(); + return 0; + case WM_INITDIALOG: + m_hwnd=hwndDlg; + + *(void **)&wad_getColor=(void*)SendMessage(plugin.hwndLibraryParent,WM_ML_IPC,1,ML_IPC_SKIN_WADLG_GETFUNC); + *(void **)&wad_handleDialogMsgs=(void*)SendMessage(plugin.hwndLibraryParent,WM_ML_IPC,2,ML_IPC_SKIN_WADLG_GETFUNC); + *(void **)&wad_DrawChildWindowBorders=(void*)SendMessage(plugin.hwndLibraryParent,WM_ML_IPC,3,ML_IPC_SKIN_WADLG_GETFUNC); + + *(void **)&cr_init=(void*)SendMessage(plugin.hwndLibraryParent,WM_ML_IPC,32,ML_IPC_SKIN_WADLG_GETFUNC); +// woof: *(void **)&cr_resize=(void*)SendMessage(plugin.hwndLibraryParent,WM_ML_IPC,33,ML_IPC_SKIN_WADLG_GETFUNC); + + if (cr_init) cr_init(hwndDlg,resize_rlist,sizeof(resize_rlist)/sizeof(resize_rlist[0])); + + m_list.setLibraryParentWnd(plugin.hwndLibraryParent); + m_list.setwnd(GetDlgItem(hwndDlg,IDC_LIST)); + m_list.AddCol("Artist",200); + m_list.AddCol("Title",200); + m_list.AddCol("Album",200); + m_list.AddCol("Length",64); + m_list.AddCol("Track #",64); + m_list.AddCol("Genre",100); + m_list.AddCol("Year",64); + m_list.AddCol("Filename",80); + ListView_SetTextColor(m_list.getwnd(),wad_getColor?wad_getColor(WADLG_ITEMFG):RGB(0xff,0xff,0xff)); + ListView_SetBkColor(m_list.getwnd(),wad_getColor?wad_getColor(WADLG_ITEMBG):RGB(0x00,0x00,0x00)); + ListView_SetTextBkColor(m_list.getwnd(),wad_getColor?wad_getColor(WADLG_ITEMBG):RGB(0x00,0x00,0x00)); + + g_sortdir=GetPrivateProfileInt(CONFIG_SEC,"sortdir",0,conf_file); + g_sortcol=GetPrivateProfileInt(CONFIG_SEC,"sortcol",g_sortcol,conf_file); + + m_skinlistview_handle=SendMessage(plugin.hwndLibraryParent,WM_ML_IPC,(int)m_list.getwnd(),ML_IPC_SKIN_LISTVIEW); + + SetTimer(hwndDlg,32,50,NULL); + + return 0; + case WM_NOTIFY: + { + LPNMHDR l=(LPNMHDR)lParam; + if (l->idFrom==IDC_LIST) + { + if (l->code == NM_DBLCLK) + { + playFiles(!!(GetAsyncKeyState(VK_SHIFT)&0x8000),0); + } + else if (l->code == LVN_BEGINDRAG) + { + SetCapture(hwndDlg); + } + else if (l->code == LVN_ODFINDITEM) // yay we find an item (for kb shortcuts) + { + NMLVFINDITEM *t = (NMLVFINDITEM *)lParam; + int i=t->iStart; + if (i >= m_songs_sorted->GetSize()) i=0; + + int cnt=m_songs_sorted->GetSize()-i; + if (t->lvfi.flags & LVFI_WRAP) cnt+=i; + + while (cnt-->0) + { + Song *thissong = (Song *)m_songs_sorted->Get(i); + char tmp[128]; + char *name=0; + + switch (g_sortcol) + { + case COL_ARTIST: name=thissong->artist; break; + case COL_TITLE: name=thissong->title; break; + case COL_ALBUM: name=thissong->album; break; + case COL_LENGTH: + wsprintf(tmp,"%d:%02d",thissong->songlen/60,(thissong->songlen)%60); name=tmp; + break; + case COL_TRACK: if (thissong->track_nr > 0 && thissong->track_nr < 1000) { wsprintf(tmp,"%d",thissong->track_nr); name=tmp; } break; + case COL_GENRE: name=thissong->genre; break; + case COL_YEAR: if (thissong->year < 5000 && thissong->year > 0) { wsprintf(tmp,"%d",thissong->year); name=tmp; } break; + case COL_FILENAME: name=thissong->filename; break; + } + + + if (!name) name=""; + else SKIP_THE_AND_WHITESPACE(name) + + if (t->lvfi.flags & (4|LVFI_PARTIAL)) + { + if (!_strnicmp(name,t->lvfi.psz,strlen(t->lvfi.psz))) + { + SetWindowLong(hwndDlg,DWL_MSGRESULT,i); + return 1; + } + } + else if (t->lvfi.flags & LVFI_STRING) + { + if (!_stricmp(name,t->lvfi.psz)) + { + SetWindowLong(hwndDlg,DWL_MSGRESULT,i); + return 1; + } + } + else + { + SetWindowLong(hwndDlg,DWL_MSGRESULT,-1); + return 1; + } + if (++i == m_songs_sorted->GetSize()) i=0; + } + SetWindowLong(hwndDlg,DWL_MSGRESULT,-1); + return 1; + } + else if (l->code == LVN_GETDISPINFO) + { + NMLVDISPINFO *lpdi = (NMLVDISPINFO*) lParam; + int item=lpdi->item.iItem; + + if (item < 0 || item >= m_songs_sorted->GetSize()) return 0; + + Song *thissong = (Song *)m_songs_sorted->Get(item); + + if (lpdi->item.mask & (LVIF_TEXT|/*LVIF_IMAGE*/0)) // we can always do images too :) + { + if (lpdi->item.mask & LVIF_TEXT) + { + char tmpbuf[128]; + char *nameptr=0; + switch (lpdi->item.iSubItem) + { + case COL_ARTIST: nameptr=thissong->artist; break; + case COL_TITLE: nameptr=thissong->title; break; + case COL_ALBUM: nameptr=thissong->album; break; + case COL_LENGTH: + wsprintf(tmpbuf,"%d:%02d",thissong->songlen/60,(thissong->songlen)%60); nameptr=tmpbuf; + break; + case COL_TRACK: if (thissong->track_nr > 0 && thissong->track_nr < 1000) { wsprintf(tmpbuf,"%d",thissong->track_nr); nameptr=tmpbuf; } break; + case COL_GENRE: nameptr=thissong->genre; break; + case COL_YEAR: if (thissong->year>0 && thissong->year<5000) { wsprintf(tmpbuf,"%d",thissong->year); nameptr=tmpbuf; } break; + case COL_FILENAME: nameptr=thissong->filename; break; + } + if (nameptr) lstrcpyn(lpdi->item.pszText,nameptr,lpdi->item.cchTextMax); + else lpdi->item.pszText[0]=0; + } + // if(lpdi->item.mask & LVIF_IMAGE) + } // bother + return 0; + } // LVN_GETDISPINFO + else if (l->code == LVN_COLUMNCLICK) + { + NMLISTVIEW *p=(NMLISTVIEW*)lParam; + if (p->iSubItem == g_sortcol) g_sortdir=!g_sortdir; + else g_sortcol=p->iSubItem; + + char str[32]; + sprintf(str,"%d",g_sortdir); + WritePrivateProfileString(CONFIG_SEC,"sortdir",str,conf_file); + sprintf(str,"%d",g_sortcol); + WritePrivateProfileString(CONFIG_SEC,"sortcol",str,conf_file); + + sortResults(); + } + } + } + break; + case WM_COMMAND: + switch(LOWORD(wParam)) + { + case IDC_BUTTON_CONFIG: + config(hwndDlg); + break; + case IDC_QUICKSEARCH: + if (HIWORD(wParam) == EN_CHANGE) + { + KillTimer(hwndDlg,500); + SetTimer(hwndDlg,500,150,NULL); + } + break; + } + break; + case WM_TIMER: + if (wParam == 500) + { + KillTimer(hwndDlg,500); + char buf[256]; + GetDlgItemText(hwndDlg,IDC_QUICKSEARCH,buf,sizeof(buf)); + buf[255]=0; + WritePrivateProfileString(CONFIG_SEC,"lastfilter",buf,conf_file); + updateList(); + } + else if (wParam == 32) + { + KillTimer(hwndDlg,32); + if (!m_songs.GetSize()) loadSongList(); + char buf[256]; + GetPrivateProfileString(CONFIG_SEC,"lastfilter","",buf,sizeof(buf),conf_file); + SetDlgItemText(hwndDlg,IDC_QUICKSEARCH,buf); // automatically updates the list via EN_CHANGE + } + break; + case WM_SIZE: + #if 0 // BP: + if (wParam != SIZE_MINIMIZED) + { + if (cr_resize) cr_resize(hwndDlg,resize_rlist,sizeof(resize_rlist)/sizeof(resize_rlist[0])); + } + #endif + break; + case WM_PAINT: + { + if (wad_DrawChildWindowBorders) + { + int tab[] = { IDC_QUICKSEARCH|DCW_SUNKENBORDER, IDC_LIST|DCW_SUNKENBORDER}; + wad_DrawChildWindowBorders(hwndDlg,tab,2); + } + } + return 0; + case WM_DESTROY: + //clearSongList(); + m_hwnd=NULL; + SendMessage(plugin.hwndLibraryParent,WM_ML_IPC,m_skinlistview_handle,ML_IPC_UNSKIN_LISTVIEW); + return 0; + case WM_ML_CHILDIPC: + if (lParam == ML_CHILDIPC_DROPITEM && wParam) + { + mlDropItemStruct *t=(mlDropItemStruct*)wParam; + if (t->type == ML_TYPE_ITEMRECORDLIST) t->result=1; + if (t->data) + { + if (t->type == ML_TYPE_ITEMRECORDLIST) // we got a drag&drop to our window, hot! + { + addItemListToSongs((itemRecordList*)t->data); + updateList(); + } + } + } + return 0; + case WM_LBUTTONUP: + if (GetCapture() == hwndDlg) + { + ReleaseCapture(); + + POINT p; + p.x=GET_X_LPARAM(lParam); + p.y=GET_Y_LPARAM(lParam); + ClientToScreen(hwndDlg,&p); + + HWND h=WindowFromPoint(p); + if (h != hwndDlg && !IsChild(hwndDlg,h)) + { + mlDropItemStruct m={ML_TYPE_ITEMRECORDLIST,NULL,0}; + m.p=p; + m.flags=ML_HANDLEDRAG_FLAG_NOCURSOR; + + SendMessage(plugin.hwndLibraryParent,WM_ML_IPC,(WPARAM)&m,ML_IPC_HANDLEDRAG); + + if (m.result>0) + { + itemRecordList o={0,}; + SongsToItemList(&o,0); + if (o.Size) + { + //fill in this itemCacheObject if you want to provide drag&drop out of the window + m.flags=0; + m.result=0; + m.data=(void*)&o; + SendMessage(plugin.hwndLibraryParent,WM_ML_IPC,(WPARAM)&m,ML_IPC_HANDLEDROP); + } + free(o.Items); + } + } + } + break; + case WM_MOUSEMOVE: + if (GetCapture()==hwndDlg) + { + POINT p; + p.x=GET_X_LPARAM(lParam); + p.y=GET_Y_LPARAM(lParam); + ClientToScreen(hwndDlg,&p); + mlDropItemStruct m={ML_TYPE_ITEMRECORDLIST,NULL,0}; + m.p=p; + HWND h=WindowFromPoint(p); + if (!h || h == hwndDlg || IsChild(hwndDlg,h)) + { + SetCursor(LoadCursor(NULL,IDC_NO)); + } + else + SendMessage(plugin.hwndLibraryParent,WM_ML_IPC,(WPARAM)&m,ML_IPC_HANDLEDRAG); + } + break; + + } + return 0; +} + +static BOOL CALLBACK config_dlgproc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam) +{ + switch (uMsg) + { + case WM_INITDIALOG: + return 0; + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDOK: + // save combo box + case IDCANCEL: + EndDialog(hwndDlg,LOWORD(wParam) == IDOK); + break; + } + return 0; + } + return 0; +} + +static void config(HWND parent) +{ + DialogBox(plugin.hDllInstance,MAKEINTRESOURCE(IDD_CONFIG),parent,config_dlgproc); +} + +int onTreeItemClick(int param, int action, HWND hwndParent) // if param is not yours, return 0 +{ + if (action == ML_ACTION_RCLICK) + { + POINT p; + GetCursorPos(&p); + int r=TrackPopupMenu(GetSubMenu(m_context_menus,0),TPM_RETURNCMD|TPM_RIGHTBUTTON|TPM_LEFTBUTTON|TPM_NONOTIFY,p.x,p.y,0,hwndParent,NULL); + switch (r) + { + case ID_ABOUT: + MessageBox(hwndParent,"ml_ex!!!","About ml_ex!!!",MB_OK); + break; + case ID_CONFIG: + config(hwndParent); + break; + } + } + return 1; +} + + +int onTreeItemDropTarget(int param, int type, void *obj) +{ + if (type != ML_TYPE_ITEMRECORDLIST) return -1; + + if (!obj) return 1; + + // do somethihng with the itemCache object. do not free it however, since the caller owns it + addItemListToSongs((itemRecordList*)obj); + + updateList(); + + return 1; +} + +int onTreeItemDrag(int param, POINT p, int *type) +{ + HWND h=WindowFromPoint(p); + if (h && (h == m_hwnd || IsChild(m_hwnd,h))) return -1; // prevent from dropping into ourselves + + + // if we wanted to be able to drag&drop our tree item to other people, we'd + // return 1 and set type to ML_TYPE_ITEMRECORDLIST or ML_TYPE_FILENAMES etc. + + // *type = ML_TYPE_ITEMRECORDLIST; + return -1; +} + +int onTreeItemDrop(int param, POINT p) // you should send the appropriate ML_IPC_HANDLEDROP if you support it +{ + HWND h=WindowFromPoint(p); + if (h && (h == m_hwnd || IsChild(m_hwnd,h))) return -1; // prevent from dropping into ourselves + + // if we wanted to be able to drag&drop our tree item to other people, we'd + // create an itemCacheObject or a doublenull terminated list (depending on what we want), + // and send it back to the media library so it can route it to the appropriate destination: + // + // itemCacheObject o={0,}; + // fillInMyObject(&o); + // mlDropItemStruct m={0,}; + // m.type = ML_TYPE_ITEMRECORDLIST; + // m.data = (void*)&o; + // m.p=p; + // SendMessage(plugin.hwndLibraryParent,WM_ML_IPC,(WPARAM)&m,ML_IPC_HANDLEDROP); + // freeMyObject(&o); + + // or this: + itemRecordList o={0,}; + SongsToItemList(&o,1); + if (o.Size) + { + mlDropItemStruct m={0,}; + m.type = ML_TYPE_ITEMRECORDLIST; + m.data = (void*)&o; + m.p=p; + SendMessage(plugin.hwndLibraryParent,WM_ML_IPC,(WPARAM)&m,ML_IPC_HANDLEDROP); + } + free(o.Items); + + return 1; +} + +int PluginMessageProc(int message_type, int param1, int param2, int param3) +{ + // check for any global messages here + + if (message_type >= ML_MSG_TREE_BEGIN && message_type <= ML_MSG_TREE_END) + { + if (param1 != myParam) return 0; + // local messages for a tree item + + switch (message_type) + { + case ML_MSG_TREE_ONCREATEVIEW: + return (int)CreateDialog(plugin.hDllInstance,MAKEINTRESOURCE(IDD_VIEW_EX),(HWND)param2,dlgproc); + case ML_MSG_TREE_ONCLICK: + return onTreeItemClick(param1,param2,(HWND)param3); + case ML_MSG_TREE_ONDROPTARGET: + return onTreeItemDropTarget(param1,param2,(void*)param3); + case ML_MSG_TREE_ONDRAG: + return onTreeItemDrag(param1,*(POINT*)param2,(int*)param3); + case ML_MSG_TREE_ONDROP: + return onTreeItemDrop(param1,*(POINT*)param2); + } + } + else if (message_type == ML_MSG_ONSENDTOBUILD) + { + if (param1 == ML_TYPE_ITEMRECORDLIST) + { + mlAddToSendToStruct s; + s.context=param2; + s.desc="ItemCacheEx"; + s.user32=(int)PluginMessageProc; + SendMessage(plugin.hwndLibraryParent,WM_ML_IPC,(WPARAM)&s,ML_IPC_ADDTOSENDTO); + } + } + else if (message_type == ML_MSG_ONSENDTOSELECT) + { + if (param1 == ML_TYPE_ITEMRECORDLIST && param2 && param3 == (int)PluginMessageProc) + { + addItemListToSongs((itemRecordList*)param2); + updateList(); + return 1; + } + } + + return 0; +} + +winampMediaLibraryPlugin plugin = +{ + MLHDR_VER, + "ml_ex v0.1", + init, + quit, + PluginMessageProc, +}; + +extern "C" { + +__declspec( dllexport ) winampMediaLibraryPlugin * winampGetMediaLibraryPlugin() +{ + return &plugin; +} + +}; \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/ml_imagefilter.cpp b/Src/Plugins/General/gen_ml/ml_imagefilter.cpp new file mode 100644 index 00000000..7feeac3b --- /dev/null +++ b/Src/Plugins/General/gen_ml/ml_imagefilter.cpp @@ -0,0 +1,169 @@ +#include "./ml_imagefilter.h" + +#include + +typedef struct _IMAGEFILTER +{ + GUID uid; + MLIMAGEFILTERPROC proc; + LPARAM param; + UINT flags; + LPWSTR pszName; +} IMAGEFILTER; + +typedef struct _IMAGEFILTERMGNR +{ + IMAGEFILTER *filters; + INT count; + INT allocated; + INT allocstep; +} IMAGEFILTERMGNR; + +HMLIMGFLTRMNGR MLImageFilterI_CreateManager(INT cInitial, INT cGrow) +{ + IMAGEFILTERMGNR *pMngr = (IMAGEFILTERMGNR*)calloc(1, sizeof(IMAGEFILTERMGNR)); + if (!pMngr) return NULL; + + pMngr->allocstep = (cGrow < 1) ? 1 : cGrow; + + if (cInitial > 0) + { + if (cInitial > 40) cInitial = 40; + pMngr->filters = (IMAGEFILTER*)calloc(cInitial, sizeof(IMAGEFILTER)); + if (!pMngr->filters) + { + free(pMngr); + return NULL; + } + pMngr->allocated = cInitial; + } + return (HMLIMGFLTRMNGR)pMngr; +} + +BOOL MLImageFilterI_DestroyManager(HMLIMGFLTRMNGR hmlifMngr) +{ + if (!hmlifMngr) return FALSE; + + if (((IMAGEFILTERMGNR*)hmlifMngr)->filters) + { + INT index; + for (index = ((IMAGEFILTERMGNR*)hmlifMngr)->count - 1; index >=0; index--) + { + if (((IMAGEFILTERMGNR*)hmlifMngr)->filters[index].pszName) free(((IMAGEFILTERMGNR*)hmlifMngr)->filters[index].pszName); + } + free(((IMAGEFILTERMGNR*)hmlifMngr)->filters); + } + free(hmlifMngr); + return TRUE; +} + +static IMAGEFILTER *FindFilter(HMLIMGFLTRMNGR hMngr, const GUID *filterUID) +{ + INT index; + if (!hMngr || !filterUID) return NULL; + for (index = ((IMAGEFILTERMGNR*)hMngr)->count - 1; index >=0; index--) + { + if (IsEqualGUID(((IMAGEFILTERMGNR*)hMngr)->filters[index].uid, *filterUID)) return &((IMAGEFILTERMGNR*)hMngr)->filters[index]; + } + return NULL; +} + +BOOL MLImageFilterI_Register(HMLIMGFLTRMNGR hmlifMngr, MLIMAGEFILTERINFO_I *pmlif) +{ + IMAGEFILTERMGNR *pMngr; + IMAGEFILTER *pF; + + if (!hmlifMngr || !pmlif || FindFilter(hmlifMngr, &pmlif->uid) || !pmlif->fnProc) return FALSE; + pMngr = (IMAGEFILTERMGNR*)hmlifMngr; + + if (pMngr->count == pMngr->allocated) + { + LPVOID data; + data = (IMAGEFILTER*)realloc(pMngr->filters, sizeof(IMAGEFILTER)*(pMngr->allocated + pMngr->allocstep)); + if (!data) return FALSE; + pMngr->filters = (IMAGEFILTER*)data; + pMngr->allocated += pMngr->allocstep; + } + + pF = &pMngr->filters[pMngr->count]; + ZeroMemory(pF, sizeof(IMAGEFILTER)); + pF->uid = pmlif->uid; + pF->proc = pmlif->fnProc; + if (MLIFF_TITLE_I & pmlif->mask && pmlif->pszTitle) pF->pszName = _wcsdup(pmlif->pszTitle); + if (MLIFF_FLAGS_I & pmlif->mask) pF->flags = pmlif->fFlags; + if (MLIFF_PARAM_I & pmlif->mask) pF->param = pmlif->lParam; + + pMngr->count++; + return TRUE; +} + +BOOL MLImageFilterI_Unregister(HMLIMGFLTRMNGR hmlifMngr, REFGUID filterUID) +{ + IMAGEFILTER *pF; + INT index; + + if (!hmlifMngr) return FALSE; + + for (index = ((IMAGEFILTERMGNR*)hmlifMngr)->count - 1; index >=0; index--) + { + if (IsEqualGUID(((IMAGEFILTERMGNR*)hmlifMngr)->filters[index].uid, filterUID)) break; + } + if (-1 == index) return FALSE; + + pF = &((IMAGEFILTERMGNR*)hmlifMngr)->filters[index]; + if (pF->pszName) free(pF->pszName); + + if (index < ((IMAGEFILTERMGNR*)hmlifMngr)->count - 1) + { + MoveMemory(pF, pF + 1, sizeof(IMAGEFILTER)*(((IMAGEFILTERMGNR*)hmlifMngr)->count - index)); + } + ((IMAGEFILTERMGNR*)hmlifMngr)->count--; + return TRUE; +} + +BOOL MLImageFilterI_GetInfo(HMLIMGFLTRMNGR hmlifMngr, MLIMAGEFILTERINFO_I *pmlif) +{ + IMAGEFILTER *pF; + + if (!hmlifMngr || !pmlif) return FALSE; + + pF = FindFilter(hmlifMngr, &pmlif->uid); + if (!pF) return FALSE; + + if (MLIFF_FLAGS_I & pmlif->mask) pmlif->fFlags = pF->flags; + if (MLIFF_PARAM_I & pmlif->mask) pmlif->lParam = pF->param; + if (MLIFF_PROC_I & pmlif->mask) pmlif->fnProc = pF->proc; + if (MLIFF_TITLE_I & pmlif->mask) + { + if ((!pmlif->pszTitle || pmlif->cchTitleMax < 1) && pF->pszName) return FALSE; + + if (!pF->pszName) pmlif->pszTitle[0] = 0x00; + else if (S_OK != StringCchCopyW(pmlif->pszTitle, pmlif->cchTitleMax, pF->pszName)) return FALSE; + } + + return TRUE; +} + +BOOL MLImageFilterI_ApplyEx(HMLIMGFLTRMNGR hmlifMngr, const GUID *filterUID, LPBYTE pData, LONG cx, LONG cy, INT bpp, COLORREF rgbBk, COLORREF rgbFg, INT_PTR imageTag) +{ + IMAGEFILTER *pF; + pF = FindFilter(hmlifMngr, filterUID); + + if (!pF || !pF->proc || !pData) return FALSE; + return pF->proc(pData, cx, cy, bpp, rgbBk, rgbFg, imageTag, pF->param); +} + +BOOL MLImageFilterI_Apply(HMLIMGFLTRMNGR hmlifMngr, const GUID *filterUID, HBITMAP hbmp, COLORREF rgbBk, COLORREF rgbFg, INT_PTR imageTag) +{ + DIBSECTION dibsec; + + if (!hbmp || + sizeof(DIBSECTION) != GetObjectW(hbmp, sizeof(DIBSECTION), &dibsec) || + BI_RGB != dibsec.dsBmih.biCompression || + 1 != dibsec.dsBmih.biPlanes) return FALSE; + + return MLImageFilterI_ApplyEx(hmlifMngr, filterUID, (LPBYTE)dibsec.dsBm.bmBits, + dibsec.dsBm.bmWidth, dibsec.dsBm.bmHeight, dibsec.dsBm.bmBitsPixel, + rgbBk, rgbFg, imageTag); + +} diff --git a/Src/Plugins/General/gen_ml/ml_imagefilter.h b/Src/Plugins/General/gen_ml/ml_imagefilter.h new file mode 100644 index 00000000..fa422232 --- /dev/null +++ b/Src/Plugins/General/gen_ml/ml_imagefilter.h @@ -0,0 +1,47 @@ +#ifndef NULLSOFT_MEIDALIBRARY_IMAGE_FILTER_HEADER +#define NULLSOFT_MEIDALIBRARY_IMAGE_FILTER_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include + +typedef LPVOID HMLIMGFLTRMNGR; + +typedef BOOL (CALLBACK *MLIMAGEFILTERPROC)(LPBYTE /*pData*/, LONG /*cx*/, LONG /*cy*/, INT /*bpp*/, COLORREF /*rgbBk*/, COLORREF /*rgbFg*/, INT_PTR /*imageTag*/, LPARAM /*lParam*/); + +typedef struct _MLIMAGEFILTERINFO_I +{ + UINT mask; + GUID uid; + MLIMAGEFILTERPROC fnProc; + LPARAM lParam; + UINT fFlags; + LPWSTR pszTitle; + INT cchTitleMax; + +} MLIMAGEFILTERINFO_I; + +// flags +#define MLIFF_IGNORE_BKCOLOR_I 0x0001 +#define MLIFF_IGNORE_FGCOLOR_I 0x0002 + +// mask +#define MLIFF_TITLE_I 0x0001 +#define MLIFF_PARAM_I 0x0002 +#define MLIFF_FLAGS_I 0x0004 +#define MLIFF_PROC_I 0x0008 + + +HMLIMGFLTRMNGR MLImageFilterI_CreateManager(INT cInitial, INT cGrow); +BOOL MLImageFilterI_DestroyManager(HMLIMGFLTRMNGR hmlifMngr); + +BOOL MLImageFilterI_Register(HMLIMGFLTRMNGR hmlifMngr, MLIMAGEFILTERINFO_I *pmlif); +BOOL MLImageFilterI_Unregister(HMLIMGFLTRMNGR hmlifMngr, REFGUID filterUID); +BOOL MLImageFilterI_GetInfo(HMLIMGFLTRMNGR hmlifMngr, MLIMAGEFILTERINFO_I *pmlif); +BOOL MLImageFilterI_ApplyEx(HMLIMGFLTRMNGR hmlifMngr, const GUID *filterUID, LPBYTE pData, LONG cx, LONG cy, INT bpp, COLORREF rgbBk, COLORREF rgbFg, INT_PTR imageTag); +BOOL MLImageFilterI_Apply(HMLIMGFLTRMNGR hmlifMngr, const GUID *filterUID, HBITMAP hbmp, COLORREF rgbBk, COLORREF rgbFg, INT_PTR imageTag); + + +#endif //NULLSOFT_MEIDALIBRARY_IMAGE_FILTER_HEADER \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/ml_imagelist.cpp b/Src/Plugins/General/gen_ml/ml_imagelist.cpp new file mode 100644 index 00000000..61b01fb6 --- /dev/null +++ b/Src/Plugins/General/gen_ml/ml_imagelist.cpp @@ -0,0 +1,384 @@ +#include "./ml_imagelist.h" +#include "../nu/trace.h" + +typedef struct _MLILSTATE +{ + INT ilIndex; + COLORREF rgbBk; + COLORREF rgbFg; + + UINT created; + UINT counter; +} MLILSTATE; + +typedef struct _MLILREC +{ + MLIMAGESOURCE_I imgSource; + GUID filterUID; + UINT filterFlags; + INT_PTR tag; + + MLILSTATE *states; + INT usedCount; + INT lastIndex; + UINT counter; +} MLILREC; + +typedef struct _MLIL +{ + HIMAGELIST ilImages; + INT maxCache; + INT imageWidth; + INT imageHeight; + UINT flags; + + MLILREC *records; + INT recCount; + INT recAllocated; + INT recGrow; + HMLIMGFLTRMNGR hmlifMngr; +} MLIL; + +static BOOL MLImageListI_PatchHILIndices(HMLIMGLST hmlil, INT hilIndex) // scan for all records - states and subtract 1 from all indices that higher than hilIndex. +{ + INT index, stateIdx; + MLIL *pmlil; + if (!hmlil || hilIndex < 0 ) return FALSE; + pmlil = (MLIL*)hmlil; + + for (index = 0; index < pmlil->recCount; index++) + { + if (pmlil->records[index].states) + { + for (stateIdx = 0; stateIdx < pmlil->records[index].usedCount; stateIdx++) + { + if (pmlil->records[index].states[stateIdx].ilIndex > hilIndex) pmlil->records[index].states[stateIdx].ilIndex--; + } + } + } + return TRUE; +} + +HMLIMGLST MLImageListI_Create(INT cx, INT cy, UINT flags, INT cInitial, INT cGrow, INT cCacheSize, HMLIMGFLTRMNGR hmlifManager) +{ + if (!cx || !cy || !cCacheSize) return NULL; + + MLIL *pmlil = (MLIL*)calloc(1, sizeof(MLIL)); + if (!pmlil) return NULL; + + BOOL fSuccess = TRUE; + if (!(0x38 & pmlil->flags)) pmlil->flags |= MLILC_COLOR24_I; + + pmlil->imageWidth = cx; + pmlil->imageHeight = cy; + pmlil->flags = flags; + pmlil->recAllocated = (cGrow < 0 ) ? 0 : ((cInitial > MAX_ALLOWED_LIST_SIZE) ? MAX_ALLOWED_LIST_SIZE : cInitial); + pmlil->recGrow = (cGrow < 1 ) ? 1 : ((cGrow > MAX_ALLOWED_LIST_SIZE) ? MAX_ALLOWED_LIST_SIZE : cGrow); + pmlil->maxCache = (cCacheSize > MAX_ALLOWED_CACHE_SIZE) ? MAX_ALLOWED_CACHE_SIZE : cCacheSize; + pmlil->hmlifMngr = hmlifManager; + + if (pmlil->recAllocated) + { + pmlil->records = (MLILREC*)calloc(pmlil->recAllocated, sizeof(MLILREC)); + if (!pmlil->records) fSuccess = FALSE; + else + { + pmlil->ilImages = ImageList_Create(pmlil->imageWidth, pmlil->imageHeight, pmlil->flags, pmlil->recAllocated * pmlil->maxCache, pmlil->recGrow * pmlil->maxCache); + if (!pmlil->ilImages) fSuccess = FALSE; + } + } + + if (!fSuccess && pmlil) + { + if (pmlil->records) free(pmlil->records); + if (pmlil->ilImages) ImageList_Destroy(pmlil->ilImages); + free(pmlil); + pmlil = NULL; + } + return (HMLIMGLST)pmlil; +} + +BOOL MLImageListI_Destroy(HMLIMGLST hmlil) +{ + if (!hmlil) return FALSE; + + if (((MLIL*)hmlil)->records) + { + for (INT index = 0; index < ((MLIL*)hmlil)->recCount; index++) + { + if (((MLIL*)hmlil)->records[index].states) free(((MLIL*)hmlil)->records[index].states); + MLImageLoaderI_FreeData(&((MLIL*)hmlil)->records[index].imgSource); + } + free(((MLIL*)hmlil)->records); + } + if (((MLIL*)hmlil)->ilImages) ImageList_Destroy(((MLIL*)hmlil)->ilImages); + free(hmlil); + return TRUE; +} + +INT MLImageListI_Add(HMLIMGLST hmlil, MLIMAGESOURCE_I *pImageSource, REFGUID filterUID, INT_PTR nTag) +{ + MLIL *pmlil; + MLIMAGEFILTERINFO_I fi; + + if (!hmlil || !pImageSource) return -1; + + pmlil = (MLIL*)hmlil; + + if (pmlil->recCount == pmlil->recAllocated) + { + LPVOID data; + data = realloc(pmlil->records, sizeof(MLILREC)* (pmlil->recAllocated + pmlil->recGrow)); + if (!data) return -1; + pmlil->records = (MLILREC*)data; + pmlil->recAllocated += pmlil->recGrow; + + if (!pmlil->ilImages) + { + pmlil->ilImages = ImageList_Create(pmlil->imageWidth, pmlil->imageHeight, pmlil->flags, + pmlil->recAllocated * pmlil->maxCache, pmlil->recGrow * pmlil->maxCache); + } + } + + if (!pmlil->ilImages) return -1; + + ZeroMemory(&pmlil->records[pmlil->recCount], sizeof(MLILREC)); + if (!MLImageLoaderI_CopyData(&pmlil->records[pmlil->recCount].imgSource, pImageSource)) return -1; + + pmlil->records[pmlil->recCount].filterUID = filterUID; + pmlil->records[pmlil->recCount].tag = nTag; + pmlil->records[pmlil->recCount].lastIndex = -1; + + fi.uid = filterUID; + fi.mask = MLIFF_FLAGS_I; + pmlil->records[pmlil->recCount].filterFlags = (MLImageFilterI_GetInfo(pmlil->hmlifMngr, &fi)) ? fi.fFlags : 0; + + return pmlil->recCount++; +} + +BOOL MLImageListI_Replace(HMLIMGLST hmlil, INT index, MLIMAGESOURCE_I *pImageSource, REFGUID filterUID, INT_PTR nTag) +{ + MLIL *pmlil; + MLIMAGEFILTERINFO_I fi; + + if (!hmlil || !pImageSource) return FALSE; + + pmlil = (MLIL*)hmlil; + if (index < 0 || index >= pmlil->recCount) return FALSE; + + MLImageLoaderI_FreeData(&pmlil->records[index].imgSource); + if (!MLImageLoaderI_CopyData(&pmlil->records[index].imgSource, pImageSource)) return -1; + + if (pmlil->records[index].states) + { + free(pmlil->records[index].states); + pmlil->records[index].states = NULL; + } + + pmlil->records[index].filterUID = filterUID; + pmlil->records[index].tag = nTag; + pmlil->records[index].lastIndex = -1; + pmlil->records[index].usedCount = 0; + pmlil->records[index].counter = 0; + + fi.uid = filterUID; + fi.mask = MLIFF_FLAGS_I; + pmlil->records[index].filterFlags = (MLImageFilterI_GetInfo(pmlil->hmlifMngr, &fi)) ? fi.fFlags : 0; + + return TRUE; +} + +BOOL MLImageListI_Remove(HMLIMGLST hmlil, INT index) +{ + MLIL *pmlil; + if (!hmlil) return FALSE; + + pmlil = (MLIL*)hmlil; + if (index < 0 || index >= pmlil->recCount) return FALSE; + + MLImageLoaderI_FreeData(&pmlil->records[index].imgSource); + + if (pmlil->records[index].states) + { + INT stateIdx; + for (stateIdx = 0; stateIdx < pmlil->records[index].usedCount; stateIdx++) + { + INT hilIdx = pmlil->records[index].states[stateIdx].ilIndex; + if (-1 != hilIdx && ImageList_Remove(pmlil->ilImages, hilIdx)) MLImageListI_PatchHILIndices(hmlil, hilIdx); + } + free(pmlil->records[index].states); + } + + if (index != (pmlil->recCount - 1)) MoveMemory(&pmlil->records[index], &pmlil->records[index + 1], sizeof(MLILREC)*(pmlil->recCount - index)); + + pmlil->recCount--; + return TRUE; +} + +HIMAGELIST MLImageListI_GetRealList(HMLIMGLST hmlil) +{ + return (hmlil) ? ((MLIL*)hmlil)->ilImages : NULL; +} + +INT MLImageListI_GetRealIndex(HMLIMGLST hmlil, INT index, COLORREF rgbBk, COLORREF rgbFg) +{ + INT realIndex; + MLIL *pmlil; + MLILREC *prec; + HBITMAP hbmp; + + pmlil = (MLIL*)hmlil; + if (!pmlil || index < 0 || index >= pmlil->recCount) + return -1; + + prec = &pmlil->records[index]; + + if (MLIFF_IGNORE_FGCOLOR_I & prec->filterFlags) + rgbFg = 0xFFFF00FF; + + if (MLIFF_IGNORE_BKCOLOR_I & prec->filterFlags) + rgbBk = 0xFFFF00FF; + + if (pmlil->ilImages && prec->states && prec->usedCount > 0) + { + if (prec->lastIndex >= 0 && prec->lastIndex < prec->usedCount && + prec->states[prec->lastIndex].rgbBk == rgbBk && prec->states[prec->lastIndex].rgbFg == rgbFg) realIndex = prec->lastIndex; + else + { + for (realIndex = 0; realIndex < prec->usedCount; realIndex++) + { + if (prec->states[realIndex].rgbBk == rgbBk && prec->states[realIndex].rgbFg == rgbFg) + { + prec->lastIndex = realIndex; + break; + } + } + } + + if (prec->lastIndex == realIndex && -1 != prec->states[realIndex].ilIndex) + { + prec->counter++; + prec->states[realIndex].counter++; + return prec->states[realIndex].ilIndex; + } + } + + hbmp = MLImageLoaderI_LoadDib(&prec->imgSource); + if (!hbmp) + return -1; + + if (!MLImageFilterI_Apply(pmlil->hmlifMngr, &prec->filterUID, hbmp, rgbBk, rgbFg, prec->tag)) + { + rgbFg = 0xFFFF00FF; + rgbBk = 0xFFFF00FF; + } + + if (!prec->states) + { + prec->states = (MLILSTATE*)calloc(pmlil->maxCache, sizeof(MLILSTATE)); + if (!prec->states) + { + DeleteObject(hbmp); + return -1; + } + prec->counter = 0; + prec->lastIndex = -1; + prec->usedCount = 0; + } + if (prec->usedCount < pmlil->maxCache) + { + realIndex = prec->usedCount++; + prec->states[realIndex].ilIndex = -1; + } + else + { /// lets find less used record + INT minVal, minIndex; + minIndex = 0; + minVal = (prec->counter != prec->states[minIndex].created) ? + (prec->counter - prec->states[minIndex].created) : + 1; + + minVal = MulDiv(100000, prec->states[minIndex].counter, minVal); + + for (realIndex = 0; realIndex < prec->usedCount; realIndex++) + { + INT testVal = (prec->counter != prec->states[realIndex].created) ? + (prec->counter - prec->states[realIndex].created) : + 1; + + testVal = MulDiv(100000, prec->states[realIndex].counter, testVal); + + if (testVal < minVal) + { + minVal = testVal; + minIndex = realIndex; + } + } + realIndex = minIndex; + } + + prec->states[realIndex].rgbBk = rgbBk; + prec->states[realIndex].rgbFg = rgbFg; + prec->states[realIndex].counter = 1; + prec->states[realIndex].created = prec->counter; + + if(NULL != pmlil->ilImages) + { + if (-1 == prec->states[realIndex].ilIndex) + prec->states[realIndex].ilIndex = ImageList_Add(pmlil->ilImages, hbmp, NULL); + else + ImageList_Replace(pmlil->ilImages, prec->states[realIndex].ilIndex, hbmp, NULL); + } + + DeleteObject(hbmp); + if (-1 != prec->states[realIndex].ilIndex) + { + prec->counter++; + prec->lastIndex = realIndex; + } + + return prec->states[realIndex].ilIndex; +} + +BOOL MLImageListI_GetImageSize(HMLIMGLST hmlil, INT *cx, INT *cy) +{ + if (!hmlil) + { + if (cx) *cx = 0; + if (cy) *cy = 0; + return FALSE; + } + if (cx) *cx = ((MLIL*)hmlil)->imageWidth; + if (cy) *cy = ((MLIL*)hmlil)->imageHeight; + return TRUE; +} + +INT MLImageListI_GetImageCount(HMLIMGLST hmlil) +{ + return (hmlil) ? ((MLIL*)hmlil)->recCount : 0; +} + +INT MLImageListI_GetIndexFromTag(HMLIMGLST hmlil, INT_PTR nTag) +{ + INT index; + if (!hmlil || !((MLIL*)hmlil)->records) return -1; + for (index = 0; index <((MLIL*)hmlil)->recCount; index++) + { + if (((MLIL*)hmlil)->records[index].tag == nTag) return index; + } + return -1; +} + +BOOL MLImageListI_GetTagFromIndex(HMLIMGLST hmlil, INT index, INT_PTR *nTag) +{ + if (!hmlil || !((MLIL*)hmlil)->records || index < 0 || index >= ((MLIL*)hmlil)->recCount) return FALSE; + if (nTag) *nTag = ((MLIL*)hmlil)->records[index].tag; + return TRUE; +} + +BOOL MLImageListI_CheckItemExist(HMLIMGLST hmlil, INT index) +{ + MLIL *pmlil = (MLIL*)hmlil; + if (NULL == pmlil || index < 0 || index >= pmlil->recCount) return FALSE; + return MLImageLoaderI_CheckExist(&pmlil->records[index].imgSource); +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/ml_imagelist.h b/Src/Plugins/General/gen_ml/ml_imagelist.h new file mode 100644 index 00000000..9362140a --- /dev/null +++ b/Src/Plugins/General/gen_ml/ml_imagelist.h @@ -0,0 +1,44 @@ +#ifndef NULLOSFT_MEDIALIBRARY_IMAGELIST_HEADER +#define NULLOSFT_MEDIALIBRARY_IMAGELIST_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + + +#include +#include +#include "./ml_imageloader.h" // image loading +#include "./ml_imagefilter.h" // image loading + +typedef LPVOID HMLIMGLST; + +#define MAX_ALLOWED_CACHE_SIZE 10 +#define MAX_ALLOWED_LIST_SIZE 400 + + +// create falgs +#define MLILC_COLOR24_I 0x00000018 // Use a 24-bit DIB section. +#define MLILC_COLOR32_I 0x00000020 // Use a 32-bit DIB section. +#define MLILC_MASK_I 0x00000001 // Use a mask. + +HMLIMGLST MLImageListI_Create(INT cx, INT cy, UINT flags, INT cInitial, INT cGrow, INT cCacheSize, HMLIMGFLTRMNGR hmlifManager); +BOOL MLImageListI_Destroy(HMLIMGLST hmlil); + +HIMAGELIST MLImageListI_GetRealList(HMLIMGLST hmlil); +INT MLImageListI_GetRealIndex(HMLIMGLST hmlil, INT index, COLORREF rgbBk, COLORREF rgbFg); + + +INT MLImageListI_Add(HMLIMGLST hmlil, MLIMAGESOURCE_I *pImageSource, REFGUID filterUID, INT_PTR nTag); +BOOL MLImageListI_Replace(HMLIMGLST hmlil, INT index, MLIMAGESOURCE_I *pImageSource, REFGUID filterUID, INT_PTR nTag); +BOOL MLImageListI_Remove(HMLIMGLST hmlil, INT index); + + +BOOL MLImageListI_GetImageSize(HMLIMGLST hmlil, INT *cx, INT *cy); +INT MLImageListI_GetImageCount(HMLIMGLST hmlil); +INT MLImageListI_GetIndexFromTag(HMLIMGLST hmlil, INT_PTR nTag); +BOOL MLImageListI_GetTagFromIndex(HMLIMGLST hmlil, INT index, INT_PTR *nTag); +BOOL MLImageListI_CheckItemExist(HMLIMGLST hmlil, INT index); + + +#endif //NULLOSFT_MEDIALIBRARY_IMAGELIST_HEADER \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/ml_imageloader.cpp b/Src/Plugins/General/gen_ml/ml_imageloader.cpp new file mode 100644 index 00000000..84438524 --- /dev/null +++ b/Src/Plugins/General/gen_ml/ml_imageloader.cpp @@ -0,0 +1,689 @@ +#include "main.h" +#include "./ml_imageloader.h" +#include "api__gen_ml.h" +#include "./ml_ipc_0313.h" +#include +#include +#include + +#include +#include + +static const GUID pngGUID = { 0x5e04fb28, 0x53f5, 0x4032, { 0xbd, 0x29, 0x3, 0x2b, 0x87, 0xec, 0x37, 0x25 } }; + +#ifndef LOAD_LIBRARY_AS_IMAGE_RESOURCE + #define LOAD_LIBRARY_AS_IMAGE_RESOURCE 0x000000020 +#endif //LOAD_LIBRARY_AS_IMAGE_RESOURCE + +static svc_imageLoader *wasabiPNGLoader = NULL; +static api_memmgr *wasabiMemMgr = NULL; + +static BOOL InitializePNGService(void) +{ + waServiceFactory *sf; + if (!wasabiMemMgr) + { + sf = WASABI_API_SVC->service_getServiceByGuid(memMgrApiServiceGuid); + if (sf) wasabiMemMgr = reinterpret_cast(sf->getInterface()); + } + if (wasabiMemMgr && !wasabiPNGLoader) + { + sf = WASABI_API_SVC->service_getServiceByGuid(pngGUID); + if (sf) wasabiPNGLoader = reinterpret_cast(sf->getInterface()); + } + return (wasabiMemMgr && wasabiPNGLoader); +} + +static HBITMAP LoadImage_LoadPngData(void *pngData, UINT pngSize, BOOL fPremultiply) +{ + INT cx, cy; + HBITMAP bitmap; + + if (NULL == wasabiPNGLoader) + { + if (FALSE == InitializePNGService() || + NULL == wasabiPNGLoader) + { + return NULL; + } + } + + pngData = (FALSE != fPremultiply) ? + wasabiPNGLoader->loadImage(pngData, pngSize, &cx, &cy) : + wasabiPNGLoader->loadImageData(pngData, pngSize, &cx, &cy); + + + if (NULL != pngData) + { + BITMAPINFOHEADER header; + void *pixelData; + + ZeroMemory(&header, sizeof(BITMAPINFOHEADER)); + header.biSize = sizeof(BITMAPINFOHEADER); + header.biBitCount = 32; + header.biPlanes = 1; + header.biWidth = cx; + header.biHeight = -cy; + + bitmap = CreateDIBSection(NULL, (LPBITMAPINFO)&header, DIB_RGB_COLORS, &pixelData, NULL, 0); + if (NULL != bitmap) + CopyMemory(pixelData, pngData, cx * cy * sizeof(DWORD)); + + wasabiMemMgr->sysFree(pngData); + } + else + bitmap = NULL; + + return bitmap; +} + +static HBITMAP LoadImage_PngResource(HINSTANCE hInstance, HINSTANCE langModule, + LPCWSTR pszName, LPCWSTR pszType, BOOL fPremultiply) +{ + HRSRC res; + + res = (NULL != langModule) ? + FindResourceW(langModule, pszName, pszType) : + NULL; + + if (NULL == res) + { + res = FindResourceW(hInstance, pszName, pszType); + } + else + { + hInstance = langModule; + } + + if (NULL == res) + return NULL; + + HANDLE handle = LoadResource(hInstance, res); + if (NULL == handle) + return NULL; + + UINT pngSize = SizeofResource(hInstance, res); + if (0 == pngSize) + return NULL; + + + void *pngData = LockResource(handle); + if (NULL == pngData) + return NULL; + + return LoadImage_LoadPngData(pngData, pngSize, fPremultiply); + +} + +static HBITMAP LoadImage_BmpResource(HINSTANCE hInstace, HINSTANCE langModule, LPCWSTR pszName, LPCWSTR pszType, BOOL fPremultiply) +{ + UINT flags = LR_DEFAULTCOLOR | LR_CREATEDIBSECTION; + + if (NULL != langModule) + { + HBITMAP bitmap; + + bitmap = (HBITMAP)LoadImageW(langModule, pszName, IMAGE_BITMAP, 0, 0, flags); + if (NULL != bitmap) + return bitmap; + } + + return (HBITMAP)LoadImageW(hInstace, pszName, IMAGE_BITMAP, 0, 0, flags); +} + +static BOOL LoadImage_IsValidGuidChar(wchar_t c) +{ + if ((c >= L'0' && c <= L'9') || + (c >= L'A' && c <= L'F') || + (c >= L'a' && c <= L'f') || + (L'-' == c)) + { + return TRUE; + } + return FALSE; +} + +static HRESULT LoadImage_CrackResProtocol(LPCWSTR pszAddress, LPCWSTR pszDefaultType, HINSTANCE *module, + HINSTANCE *langModule, LPCWSTR *name, wchar_t **type) +{ + if (NULL == module) return E_POINTER; + if (NULL == pszAddress || L'\0' == *pszAddress) + return E_INVALIDARG; + + INT cchAddress = lstrlenW(pszAddress); + const WCHAR szPrefix[] = L"res://"; + INT cchPrefix = ARRAYSIZE(szPrefix) - 1; + + if (cchAddress <= cchPrefix || + CSTR_EQUAL != CompareStringW(CSTR_INVARIANT, NORM_IGNORECASE, pszAddress, cchPrefix, szPrefix, cchPrefix)) + { + return S_FALSE; + } + + pszAddress += cchPrefix; + cchAddress -= cchPrefix; + + LPCWSTR resType = NULL; + LPCWSTR resName = NULL; + + LPCWSTR p = pszAddress + cchAddress; + while (p != pszAddress && L'/' != *p) p--; + if (p != pszAddress && p < (pszAddress + cchAddress)) + { + resName = p + 1; + p--; + } + + if (NULL == resName || L'\0' == *resName) + return E_FAIL; + + WCHAR szFile[MAX_PATH + 128] = {0}; + if (FAILED(StringCchCopyNW(szFile, ARRAYSIZE(szFile), pszAddress, (resName - pszAddress - 1)))) + return E_OUTOFMEMORY; + + while (p != pszAddress && L'/' != *p) p--; + if (p != pszAddress && p < resName) + { + resType = p + 1; + if (L'\0' == *resType) + { + resType = NULL; + } + else + { + size_t pos = (resType - pszAddress); + szFile[pos - 1] = L'\0'; + resType = &szFile[pos]; + } + } + + HINSTANCE hModule = LoadLibraryExW(szFile, NULL, LOAD_LIBRARY_AS_DATAFILE | LOAD_LIBRARY_AS_IMAGE_RESOURCE); + + if (NULL == hModule && NULL != resType) + { + DWORD errorCode = GetLastError(); + if (ERROR_FILE_NOT_FOUND == errorCode) + { + *((LPWSTR)(resType - 1)) = L'/'; + resType = NULL; + hModule = LoadLibraryExW(szFile, NULL, LOAD_LIBRARY_AS_DATAFILE | LOAD_LIBRARY_AS_IMAGE_RESOURCE); + } + } + + if (NULL == hModule) + return E_FAIL; + + if (NULL != type) + { + if (NULL == resType) + resType = pszDefaultType; + + if (FALSE == IS_INTRESOURCE(resType)) + { + if (L'#' == *resType) + { + INT typeId; + if (FALSE != StrToIntExW(resType + 1, STIF_DEFAULT, &typeId)) + resType = MAKEINTRESOURCEW(typeId); + else + resType = NULL; + } + else + { + int length = lstrlenW(resType); + wchar_t *str = (wchar_t*)malloc((length + 1) * sizeof(wchar_t)); + if (NULL != str) + CopyMemory(str, resType, sizeof(wchar_t) * (length + 1)); + resType = str; + } + } + + *type = (wchar_t*)resType; + } + + if (NULL != langModule) + { + *langModule = NULL; + + if (NULL != WASABI_API_LNG && + FALSE == SENDWAIPC(plugin.hwndParent, IPC_GETLANGUAGEPACKINSTANCE, 1)) + { + wchar_t buffer[64], *lang_str; + GUID lang_id; + + if (0 != LoadStringW(hModule, LANG_DLL_GUID_STRING_ID, buffer, ARRAYSIZE(buffer))) + { + lang_str = buffer; + while(FALSE == LoadImage_IsValidGuidChar(*lang_str)) + { + if (L'0' == *lang_str) + break; + lang_str++; + } + + int len = lstrlenW(lang_str); + if (len > 0) + { + wchar_t *cursor = lang_str + (len - 1); + while(FALSE == LoadImage_IsValidGuidChar(*cursor)) + { + if (lang_str == cursor) + break; + cursor--; + } + *(cursor+1) = L'\0'; + } + + if (RPC_S_OK == UuidFromStringW((RPC_WSTR)lang_str, &lang_id)) + { + *langModule = WASABI_API_LNG->FindDllHandleByGUID(lang_id); + if (*langModule == hModule) + *langModule = NULL; + } + } + } + } + + *module = hModule; + + if (NULL != name) + *name = resName; + + return S_OK; +} + +static HBITMAP +LoadImage_CrackHBitmapProtocol(const wchar_t *address) +{ + INT addressLen, prefixLen; + const WCHAR prefix[] = L"hbitmap://"; + LONGLONG value; + + if (NULL == address || L'\0' == *address) + return NULL; + + addressLen = lstrlenW(address); + prefixLen = ARRAYSIZE(prefix) - 1; + + if (addressLen <= prefixLen || + CSTR_EQUAL != CompareStringW(CSTR_INVARIANT, NORM_IGNORECASE, + address, prefixLen, prefix, prefixLen)) + { + return NULL; + } + + address += prefixLen; + while(L'\0' != *address && L' ' == *address) + address++; + + if (FALSE == StrToInt64ExW(address, STIF_SUPPORT_HEX, &value)) + return NULL; + + return (HBITMAP)value; +} + +static HBITMAP LoadImage_ResProtocol(LPCWSTR pszAddress, LPCWSTR pszDefaultType, + BOOL fPremultiply, BOOL ignoreLocalized) +{ + HRESULT hr; + HINSTANCE hModule, langModule; + LPCWSTR resName; + wchar_t *resType; + HBITMAP bitmap; + BOOL loaded = FALSE; + + hr = LoadImage_CrackResProtocol(pszAddress, pszDefaultType, &hModule, + (FALSE == ignoreLocalized) ? &langModule : NULL, + &resName, &resType); + + if (FAILED(hr) || S_FALSE == hr) + return NULL; + + if (FALSE != ignoreLocalized) + langModule = NULL; + + if (NULL != resType) + { + if (IS_INTRESOURCE(resType)) + { + switch((INT)(INT_PTR)resType) + { + case (INT)(INT_PTR)RT_BITMAP: + bitmap = LoadImage_BmpResource(hModule, langModule, resName, resType, fPremultiply); + loaded = TRUE; + break; + } + } + } + + if (FALSE == loaded) + { + bitmap = LoadImage_PngResource(hModule, langModule, resName, resType, fPremultiply); + } + + if (NULL != langModule) + { + // do not call FreeLibrary for langModule (it was never loaded) + // FreeLibrary(langModule); + } + + FreeLibrary(hModule); + + if (FALSE == IS_INTRESOURCE(resType)) + free(resType); + + return bitmap; +} + +static HBITMAP LoadImage_PngFile(LPCWSTR pszPath, BOOL fPremultiply, BOOL ignoreLocalized) +{ + HANDLE hFile = CreateFileW(pszPath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); + if (INVALID_HANDLE_VALUE == hFile) + return NULL; + + HBITMAP bitmap = NULL; + UINT pngSize = GetFileSize(hFile, NULL); + if (INVALID_FILE_SIZE != pngSize) + { + void *pngData = malloc(pngSize); + if (NULL != pngData) + { + DWORD readed = 0; + if (0 != ReadFile(hFile, pngData, pngSize, &readed, NULL) || pngSize != readed) + { + bitmap = LoadImage_LoadPngData(pngData, pngSize, fPremultiply); + } + free(pngData); + } + } + + CloseHandle(hFile); + return bitmap; +} + +static HBITMAP LoadImage_Png(const MLIMAGESOURCE_I *pImgSource) +{ + HBITMAP bitmap; + const wchar_t *path; + BOOL premultiply, ignoreLocalized; + + path = pImgSource->lpszName; + premultiply = (0 != (ISF_PREMULTIPLY_I & pImgSource->flags)); + ignoreLocalized = (0 != (ISF_NOLOCALIZED_LOAD_I & pImgSource->flags)); + + + if (FALSE == IS_INTRESOURCE(path)) + { + bitmap = LoadImage_CrackHBitmapProtocol(path); + if (NULL != bitmap) + { + bitmap = (HBITMAP)CopyImage(bitmap,IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION); + return bitmap; + } + + bitmap = LoadImage_ResProtocol(path, (LPCWSTR)RT_RCDATA, premultiply, ignoreLocalized); + if (NULL != bitmap) + return bitmap; + + if (0 != (ISF_LOADFROMFILE_I & pImgSource->flags)) + return LoadImage_PngFile(path, premultiply, ignoreLocalized); + } + + bitmap = LoadImage_PngResource(pImgSource->hInst, NULL, path, (LPCWSTR)RT_RCDATA, premultiply); + if (NULL != bitmap) + return bitmap; + + bitmap = LoadImage_PngResource(pImgSource->hInst, NULL, path, (LPCWSTR)L"PNG", premultiply); + return bitmap; +} + +static HBITMAP LoadImage_Bmp(const MLIMAGESOURCE_I *pImgSource) +{ + HBITMAP bitmap; + const wchar_t *path; + BOOL premultiply, ignoreLocalized; + + path = pImgSource->lpszName; + premultiply = (0 != (ISF_PREMULTIPLY_I & pImgSource->flags)); + ignoreLocalized = (0 != (ISF_NOLOCALIZED_LOAD_I & pImgSource->flags)); + + if (FALSE == IS_INTRESOURCE(path)) + { + bitmap = LoadImage_CrackHBitmapProtocol(path); + if (NULL != bitmap) + { + bitmap = (HBITMAP)CopyImage(bitmap,IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION); + return bitmap; + } + + bitmap = LoadImage_ResProtocol(path, (LPCWSTR)RT_BITMAP, premultiply, ignoreLocalized); + if (NULL != bitmap) + return bitmap; + } + + UINT flags = LR_DEFAULTCOLOR | LR_CREATEDIBSECTION; + if (0 != (ISF_LOADFROMFILE_I & pImgSource->flags)) + flags |= LR_LOADFROMFILE; + + bitmap = (HBITMAP)LoadImageW(pImgSource->hInst, path, IMAGE_BITMAP, 0, 0, flags); + + return bitmap; +} + +static HBITMAP LoadImage_HIMAGELIST(const MLIMAGESOURCE_I *pImgSource) +{ +// IMAGEINFO ii; +// if (!pImgSource) return NULL; +// return (ImageList_GetImageInfo((HIMAGELIST)pImgSource->hInst, (INT)(INT_PTR)pImgSource->lpszName, &ii)) ? ii.hbmImage : NULL; + return NULL; // not supported; +} + +static HBITMAP DuplicateStretchedDib(HBITMAP hbmpSrc, INT xSrc, INT ySrc, INT cxSrc, INT cySrc, INT cxDst, INT cyDst, INT bppDst, INT bppSrc, BOOL fStretch) +{ + HDC hdcSrc, hdcDst; + HBITMAP hbmpDst; + LPVOID dib; + BITMAPINFOHEADER bi; + + hdcSrc = CreateCompatibleDC(0); + if (!hdcSrc) return NULL; + hdcDst = CreateCompatibleDC(0); + if (!hdcDst) { DeleteDC(hdcSrc); return NULL; } + + ZeroMemory(&bi, sizeof(BITMAPINFOHEADER)); + bi.biSize = sizeof (BITMAPINFOHEADER); + bi.biWidth = cxDst; + bi.biHeight = -ABS(cyDst); + bi.biPlanes = 1; + bi.biBitCount = bppDst; + + hbmpDst = CreateDIBSection(hdcDst, (BITMAPINFO *)&bi, DIB_RGB_COLORS, &dib, NULL, NULL); + if (hbmpDst) + { + HGDIOBJ hgdiDst = SelectObject(hdcDst, hbmpDst); + HGDIOBJ hgdiSrc = SelectObject(hdcSrc, hbmpSrc); + + if (fStretch && (cxDst != cxSrc || cyDst != cySrc)) + { + if (32 == bppDst || 32 == bppSrc) + { + BLENDFUNCTION bf = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA }; + GdiAlphaBlend(hdcDst, 0, 0, cxDst, cyDst, hdcSrc, xSrc, ySrc, cxSrc, cySrc, bf); + } + else + { + INT stretchModeOld = SetStretchBltMode(hdcDst, HALFTONE); + StretchBlt(hdcDst, 0, 0, cxDst, ABS(cyDst), hdcSrc, xSrc, ySrc, cxSrc, cySrc, SRCCOPY); + SetStretchBltMode(hdcDst, stretchModeOld); + } + } + else BitBlt(hdcDst, 0, 0, cxDst, ABS(cyDst), hdcSrc, xSrc, ySrc, SRCCOPY); + + SelectObject(hdcSrc, hgdiSrc); + SelectObject(hdcDst, hgdiDst); + } + + DeleteDC(hdcSrc); + DeleteDC(hdcDst); + + return hbmpDst; +} + +HBITMAP MLImageLoaderI_LoadDib(const MLIMAGESOURCE_I *pImgSource) +{ + HBITMAP hbmp; + + if (NULL == pImgSource) + return NULL; + + switch(pImgSource->type) + { + case SRC_TYPE_BMP_I: hbmp = LoadImage_Bmp(pImgSource); break; + case SRC_TYPE_PNG_I: hbmp = LoadImage_Png(pImgSource); break; + case SRC_TYPE_HBITMAP_I: hbmp = (HBITMAP)CopyImage((HANDLE)pImgSource->lpszName, IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION); break; + case SRC_TYPE_HIMAGELIST_I: hbmp = LoadImage_HIMAGELIST(pImgSource); break; + default: hbmp = NULL; break; + } + + if (NULL != hbmp) // + { + // get bitmap info + BITMAP bm; + + if (sizeof(bm) == GetObjectW(hbmp, sizeof(bm), &bm)) + { + if (((ISF_USE_OFFSET_I & pImgSource->flags) && (pImgSource->xSrc || pImgSource->ySrc)) || + ((ISF_USE_SIZE_I & pImgSource->flags) && (pImgSource->cxSrc != bm.bmWidth || pImgSource->cySrc != bm.bmHeight)) || + ((ISF_FORCE_SIZE_I & pImgSource->flags) && (pImgSource->cxDst != bm.bmWidth || pImgSource->cyDst != bm.bmHeight)) || + ((ISF_FORCE_BPP_I & pImgSource->flags) && (pImgSource->bpp != bm.bmBitsPixel))) + { + HBITMAP hOldBmp; + hOldBmp = hbmp; + hbmp = DuplicateStretchedDib( hbmp, + (ISF_USE_OFFSET_I & pImgSource->flags) ? pImgSource->xSrc : 0, + (ISF_USE_OFFSET_I & pImgSource->flags) ? pImgSource->ySrc : 0, + (ISF_USE_SIZE_I & pImgSource->flags) ? pImgSource->cxSrc : bm.bmWidth, + (ISF_USE_SIZE_I & pImgSource->flags) ? pImgSource->cySrc : bm.bmHeight, + (ISF_FORCE_SIZE_I & pImgSource->flags) ? pImgSource->cxDst: bm.bmWidth, + (ISF_FORCE_SIZE_I & pImgSource->flags) ? pImgSource->cyDst: bm.bmHeight, + (ISF_FORCE_BPP_I & pImgSource->flags) ? pImgSource->bpp : bm.bmBitsPixel, + bm.bmBitsPixel, + (ISF_SCALE_I & pImgSource->flags)); + DeleteObject(hOldBmp); + } + } + else + { + DeleteObject(hbmp); + hbmp = NULL; + } + } + + return hbmp; +} + +BOOL MLImageLoaderI_CopyData(MLIMAGESOURCE_I *pisDst, const MLIMAGESOURCE_I *pisSrc) +{ + if (!pisDst || !pisSrc) return FALSE; + CopyMemory(pisDst, pisSrc, sizeof(MLIMAGESOURCE_I)); + if (SRC_TYPE_HBITMAP_I != pisSrc->type && SRC_TYPE_HIMAGELIST_I != pisSrc->type && + pisSrc->lpszName && !IS_INTRESOURCE(pisSrc->lpszName)) pisDst->lpszName = _wcsdup(pisSrc->lpszName); + return TRUE; +} + +BOOL MLImageLoaderI_FreeData(MLIMAGESOURCE_I *pis) +{ + if (!pis) return FALSE; + if (SRC_TYPE_HBITMAP_I != pis->type && SRC_TYPE_HIMAGELIST_I != pis->type && + pis->lpszName && !IS_INTRESOURCE(pis->lpszName)) free((LPWSTR)pis->lpszName); + return TRUE; +} + +BOOL MLImageLoaderI_CheckExist(const MLIMAGESOURCE_I *pis) +{ + const wchar_t *resType; + + if (NULL == pis) + return FALSE; + + switch(pis->type) + { + case SRC_TYPE_HBITMAP_I: return (NULL != pis->lpszName); + case SRC_TYPE_HIMAGELIST_I: return FALSE; + case SRC_TYPE_PNG_I: resType = (LPCWSTR)RT_RCDATA; break; + case SRC_TYPE_BMP_I: resType = (LPCWSTR)RT_BITMAP; break; + default: resType = NULL; break; + } + + if (FALSE == IS_INTRESOURCE(pis->lpszName)) + { + BOOL result; + HINSTANCE module, langModule; + LPCWSTR name; + wchar_t *type; + HRESULT hr; + + if (NULL != LoadImage_CrackHBitmapProtocol(pis->lpszName)) + return TRUE; + + result = FALSE; + + langModule = NULL; + + hr = LoadImage_CrackResProtocol(pis->lpszName, resType, &module, + (0 == (ISF_NOLOCALIZED_LOAD_I & pis->flags)) ? &langModule : NULL, + &name, &type); + + if (0 != (ISF_NOLOCALIZED_LOAD_I & pis->flags)) + langModule = NULL; + + if (S_OK == hr) + { + if (NULL != langModule) + { + result = (NULL != FindResourceW(langModule, name, type)); + // do not call FreeLibrary for langModule (it was never loaded) + //FreeLibrary(langModule); + } + + if (FALSE == result) + result = (NULL != FindResourceW(module, name, type)); + + FreeLibrary(module); + + if (FALSE == IS_INTRESOURCE(type)) + free(type); + + return result; + } + + if (0 != (ISF_LOADFROMFILE_I & pis->flags)) + { + HANDLE hFile = CreateFileW(pis->lpszName, GENERIC_READ, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if (INVALID_HANDLE_VALUE != hFile) + { + switch(pis->type) + { + case SRC_TYPE_PNG_I: + if (NULL != wasabiPNGLoader || + InitializePNGService()) + { + BYTE data[8] = {0}; // png signature len + DWORD dataRead = 0; + if(ReadFile(hFile, data, sizeof(data), &dataRead, NULL) && 0 != dataRead) + result = wasabiPNGLoader->testData(data, dataRead); + } + break; + default: + result = TRUE; + break; + } + CloseHandle(hFile); + } + return result; + } + } + + return (NULL != resType && + NULL != FindResourceW(pis->hInst, pis->lpszName, resType)); +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/ml_imageloader.h b/Src/Plugins/General/gen_ml/ml_imageloader.h new file mode 100644 index 00000000..0697adbc --- /dev/null +++ b/Src/Plugins/General/gen_ml/ml_imageloader.h @@ -0,0 +1,52 @@ +#ifndef NULLSOFT_MEIDALIBRARY_IMAGE_SOURCE_HEADER +#define NULLSOFT_MEIDALIBRARY_IMAGE_SOURCE_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include + + +typedef struct _MLIMAGESOURCE_I +{ + HINSTANCE hInst; // + LPCWSTR lpszName; // + UINT bpp; // + INT xSrc; // + INT ySrc; // + INT cxSrc; // + INT cySrc; // + INT cxDst; // + INT cyDst; // + UINT type; // + UINT flags; // +} MLIMAGESOURCE_I; + +// Image Source types: +#define SRC_TYPE_BMP_I 0x01 // hInst = depends on ISF_LOADFROMFILE, lpszName = resource (file) name. To make resource name from resource id use MAKEINTERESOURCEW(). +#define SRC_TYPE_PNG_I 0x02 // hInst = depends on ISF_LOADFROMFILE, lpszName = resource (file) name. To make resource name from resource id use MAKEINTERESOURCEW(). +#define SRC_TYPE_HBITMAP_I 0x03 // hInst = NULL, lpszName =(HBITMAP)hbmp. +#define SRC_TYPE_HIMAGELIST_I 0x04 // hInst = (HIMAGELIST)himl, lpszName = MAKEINTERESOURCEW(index). Make Sure that common controls initialized before using this. + +// Image Source flags: +#define ISF_LOADFROMFILE_I 0x0001 // Load image from file. hInst ignored. +#define ISF_USE_OFFSET_I 0x0002 // xSrc and ySrc valid. +#define ISF_USE_SIZE_I 0x0004 // cxSrc and cySrc valid. + +#define ISF_FORCE_BPP_I 0x0010 // +#define ISF_FORCE_SIZE_I 0x0020 // +#define ISF_SCALE_I 0x0040 // + +#define ISF_PREMULTIPLY_I 0x0100 // supported only by png +#define ISF_NOLOCALIZED_LOAD_I 0x0200 // when loading res:// protocol do not try to load resource from localized resource first + + +HBITMAP MLImageLoaderI_LoadDib(const MLIMAGESOURCE_I *pImgSource); +BOOL MLImageLoaderI_CopyData(MLIMAGESOURCE_I *pisDst, const MLIMAGESOURCE_I *pisSrc); /// Creates copy of MLIMAGESOURCE. +BOOL MLImageLoaderI_FreeData(MLIMAGESOURCE_I *pisDst); /// use it to properly free MLIMAGESOURCE that was set using MLImageLoader_CopyData +BOOL MLImageLoaderI_CheckExist(const MLIMAGESOURCE_I *pis); + + + +#endif //NULLSOFT_MEIDALIBRARY_IMAGE_SOURCE_HEADER \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/ml_ipc.h b/Src/Plugins/General/gen_ml/ml_ipc.h new file mode 100644 index 00000000..341d11c4 --- /dev/null +++ b/Src/Plugins/General/gen_ml/ml_ipc.h @@ -0,0 +1,92 @@ +#ifndef _WA_ML_IPC_H_ +#define _WA_ML_IPC_H_ + +#include +#include + +//# IPC_LIBRARY_SENDTOMENU +/* + Basically for the sendto menu, do this: + librarySendToMenuStruct s={0,}; + int IPC_LIBRARY_SENDTOMENU=SendMessage(hMainWindow,WM_WA_IPC,(WPARAM)&"LibrarySendToMenu",IPC_REGISTER_WINAMP_IPCMESSAGE); + if (IPC_LIBRARY_SENDTOMENU > 65536 && SendMessage(hMainWindow,WM_WA_IPC,(WPARAM)0,IPC_LIBRARY_SENDTOMENU)==0xffffffff) + { + s.mode=1; + s.hwnd=myWnd; + s.data_type=mydatatype; + s.build_hMenu=mysubmenu; + } + TrackPopupMenu(); + if (ret) + { + if (unrecognized ret) + { + if (s.mode == 2) + { + s.menu_id=ret; + if (SendMessage(hMainWindow,WM_WA_IPC,(WPARAM)&s,IPC_LIBRARY_SENDTOMENU)==0xffffffff) + { + // build my data. + s.mode=3; + s.data=my data; + SendMessage(hMainWindow,WM_WA_IPC,(WPARAM)&s,IPC_LIBRARY_SENDTOMENU) + // free my data + } + } + } + } + if (s.mode) + { + s.mode=4; + SendMessage(hMainWindow,WM_WA_IPC,(WPARAM)&s,IPC_LIBRARY_SENDTOMENU) // cleanup + } + + ... + + WM_INITMENUPOPUP: + if (wParam && (HMENU)wParam == s.build_hMenu && s.mode==1) + { + if (SendMessage(hMainWindow,WM_WA_IPC,(WPARAM)&s,IPC_LIBRARY_SENDTOMENU)==0xffffffff) + s.mode=2; + } + + kinda complex and gross, yes? + + + */ +typedef struct { // always init this to all 0s + + int mode; // mode can be 0, to see if sendto is available. If sendto is available, we'll return 0xffffffff. + // mode = 1 means we are building the menu. data_type should be set. on success, this will return 0xffffffff. + // mode = 2 means we are querying if our menu_id is handled by the sendto menu. returns 0xffffffff if it was. + // mode = 3 means we are sending the data. return value is not important =) + // be sure to have set data_type, menu_id, and data, for this one. + // mode = 4 means to cleanup this structure. + + // build parms + HMENU build_hMenu; // set this to the HMENU + int build_start_id; // override the start and endpoints of IDs it can use + int build_end_id; // or leave as 0s for defaults. + + // type used for build and send modes (ML_TYPE_*) + int data_type; + + int menu_id; + + void *data; // only used in mode = 3 + + HWND hwnd; // parent for sendto + + // specify ctx[1]=1 to disable 'enqueue in winamp' on the menu + // specify ctx[2]=(ML_TYPE_*)+1 as the originally intended data_type + intptr_t ctx[32]; // internal winamp use +} librarySendToMenuStruct; + +//IPC_GETMLWINDOW +//int IPC_GETMLWINDOW=SendMessage(hMainWindow,WM_WA_IPC,(WPARAM)&"LibraryGetWnd",IPC_REGISTER_WINAMP_IPCMESSAGE); +// +//then: +// to ensure library's db is loaded: if (IPC_GETMLWINDOW>65536) SendMessage(hMainWindow,WM_WA_IPC,-1,IPC_GETMLWINDOW); +// or to get the HWND of the library (for library APIs): HWND hwndlib = IPC_GETMLWINDOW>65536 ? SendMessage(hMainWindow,WM_WA_IPC,0,IPC_GETMLWINDOW) : 0; + +#endif//_WA_ML_IPC_H_ diff --git a/Src/Plugins/General/gen_ml/ml_ipc_0313.h b/Src/Plugins/General/gen_ml/ml_ipc_0313.h new file mode 100644 index 00000000..f83e205d --- /dev/null +++ b/Src/Plugins/General/gen_ml/ml_ipc_0313.h @@ -0,0 +1,1852 @@ +#ifndef NULLOSFT_MEDIALIBRARY_IPC_EXTENSION_HEADER_0x0313 +#define NULLOSFT_MEDIALIBRARY_IPC_EXTENSION_HEADER_0x0313 + +#include + +//////////////////////////////////////////////////////////////////// +// Notifications +// +// + +#define ML_MSG_NAVIGATION_FIRST 0x106 // base + +#define ML_MSG_NAVIGATION_ONMOVE (ML_MSG_NAVIGATION_FIRST + 1) // Notifies that item was moved. + // param1 = item handle. param2 - item old sort order, param3 - item new sort order + // Return value ignored. +#define ML_MSG_NAVIGATION_ONDELETE (ML_MSG_NAVIGATION_FIRST + 2) // Notifies that item is being deleted. + // param1 = item handle. + // Return value ignored. +#define ML_MSG_NAVIGATION_ONBEGINTITLEEDIT (ML_MSG_NAVIGATION_FIRST + 3) // Notifies about the start of title editing for an item. + // param1 = item handle. + // Return TRUE to cancel title editing. +#define ML_MSG_NAVIGATION_ONENDTITLEEDIT (ML_MSG_NAVIGATION_FIRST + 4) // Notifies about the end of title editing for an item. + // param1 = item handle, param2 = (LPCWSTR)pszNewTitle. + // Return TRUE to accept new title. if (NULL == pszNewText) editing was canceled and return value ignored. + +#define ML_MSG_NAVIGATION_ONCUSTOMDRAW (ML_MSG_NAVIGATION_FIRST + 5) // Perform custom draw here. + // param1 = item handle, param2 = (NAVITEMDRAW*)pnicd, param3 = (LPARAM)lParam. + // Return combination of the NICDRF_XXX flags. + +#define ML_MSG_NAVIGATION_ONHITTEST (ML_MSG_NAVIGATION_FIRST + 6) // Perform custom hit test + // param1 = item handle, param2 = (NAVHITTEST*)pnavHitTest, param3 = (LPARAM)lParam. + // Return non zero if you handle it. + +#define ML_MSG_NAVIGATION_ONSETCURSOR (ML_MSG_NAVIGATION_FIRST + 7) // Set cursor. + // param1 = item handle, param2 = 0, param3 = (LPARAM)lParam. + // Return 1 if you proccessed this messeage and set cursor, -1 if you want default processing, 0 - if not yours. + +#define ML_MSG_NAVIGATION_ONDESTROY (ML_MSG_NAVIGATION_FIRST + 8) // Notifies that navigation control about to be destroyed. + // param1 = not used, param2 = not used, param3 = not used. + // Return value ignored. + +#define ML_MSG_NAVIGATION_CONTEXTMENU (ML_MSG_NAVIGATION_FIRST + 9) // Notifies that context menu was requested for the item + // param1 = item handle, param2 = (HWND)hHost, param3 = (POINTS)cursor + // Return non zero if you handle it. + +#define ML_MSG_NAVIGATION_HELP (ML_MSG_NAVIGATION_FIRST + 10) // Notifies that help was requested for the item + // param1 = item handle, param2 = (HWND)hHost, param3 = (POINTS)cursor + // Return non zero if you handle it. + +#define ML_MSG_MLVISIBLE 0x410 // Notifies that Media Library Window changed visible state. + // param1 = (BOOL)fVisible, param2 = Not used, param3 = Not used. + // Return value ignored. (Return zero). + + +///////////////////////////////////////////////////////////////////// +// Drag&Drop for outsiders <<< will be discontinued at some point >>> +// + +#define WAMLM_DRAGDROP L"WaMediaLibraryDragDrop" // use RegisterWindowMessage + +// values for wParam +#define DRAGDROP_DRAGENTER 0 // lParam = (LPARAM)(mlDropItemStruct*)pdis. return TRUE if you accepted this message. +#define DRAGDROP_DRAGLEAVE 1 // lParam = not used. Return: nothing. +#define DRAGDROP_DRAGOVER 2 // lParam = (LPARAM)(mlDropItemStruct*)pdis. Return: nothing. +#define DRAGDROP_DROP 3 // (LPARAM)(mlDropItemStruct*)pdis. Return: nothing. + +//////////////////////////////////////////////////////////////////// +// IPC +// + + +#ifdef __cplusplus +#define SENDMSG(__hwnd, __msgId, __wParam, __lParam) ::SendMessageW((__hwnd), (__msgId), (__wParam), (__lParam)) +#else +#define SENDMSG(__hwnd, __msgId, __wParam, __lParam) SendMessageW((__hwnd), (__msgId), (__wParam), (__lParam)) +#endif // __cplusplus + +#define SENDMLIPC(__hwndML, __ipcMsgId, __param) SENDMSG((__hwndML), WM_ML_IPC, (WPARAM)(__param), (LPARAM)(__ipcMsgId)) +#define SENDWAIPC(__hwndWA, __ipcMsgId, __param) SENDMSG((__hwndWA), WM_WA_IPC, (WPARAM)(__param), (LPARAM)(__ipcMsgId)) + + +///////////////////////////////////////////////////////////////// +// Getting MediaLibrary (gen_ml) version +// if version is less then 3.13 return value will be 0. + +// Build types +#define ML_BUILDTYPE_FINAL 0x0000 +#define ML_BUILDTYPE_BETA 0x0001 +#define ML_BUILDTYPE_NIGHTLY 0x0002 + +// Message +#define ML_IPC_GETVERSION 0x0000L // Returns DWORD where LOWORD contains version number and HIWORD - build type . param is ignored. +#define GetMLVersion(/*HWND*/ hwndML) (DWORD)SENDMLIPC(hwndML, ML_IPC_GETVERSION, 0) + +#define ML_IPC_SHOW_HELP (0x1200L - 1) // Shows Help. param = (const wchar_t*)helpUrl, Returns TRUE on SUCCESS. +#define MediaLibrary_ShowHelp(/*HWND*/ _hwndML, /* const wchar_t* */ _helpUrl)\ + ((BOOL)SENDMLIPC((_hwndML), ML_IPC_SHOW_HELP, (WPARAM)(_helpUrl))) + +////////////////////////////////////////////////////////////////// +// MLImageLoader +// +// +// + +/// Structs +typedef struct _MLIMAGESOURCE +{ + INT cbSize; // sizeof(MLIMAGESOURCE) + HINSTANCE hInst; // + LPCWSTR lpszName; // + UINT bpp; // + INT xSrc; // + INT ySrc; // + INT cxSrc; // + INT cySrc; // + INT cxDst; // + INT cyDst; // + UINT type; // + UINT flags; // +} MLIMAGESOURCE; + +// Image Source types: +#define SRC_TYPE_BMP 0x01 // hInst = depends on ISF_LOADFROMFILE, lpszName = resource (file) name. To make resource name from resource id use MAKEINTERESOURCEW(). +#define SRC_TYPE_PNG 0x02 // hInst = depends on ISF_LOADFROMFILE, lpszName = resource (file) name. To make resource name from resource id use MAKEINTERESOURCEW(). +#define SRC_TYPE_HBITMAP 0x03 // hInst = NULL, lpszName =(HBITMAP)hbmp. +#define SRC_TYPE_HIMAGELIST 0x04 // hInst = (HIMAGELIST)himl, lpszName = MAKEINTERESOURCEW(index). Make Sure that common controls initialized before using this. + +// Image Source flags: +#define ISF_LOADFROMFILE 0x0001 // Load image from file. hInst ignored. +#define ISF_USE_OFFSET 0x0002 // xSrc and ySrc valid. +#define ISF_USE_SIZE 0x0004 // cxSrc and cySrc valid. + +#define ISF_FORCE_BPP 0x0010 // +#define ISF_FORCE_SIZE 0x0020 // +#define ISF_SCALE 0x0040 // + +#define ISF_PREMULTIPLY 0x0100 // supported only by png +#define ISF_NOLOCALIZED_LOAD 0x0200 // when loading res:// protocol do not try to load resource from localized resource first + + +// use with ML_IPC_IMAGESOURCE_COPYSTRUCT +typedef struct _MLIMAGESOURCECOPY +{ + MLIMAGESOURCE *src; // pointer to the source struct + MLIMAGESOURCE *dest; // pointer to the destination struct +} MLIMAGESOURCECOPY; + +// Messages + +#define ML_IPC_IMAGELOADER_FIRST 0x1200L + +#define ML_IPC_IMAGELOADER_LOADDIB (ML_IPC_IMAGELOADER_FIRST + 1) // Loads Dib from source. param = (MLIMAGESOURCE*)pmlis; +#define ML_IPC_IMAGELOADER_COPYSTRUCT (ML_IPC_IMAGELOADER_FIRST + 2) // Creates copy of image source struct. Use ML_IPC_IMAGESOURCE_FREESTRUCT to free copy data. Returns TRUE if success. param = (MLIMAGESOURCECOPY*)pmlis; +#define ML_IPC_IMAGELOADER_FREESTRUCT (ML_IPC_IMAGELOADER_FIRST + 3) // Use it to free MLIMAGESOURCE that was filled using ML_IPC_IMAGESOURCE_COPYSTRUCT. Returns TRUE if sucess. param = (MLIMAGESOURCE*)pmlis; +#define ML_IPC_IMAGELOADER_CHECKEXIST (ML_IPC_IMAGELOADER_FIRST + 4) // Performs simple checks to validate source. Returns TRUE if sucess. param = (MLIMAGESOURCE*)pmlis; + + +// Macros +#define MLImageLoader_LoadDib(/*HWND*/ hwndML, /*MLIMAGESOURCE**/ pmlisStruct) \ + (HBITMAP)SENDMLIPC((hwndML), ML_IPC_IMAGELOADER_LOADDIB, (WPARAM)(pmlisStruct)) + +#define MLImageLoader_CopyStruct(/*HWND*/ hwndML, /*MLIMAGESOURCECOPY**/ pmlisCopyStrust) \ + ((BOOL)SENDMLIPC((hwndML), ML_IPC_IMAGELOADER_COPYSTRUCT, (WPARAM)(pmlisCopyStrust))) + +#define MLImageLoader_FreeStruct(/*HWND*/ hwndML, /*MLIMAGESOURCE*/ pmlisStruct) \ + ((BOOL)SENDMLIPC((hwndML), ML_IPC_IMAGELOADER_FREESTRUCT, (WPARAM)(pmlisStruct))) + +#define MLImageLoader_CheckExist(/*HWND*/ hwndML, /*MLIMAGESOURCE**/ pmlisStruct) \ + ((BOOL)SENDMLIPC((hwndML), ML_IPC_IMAGELOADER_CHECKEXIST, (WPARAM)(pmlisStruct))) + + +////////////////////////////////////////////////////////////////////////////// +// MLImageFilter +// +// +// +// Filter callback +typedef BOOL (CALLBACK *MLIMAGEFILTERPROC)(LPBYTE /*pData*/, LONG /*cx*/, LONG /*cy*/, INT /*bpp*/, COLORREF /*rgbBk*/, + COLORREF /*rgbFg*/, INT_PTR /*imageTag*/, LPARAM /*lParam*/); + +typedef struct _MLIMAGEFILTERINFO +{ + INT cbSize; // sizeof(MLIMAGEFILTERINFO) + UINT mask; // mask + GUID uid; // filterUID + MLIMAGEFILTERPROC fnProc; // procedure to call + LPARAM lParam; // user parameter ( will be passed to every call of filter proc) + UINT fFlags; // filter flags + LPWSTR pszTitle; // filter title + INT cchTitleMax; // max title size (only for get info) +} MLIMAGEFILTERINFO; + +typedef struct _MLIMAGEFILTERAPPLYEX +{ + INT cbSize; + GUID filterUID; // Filter to use. + LPBYTE pData; // Pointer to dib data. + LONG cx; // Image width in pixels. + LONG cy; // Image height in pixels. + INT bpp; // Bits per pixel + COLORREF rgbBk; // Back color to use + COLORREF rgbFg; // Front color to use + INT_PTR imageTag; // Image tag. will be passed to the filter +} MLIMAGEFILTERAPPLYEX; + +// filter flags +#define MLIFF_IGNORE_BKCOLOR 0x0001 // Filter result doesn't depend on rgbBk. +#define MLIFF_IGNORE_FGCOLOR 0x0002 // Filter result doesn't depend on rgbFg. + +// mask used to set/get filter info +#define MLIFF_TITLE 0x0001 // +#define MLIFF_PARAM 0x0002 // +#define MLIFF_FLAGS 0x0004 // +#define MLIFF_PROC 0x0008 // + + +// Messages +#define ML_IPC_IMAGEFILTER_FIRST 0x1240L + +#define ML_IPC_IMAGEFILTER_REGISTER (ML_IPC_IMAGEFILTER_FIRST + 1) // Registers filter in ml. param = (MLIMAGEFILTERINFO*)pmlif. uid and proc must be always set. Returns TRUE on success. +#define ML_IPC_IMAGEFILTER_UNREGISTER (ML_IPC_IMAGEFILTER_FIRST + 2) // Unregisters filter. param = (const GUID*)filterUID. Returns TRUE on success. +#define ML_IPC_IMAGEFILTER_GETINFO (ML_IPC_IMAGEFILTER_FIRST + 3) // Get fitler info. param = (MLIMAGEFILTERINFO*)pmlif. set mask to whatever you wnat to get. set uid to filterUID. Returnd TRUE on success +#define ML_IPC_IMAGEFILTER_APPLYEX (ML_IPC_IMAGEFILTER_FIRST + 4) // Apply Filter. param = (MLIMAGEFILTERAPPLYEX)pmlif. Runs Filter over image. Returns TRUE if ok. + +// Macros +#define MLImageFilter_Register(/*HWND*/ hwndML, /*MLIMAGEFILTERINFO**/ pmlifInfo) \ + (BOOL)SENDMLIPC((hwndML), ML_IPC_IMAGEFILTER_REGISTER, (WPARAM)(pmlifInfo)) + +#define MLImageFilter_Unregister(/*HWND*/ hwndML, /*const GUID**/ filterUID) \ + (BOOL)SENDMLIPC((hwndML), ML_IPC_IMAGEFILTER_UNREGISTER, (WPARAM)(filterUID)) + +#define MLImageFilter_GetInfo(/*HWND*/ hwndML, /*MLIMAGEFILTERINFO**/ pmlifInfo) \ + (BOOL)SENDMLIPC((hwndML), ML_IPC_IMAGEFILTER_GETINFO, (WPARAM)(pmlifInfo)) + +#define MLImageFilter_ApplyEx(/*HWND*/ hwndML, /*MLIMAGEFILTERAPPLY**/ pmlifApply) \ + (BOOL)SENDMLIPC((hwndML), ML_IPC_IMAGEFILTER_APPLYEX, (WPARAM)(pmlifApply)) + +/// Default Filters + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +extern const GUID MLIF_FILTER1_UID; // {8A054D1F-E38E-4cc0-A78A-F216F059F57E} +extern const GUID MLIF_FILTER2_UID; // {BE1A6A40-39D1-4cfb-8C33-D8988E8DD2F8} +extern const GUID MLIF_FILTER3_UID; // {721E9E62-CC6D-4fd7-A6ED-DD4CD2B2612E} +extern const GUID MLIF_GRAYSCALE_UID; // {B6310C20-E731-44dd-83BD-FBC3349798F2} +extern const GUID MLIF_BLENDONBK_UID; // {526C6F4A-C979-4d6a-B8ED-1A90F5A26F7B} +extern const GUID MLIF_BUTTONBLEND_UID; // {E61C5E67-B2CC-4f89-9C95-40D18FCAF1F8} +extern const GUID MLIF_BUTTONBLENDPLUSCOLOR_UID; // {3619BA52-5088-4f21-9AF1-C5FCFE5AAA99} +extern const GUID MLIF_FILTER1_PRESERVE_ALPHA_UID; // {F1DD3228-7DA8-4524-B7D3-46F651BFB680} + +#ifdef __cplusplus +} +#endif // __cplusplus + + +////////////////////////////////////////////////////////////////////////////// +// MLImageList +// +// +// + +typedef LPVOID HMLIMGLST; + +// Used with ML_IPC_IMAGELIST_CREATE +typedef struct _MLIMAGELISTCREATE +{ + INT cx; // image width + INT cy; // image height + UINT flags; // see MLIMAGELISTCREATE flags + INT cInitial; // initial size + INT cGrow; // realloc step + INT cCacheSize; // how many colors can be cached for each entry +}MLIMAGELISTCREATE; + +// MLIMAGELISTCREATE flags +#define MLILC_COLOR24 0x00000018 // Use a 24-bit DIB section. +#define MLILC_COLOR32 0x00000020 // Use a 32-bit DIB section. +#define MLILC_MASK 0x00000001 // Creates underlying HIMAGELIST with mask parameter. + +// Used with ML_IPC_IMAGELIST_GETREALINDEX +typedef struct _MLIMAGELISTREALINDEX +{ + INT cbSize; // sizeof(MLIMAGELISTREALINDEX) + HMLIMGLST hmlil; // MLImageList handle returned from ML_IPC_IMAGELIST_CREATE. + INT mlilIndex; // Image index in MLImageList + COLORREF rgbBk; // Requested background color. + COLORREF rgbFg; // Requested foreground color. +} MLIMAGELISTREALINDEX; + +// Used with ML_IPC_IMAGELIST_ADD, ML_IPC_IMAGELIST_REPLACE, ML_IPC_IMAGELIST_REMOVE +typedef struct _MLIMAGELISTITEM +{ + INT cbSize; // sizeof(MLIMAGELISTITEM) + HMLIMGLST hmlil; // MLImageList handle returned from ML_IPC_IMAGELIST_CREATE. + MLIMAGESOURCE *pmlImgSource; // handle to the MLIMAGESOURCE struct. + GUID filterUID; // MLImageFilter to use. // Set to GUID_NULL if you don't wnat any filtering. + INT_PTR nTag; // Tag assign to image. Note: MLImageList dosn't check if tag is unique! + INT mlilIndex; // MLImageList index. This field ignored in ML_IPC_IMAGELIST_ADD. +} MLIMAGELISTITEM; + +// Used with ML_IPC_IMAGELIST_GETIMAGESIZE +typedef struct _MLIMAGELISTIMAGESIZE +{ + HMLIMGLST hmlil; // MLImageList handle returned from ML_IPC_IMAGELIST_CREATE. + INT cx; // Image width + INT cy; // Image height +} MLIMAGELISTIMAGESIZE; + +// Used with ML_IPC_IMAGELIST_GETINDEXFROMTAG and ML_IPC_IMAGELIST_GETTAGFROMINDEX +typedef struct _MLIMAGELISTTAG +{ + HMLIMGLST hmlil; // MLImageList handle returned from ML_IPC_IMAGELIST_CREATE. + INT mlilIndex; // MLImageList image index. ML_IPC_IMAGELIST_GETINDEXFROMTAG will contain index if any. + INT_PTR nTag; // MLImageList image tag. ML_IPC_IMAGELIST_GETTAGFROMINDEX will contain tag if any. +} MLIMAGELISTTAG; + +#define ML_IPC_IMAGELIST_FIRST 0x1260L + +#define ML_IPC_IMAGELIST_CREATE (ML_IPC_IMAGELIST_FIRST + 1) // Creates new MLImageList +#define ML_IPC_IMAGELIST_DESTROY (ML_IPC_IMAGELIST_FIRST + 2) // Destroys MLImageList +#define ML_IPC_IMAGELIST_GETREALLIST (ML_IPC_IMAGELIST_FIRST + 3) // Returns handle to the system HIMAGELIST +#define ML_IPC_IMAGELIST_GETREALINDEX (ML_IPC_IMAGELIST_FIRST + 4) // Returns image index in the HIMAGELIST if ok or -1 if error. If image with this colors is not cached yet it will be loaded and if Filter function speicified it will be Filtered + // ( user param in Filter function will be set to the tag assigned to the image) +#define ML_IPC_IMAGELIST_ADD (ML_IPC_IMAGELIST_FIRST + 5) // Add new item. Returns item index or -1 if failed. + // When adding images IMAGESOURCE can have different bpp from imagelist. After image loaded it first filtered and than added to to the system imagelist +#define ML_IPC_IMAGELIST_REPLACE (ML_IPC_IMAGELIST_FIRST + 6) // Replace existing item. Returns TRUE if ok. +#define ML_IPC_IMAGELIST_REMOVE (ML_IPC_IMAGELIST_FIRST + 7) // Removes existing item. Returns TRUE if ok. +#define ML_IPC_IMAGELIST_GETIMAGESIZE (ML_IPC_IMAGELIST_FIRST + 8) // Retrives size of stored images. Returns TRUE if ok. +#define ML_IPC_IMAGELIST_GETIMAGECOUNT (ML_IPC_IMAGELIST_FIRST + 9) // Gets images count in MLImageList. +#define ML_IPC_IMAGELIST_GETINDEXFROMTAG (ML_IPC_IMAGELIST_FIRST + 10) // Finds item with specified tag. Returns TRUE if found. +#define ML_IPC_IMAGELIST_GETTAGFROMINDEX (ML_IPC_IMAGELIST_FIRST + 11) // Finds item tag by index. Returns TRUE if found. +#define ML_IPC_IMAGELIST_CHECKEXIST (ML_IPC_IMAGELIST_FIRST + 12) // Performs simple checks to validate source. Returns TRUE ok. + +// Macros +#define MLImageList_Create(/*HWND*/ hwndML, /*MLIMAGELISTCREATE**/ pmlilCreate) \ + (HMLIMGLST)SENDMLIPC((hwndML), ML_IPC_IMAGELIST_CREATE, (WPARAM)(pmlilCreate)) + +#define MLImageList_Destroy(/*HWND*/ hwndML, /*HMLIMGLST*/ hmlil) \ + (BOOL)SENDMLIPC((hwndML), ML_IPC_IMAGELIST_DESTROY, (WPARAM)(hmlil)) + +#define MLImageList_GetRealList(/*HWND*/ hwndML, /*HMLIMGLST*/ hmlil) \ + (HIMAGELIST)SENDMLIPC((hwndML), ML_IPC_IMAGELIST_GETREALLIST, (WPARAM)(hmlil)) + +#define MLImageList_GetRealIndex(/*HWND*/ hwndML, /*MLIMAGELISTREALINDEX**/ pmlilRealIndex) \ + (INT)SENDMLIPC((hwndML), ML_IPC_IMAGELIST_GETREALINDEX, (WPARAM)(pmlilRealIndex)) + +#define MLImageList_Add(/*HWND*/ __hwndML, /*MLIMAGELISTITEM**/ pmlilItem) \ + (INT)SENDMLIPC((__hwndML), ML_IPC_IMAGELIST_ADD, (WPARAM)(pmlilItem)) + +#define MLImageList_Add2(/*HWND*/ __hwndML, /*HMLIMGLST*/ __hmlil, /*GUID*/ __filterUID, /*MLIMAGESOURCE* */__pmlis, /*INT_PTR*/ __nTag) \ + { MLIMAGELISTITEM mli;\ + mli.cbSize = sizeof(MLIMAGELISTITEM);\ + mli.hmlil = (__hmlil);\ + mli.filterUID = (__filterUID);\ + mli.pmlImgSource = (__pmlis);\ + mli.nTag = (__nTag);\ + ((INT)SENDMLIPC((__hwndML), ML_IPC_IMAGELIST_ADD, (WPARAM)(&mli)));} + +#define MLImageList_Replace(/*HWND*/ hwndML, /*MLIMAGELISTITEM**/ pmlilItem) \ + (BOOL)SENDMLIPC((hwndML), ML_IPC_IMAGELIST_REPLACE, (WPARAM)(pmlilItem)) + +#define MLImageList_Remove(/*HWND*/ hwndML, /*MLIMAGELISTITEM**/ pmlilItem) \ + (BOOL)SENDMLIPC((hwndML), ML_IPC_IMAGELIST_REMOVE, (WPARAM)(pmlilItem)) + +#define MLImageList_GetImageSize(/*HWND*/ hwndML, /*MLIMAGELISTIMAGESIZE**/ pmlilImageSize) \ + (BOOL)SENDMLIPC((hwndML), ML_IPC_IMAGELIST_GETIMAGESIZE, (WPARAM)(pmlilImageSize)) + +#define MLImageList_GetImageCount(/*HWND*/ hwndML, /*HMLIMGLST*/ hmlil) \ + (INT)SENDMLIPC((hwndML), ML_IPC_IMAGELIST_GETIMAGECOUNT, (WPARAM)(hmlil)) + +#define MLImageList_GetIndexFromTag(/*HWND*/ hwndML, /*MLIMAGELISTTAG**/ pmlilTag)\ + (BOOL)SENDMLIPC((hwndML), ML_IPC_IMAGELIST_GETINDEXFROMTAG, (WPARAM)(pmlilTag)) + +#define MLImageList_GetTagFromIndex(/*HWND*/ hwndML, /*MLIMAGELISTTAG**/ pmlilTag)\ + (BOOL)SENDMLIPC((hwndML), ML_IPC_IMAGELIST_GETTAGFROMINDEX, (WPARAM)(pmlilTag)) + +#define MLImageList_CheckItemExist(/*HWND*/ hwndML, /*MLIMAGELISTITEM**/ pmlilItem)\ + (BOOL)SENDMLIPC((hwndML), ML_IPC_IMAGELIST_CHECKEXIST, (WPARAM)(pmlilItem)) + + +////////////////////////////////////////////////////////////////////////////// +// MLNavigation +// Navigation Control & Items +// +// + +typedef LPVOID HNAVCTRL; +typedef LPVOID HNAVITEM; + +// Structs +// Navigation Item Info +typedef struct _NAVITEM +{ + INT cbSize; // Always set to sizeof(NAVITEM). + HNAVITEM hItem; // handle to the item (must be set for ML_IPC_NAVITEM_GETINFO and ML_IPC_NAVITEM_SETINFO) + UINT mask; // Mask valid values. + INT id; // Item id. + LPWSTR pszText; // Text to display. + INT cchTextMax; // Text buffer length (used to GET item info). + LPWSTR pszInvariant; // Invariant text name. Any unique string you can assign to the item that will not change. + // For expample if item can be renamed by user invariat text will stay the same and can be used to save/load item state and position. + INT cchInvariantMax; // Invariant text buffer size ( used to GET item info). + INT iImage; // Image index. This is image index in the MLImageList assigned to the Navigation control. If you want to use default images set index to -1 + INT iSelectedImage; // Selected image index. This is image index in the MLImageList assigned to the Navigation control. + // You need/want to assign selected image index only if you want to display different image on selection. + UINT state; // Item state. + UINT stateMask; // Item state mask. + UINT style; // Item style. + UINT styleMask; // Item style mask. + HFONT hFont; // Item font. You can set this if you want item to use special font. You are responsible about free font object. If you want item to use default font set hFont to NULL. + LPARAM lParam; // User parameter. You can use it as you want. +} NAVITEM; + +typedef struct _NAVINSERTSTRUCT +{ + HNAVITEM hParent; // Parent Item (NULL to insert in the root). + HNAVITEM hInsertAfter; // insert after item. You can use NCI_FIRST or NCI_LAST or you can use MAKE_NAVITEMSORTORDER to specify order index instead. + NAVITEM item; +} NAVINSERTSTRUCT; + +typedef struct _NAVITEMDRAW +{ + HDC hdc; // Handle to the DC. + COLORREF clrText; // Text Fore color + COLORREF clrTextBk; // Text Back color. + HFONT hFont; // Handle to the selected font. + RECT *prc; // pointer to the item rect. + UINT itemState; // NIIS_XXX + UINT drawStage; // NIDS_XXX + INT iLevel; // Zero-based level of the item being drawn. +} NAVITEMDRAW; + +// ML_IPC_NAVCTRL_ENUMITEMS callback +typedef BOOL (CALLBACK *NAVENUMPROC)(HNAVITEM /*hItem*/, LPARAM /*lParam*/); // Return FALSE to stop enumeration + +// Use it to enumerate navigation items with ML_IPC_NAVCTRL_ENUMITEMS +typedef struct _NAVCTRLENUMPARAMS +{ + NAVENUMPROC enumProc; // Enum proc.. + HNAVITEM hItemStart; // Item to start enumeration from. If hItemStar == NULL enumeration will start from the root. + LPARAM lParam; // User value +} NAVCTRLENUMPARAMS; + +/// Find by name prarams (ML_IPC_NAVCTRL_FINDBYNAME) +typedef struct _NAVCTRLFINDPARAMS +{ + LCID Locale; // Locale to use. Note: when searching using invariant name locale forced to invariant and case ignored. + UINT compFlags; // Name compare flags (NCCF_XXX). + LPWSTR pszName; // Name to look for. + INT cchLength; // Name length (can be -1). + BOOL fFullNameSearch; // Perform full name search. + BOOL fAncestorOk; // When performing full name search if fAncestorOk is set and item with this name not exist - will fall back to closest ancestor +} NAVCTRLFINDPARAMS; + +// Perform hit test +typedef struct _NAVHITTEST +{ + POINT pt; // [in] Point in control coordinates. + UINT flags; // [out] Test result flags + HNAVITEM hItem; // [out] Navigation Item at this point if any. +} NAVHITTEST; + +// used to send item epxnd command ML_IPC_NAVITEM_EXPAND +typedef struct _NAVITEMEXPAND +{ + HNAVITEM hItem; // handle to the item + UINT fCmdExpand; // command to execute (NAITEM_TOGGLE/ NAITEM_EXOPAND/NAITEM_COLLAPSE). +} NAVITEMEXPAND; + +// used to get fullitem name via ML_IPC_NAVITEM_GETFULLNAME +typedef struct _NAVITEMFULLNAME +{ + HNAVITEM hItem; // Handle to the item. + LPWSTR pszText; // Buffer that will recive full name. + INT cchTextMax; // Maximum amount of characters that can be wirtten to buffer. +} NAVITEMFULLNAME; + +// ML_IPC_NAVITEM_GETRECT +typedef struct _NAVITEMGETRECT +{ + HNAVITEM hItem; // [in] Handle to the item. + RECT rc; // [out] Bounding rectangle. + BOOL fItem; // [in] If this parameter is TRUE, the bounding rectangle includes only the text of the item. Otherwise, it includes the entire line that the item occupies in the navigation control. +} NAVITEMGETRECT; + +typedef struct _NAVITEMINAVLIDATE +{ + HNAVITEM hItem; // [in] Handle to the item. + RECT *prc; // [in] If this parameter is set specifies portion of item to invalidate in control coordinates. if it is NULL whole item will be invalidated + BOOL fErase; // [in] If TRUE background will be erased. +} NAVITEMINAVLIDATE; + +//ML_IPC_NAVITEM_MOVE +typedef struct _NAVITEMMOVE +{ + HNAVITEM hItem; // [in] Handle to the item that need to be moved. + HNAVITEM hItemDest; // [in] Handle to the item where to move. + BOOL fAfter; // [in] If set to TRUE hItem will be moved after hItemDest. +} NAVITEMMOVE; + +typedef struct _NAVITEMORDER +{ + HNAVITEM hItem; // Handle to the item that need to be moved. + WORD order; // Minimal order value 1. + UINT flags; // Flags one of the NOF_XXX +} NAVITEMORDER; + +// Nav control styles +#define NCS_NORMAL 0x0000 +#define NCS_FULLROWSELECT 0x0001 +#define NCS_SHOWICONS 0x0002 + +// NAVITEM mask flags. if mask set filed considered to be valid +#define NIMF_ITEMID 0x0001 +#define NIMF_TEXT 0x0002 +#define NIMF_TEXTINVARIANT 0x0004 +#define NIMF_IMAGE 0x0008 +#define NIMF_IMAGESEL 0x0010 +#define NIMF_STATE 0x0020 +#define NIMF_STYLE 0x0040 +#define NIMF_FONT 0x0080 +#define NIMF_PARAM 0x0100 + +// NAVITEM states. +#define NIS_NORMAL 0x0000 +#define NIS_SELECTED 0x0001 +#define NIS_EXPANDED 0x0002 +#define NIS_DROPHILITED 0x0004 +#define NIS_FOCUSED 0x0008 // used with draw item + +// NAVITEM styles +#define NIS_HASCHILDREN 0x0001 // item has children +#define NIS_ALLOWCHILDMOVE 0x0002 // allow children to be moved (re-ordered) +#define NIS_ALLOWEDIT 0x0004 // allow title edit +#define NIS_ITALIC 0x0100 // when displaying item text draw it with italic style +#define NIS_BOLD 0x0200 // when displaying item text draw it with bold style +#define NIS_UNDERLINE 0x0400 // when displaying item text draw it with underline style +#define NIS_CUSTOMDRAW 0x0010 // use ML_MSG_NAVIGATION_ONCUSTOMDRAW +#define NIS_WANTSETCURSOR 0x0020 // item want to recive set cursor notification +#define NIS_WANTHITTEST 0x0040 // item want to monitor/modify hittest results +#define NIS_DEFAULTIMAGE 0x0080 // navigation control will ignore iImage and iSelectedImage + +// NAVITEMINSERT hInsertAfter flags +#define NCI_FIRST ((HNAVITEM)(ULONG_PTR)-0x0FFFF) // insert first item +#define NCI_LAST ((HNAVITEM)(ULONG_PTR)-0x00000) // insert as last item + +// BeginUpdate Lock flags +#define NUF_LOCK_NONE 0x00 // Do not lock... +#define NUF_LOCK_SELECTED 0x01 // Lock selected item position. +#define NUF_LOCK_TOP 0x02 // Lock top item position + +// Name compare flags +#define NICF_DISPLAY 0x0001 // Compare display name (if specified in combination with othe flags will be used last). +#define NICF_INVARIANT 0x0002 // Compare invariant name (if specified in combination with DISPLAY will be used first). +#define NICF_IGNORECASE 0x0004 // Ignore case (always used when comparing invariant names). + +// Navigation control hit test result flags. +#define NAVHT_NOWHERE 0x0001 // +#define NAVHT_ONITEM 0x0002 // +#define NAVHT_ONITEMBUTTON 0x0004 // only if item currently has children +#define NAVHT_ONITEMINDENT 0x0010 // +#define NAVHT_ONITEMRIGHT 0x0020 // +#define NAVHT_ABOVE 0x0100 // +#define NAVHT_BELOW 0x0200 // +#define NAVHT_TORIGHT 0x0400 // +#define NAVHT_TOLEFT 0x0800 // + +// item expand command flags +#define NAVITEM_TOGGLE 0x0000 +#define NAVITEM_EXPAND 0x0001 +#define NAVITEM_COLLAPSE 0x0002 + +// item drawing stages +#define NIDS_PREPAINT 0x0001 +#define NIDS_POSTPAINT 0x0002 + +// item custom draw return flags +#define NICDRF_NOTMINE 0x0000 // It is not your item. +#define NICDRF_DODEFAULT 0x0001 // Perform default drawing. +#define NICDRF_SKIPDEFAULT 0x0002 // Do not perform default drawing. +#define NICDRF_NOTIFYPOSTPAINT 0x0004 // You want to be called after paint. +#define NICDRF_NEWFONT 0x0008 // Font changed. + + +// Messages +#define ML_IPC_NAVIGATION_FIRST 0x1280L + +#define ML_IPC_NAVCTRL_BEGINUPDATE (ML_IPC_NAVIGATION_FIRST + 1) // Use it to lock control updates while you managing items. param = NUF_LOCK_XXX. Do not forget to call ML_IPC_NAVCTRL_ENDUPDATE when done. Returns lock counter or -1 if error. +#define ML_IPC_NAVCTRL_DELETEITEM (ML_IPC_NAVIGATION_FIRST + 2) // Delete Item. param = (WPARAM)(HNAVITEM)hItem. Return TRUE if success. +#define ML_IPC_NAVCTRL_ENDEDITTITLE (ML_IPC_NAVIGATION_FIRST + 13) // Ends Title edit, param = (WPARAM)(BOOL)fCancel, if fCancel == TRUE item editing will be terminated and changes lost otherwise new title will be saved. +#define ML_IPC_NAVCTRL_ENDUPDATE (ML_IPC_NAVIGATION_FIRST + 3) // Call this when you finished updating items. Returns lock counter or -1 if error. if loock reached 0 - window will be updated. +#define ML_IPC_NAVCTRL_ENUMITEMS (ML_IPC_NAVIGATION_FIRST + 4) // Enumerates items in navigation control. param= (WPARAM)(NAVCTRLENUMPARAMS*)pnavEnumParams. Returns TRUE if all items was enumerated successfully. +#define ML_IPC_NAVCTRL_FINDITEMBYID (ML_IPC_NAVIGATION_FIRST + 5) // Search for an item in navigation control by it's id. param = (WPARAM)(INT)id. Returns HNAVITEM on success or NULL. +#define ML_IPC_NAVCTRL_FINDITEMBYNAME (ML_IPC_NAVIGATION_FIRST + 6) // Search for an item in navigation control by it's name. param = (WPARAM)(NAVCTRLFINDPARAMS)pnavFindParams. Returns HNAVITEM on success or NULL. +#define ML_IPC_NAVCTRL_GETIMAGELIST (ML_IPC_NAVIGATION_FIRST + 7) // Returnrs handle to MLImageList associated with navigation control. param = (WPARAM)0. Return HMLIMGLST if success or NULL. +#define ML_IPC_NAVCTRL_GETFIRST (ML_IPC_NAVIGATION_FIRST + 8) // Returns handle to first item in the navigation control. param = (WPARAM)0. Return HNAVITEM or NULL. +#define ML_IPC_NAVCTRL_GETINDENT (ML_IPC_NAVIGATION_FIRST + 14) // Retrieves the amount, in pixels, that child items are indented relative to their parent items. param = (WPARAM)0. +#define ML_IPC_NAVCTRL_GETSELECTION (ML_IPC_NAVIGATION_FIRST + 9) // Returns handle to selected item in the navigation control. param = (WPARAM)0. Return HNAVITEM or NULL. +#define ML_IPC_NAVCTRL_GETHWND (ML_IPC_NAVIGATION_FIRST + 10) // Returns host window HWND. param = (WPARAM)0. Return HWND. +#define ML_IPC_NAVCTRL_HITTEST (ML_IPC_NAVIGATION_FIRST + 11) // Performs hit-test. param = (WPARAM)(NAVHITTEST*)pnavHitTest. Return HNAVITEM if any item under this point or NULL. +#define ML_IPC_NAVCTRL_INSERTITEM (ML_IPC_NAVIGATION_FIRST + 12) // Insert Item. param = (WPARAM)(NAVINSERTSTRUCT*)pnavInsert. Returns handle to created item if you not speicified item id - you wil be assigned auto generated one pnavInsert->id +#define ML_IPC_NAVCTRL_GETSTYLE (ML_IPC_NAVIGATION_FIRST + 15) // Returns NCS_XXX. param = (WPARAM)0. + +#define ML_IPC_NAVITEM_EDITTITLE (ML_IPC_NAVIGATION_FIRST + 58) // Begins Title edit. param = (WPARAM)(HNAVITEM)hItem. Returns TRUE if item edit begins +#define ML_IPC_NAVITEM_ENSUREVISIBLE (ML_IPC_NAVIGATION_FIRST + 40) // Enusres that item visible. param =(WPARAM)(HNAVITEM)hItem. Return TRUE if ok. +#define ML_IPC_NAVITEM_EXPAND (ML_IPC_NAVIGATION_FIRST + 41) // Command to change item expand state. param = (WPARAM)(NAVITEMEXPAND*)hnavItemExpand. Returns TRUE if ok. +#define ML_IPC_NAVITEM_GETCHILD (ML_IPC_NAVIGATION_FIRST + 42) // Returns handle to the child item of current one. param =(WPARAM)(HNAVITEM)hItem. Return HNAVITEM or NULL. +#define ML_IPC_NAVITEM_GETCHILDRENCOUNT (ML_IPC_NAVIGATION_FIRST + 43) // Returns children count. param = (WPARAM)(HNAVITEM)hItem. +#define ML_IPC_NAVITEM_GETFULLNAME (ML_IPC_NAVIGATION_FIRST + 44) // Returns item full name. param = (WPARAM)(NAVITEMFULLNAME*)pnavFullName. Returns INT number of characters copied or 0. Full name includes names of all item parents separated by '/' if character '/' was used in name it is doubled +#define ML_IPC_NAVITEM_GETINFO (ML_IPC_NAVIGATION_FIRST + 45) // Retrives item info. param = (WPARAM)(NAVITEM*)pnavItem. Returns TRUE if success. You must set hItem. +#define ML_IPC_NAVITEM_GETNEXT (ML_IPC_NAVIGATION_FIRST + 46) // Returns handle to the next item of current one. param =(WPARAM)(HNAVITEM)hItem. Return HNAVITEM or NULL. +#define ML_IPC_NAVITEM_GETORDER (ML_IPC_NAVIGATION_FIRST + 47) // Returns item sort order. param = (WPARAM)(HNAVITEM)hItem. Return item sort order or -1 if error. +#define ML_IPC_NAVITEM_GETPARENT (ML_IPC_NAVIGATION_FIRST + 48) // Returns handle to the parent item of current one. param =(WPARAM)(HNAVITEM)hItem. Return HNAVITEM or NULL. +#define ML_IPC_NAVITEM_GETPREVIOUS (ML_IPC_NAVIGATION_FIRST + 49) // Returns handle to the previous item of current one. param =(WPARAM)(HNAVITEM)hItem. Return HNAVITEM or NULL. +#define ML_IPC_NAVITEM_GETRECT (ML_IPC_NAVIGATION_FIRST + 50) // Retrive item rect. param =(WPARAM)(NAVITEMGETRECT*)hnavRect. If the item is visible and the bounding rectangle was successfully retrieved, the return value is TRUE. +#define ML_IPC_NAVITEM_GETSTATE (ML_IPC_NAVIGATION_FIRST + 51) // Returns item state flags. param = (WPARAM)(HNAVITEM)hItem. Return item state flags. +#define ML_IPC_NAVITEM_HASCHILDRENREAL (ML_IPC_NAVIGATION_FIRST + 52) // Returns TRUE if item has at least one child right now. param =(WPARAM)(HNAVITEM)hItem. Return BOOL. +#define ML_IPC_NAVITEM_INVALIDATE (ML_IPC_NAVIGATION_FIRST + 53) // Invalidate Item. param = (WPARAM)(NAVITEMINAVLIDATE*)hnavItemInvalidate. Return TRUE if success. +#define ML_IPC_NAVITEM_MOVE (ML_IPC_NAVIGATION_FIRST + 54) // Move Item inside parent group. param =(WPARAM)(NAVITEMMOVE*)pnavMove. Return TRUE on success. +#define ML_IPC_NAVITEM_SELECT (ML_IPC_NAVIGATION_FIRST + 55) // Select current item. param =(WPARAM)(HNAVITEM)hItem. Return TRUE if success. +#define ML_IPC_NAVITEM_SETINFO (ML_IPC_NAVIGATION_FIRST + 56) // Modifies item info. param = (WPARAM)(NAVITEM*)pnavItem. Returns TRUE if success. You must set hItem. +#define ML_IPC_NAVITEM_SETORDER (ML_IPC_NAVIGATION_FIRST + 57) // Sets item order and modifies all items oreder after it if required. if oder == 0xFFFF order will be set to max + 1 for this group. param = (WPARAM)(NAVITEMORDER*)pnavOrder. Returns new item order or 0xFFFF if error + + +// Macros + +// Use this in ML_IPC_NAVCTRL_INSERTITEM to create hInsertAfter +#define MAKE_NAVITEMSORTORDER(o) ((HNAVITEM)((ULONG_PTR)((WORD)(o)))) +#define IS_NAVITEMSORTORDER(_i) ((((ULONG_PTR)(_i)) >> 16) == 0) + +#define MLNavCtrl_BeginUpdate(/*HWND*/ hwndML, /*UINT*/ fLockFlags) \ + (INT)SENDMLIPC((hwndML), ML_IPC_NAVCTRL_BEGINUPDATE, (WPARAM)(fLockFlags)) + +#define MLNavCtrl_DeleteItem(/*HWND*/ hwndML, /*HNAVITEM*/hItem) \ + (INT)SENDMLIPC((hwndML), ML_IPC_NAVCTRL_DELETEITEM, (WPARAM)(hItem)) + +#define MLNavCtrl_EndEditTitle(/*HWND*/ hwndML, /*BOOL*/fCancel) \ + (BOOL)SENDMLIPC((hwndML), ML_IPC_NAVCTRL_ENDEDITTITLE, (WPARAM)(fCancel)) + +#define MLNavCtrl_EndUpdate(/*HWND*/ hwndML) \ + (INT)SENDMLIPC((hwndML), ML_IPC_NAVCTRL_ENDUPDATE, (WPARAM)0) + +#define MLNavCtrl_EnumItems(/*HWND*/ hwndML, /*NAVCTRLENUMPARAMS**/pnavEnumParams) \ + (BOOL)SENDMLIPC((hwndML), ML_IPC_NAVCTRL_ENUMITEMS, (WPARAM)(pnavEnumParams)) + +#define MLNavCtrl_FindItemById(/*HWND*/ hwndML, /*INT*/itemId) \ + (HNAVITEM)SENDMLIPC((hwndML), ML_IPC_NAVCTRL_FINDITEMBYID, (WPARAM)(itemId)) + +#define MLNavCtrl_FindItemByName(/*HWND*/ hwndML, /*NAVCTRLFINDPARAMS**/pnavFindParams) \ + (HNAVITEM)SENDMLIPC((hwndML), ML_IPC_NAVCTRL_FINDITEMBYNAME, (WPARAM)(pnavFindParams)) + +#define MLNavCtrl_GetImageList(/*HWND*/ hwndML) \ + (HNAVITEM)SENDMLIPC((hwndML), ML_IPC_NAVCTRL_GETIMAGELIST, (WPARAM)0) + +#define MLNavCtrl_GetFirst(/*HWND*/ hwndML) \ + (HNAVITEM)SENDMLIPC((hwndML), ML_IPC_NAVCTRL_GETFIRST, (WPARAM)0) + +#define MLNavCtrl_GetIndent(/*HWND*/ hwndML) \ + (INT)SENDMLIPC((hwndML), ML_IPC_NAVCTRL_GETINDENT, (WPARAM)0) + +#define MLNavCtrl_GetSelection(/*HWND*/ hwndML) \ + (HNAVITEM)SENDMLIPC((hwndML), ML_IPC_NAVCTRL_GETSELECTION, (WPARAM)0) + +#define MLNavCtrl_GetHWND(/*HWND*/ hwndML) \ + (HWND)SENDMLIPC((hwndML), ML_IPC_NAVCTRL_GETHWND, (WPARAM)0) + +#define MLNavCtrl_HitTest(/*HWND*/ hwndML, /*NAVHITTEST**/pnavHitTest) \ + (HNAVITEM)SENDMLIPC((hwndML), ML_IPC_NAVCTRL_HITTEST, (WPARAM)(pnavHitTest)) + +#define MLNavCtrl_InsertItem(/*HWND*/ hwndML, /*NAVINSERTSTRUCT**/pnavInsert) \ + (HNAVITEM)SENDMLIPC((hwndML), ML_IPC_NAVCTRL_INSERTITEM, (WPARAM)(pnavInsert)) + +#define MLNavCtrl_GetStyle(/*HWND*/ hwndML) \ + (DWORD)SENDMLIPC((hwndML), ML_IPC_NAVCTRL_GETSTYLE, (WPARAM)0) + +#define MLNavItem_EditTitle(/*HWND*/ hwndML, /*HNAVITEM*/hItem) \ + (BOOL)SENDMLIPC((hwndML), ML_IPC_NAVITEM_EDITTITLE, (WPARAM)(hItem)) + +#define MLNavItem_EnsureVisible(/*HWND*/ hwndML, /*HNAVITEM*/hItem) \ + (BOOL)SENDMLIPC((hwndML), ML_IPC_NAVITEM_ENSUREVISIBLE, (WPARAM)(hItem)) + +#define MLNavItem_Expand(/*HWND*/ hwndML, /*NAVITEMEXPAND**/pnavItemExpand) \ + (BOOL)SENDMLIPC((hwndML), ML_IPC_NAVITEM_EXPAND, (WPARAM)(pnavItemExpand)) + +#define MLNavItem_GetChild(/*HWND*/ hwndML, /*HNAVITEM*/hItem) \ + (HNAVITEM)SENDMLIPC((hwndML), ML_IPC_NAVITEM_GETCHILD, (WPARAM)(hItem)) + +#define MLNavItem_GetChildrenCount(/*HWND*/ hwndML, /*HNAVITEM*/hItem) \ + (INT)SENDMLIPC((hwndML), ML_IPC_NAVITEM_GETCHILDRENCOUNT, (WPARAM)(hItem)) + +#define MLNavItem_GetFullName(/*HWND*/ hwndML, /*NAVITEMFULLNAME**/pnavFullName) \ + (INT)SENDMLIPC((hwndML), ML_IPC_NAVITEM_GETFULLNAME, (WPARAM)(pnavFullName)) + +#define MLNavItem_GetInfo(/*HWND*/ hwndML, /*NAVITEM**/pnavItem) \ + (INT)SENDMLIPC((hwndML), ML_IPC_NAVITEM_GETINFO, (WPARAM)(pnavItem)) + +#define MLNavItem_GetNext(/*HWND*/ hwndML, /*HNAVITEM*/hItem) \ + (HNAVITEM)SENDMLIPC((hwndML), ML_IPC_NAVITEM_GETNEXT, (WPARAM)(hItem)) + +#define MLNavItem_GetOrder(/*HWND*/ hwndML, /*HNAVITEM*/hItem) \ + (WORD)SENDMLIPC((hwndML), ML_IPC_NAVITEM_GETORDER, (WPARAM)(hItem)) + +#define MLNavItem_GetParent(/*HWND*/ hwndML, /*HNAVITEM*/hItem) \ + (HNAVITEM)SENDMLIPC((hwndML), ML_IPC_NAVITEM_GETPARENT, (WPARAM)(hItem)) + +#define MLNavItem_GetPrevious(/*HWND*/ hwndML, /*HNAVITEM*/hItem) \ + (HNAVITEM)SENDMLIPC((hwndML), ML_IPC_NAVITEM_GETPREVIOUS, (WPARAM)(hItem)) + +#define MLNavItem_GetRect(/*HWND*/ hwndML, /*NAVITEMGETRECT**/pnavRect) \ + (BOOL)SENDMLIPC((hwndML), ML_IPC_NAVITEM_GETRECT, (WPARAM)(pnavRect)) + +#define MLNavItem_GetState(/*HWND*/ hwndML, /*HNAVITEM*/hItem) \ + (BOOL)SENDMLIPC((hwndML), ML_IPC_NAVITEM_GETSTATE, (WPARAM)(hItem)) + +#define MLNavItem_HasChildrenReal(/*HWND*/ hwndML, /*HNAVITEM*/hItem) \ + (BOOL)SENDMLIPC((hwndML), ML_IPC_NAVITEM_HASCHILDRENREAL, (WPARAM)(hItem)) + +#define MLNavItem_Invalidate(/*HWND*/ hwndML, /*NAVITEMINAVLIDATE**/pnavItemInvalidate) \ + (BOOL)SENDMLIPC((hwndML), ML_IPC_NAVITEM_INVALIDATE, (WPARAM)(pnavItemInvalidate)) + +#define MLNavItem_Move(/*HWND*/ hwndML, /*NAVITEMMOVE**/pnavMove) \ + (BOOL)SENDMLIPC((hwndML), ML_IPC_NAVITEM_MOVE, (WPARAM)(pnavMove)) + +#define MLNavItem_Select(/*HWND*/ hwndML, /*HNAVITEM*/hItem) \ + (BOOL)SENDMLIPC((hwndML), ML_IPC_NAVITEM_SELECT, (WPARAM)(hItem)) + +#define MLNavItem_SetInfo(/*HWND*/ hwndML, /*NAVITEM**/pnavItem) \ + (BOOL)SENDMLIPC((hwndML), ML_IPC_NAVITEM_SETINFO, (WPARAM)(pnavItem)) + +#define MLNavItem_SetOrder(/*HWND*/ hwndML, /*NAVITEMORDER**/pNavOrder) \ + (WORD)SENDMLIPC((hwndML), ML_IPC_NAVITEM_SETORDER, (WPARAM)(pNavOrder)) + + +////////////////////////////////////////////////////////////////////////////// +// MLRating +// Rating control +// +// + +typedef struct _RATINGDRAWPARAMS +{ + INT cbSize; // The size of this structure, in bytes. You must specify size !!! + HDC hdcDst; // A handle to the destination device context. + RECT rc; // Rectangle that specifies where the image is drawn. + INT value; // Rating value. + INT maxValue; // Maximum rating value. + INT trackingValue; // Current tracking value. + UINT fStyle; // combination of RDS_XXX + HMLIMGLST hMLIL; // Media Libary ImageList. Set to NULL if you want to use default media library list. + INT index; // index of image inside Media Libary ImageList to use. Ignored if used with default media library list. +} RATINGDRAWPARAMS; + +typedef struct _RATINGHITTESTPARAMS +{ + INT cbSize; // The size of this structure, in bytes. You must specify size !!! + POINT pt; // Client coordinates of the point to test. + RECT rc; // Rectangle that specifies where the image is drawn. + INT maxValue; // Maximum rating value. + UINT fStyle; // combination of RDS_XXX + HMLIMGLST hMLIL; // Media Libary ImageList. Set to NULL if you want to use default media library list. + INT value; // Rating value at this point. + UINT hitFlags; // Variable that receives information about the results of a hit test. +} RATINGHITTESTPARAMS; + +/// Rating_Draw Styles +#define RDS_SHOWEMPTY 0x00000001 // Draw elements that not set. +#define RDS_OPAQUE 0x00000002 // Fill rest of the rectangle with rgbBk. +#define RDS_HOT 0x00000004 // Draw elements as "hot". +#define RDS_LEFT 0x00000000 // Aligns elements to the left. +#define RDS_TOP 0x00000000 // Justifies elements to the top of the rectangle. +#define RDS_RIGHT 0x00000010 // Aligns elements to the right. +#define RDS_BOTTOM 0x00000020 // Justifies elements to the bottom of the rectangle. +#define RDS_HCENTER 0x00000040 // Centers elements horizontally in the rectangle. +#define RDS_VCENTER 0x00000080 // Centers elements horizontally in the rectangle. +#define RDS_NORMAL (RDS_SHOWEMPTY | RDS_OPAQUE | RDS_LEFT | RDS_TOP) + + +// Rating_HitTest hitFlags + +#define RHT_NOWHERE 0x0001 +#define RHT_ONVALUE 0x0002 +#define RHT_ONVALUEABOVE 0x0004 +#define RHT_ONVALUEBELOW 0x0008 +#define RHT_TOLEFT 0x0100 +#define RHT_TORIGHT 0x0200 + + +// Messages +#define ML_IPC_RATING_FIRST 0x1380L + +#define ML_IPC_RATING_DRAW (ML_IPC_RATING_FIRST + 1) // Draws rating. param = (WPARAM)(RATINGDRAWPARAMS*)prdp. return TRUE on success. +#define ML_IPC_RATING_HITTEST (ML_IPC_RATING_FIRST + 2) // Performs hit test. param = (WPARAM)(RATINGDRAWPARAMS*)prdp. returns value. +#define ML_IPC_RATING_CALCRECT (ML_IPC_RATING_FIRST + 3) // calculates minimal rect required to show rating. (param) = (WPARAM)(RECT*)prc. Set prc->left to HMLIMGLST (or NULL to use default) and prc->top to maxValue. Returns TRUE if ok. + +#define MLRating_Draw(/*HWND*/ hwndML, /*RATINGDRAWPARAMS**/prdp) \ + (BOOL)SENDMLIPC((hwndML), ML_IPC_RATING_DRAW, (WPARAM)(RATINGDRAWPARAMS*)(prdp)) + +#define MLRating_HitTest(/*HWND*/ hwndML, /*RATINGHITTESTPARAMS**/prhtp) \ + (INT)SENDMLIPC((hwndML), ML_IPC_RATING_HITTEST, (WPARAM)(RATINGHITTESTPARAMS*)(prhtp)) + + +#define MLRating_CalcRect(/*HWND*/ hwndML, /*HMLIMGLST*/ hmlil, /*INT*/ maxVal, /*RECT**/prc) \ + (BOOL)SENDMLIPC((hwndML), ML_IPC_RATING_CALCRECT, ((prc) ? ((((RECT*)(prc))->left = hmlil), \ + (((RECT*)(prc))->top = maxVal), (WPARAM)(prc)) : (WPARAM)(RECT*)NULL)) + + +////////////////////////////////////////////////////////////////////////////// +// MLRatingColumn +// Helpers to display rating in the listview +// +// + +typedef struct _RATINGCOLUMNPAINT +{ + HWND hwndList; // hwnd of the listview + HDC hdc; // hdc + UINT iItem; // item index + UINT iSubItem; // subitem index + INT value; // database rating value + RECT *prcItem; // whole item rect (plvcd->nmcd.rc) + RECT *prcView; // client area size (you can get it at CDDS_PREPAINT in plvcd->nmcd.rc) + COLORREF rgbBk; // color to use as background (plvcd->clrTextBk) + COLORREF rgbFg; // color to use as foreground (plvcd->clrText) + UINT fStyle; // style to use RCS_XXX +} RATINGCOLUMNPAINT; + +typedef struct _RATINGCOLUMN +{ + HWND hwndList; + UINT iItem; + UINT iSubItem; + INT value; + POINT ptAction; // + BOOL bRedrawNow; // You want list to be redrawn immediatly + BOOL bCanceled; // Used with EndDrag - i + UINT fStyle; // RCS_XXX +} RATINGCOLUMN; + +typedef struct _RATINGBACKTEXT +{ + LPWSTR pszText; + INT cchTextMax; + INT nColumnWidth; // used if style is RCS_ALLIGN_CENTER or RCS_ALLIGN_RIGHT + UINT fStyle; +} RATINGBACKTEXT; + +typedef struct _RATINGWIDTH +{ + INT width; // desired width + UINT fStyle; // RCS_XXX +} RATINGWIDTH; + +// styles +#define RCS_DEFAULT 0xFFFFFFFF // set this if you want to use gen_ml provided style +// layout +#define RCS_ALLIGN_LEFT 0x00000000 // allign column left +#define RCS_ALLIGN_CENTER 0x00000001 // allign column center +#define RCS_ALLIGN_RIGHT 0x00000002 // allign column right +// showepmty +#define RCS_SHOWEMPTY_NEVER 0x00000000 +#define RCS_SHOWEMPTY_NORMAL 0x00000010 +#define RCS_SHOWEMPTY_HOT 0x00000020 +#define RCS_SHOWEMPTY_ANIMATION 0x00000040 +#define RCS_SHOWEMPTY_ALWAYS 0x00000070 +// traking (when) +#define RCS_TRACK_NEVER 0x00000000 +#define RCS_TRACK_WNDFOCUSED 0x00000100 +#define RCS_TRACK_ANCESTORACITVE 0x00000200 +#define RCS_TRACK_PROCESSACTIVE 0x00000400 +#define RCS_TRACK_ALWAYS 0x00000800 +// traking (what) +#define RCS_TRACKITEM_ALL 0x00000000 +#define RCS_TRACKITEM_SELECTED 0x00100000 +#define RCS_TRACKITEM_FOCUSED 0x00200000 + +#define RCS_BLOCKCLICK 0x01000000 +#define RCS_BLOCKUNRATECLICK 0x02000000 +#define RCS_BLOCKDRAG 0x04000000 + +#define RCS_SIZE_ALLOWDECREASE 0x10000000 +#define RCS_SIZE_ALLOWINCREASE 0x20000000 + + +#define ML_IPC_RATINGCOLUMN_FIRST 0x1420L + +#define ML_IPC_RATINGCOLUMN_PAINT (ML_IPC_RATINGCOLUMN_FIRST + 1) // Paints rating column (call it in (CDDS_SUBITEM | CDDS_ITEMPREPAINT) handler). + // param = (WPARAM)(RATINGCOLUMNPAINT*)prcp. if Return TRUE - you need to return SDRF_SKIPDEFAULT +#define ML_IPC_RATINGCOLUMN_CLICK (ML_IPC_RATINGCOLUMN_FIRST + 2) // param = (WPARAM)(RATINGCOLUMN*)prc. You need to set: hwndList, ptAction, bRedraw. iItem - will be set to iItem where click happened. Returns TRUE if click is on the column. +#define ML_IPC_RATINGCOLUMN_TRACK (ML_IPC_RATINGCOLUMN_FIRST + 3) // param = (WPARAM)(RATINGCOLUMN*)prc. You need to set: hwndList, ptAction, iItem, iSubItem, vale, bRedraw. iItem - will be set to iItem where click happened. No return value. +#define ML_IPC_RATINGCOLUMN_CANCELTRACKING (ML_IPC_RATINGCOLUMN_FIRST + 4) // param = (WPARAM)(BOOL)bRedraw. No return value. +#define ML_IPC_RATINGCOLUMN_BEGINDRAG (ML_IPC_RATINGCOLUMN_FIRST + 5) // param = (WPARAM)(RATINGCOLUMN*)prc. Set: hwndList, iItem, iSubItem, value. No return value. +#define ML_IPC_RATINGCOLUMN_DRAG (ML_IPC_RATINGCOLUMN_FIRST + 6) // param = (WPARAM)(POINT*)ppt. Returns TRUE if drag is porcessed. +#define ML_IPC_RATINGCOLUMN_ENDDRAG (ML_IPC_RATINGCOLUMN_FIRST + 7) // param = (WPARAM)(RATINGCOLUMN*)prc. Set: ptAction, bCanceled, bRedrawNow. Returns TRUE if value modified. Sets: iItem, iSubItemVal. +#define ML_IPC_RATINGCOLUMN_ANIMATE (ML_IPC_RATINGCOLUMN_FIRST + 8) // param = (WPARAM)(RATINGCOLUMN*)prc. Set: hwndList, iItem, iSubItem. iSubItem - contains duration in ms. No return. +#define ML_IPC_RATINGCOLUMN_GETMINWIDTH (ML_IPC_RATINGCOLUMN_FIRST + 9) // param = (WPARAM)0. Returns minimum width required to display rating column. +#define ML_IPC_RATINGCOLUMN_FILLBACKSTRING (ML_IPC_RATINGCOLUMN_FIRST + 10) // Generates string thats when displayed equal to the right border of maximum rating value. If rating column has 0 index you can use this string as column text to prevent mouse group selection over rating. If width is less than allowed pszText[0] = 0x00 + // param = (WPARAM)(RATINGBACKTEXT*)prct. Returns TRUE if ok +#define ML_IPC_RATINGCOLUMN_GETWIDTH (ML_IPC_RATINGCOLUMN_FIRST + 11) // Set width according to the policies. param = (WPARAM)(RATINGWIDTH*)prw. Return TRUE if ok. prw->width will contain allowed width + + +#define MLRatingColumn_Paint(/*HWND*/ hwndML, /*RATINGCOLUMNPAINT**/prcp) \ + (BOOL)SENDMLIPC((hwndML), ML_IPC_RATINGCOLUMN_PAINT, (WPARAM)(RATINGCOLUMNPAINT*)(prcp)) + +#define MLRatingColumn_Click(/*HWND*/ hwndML, /*RATINGCOLUMN**/pRating) \ + (BOOL)SENDMLIPC((hwndML), ML_IPC_RATINGCOLUMN_CLICK, (WPARAM)(RATINGCOLUMN*)(pRating)) + +#define MLRatingColumn_Track(/*HWND*/ hwndML, /*RATINGCOLUMN**/pRating) \ + (BOOL)SENDMLIPC((hwndML), ML_IPC_RATINGCOLUMN_TRACK, (WPARAM)(RATINGCOLUMN*)(pRating)) + +#define MLRatingColumn_CancelTracking(/*HWND*/ hwndML, /*BOOL*/bRedrawNow) \ + (BOOL)SENDMLIPC((hwndML), ML_IPC_RATINGCOLUMN_CANCELTRACKING, (WPARAM)(BOOL)(bRedrawNow)) + +#define MLRatingColumn_BeginDrag(/*HWND*/ hwndML, /*RATINGCOLUMN**/pRating) \ + (BOOL)SENDMLIPC((hwndML), ML_IPC_RATINGCOLUMN_BEGINDRAG, (WPARAM)(RATINGCOLUMN*)(pRating)) + +#define MLRatingColumn_Drag(/*HWND*/ hwndML, /*POINT**/ppt) \ + (BOOL)SENDMLIPC((hwndML), ML_IPC_RATINGCOLUMN_DRAG, (WPARAM)(POINT*)(ppt)) + +#define MLRatingColumn_EndDrag(/*HWND*/ hwndML, /*RATINGCOLUMN**/pRating) \ + (BOOL)SENDMLIPC((hwndML), ML_IPC_RATINGCOLUMN_ENDDRAG, (WPARAM)(RATINGCOLUMN*)(pRating)) + +#define MLRatingColumn_Animate(/*HWND*/ hwndML, /*RATINGCOLUMN**/pRating) \ + (BOOL)SENDMLIPC((hwndML), ML_IPC_RATINGCOLUMN_ANIMATE, (WPARAM)(RATINGCOLUMN*)(pRating)) + +#define MLRatingColumn_GetMinWidth(/*HWND*/ hwndML) \ + (INT)SENDMLIPC((hwndML), ML_IPC_RATINGCOLUMN_GETMINWIDTH, (WPARAM)0) + +#define MLRatingColumn_FillBackString(/*HWND*/ hwndML, /*RATINGBACKTEXT**/prbt) \ + (BOOL)SENDMLIPC((hwndML), ML_IPC_RATINGCOLUMN_FILLBACKSTRING, (WPARAM)(RATINGBACKTEXT*)(prbt)) + +#define MLRatingColumn_GetWidth(/*HWND*/ hwndML, /*RATINGWIDTH**/prw) \ + (BOOL)SENDMLIPC((hwndML), ML_IPC_RATINGCOLUMN_GETWIDTH, (WPARAM)(RATINGWIDTH*)(prw)) + + +////////////////////////////////////////////////////////////////////////////// +// MLCloud +// Cloud control +// +// + +typedef struct _CLOUDDRAWPARAMS +{ + INT cbSize; // The size of this structure, in bytes. You must specify size !!! + HDC hdcDst; // A handle to the destination device context. + RECT rc; // Rectangle that specifies where the image is drawn. + INT value; // Database cloud status (1=full,2=partial,3=unavail) + HMLIMGLST hMLIL; // Media Libary ImageList. Set to NULL if you want to use default media library list. + INT index; // index of image inside Media Libary ImageList to use. Ignored if used with default media library list. +} CLOUDDRAWPARAMS; + +#define ML_IPC_CLOUD_FIRST 0x1500L + +#define ML_IPC_CLOUD_DRAW (ML_IPC_CLOUD_FIRST + 1) // Draws cloud status. param = (WPARAM)(CLOUDDRAWPARAMS*)prdp. return TRUE on success. +#define ML_IPC_CLOUD_CALCRECT (ML_IPC_CLOUD_FIRST + 2) // calculates minimal rect required to show cloud status. (param) = (WPARAM)(RECT*)prc. Set prc->left to HMLIMGLST (or NULL to use default) and prc->top to maxValue. Returns TRUE if ok. + +#define MLCloud_Draw(/*HWND*/ hwndML, /*CLOUDDRAWPARAMS**/prdp) \ + (BOOL)SENDMLIPC((hwndML), ML_IPC_CLOUD_DRAW, (WPARAM)(CLOUDDRAWPARAMS*)(prdp)) + +#define MLCloud_CalcRect(/*HWND*/ hwndML, /*HMLIMGLST*/ hmlil, /*RECT**/prc) \ + (BOOL)SENDMLIPC((hwndML), ML_IPC_CLOUD_CALCRECT, \ + ((prc) ? ((((RECT*)(prc))->left = hmlil), \ + (WPARAM)(prc)) : (WPARAM)(RECT*)NULL)) + + +////////////////////////////////////////////////////////////////////////////// +// MLCloudColumn +// Helpers to display cloud in the listview +// +// + +typedef struct _CLOUDCOLUMNPAINT +{ + HWND hwndList; // hwnd of the listview + HDC hdc; // hdc + UINT iItem; // item index + UINT iSubItem; // subitem index + INT value; // database cloud status (1=full,2=partial,3=unavail) + RECT *prcItem; // whole item rect (plvcd->nmcd.rc) + RECT *prcView; // client area size (you can get it at CDDS_PREPAINT in plvcd->nmcd.rc) + COLORREF rgbBk; // color to use as background (plvcd->clrTextBk) + COLORREF rgbFg; // color to use as foreground (plvcd->clrText) +} CLOUDCOLUMNPAINT; + +typedef struct _CLOUDBACKTEXT +{ + LPWSTR pszText; + INT cchTextMax; + INT nColumnWidth; // used if style is RCS_ALLIGN_CENTER or RCS_ALLIGN_RIGHT +} CLOUDBACKTEXT; + +#define ML_IPC_CLOUDCOLUMN_PAINT (ML_IPC_CLOUD_FIRST + 3) // Paints cloud column (call it in (CDDS_SUBITEM | CDDS_ITEMPREPAINT) handler). + // param = (WPARAM)(CLOUDCOLUMNPAINT*)prcp. if Return TRUE - you need to return SDRF_SKIPDEFAULT +#define ML_IPC_CLOUDCOLUMN_GETMINWIDTH (ML_IPC_CLOUD_FIRST + 4) // param = (WPARAM)0. Returns minimum width required to display cloud status column. +#define ML_IPC_CLOUDCOLUMN_GETWIDTH (ML_IPC_CLOUD_FIRST + 5) // Set width according to the policies. param = (WPARAM)(INT*)width. Return TRUE if ok. 'width' will contain allowed width + + +#define MLCloudColumn_Paint(/*HWND*/ hwndML, /*CLOUDCOLUMNPAINT**/prcp) \ + (BOOL)SENDMLIPC((hwndML), ML_IPC_CLOUDCOLUMN_PAINT, (WPARAM)(CLOUDCOLUMNPAINT*)(prcp)) + +#define MLCloudColumn_GetMinWidth(/*HWND*/ hwndML) \ + (INT)SENDMLIPC((hwndML), ML_IPC_CLOUDCOLUMN_GETMINWIDTH, (WPARAM)0) + +#define MLCloudColumn_GetWidth(/*HWND*/ hwndML, /*INT**/width) \ + (BOOL)SENDMLIPC((hwndML), ML_IPC_CLOUDCOLUMN_GETWIDTH, (WPARAM)(INT*)(width)) + + +////////////////////////////////////////////////////////////////////////////////// +// Skinning +/// + +// supported window types +#define SKINNEDWND_TYPE_AUTO 0x0000 // type will be determine based on class name +#define SKINNEDWND_TYPE_ERROR 0x0000 // return code for ML_IPC_SKINNEDWND_GETTYPE in case of error +#define SKINNEDWND_TYPE_WINDOW 0x0001 // base skinned window type. +#define SKINNEDWND_TYPE_SCROLLWND 0x0002 // base skinned window with skinned scrollbar. +#define SKINNEDWND_TYPE_DIALOG 0x0003 +#define SKINNEDWND_TYPE_HEADER 0x0004 +#define SKINNEDWND_TYPE_LISTVIEW 0x0005 +#define SKINNEDWND_TYPE_BUTTON 0x0006 +#define SKINNEDWND_TYPE_DIVIDER 0x0007 +#define SKINNEDWND_TYPE_EDIT 0x0008 +#define SKINNEDWND_TYPE_STATIC 0x0009 +#define SKINNEDWND_TYPE_LISTBOX 0x000A // you need to set owner draw style to allow selection skinning +#define SKINNEDWND_TYPE_COMBOBOX 0x000B +#define SKINNEDWND_TYPE_FOLDERBROWSER 0x000C +#define SKINNEDWND_TYPE_POPUPMENU 0x000D +#define SKINNEDWND_TYPE_TOOLTIP 0x000E +#define SKINNEDWND_TYPE_PROGRESSBAR 0x000F + +// skin styles +#define SWS_NORMAL 0x00000000 +#define SWS_USESKINFONT 0x00000001 +#define SWS_USESKINCOLORS 0x00000002 +#define SWS_USESKINCURSORS 0x00000004 +#define SWS_NO_DIRECT_MOUSE_WHEEL 0x00000008 + +// styles listview +#define SWLVS_FULLROWSELECT 0x00010000 +#define SWLVS_DOUBLEBUFFER 0x00020000 +#define SWLVS_ALTERNATEITEMS 0x00040000 // Since Winamp 5.55 +#define SWLVS_SELALWAYS 0x00080000 // Since Winamp 5.56 + +// styles divider +#define SWDIV_HORZ 0x00000000 +#define SWDIV_VERT 0x00010000 +#define SWDIV_NOHILITE 0x00020000 // do not draw hilite line + +// styles edit (single line) +#define SWES_TOP 0x00000000 +#define SWES_BOTTOM 0x00010000 +#define SWES_VCENTER 0x00020000 +#define SWES_SELECTONCLICK 0x00040000 + +// styles button +#define SWBS_SPLITBUTTON 0x00010000 +#define SWBS_DROPDOWNBUTTON 0x00020000 +#define SWBS_SPLITPRESSED 0x00100000 +#define SWBS_TOOLBAR 0x00080000 // draw control as a part of the toolbar + +// styles combobox +#define SWCBS_TOOLBAR 0x00080000 // draw control as a part of the toolbar + +// reserved range 0x10000000 - 0xF0000000 + +// Use to skin window (ML_IPC_SKINWINDOW) +typedef struct _MLSKINWINDOW +{ + HWND hwndToSkin; // hwnd to skin + UINT skinType; // skin as... SKINNEDWND_TYPE_XXX + UINT style; // skin style supported by this window +} MLSKINWINDOW; + +#define ML_IPC_SKINWINDOW 0x1400L // param = (WPARAM)(MLSKINWINDOW)pmlSkinWnd. Return TRUE if ok. + // Note: If window already skinned you will get error. + // Some controls require they parent to support reflection which provided by all skinned windows. + // In case parent not skined it will be skinned automaticly as SKINNEDWND_TYPE_WINDOW. + // You can always reskin parent window later with desired type. + // Skinned window will unskin itself on WM_NCDESTROY. +#define ML_IPC_UNSKINWINDOW 0x1401L // param = (WPARAM)(HWND)hwndToUnskin. + +// Macros +#define MLSkinWindow(/*HWND*/ hwndML, /*MLSKINWINDOW**/pmlSkinWnd) \ + (BOOL)SENDMLIPC((HWND)(hwndML), ML_IPC_SKINWINDOW, (WPARAM)(MLSKINWINDOW*)(pmlSkinWnd)) + +#define MLSkinWindow2(/*HWND*/ __hwndML, /*HWND*/__hwndToSkin, /*UINT*/__skinType, /*UINT*/__skinStyle) \ + { MLSKINWINDOW __mlsw;\ + __mlsw.hwndToSkin = __hwndToSkin;\ + __mlsw.skinType = __skinType;\ + __mlsw.style = __skinStyle;\ + (BOOL)SENDMLIPC((HWND)(__hwndML), ML_IPC_SKINWINDOW, (WPARAM)(MLSKINWINDOW*)(&__mlsw));} + +#define MLUnskinWindow(/*HWND*/ hwndML, /*HWND*/hwndToUnskin) \ + (BOOL)SENDMLIPC((HWND)(hwndML), ML_IPC_UNSKINWINDOW, (WPARAM)(HWND)(hwndToUnskin)) + +#define SMS_NORMAL 0x00000000 +#define SMS_USESKINFONT 0x00000001 +#define SMS_FORCEWIDTH 0x00000002 +#define SMS_DISABLESHADOW 0x00000004 +#define SMS_SYSCOLORS 0x00000008 + +#define MLMENU_WANT_DRAWPART ((-1)) + +#define MLMENU_ACTION_MEASUREITEM 0 // param = (LPARAM)(MEASUREITEMSTRUCT*)pmis; +#define MLMENU_ACTION_DRAWITEM 1 // param = (LPARAM)(DRAWITEMSTRUCT*)pdis; Return: 0 - default processing, 1 - if you draw it all, MLMENU_WANT_DRAWPART - want to receive parts +#define MLMENU_ACTION_DRAWBACK 2 // param = (LPARAM)(DRAWITEMSTRUCT*)pdis; Return: 0 - default processing, 1 - if you draw it all +#define MLMENU_ACTION_DRAWICON 3 // param = (LPARAM)(DRAWITEMSTRUCT*)pdis; Return: 0 - default processing, 1 - if you draw it all +#define MLMENU_ACTION_DRAWTEXT 4 // param = (LPARAM)(DRAWITEMSTRUCT*)pdis; Return: 0 - default processing, 1 - if you draw it all + +typedef INT (CALLBACK *MENUCUSTOMIZEPROC)(INT /*action*/, HMENU /*hMenu*/, HDC /*hdc*/, LPARAM /*param*/, ULONG_PTR /*user*/); + +typedef struct _MLSKINNEDPOPUP +{ + INT cbSize; + HMENU hmenu; + UINT fuFlags; + int x; + int y; + HWND hwnd; + LPTPMPARAMS lptpm; + HMLIMGLST hmlil; + INT width; + UINT skinStyle; + MENUCUSTOMIZEPROC customProc; + ULONG_PTR customParam; +} MLSKINNEDPOPUP; + +#define ML_IPC_TRACKSKINNEDPOPUPEX 0x1402L // param = (WPARAM)(MLSKINNEDPOPUP)pmlSkinnedPopup +#define MLTrackSkinnedPopupMenuEx(/*HWND*/ __hwndML, /*HMENU*/__hmenu, /*UINT*/__uFlags, /*INT*/__x, /*INT*/__y,\ + /*HWND*/ __hwndOwner, /*LPTPMPARAMS*/__lptpm, /*HMLIMGLST*/ __hmlil, /*INT*/__width,\ + /*UINT*/__skinStyle, /*MENUCUSTOMIZEPROC*/ __customProc, /*ULONG_PTR*/ __customParam) \ + { MLSKINNEDPOPUP __mlspm;\ + __mlspm.cbSize = sizeof(MLSKINNEDPOPUP);\ + __mlspm.hmenu = __hmenu;\ + __mlspm.fuFlags = __uFlags;\ + __mlspm.x = __x;\ + __mlspm.y = __y;\ + __mlspm.hwnd = __hwndOwner;\ + __mlspm.lptpm = __lptpm;\ + __mlspm.hmlil = __hmlil;\ + __mlspm.width = __width;\ + __mlspm.skinStyle = __skinStyle;\ + __mlspm.customProc = __customProc;\ + __mlspm.customParam = __customParam;\ + (BOOL)SENDMLIPC((HWND)(__hwndML), ML_IPC_TRACKSKINNEDPOPUPEX, (WPARAM)(MLSKINNEDPOPUP*)(&__mlspm));} + +#define ML_IPC_INITSKINNEDPOPUPHOOK 0x1403L // param = (WPARAM)(MLSKINNEDPOPUP)pmlSkinnedPopup (you need to fill only hwnd, hmlil, width, skinStyle, customProc, customParam). Return handle +#define ML_IPC_REMOVESKINNEDPOPUPHOOK 0x1404L // param = (WPARAM)(HANDLE)hPopupHook +#define MLRemoveSkinnedPopupHook(/*HWND*/ __hwndML, /*HANDLE*/__hPopupHook) \ + (SENDMLIPC((HWND)(__hwndML), ML_IPC_REMOVESKINNEDPOPUPHOOK, (WPARAM)(__hPopupHook))) + +typedef struct MLSKINNEDMINMAXINFO +{ + SIZE min; + SIZE max; +} MLSKINNEDMINMAXINFO; + +// Send this messages directly to the skinned window +#define ML_IPC_SKINNEDWND_ISSKINNED 0x0001 // param ignored +#define ML_IPC_SKINNEDWND_GETTYPE 0x0002 // param ignored. return SKINNEDWND_TYPE_XXX or SKINNEDWND_TYPE_ERROR if error. +#define ML_IPC_SKINNEDWND_SKINCHANGED 0x0003 // param = (WPARAM)MAKEWPARAM(NotifyChldren, bRedraw). Return TRUE if ok. +#define ML_IPC_SKINNEDWND_ENABLEREFLECTION 0x0004 // param = (WPARAM)(BOOL)bEnable. Return TRUE if ok. Note: as a lot of skinned controls relay on reflection be carefull using this command +#define ML_IPC_SKINNEDWND_GETPREVWNDPROC 0x0005 // param = (WPARAM)(BOOL*)pbUnicode. Return WNDPROC if ok. +#define ML_IPC_SKINNEDWND_SETSTYLE 0x0006 // param = (WPARAM)(UINT)newStyle. Returns previous style. +#define ML_IPC_SKINNEDWND_GETSTYLE 0x0007 // param igonred. Return current styles +#define ML_IPC_SKINNEDWND_SETMINMAXINFO 0x0008 // param = (WPARAM)(MLSKINNEDMINMAXINFO*)minMax. Returns TRUE if ok. +#define ML_IPC_SKINNEDWND_SKINUPDATED 0x0009 // param = (WPARAM)MAKEWPARAM(NotifyChldren, bRedraw). Return ignored. Send after ML_IPC_SKINNEDWND_SKINCHANGED processed right before redraw. + +// Macros +#define MLSkinnedWnd_IsSkinned(/*HWND*/ hwndSkinned) \ + (BOOL)SENDMLIPC((HWND)(hwndSkinned), ML_IPC_SKINNEDWND_ISSKINNED, (WPARAM)0) + +#define MLSkinnedWnd_GetType(/*HWND*/ hwndSkinned) \ + (UINT)SENDMLIPC((HWND)(hwndSkinned), ML_IPC_SKINNEDWND_GETTYPE, (WPARAM)0) + +#define MLSkinnedWnd_SkinChanged(/*HWND*/ hwndSkinned, /*BOOL*/ bNotifyChildren, /*BOOL*/ bRedraw) \ + (BOOL)SENDMLIPC((HWND)(hwndSkinned), ML_IPC_SKINNEDWND_SKINCHANGED, MAKEWPARAM( (0 != (bNotifyChildren)), (0 != (bRedraw)))) + +#define MLSkinnedWnd_EnableReflection(/*HWND*/ hwndSkinned, /*BOOL*/ bEnable) \ + (BOOL)SENDMLIPC((HWND)(hwndSkinned), ML_IPC_SKINNEDWND_ENABLEREFLECTION, (WPARAM)(BOOL)(bEnable)) + +#define MLSkinnedWnd_GetPrevWndProc(/*HWND*/ hwndSkinned, /*BOOL* */ pbUnicode) \ + (WNDPROC)SENDMLIPC((HWND)(hwndSkinned), ML_IPC_SKINNEDWND_GETPREVWNDPROC, (WPARAM)(BOOL*)(pbUnicode)) + +#define MLSkinnedWnd_SetStyle(/*HWND*/ __hwndSkinned, /*UINT*/ __newStyles) \ + (UINT)SENDMLIPC((HWND)(__hwndSkinned), ML_IPC_SKINNEDWND_SETSTYLE, (WPARAM)(__newStyles)) + +#define MLSkinnedWnd_GetStyle(/*HWND*/ __hwndSkinned) \ + (UINT)SENDMLIPC((HWND)(__hwndSkinned), ML_IPC_SKINNEDWND_GETSTYLE, (WPARAM)0) + +#define MLSkinnedWnd_SetMinMaxInfo(/*HWND*/ __hwndSkinned, /*MLSKINNEDMINMAXINFO* */ _minMax) \ + (UINT)SENDMLIPC((HWND)(__hwndSkinned), ML_IPC_SKINNEDWND_SETMINMAXINFO, (WPARAM)(_minMax)) + + +#define SCROLLMODE_STANDARD 0x0000 +#define SCROLLMODE_LISTVIEW 0x0001 +#define SCROLLMODE_TREEVIEW 0x0002 +#define SCROLLMODE_COMBOLBOX 0x0003 + + +// Send this messages directly to the skinned window +#define IPC_ML_SKINNEDSCROLLWND_UPDATEBARS 0x0010 // param = (WPARAM)(BOOL)fInvalidate +#define IPC_ML_SKINNEDSCROLLWND_SHOWHORZBAR 0x0011 // param = (WPARAM)(BOOL)bEnable +#define IPC_ML_SKINNEDSCROLLWND_SHOWVERTBAR 0x0013 // param = (WPARAM)(BOOL)bEnable +#define IPC_ML_SKINNEDSCROLLWND_SETMODE 0x0012 // param = (WPARAM)(UINT)nScrollMode +#define IPC_ML_SKINNEDSCROLLWND_DISABLENOSCROLL 0x0014 // param = (WPARAM)(BOOL)bDisableNoScroll + +typedef struct _SBADJUSTHOVER +{ + UINT hitTest; + POINTS ptMouse; + INT nResult; +} SBADJUSTHOVER; + +#define IPC_ML_SKINNEDSCROLLWND_ADJUSTHOVER 0x0015 // param = (WPARAM)(SBADJUSTHOVER*)pAdjustData +#define IPC_ML_SKINNEDSCROLLWND_GETHORZBARHIDDEN 0x0016 // param - not used, returns TRUE if horizontal scrollbar always hidden +#define IPC_ML_SKINNEDSCROLLWND_GETVERTBARHIDDEN 0x0017 // param - not used, returns TRUE if vertical scrollbar always hidden +#define IPC_ML_SKINNEDSCROLLWND_GETMODE 0x0018 // param - not used, returns current mode +#define IPC_ML_SKINNEDSCROLLWND_GETNOSCROLLDISABLED 0x0019 // param - not used, returns TRUE if no scroll disabled. + +#define MLSkinnedScrollWnd_UpdateBars(/*HWND*/ hwndSkinned, /*BOOL*/ fInvalidate) \ + (BOOL)SENDMLIPC((HWND)(hwndSkinned), IPC_ML_SKINNEDSCROLLWND_UPDATEBARS, (WPARAM)(BOOL)(fInvalidate)) + +#define MLSkinnedScrollWnd_ShowHorzBar(/*HWND*/ hwndSkinned, /*BOOL*/ bEnable) \ + (BOOL)SENDMLIPC((HWND)(hwndSkinned), IPC_ML_SKINNEDSCROLLWND_SHOWHORZBAR, (WPARAM)(BOOL)(bEnable)) + +#define MLSkinnedScrollWnd_ShowVertBar(/*HWND*/ hwndSkinned, /*BOOL*/ bEnable) \ + (BOOL)SENDMLIPC((HWND)(hwndSkinned), IPC_ML_SKINNEDSCROLLWND_SHOWVERTBAR, (WPARAM)(BOOL)(bEnable)) + +#define MLSkinnedScrollWnd_SetMode(/*HWND*/ hwndSkinned, /*UINT*/ nScrollMode) \ + (BOOL)SENDMLIPC((HWND)(hwndSkinned), IPC_ML_SKINNEDSCROLLWND_SETMODE, (WPARAM)(UINT)(nScrollMode)) + +#define MLSkinnedScrollWnd_DisableNoScroll(/*HWND*/ __hwndSkinned, /*BOOL*/ __bDisable) \ + (BOOL)SENDMLIPC((__hwndSkinned), IPC_ML_SKINNEDSCROLLWND_DISABLENOSCROLL, (WPARAM)(__bDisable)) + +#define MLSkinnedScrollWnd_GetHorzBarHidden(/*HWND*/ __hwndSkinned) \ + (BOOL)SENDMLIPC((__hwndSkinned), IPC_ML_SKINNEDSCROLLWND_GETHORZBARHIDDEN, 0) + +#define MLSkinnedScrollWnd_GetVertBarHidden(/*HWND*/ __hwndSkinned) \ + (BOOL)SENDMLIPC((__hwndSkinned), IPC_ML_SKINNEDSCROLLWND_GETVERTBARHIDDEN, 0) + +#define MLSkinnedScrollWnd_GetMode(/*HWND*/ __hwndSkinned) \ + (UINT)SENDMLIPC((__hwndSkinned), IPC_ML_SKINNEDSCROLLWND_GETMODE, 0) + +#define MLSkinnedScrollWnd_GetNoScrollDisabled(/*HWND*/ __hwndSkinned) \ + (BOOL)SENDMLIPC((__hwndSkinned), IPC_ML_SKINNEDSCROLLWND_GETNOSCROLLDISABLED, 0) + +// Send this messages directly to the skinned window +#define ML_IPC_SKINNEDLISTVIEW_DISPLAYSORT 0x0020 // param = MAKEWPARAM(sortIndex, ascending), To hide set sortIndex to -1. +#define ML_IPC_SKINNEDLISTVIEW_GETSORT 0x0021 // param not used. Return LOWORD() - index, HIWORD() - ascending +#define ML_IPC_SKINNEDLISTVIEW_SETCURRENT 0x0022 // param = (WPARAM)(INT)currentItem. if currentItem == -1 - the current item text will be removed, + // otherwise it will select the item specified (used for showing the currently playing item - 5.7+) +#define ML_IPC_SKINNEDHEADER_DISPLAYSORT 0x0030 // param = MAKEWPARAM(sortIndex, ascending) +#define ML_IPC_SKINNEDHEADER_GETSORT 0x0031 // param not used. Return LOWORD() - index, HIWORD() - ascending +#define ML_IPC_SKINNEDHEADER_SETHEIGHT 0x0032 // param = (WPARAM)(INT)newHeight. if newHeight == -1 - height will be calculated based on the font size. +#define ML_IPC_SKINNEDHEADER_SETCLOUDCOLUMN 0x0033 // param = (WPARAM)(INT)cloudColumn. sets the column which will show a cloud icon header image + +#define MLSkinnedListView_DisplaySort(/*HWND*/hwndSkinned, /*WORD*/ index, /*WORD*/ascending) \ + (BOOL)SENDMLIPC((HWND)(hwndSkinned), ML_IPC_SKINNEDLISTVIEW_DISPLAYSORT, MAKEWPARAM((index), (ascending))) +#define MLSkinnedListView_GetSort(/*HWND*/hwndSkinned) \ + (UINT)SENDMLIPC((HWND)(hwndSkinned), ML_IPC_SKINNEDLISTVIEW_GETSORT, 0) + +#define MLSkinnedHeader_GetSort(/*HWND*/hwndSkinned) \ + (UINT)SENDMLIPC((HWND)(hwndSkinned), ML_IPC_SKINNEDHEADER_GETSORT, 0) +#define MLSkinnedHeader_SetHeight(/*HWND*/hwndSkinned, /*INT*/ newHeight) \ + (UINT)SENDMLIPC((HWND)(hwndSkinned), ML_IPC_SKINNEDHEADER_SETHEIGHT, (WPARAM)newHeight) + +#define MLSkinnedHeader_SetCloudColumn(/*HWND*/hwndSkinned, /*INT*/ cloudColumn) \ + (UINT)SENDMLIPC((HWND)(hwndSkinned), ML_IPC_SKINNEDHEADER_SETCLOUDCOLUMN, (WPARAM)cloudColumn) + +// Skinned divider messages + +typedef void (CALLBACK *MLDIVIDERMOVED)(HWND /*hwndDivider*/, INT /*nPos*/, LPARAM /*userParam*/); + +typedef struct _MLDIVIDERCALLBACK +{ + MLDIVIDERMOVED fnCallback; + LPARAM userParam; +} MLDIVIDERCALLBACK; + +#define ML_IPC_SKINNEDDIVIDER_SETCALLBACK 0x0010 // param = (WPARAM)(MLDIVIDERCALLBACK*)dividerCallback. + +#define MLSkinnedDivider_SetCallback(/*HWND*/_hwndSkinned, /*MLDIVIDERMOVED*/ _fnCallback, /*LPARAM*/_userParam) \ + { MLDIVIDERCALLBACK _cbData;\ + _cbData.fnCallback = (_fnCallback);\ + _cbData.userParam = (_userParam);\ + (BOOL)SENDMLIPC((HWND)(_hwndSkinned), ML_IPC_SKINNEDDIVIDER_SETCALLBACK, ((WPARAM)(MLDIVIDERCALLBACK*)(&_cbData)));} + +// Skinned button +#define MLBN_DROPDOWN 20 + +typedef struct _MLBUTTONIMAGELIST +{ + HMLIMGLST hmlil; + INT normalIndex; // set index to -1 if you don't have special image for this type + INT pressedIndex; + INT hoverIndex; + INT disabledIndex; +} MLBUTTONIMAGELIST; + +#define ML_IPC_SKINNEDBUTTON_SETIMAGELIST 0x0010 // param = (WPARAM)(MLBUTTONIMAGELIST*)buttonImagelist. +#define ML_IPC_SKINNEDBUTTON_GETIMAGELIST 0x0011 // param = (WPARAM)(MLBUTTONIMAGELIST*)buttonImagelist. +#define ML_IPC_SKINNEDBUTTON_SETDROPDOWNSTATE 0x0012 // param = (WPARAM)bPressed. +#define ML_IPC_SKINNEDBUTTON_GETIDEALSIZE 0x0013 // param = (WPARAN)(LPCWSTR)pszText - if pszText == NULL will use button text. Returns: HIWORD - height, LOWORD width + +#define MLSkinnedButton_SetImageList(/*HWND*/__hwndSkinned, /*HMLIMGLST*/ __hmlil, /*INT*/__normalIndex, \ + /*INT*/__pressedIndex, /*INT*/ __hoverIndex, /*INT*/__disabledIndex) \ + { MLBUTTONIMAGELIST __buttonImagelist;\ + __buttonImagelist.hmlil = (__hmlil);\ + __buttonImagelist.normalIndex = (__normalIndex);\ + __buttonImagelist.pressedIndex = (__pressedIndex);\ + __buttonImagelist.hoverIndex = (__hoverIndex);\ + __buttonImagelist.disabledIndex = (__disabledIndex);\ + (BOOL)SENDMLIPC((HWND)(__hwndSkinned), ML_IPC_SKINNEDBUTTON_SETIMAGELIST, ((WPARAM)(MLBUTTONIMAGELIST*)(&__buttonImagelist)));} + +#define MLSkinnedButton_GetImageList(/*HWND*/__hwndSkinned, /*MLBUTTONIMAGELIST* */ __pButtonImagelist)\ + (BOOL)SENDMLIPC((HWND)(__hwndSkinned), ML_IPC_SKINNEDBUTTON_GETIMAGELIST, ((WPARAM)(MLBUTTONIMAGELIST*)(__pButtonImagelist))) + +#define MLSkinnedButton_SetDropDownState(/*HWND*/__hwndSkinned, /*BOOL*/ __bPressed)\ + SENDMLIPC((HWND)(__hwndSkinned), ML_IPC_SKINNEDBUTTON_SETDROPDOWNSTATE, ((WPARAM)(__bPressed))) + +#define MLSkinnedButton_GetIdealSize(/*HWND*/__hwndSkinned, /*LPCWSTR*/__pszText)\ + ((DWORD)SENDMLIPC((HWND)(__hwndSkinned), ML_IPC_SKINNEDBUTTON_GETIDEALSIZE, (WPARAM)(__pszText))) + +// Skinned static +#define ML_IPC_SKINNEDSTATIC_GETIDEALSIZE 0x0014 // param = (WPARAN)(LPCWSTR)pszText - if pszText == NULL will use button text. Returns: HIWORD - height, LOWORD width +#define MLSkinnedStatic_GetIdealSize(/*HWND*/__hwndSkinned, /*LPCWSTR*/__pszText)\ + ((DWORD)SENDMLIPC((HWND)(__hwndSkinned), ML_IPC_SKINNEDSTATIC_GETIDEALSIZE, (WPARAM)(__pszText))) + +////////////////////////////////////////////////////////////////////////////////// +// WebInfo +/// + +typedef struct _WEBINFOCREATE +{ + HWND hwndParent; // HWND of the parent + UINT uMsgQuery; // message that WebInfo can use to query filename + INT x; // position x + INT y; // position y + INT cx; // size cx; + INT cy; // size cy ( if size cy == -1 height will be set to preffer height and cy will be modified + INT ctrlId; // ctrlId you want to use +} WEBINFOCREATE; + +typedef struct _WEBINFOSHOW +{ + LPCWSTR pszFileName; // File name. + UINT fFlags; // flags WIDF_XXX +} WEBINFOSHOW; + +#define WISF_NORMAL 0x0000 +#define WISF_FORCE 0x0001 // even if file name same as was in previous call command will be executed +#define WISF_MESSAGE 0x0100 // pszFileName contains pointer to the message that need to be displayed + +// Messages +#define ML_IPC_CREATEWEBINFO 0x1410L // param = (WPARAM)(WEBINFOCREATE*)pWebInfoParam. Return HWND of the WebInfo dialog or NULL if error + +/// Send this messages directly to the window +#define ML_IPC_WEBINFO_RELEASE 0x0100 // Call this in your WM_DESTROY. +#define ML_IPC_WEBINFO_SHOWINFO 0x0101 // Call this to show file info. param = (WPARAM)(WEBINFOSHOW)pWebInfoShow. Returns TRUE on ok. + + +////////// FolderBrowser + +#define FOLDERBROWSER_NAME L"MLFolderBrowser" + +// styles +#define FBS_IGNOREHIDDEN 0x0001 +#define FBS_IGNORESYSTEM 0x0002 + + +#define FBM_FIRST 0x2100 + +#define FBM_GETBKCOLOR (FBM_FIRST + 0) +#define FolderBrowser_GetBkColor(hwnd) \ + ((COLORREF)SNDMSG((hwnd), FBM_GETBKCOLOR, 0, 0L)) + +#define FBM_SETBKCOLOR (FBM_FIRST + 1) +#define FolderBrowser_SetBkColor(hwnd, clrBk) \ + ((BOOL)SNDMSG((hwnd), FBM_SETBKCOLOR, 0, (LPARAM)(COLORREF)(clrBk))) + +#define FBM_GETTEXTCOLOR (FBM_FIRST + 2) +#define FolderBrowser_GetTextColor(hwnd) \ + ((COLORREF)SNDMSG((hwnd), FBM_GETTEXTCOLOR, 0, 0L)) + +#define FBM_SETTEXTCOLOR (FBM_FIRST + 3) +#define FolderBrowser_SetTextColor(hwnd, clrText) \ + ((BOOL)SNDMSG((hwnd), FBM_SETTEXTCOLOR, 0, (LPARAM)(COLORREF)(clrText))) + +typedef struct _FOLDERBROWSERINFO +{ + DWORD cbSize; + HWND hwndDraw; + HWND hwndActive; + size_t activeColumn; +} FOLDERBROWSERINFO; + +#define FBM_GETFOLDERBROWSERINFO (FBM_FIRST + 4) +#define FolderBrowser_GetInfo(__hwnd, /*FOLDERBROWSERINFO* */__pControlInfo) \ + ((BOOL)SNDMSG((__hwnd), FBM_GETFOLDERBROWSERINFO, 0, (LPARAM)(__pControlInfo))) + +#define FBM_SETROOT (FBM_FIRST + 5) +#define FolderBrowser_SetRoot(__hwnd, /*LPCWSTR*/__pszRoot) \ + ((BOOL)SNDMSG((__hwnd), FBM_SETROOT, 0, (LPARAM)(__pszRoot))) + +#define FBM_GETROOT (FBM_FIRST + 6) +#define FolderBrowser_GetRoot(__hwnd, /*LPWSTR*/__pszBuffer, /*INT*/ __cchMax) \ + ((INT)SNDMSG((__hwnd), FBM_GETROOT, (WPARAM)(__cchMax), (LPARAM)(__pszBuffer))) + +typedef HANDLE (CALLBACK *FBC_FINDFIRSTFILE)(LPCWSTR /*lpFileName*/, WIN32_FIND_DATAW* /*lpFindFileData*/); +typedef BOOL (CALLBACK *FBC_FINDNEXTFILE)(HANDLE /*hFindFile*/, WIN32_FIND_DATAW* /*lpFindFileData*/); +typedef BOOL (CALLBACK *FBC_FINDCLOSE)(HANDLE /*hFindFile*/); + +typedef struct _FILESYSTEMINFO +{ + UINT cbSize; + FBC_FINDFIRSTFILE fnFindFirstFile; + FBC_FINDNEXTFILE fnFindNextFile; + FBC_FINDCLOSE fnFindClose; +} FILESYSTEMINFO; + +#define FBM_SETFILESYSTEMINFO (FBM_FIRST + 7) // lParam = (FILESYSTEMINFO*)pFSInfo ; Set pFSInfo == NULL to use default. +#define FolderBrowser_SetFileSystemInfo(__hwnd, /*FILESYSTEMINFO* */__pFSInfo) \ + ((BOOL)SNDMSG((__hwnd), FBM_SETFILESYSTEMINFO, 0, (LPARAM)(__pFSInfo))) + +#define FBM_GETCURRENTPATH (FBM_FIRST + 8) // wParam - (INT) cchTextMax, lParam = (LPWSTR*)pszText. +#define FolderBrowser_GetCurrentPath(__hwnd, /*LPWSTR */ __pszText, /*INT*/ __cchTextMax) \ + ((BOOL)SNDMSG((__hwnd), FBM_GETCURRENTPATH, (WPARAM)(__cchTextMax), (LPARAM)(__pszText))) + +#define FBM_SETCURRENTPATH (FBM_FIRST + 9) // lParam = (LPCWSTR*)pszPath. +#define FolderBrowser_SetCurrentPath(__hwnd, /*LPCWSTR */ __pszPath, /*BOOL*/__bRedraw) \ + ((BOOL)SNDMSG((__hwnd), FBM_SETCURRENTPATH, (WPARAM)(__bRedraw), (LPARAM)(__pszPath))) + +#define FBM_GETFILESYSTEMINFO (FBM_FIRST + 10) // lParam = (FILESYSTEMINFO*)pFSInfo; +#define FolderBrowser_GetFileSystemInfo(__hwnd, /*FILESYSTEMINFO* */__pFSInfo) \ + ((BOOL)SNDMSG((__hwnd), FBM_GETFILESYSTEMINFO, 0, (LPARAM)(__pFSInfo))) + +#define EVF_NOEXTRARSPACE 0x001 +#define EVF_NOEXTRALSPACE 0x002 +#define EVF_NOREDRAW 0x004 + +#define FBM_ENSUREVISIBLE (FBM_FIRST + 11) +#define FolderBrowser_EnsureVisible(__hwnd, /*INT*/ __columnIndex, /*UINT*/ __uFlags) \ + ((BOOL)SNDMSG((__hwnd), FBM_ENSUREVISIBLE, MAKEWPARAM((__columnIndex), (__uFlags)), 0L)) + +// Folder browser notifications +#define FBN_FIRST (0U-3000U) +#define FBN_SELCHANGED (FBN_FIRST + 1) //phdr = (NMHDR*)lParam. No return value. + +////////// FileView + +typedef struct __MLFILEVIEWCREATESTRUCT +{ + HWND hwndParent; + UINT fStyle; + HWND hwndInsertAfter; + INT x; + INT y; + INT cx; + INT cy; +} MLFILEVIEWCREATESTRUCT; + +#define ML_IPC_CREATEFILEVIEW 0x1412L //param = (WPARAM)(MLFILEVIEWCREATESTRUCT*)pmlFileViewCreateStruct + +// styles +#define FVS_VIEWMASK 0x000F +#define FVS_LISTVIEW 0x0000 +#define FVS_ICONVIEW 0x0001 +#define FVS_DETAILVIEW 0x0002 + +#define FVS_IGNOREHIDDEN 0x0010 +#define FVS_HIDEEXTENSION 0x0020 + +#define FVS_SHOWAUDIO 0x0100 +#define FVS_SHOWVIDEO 0x0200 +#define FVS_SHOWPLAYLIST 0x0400 +#define FVS_SHOWUNKNOWN 0x0800 +#define FVS_ENQUEUE 0x1000 // will use enqueue as default command + +// messages +#define MLFVM_FIRST (WM_APP + 0x0001) + +#define FVM_SETROOT (MLFVM_FIRST + 0) + +#define FileView_SetRoot(/*HWND*/ __hwndFV, /*LPCWSTR*/ __pszRoot)\ + ((BOOL)SENDMSG((__hwndFV), FVM_SETROOT, 0, (LPARAM)(__pszRoot))) + +#define FVM_GETROOT (MLFVM_FIRST + 1) + +#define FileView_GetRoot(/*HWND*/ __hwndFV, /*LPCWSTR*/ __pszBuffer, /*INT*/ __cchMax)\ + ((INT)SENDMSG((__hwndFV), FVM_SETROOT, (WPARAM)(__cchMax), (LPARAM)(__pszBuffer))) + +#define FVM_REFRESH (MLFVM_FIRST + 2) + +#define FileView_Refresh(/*HWND*/ __hwndFV, /*BOOL*/ __bIncludeFolder)\ + ((BOOL)SENDMSG((__hwndFV), FVM_REFRESH, (WPARAM)(__bIncludeFolder), 0L)) + +#define FVM_SETSTYLE (MLFVM_FIRST + 3) + +#define FileView_SetStyle(/*HWND*/ __hwndFV, /*UINT*/ _uStyle, /*UINT*/ _uStyleMask)\ + ((BOOL)SENDMSG((__hwndFV), FVM_SETSTYLE, (WPARAM)(_uStyleMask), (LPARAM)(_uStyle))) + +#define FVM_GETSTYLE (MLFVM_FIRST + 4) + +#define FileView_GetStyle(/*HWND*/ __hwndFV)\ + ((UINT)SENDMSG((__hwndFV), FVM_GETSTYLE, 0, 0L)) + +#define FVM_SETSORT (MLFVM_FIRST + 5) + +#define FileView_SetSort(/*HWND*/ __hwndFV, /*UINT*/ __uColumn, /*BOOL*/ __bAscending)\ + ((BOOL)SENDMSG((__hwndFV), FVM_SETSORT, MAKEWPARAM((__uColumn), (__bAscending)), 0L)) + +#define FVM_GETSORT (MLFVM_FIRST + 6) // returns MAKELONG(sortColumn, bAscending) or -1 if error. + +#define FileView_GetSort(/*HWND*/ __hwndFV)\ + ((DWORD)SENDMSG((__hwndFV), FVM_GETSORT, 0, 0L)) + +#define FVM_GETCOLUMNCOUNT (MLFVM_FIRST + 7) // wParam - not used, lParam - not used, returns column count in current view + +#define FileView_GetColumnCount(/*HWND*/ __hwndFV)\ + ((INT)SENDMSG((__hwndFV), FVM_GETCOLUMNCOUNT, 0, 0L)) + +// columns +#define FVCOLUMN_NAME 0 +#define FVCOLUMN_SIZE 1 +#define FVCOLUMN_TYPE 2 +#define FVCOLUMN_MODIFIED 3 +#define FVCOLUMN_CREATED 4 +#define FVCOLUMN_EXTENSION 5 +#define FVCOLUMN_ATTRIBUTES 6 +#define FVCOLUMN_ARTIST 7 +#define FVCOLUMN_ALBUM 8 +#define FVCOLUMN_TITLE 9 +#define FVCOLUMN_INMLDB 10 +#define FVCOLUMN_GENRE 11 +#define FVCOLUMN_COMMENT 12 +#define FVCOLUMN_LENGTH 13 +#define FVCOLUMN_BITRATE 14 +#define FVCOLUMN_TRACK 15 +#define FVCOLUMN_DISC 16 +#define FVCOLUMN_YEAR 17 +#define FVCOLUMN_PUBLISHER 18 +#define FVCOLUMN_COMPOSER 19 +#define FVCOLUMN_ALBUMARTIST 20 + +#define FVM_GETCOLUMNARRAY (MLFVM_FIRST + 8) // wParam - (WPARAM)iCount, lParam - (LPARAM)(UINT*)puArray, returns actually copied count. + +#define FileView_GetColumnArray(/*HWND*/ __hwndFV, /*INT*/__iCount, /*UINT* */ __puArray)\ + ((INT)SENDMSG((__hwndFV), FVM_GETCOLUMNARRAY, (WPARAM)(__iCount), (LPARAM)(__puArray))) + +#define FVM_DELETECOLUMN (MLFVM_FIRST + 9) // wParam - (WPARAM)ufvColumn, lParam - not used, returns TRUE on success + +#define FileView_DeleteColumn(/*HWND*/ __hwndFV, /*UINT*/__ufvColumn)\ + ((BOOL)SENDMSG((__hwndFV), FVM_DELETECOLUMN, (WPARAM)(__ufvColumn), 0L)) + +#define FVCF_WIDTH 1 +#define FVCF_ORDER 2 +#define FVCF_WIDTHMIN 4 +#define FVCF_WIDTHMAX 8 + +#define FVCO_DEFAULT_ORDER ((INT)0x80000000) // can be used only with FVM_INSERTCOLUMN + +typedef struct _FVCOLUMN +{ + UINT mask; + UINT id; + INT width; + INT order; + INT widthMin; + INT widthMax; +} FVCOLUMN; + +#define FVM_INSERTCOLUMN (MLFVM_FIRST + 10) // wParam - not used, lParam - (LPARAM)(FVCOLUMN*)pfvColumn, returns column index or -1 + +#define FileView_InsertColumn(/*HWND*/ __hwndFV, /*FVCOLUMN* */__pfvColumn)\ + ((INT)SENDMSG((__hwndFV), FVM_INSERTCOLUMN, (WPARAM)0, (LPARAM)(__pfvColumn))) + +#define FVM_GETCOLUMNAME (MLFVM_FIRST + 11) // wParam - MAKEWPARAM(ufvColumn, cchTextMax), lParam - (LPARAM)(LPWSTR)pszText, returns TRUE if ok + +#define FileView_GetColumnName(/*HWND*/ __hwndFV, /*UINT*/__ufvColumn, /*INT*/__cchTextMax, /*LPWSTR*/__pszText)\ + ((BOOL)SENDMSG((__hwndFV), FVM_GETCOLUMNAME, MAKEWPARAM((__ufvColumn), (__cchTextMax)), (LPARAM)(__pszText))) + +#define FVM_SETFILESYSTEMINFO (MLFVM_FIRST + 12) // wParam - not used,lParam = (FILESYSTEMINFO*)pFSInfo, returns TRUE on success. + +#define FileView_SetFileSystemInfo(/*HWND*/ __hwndFV, /*FILESYSTEMINFO* */__pFSInfo) \ + ((BOOL)SNDMSG((__hwndFV), FVM_SETFILESYSTEMINFO, 0, (LPARAM)(__pFSInfo))) + +#define FVM_GETFILESYSTEMINFO (MLFVM_FIRST + 13) // wParam - not used,lParam = (FILESYSTEMINFO*)pFSInfo, returns TRUE on success. + +#define FileView_GetFileSystemInfo(/*HWND*/ __hwndFV, /*FILESYSTEMINFO* */__pFSInfo) \ + ((BOOL)SNDMSG((__hwndFV), FVM_GETFILESYSTEMINFO, 0, (LPARAM)(__pFSInfo))) + +#define FVM_GETFILECOUNT (MLFVM_FIRST + 14) // wParam - not used,lParam = not used, returns the number of items. + +#define FileView_GetFileCount(/*HWND*/ __hwndFV) \ + ((INT)SNDMSG((__hwndFV), FVM_GETFILECOUNT, 0, 0L)) + +#define FVM_GETSELECTEDCOUNT (MLFVM_FIRST + 15) // wParam - not used,lParam = not used, Returns the number of selected items. + +#define FileView_GetSelectedCount(/*HWND*/ __hwndFV) \ + ((INT)SNDMSG((__hwndFV), FVM_GETSELECTEDCOUNT, 0, 0L)) + +#define FVNF_ALL 0x0000 +#define FVNF_FOCUSED 0x0001 +#define FVNF_SELECTED 0x0002 +#define FVNF_CUT 0x0004 +#define FVNF_DROPHILITED 0x0008 +#define FVNF_PLAYABLE 0x0080 + +#define FVNF_ABOVE 0x0100 +#define FVNF_BELOW 0x0200 +#define FVNF_TOLEFT 0x0400 +#define FVNF_TORIGHT 0x0800 + +#define FVM_GETNEXTFILE (MLFVM_FIRST + 16) // wParam - (WPARAM)(INT)iStart, lParam = (LPARAM)(UINT)uFlags, Returns the index of the next file if successful, or -1 otherwise. + +#define FileView_GetNextFile(/*HWND*/ __hwndFV, /*INT*/ __iStart, /*UINT*/ __uFlags) \ + ((INT)SNDMSG((__hwndFV), FVM_GETNEXTFILE, (WPARAM)(__iStart), (LPARAM)(__uFlags))) + +// file states +#define FVFS_FOCUSED 0x0001 // The file has the focus, so it is surrounded by a standard focus rectangle. Although more than one file may be selected, only one file can have the focus. +#define FVFS_SELECTED 0x0002 // The file is selected. +#define FVFS_CUT 0x0004 // The file is marked for a cut-and-paste operation. +#define FVFS_DROPHILITED 0x0008 // The file is highlighted as a drag-and-drop target. + +// FileView file types +#define FVFT_UNKNOWN 0 +#define FVFT_AUDIO 1 +#define FVFT_VIDEO 2 +#define FVFT_PLAYLIST 3 +#define FVFT_LAST FVFT_PLAYLIST + +#define FVIF_TEXT 0x0001 +#define FVIF_STATE 0x0002 +#define FVIF_ATTRIBUTES 0x0004 +#define FVIF_SIZE 0x0008 +#define FVIF_CREATETIME 0x0010 +#define FVIF_ACCESSTIME 0x0020 +#define FVIF_WRITETIME 0x0040 +#define FVIF_TYPE 0x0080 + +typedef struct __FVITEM +{ + UINT mask; + UINT state; + UINT stateMask; + LPWSTR pszText; // in some case returned pszText can be pointed on internal buffer and not one that was provided + INT cchTextMax; + UINT uAttributes; + DWORD dwSizeLow; + DWORD dwSizeHigh; + FILETIME ftCreationTime; + FILETIME ftLastAccessTime; + FILETIME ftLastWriteTime; + WORD wType; +} FVITEM, *PFVITEM; + +#define FVM_GETFILE (MLFVM_FIRST + 17) // wParam - (WPARAM)(INT)iFile, lParam = (LPARAM)(FVITEM*)pFile, Returns TRUE if successful, or FALSE otherwise. + +#define FileView_GetFile(/*HWND*/ __hwndFV, /*INT*/ __iFile, /*PFVITEM*/ __pFile) \ + ((BOOL)SNDMSG((__hwndFV), FVM_GETFILE, (WPARAM)(__iFile), (LPARAM)(__pFile))) + +#define FVM_GETCURRENTPATH (MLFVM_FIRST + 18) // wParam - (WPARAM)(INT)cchPathMax, lParam = (LPARAM)(LPWSTR)pszPath, Returns TRUE if successful, or FALSE otherwise. + +#define FileView_GetCurrentPath(/*HWND*/ __hwndFV, /*LPWSTR*/ __pszBuffer, /*INT*/ __cchBufferMax) \ + ((BOOL)SNDMSG((__hwndFV), FVM_GETCURRENTPATH, (WPARAM)(__cchBufferMax), (LPARAM)(__pszBuffer))) + +#define FVM_GETFOLDERBROWSER (MLFVM_FIRST + 19) // wParam - not used, lParam - not used, Returns HWND of the folderbrowser. + +#define FileView_GetFoderBrowser(/*HWND*/ __hwndFV) \ + ((HWND)SNDMSG((__hwndFV), FVM_GETFOLDERBROWSER, 0, 0L)) + +#define FVM_GETFOLDERSIZE (MLFVM_FIRST + 20) // wParam - (WPARAM)(BOOL)bSelectionOnly, lParam - (LPARAM)(DWORD*)pdwFoldeSizeHigh (optional), Returns dwSizeLow. + +#define FileView_GetFoderSize(/*HWND*/ __hwndFV, /*BOOL*/ __bSelectionOnly, /*LPDWROD*/ __pdwFoldeSizeHigh) \ + ((DWORD)SNDMSG((__hwndFV), FVM_GETFOLDERSIZE, (WPARAM)(__bSelectionOnly), (LPARAM)(__pdwFoldeSizeHigh))) + +#define FVM_GETSTATUSTEXT (MLFVM_FIRST + 21) // wParam - (WPARAM)(INT)cchPathMax, lParam = (LPARAM)(LPWSTR)pszPath, Returns TRUE if successful, or FALSE otherwise. + +#define FileView_GetStatusText(/*HWND*/ __hwndFV, /*LPWSTR*/ __pszText, /*INT*/ __cchTextMax) \ + ((BOOL)SNDMSG((__hwndFV), FVM_GETSTATUSTEXT, (WPARAM)(__cchTextMax), (LPARAM)(__pszText))) + +#define FVEF_AUDIO 0x0001 +#define FVEF_VIDEO 0x0002 +#define FVEF_PLAYLIST 0x0004 +#define FVEF_UNKNOWN 0x0008 + +#define FVEF_ALLKNOWN (FVEF_AUDIO | FVEF_VIDEO| FVEF_PLAYLIST) +#define FVEF_ALL (FVEF_ALLKNOWN | FVEF_UNKNOWN) + +#define FVM_ENQUEUESELECTION (MLFVM_FIRST + 22) // wParam - (WPARAM)(UINT)uEnqueueFilter, lParam - (LPARAM)(INT*)pnFocused, Returns number of files sent to playlist. pnFocused - optional (if not null will contain playlist index of focused item) + +#define FileView_EnqueueSelection(/*HWND*/ __hwndFV, /*UINT*/ __uEnqueueFilter, /*PINT*/__pnFocused) \ + ((BOOL)SNDMSG((__hwndFV), FVM_ENQUEUESELECTION, (WPARAM)(__uEnqueueFilter), (LPARAM)(__pnFocused))) + +#define FVM_ISFILEPLAYABLE (MLFVM_FIRST + 23) // wParam - (WPARAM)(UINT)uEnqueueFilter, lParam - (LPARAM)(INT)iFile - file index, Returns TRUE if successful, or FALSE otherwise. + +#define FileView_IsFilePlayable(/*HWND*/ __hwndFV, /*INT*/ __iFile, /*UINT*/ __uEnqueueFilter) \ + ((BOOL)SNDMSG((__hwndFV), FVM_ISFILEPLAYABLE, (WPARAM)(__uEnqueueFilter), (LPARAM)(__iFile))) + +#define FVM_SETFILESTATE (MLFVM_FIRST + 24) // wParam - (WPARAM)(INT)iFile ( if -1 applyed to all files), (LPARAM)(LPFVITEM)pFile - only stateMask and state are used, Returns TRUE if successful, or FALSE otherwise. + +#define FileView_SetFileState(/*HWND*/ __hwndFV, /*INT*/ __iFile, /*UINT*/ __uMask, /*UINT*/ __uData) \ + { FVITEM __fvi;\ + __fvi.stateMask = __uMask;\ + __fvi.state = __uData;\ + SNDMSG((__hwndFV), FVM_SETFILESTATE, (WPARAM)(__iFile), (LPARAM)(LV_ITEM *)&__fvi);} + +#define FVMENU_OPTIONS 0 +#define FVMENU_COLUMNS 1 +#define FVMENU_ARRANGEBY 2 +#define FVMENU_PLAY 3 +#define FVMENU_FILEVIEWCONTEXT 4 +#define FVMENU_FILEOPCONTEXT 5 +#define FVMENU_FILECONTEXT 6 // in getMenu will be resolved to FVMENU_FILEVIEWCONTEXT or FVMENU_FILEOPCONTEXT +#define FVMENU_VIEWMODE 7 + +#define IDM_COLUMN_SHOW_MIN 60000 +#define IDM_COLUMN_SHOW_MAX 60099 +#define IDM_COLUMN_ARRANGE_MIN 60100 +#define IDM_COLUMN_ARRANGE_MAX 60199 + +#define FVM_GETMENU (MLFVM_FIRST + 25) // wParam - (WPARAM)(UINT)uMenuType, lParam - not used. + +#define FileView_GetMenu(/*HWND*/ __hwndFV, /*UINT*/ __uMenuType) \ + ((HMENU)SNDMSG((__hwndFV), FVM_GETMENU, (WPARAM)(__uMenuType), 0L)) + +#define FVA_PLAY 0 +#define FVA_ENQUEUE 1 + +#define FVM_GETACTIONCMD (MLFVM_FIRST + 26) // wParam - (WPARAM)(UINT)uAction, lParam - not used. returns 0 - if no cmd, otherwise cmd id. + +#define FileView_GetActionCommand(/*HWND*/ __hwndFV, /*UINT*/ __uAction) \ + ((UINT)SNDMSG((__hwndFV), FVM_GETACTIONCMD, (WPARAM)(__uAction), 0L)) + +#define FVHT_NOWHERE 0x0001 +#define FVHT_ONFILEICON 0x0002 +#define FVHT_ONFILELABEL 0x0004 +#define FVHT_ONFILE (FVHT_ONFILEICON | FVHT_ONFILELABEL) +#define FVHT_ABOVE 0x0008 +#define FVHT_BELOW 0x0010 +#define FVHT_TORIGHT 0x0020 +#define FVHT_TOLEFT 0x0040 + +typedef struct _FVHITTEST +{ + POINT pt; // The position to hit test, in fileview client coordinates . + UINT uFlags; // + INT iItem; // file index; +} FVHITTEST; + +#define FVM_HITTEST (MLFVM_FIRST + 27) // wParam - not used, lParam - (LPARAM)(FVHITTEST*)pHitTest. Returns the index of the item at the specified position, if any, or -1 otherwise. + +#define FileView_HitTest(/*HWND*/ __hwndFV, /*FVHITTEST* */ __pHitTest) \ + ((INT)SNDMSG((__hwndFV), FVM_HITTEST, 0, (LPARAM)__pHitTest)) + +#define FVM_GETCOLUMNWIDTH (MLFVM_FIRST + 28) // wParam - (WPARAM)ufvColumn - column id, lParam - not used. returns column width + +#define FileView_GetColumnWidth(/*HWND*/ __hwndFV, /*UINT*/__ufvColumn)\ + ((BOOL)SENDMSG((__hwndFV), FVM_GETCOLUMNWIDTH, (WPARAM)(__ufvColumn), 0L)) + +#define FVM_SETCURRENTPATH (MLFVM_FIRST + 29) + +#define FileView_SetCurrentPath(__hwnd, /*LPCWSTR */ __pszPath, /*BOOL*/__bRedraw) \ + ((BOOL)SNDMSG((__hwnd), FVM_SETCURRENTPATH, (WPARAM)(__bRedraw), (LPARAM)(__pszPath))) + +#define FVM_GETDIVIDERPOS (MLFVM_FIRST + 30) +#define FileView_GetDividerPos(__hwnd) \ + ((INT)SNDMSG((__hwnd), FVM_GETDIVIDERPOS, 0, 0L)) + +#define FVRF_VALIDATE 0x0001 +#define FVRF_NOREDRAW 0x0002 + +#define FVM_SETDIVIDERPOS (MLFVM_FIRST + 31) +#define FileView_SetDividerPos(__hwnd, /*INT*/ __nPos, /*UINT*/ __uFlags) \ + ((INT)SNDMSG((__hwnd), FVM_SETDIVIDERPOS, (__nPos), (__uFlags))) + +// Folder browser notifications +#define FVN_FIRST (0U-3200U) + +#define FVN_FOLDERCHANGED (FVN_FIRST + 1) //phdr = (NMHDR*)lParam. No return value. + +typedef struct __NMFVSTATECHANGED +{ + NMHDR hdr; + INT iFrom; + INT iTo; + UINT uNewState; + UINT uOldState; +} NMFVSTATECHANGED; + +#define FVN_STATECHANGED (FVN_FIRST + 2) //phdr = (NMFVSTATECHANGED*)lParam. No return value. + +#define FVN_STATUSCHANGED (FVN_FIRST + 3) //phdr = (NMHDR*)lParam. No return value. + +typedef struct __NMFVFILEACTIVATE +{ + NMHDR hdr; + INT iFile; + UINT uNewState; + UINT uOldState; + POINT ptAction; + UINT uKeyFlags; +} NMFVFILEACTIVATE; + +typedef struct __NMFVMENU +{ + NMHDR hdr; + UINT uMenuType; // FVMENU_XXX + HMENU hMenu; // handle to thr menu + UINT uCommand; // FVN_MENUCOMMNAD - user selected command + POINT ptAction; // point where menu will be dispalayed +} NMFVMENU; + +#define FVN_INITMENU (FVN_FIRST + 4) // phdr = (NMFVMENU*)lParam. Return TRUE to prevent menu from appearing, otherwise FALSE. +#define FVN_UNINITMENU (FVN_FIRST + 5) // phdr = (NMFVMENU*)lParam. no return value. +#define FVN_MENUCOMMAND (FVN_FIRST + 6) // phdr = (NMFVMENU*)lParam. Return TRUE to prevent command processing, otherwise FALSE. + +// Fileview also implements this notifications: NM_CLICK, NM_DBLCLK, NM_RCLICK, NM_RDBLCLK. they all have phdr = (NMFVFILEACTIVATE*)lParam. + +#endif // NULLOSFT_MEDIALIBRARY_IPC_EXTENSION_HEADER_0x0313 + +#if (defined _ML_HEADER_IMPMLEMENT && !defined _ML_HEADER_IMPMLEMENT_DONE) +#define _ML_HEADER_IMPMLEMENT_DONE + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +// {8A054D1F-E38E-4cc0-A78A-F216F059F57E} +const GUID MLIF_FILTER1_UID = { 0x8a054d1f, 0xe38e, 0x4cc0, { 0xa7, 0x8a, 0xf2, 0x16, 0xf0, 0x59, 0xf5, 0x7e } }; +// {BE1A6A40-39D1-4cfb-8C33-D8988E8DD2F8} +const GUID MLIF_FILTER2_UID = { 0xbe1a6a40, 0x39d1, 0x4cfb, { 0x8c, 0x33, 0xd8, 0x98, 0x8e, 0x8d, 0xd2, 0xf8 } }; +// {721E9E62-CC6D-4fd7-A6ED-DD4CD2B2612E} +const GUID MLIF_FILTER3_UID = { 0x721e9e62, 0xcc6d, 0x4fd7, { 0xa6, 0xed, 0xdd, 0x4c, 0xd2, 0xb2, 0x61, 0x2e } }; +// {B6310C20-E731-44dd-83BD-FBC3349798F2} +const GUID MLIF_GRAYSCALE_UID = { 0xb6310c20, 0xe731, 0x44dd, { 0x83, 0xbd, 0xfb, 0xc3, 0x34, 0x97, 0x98, 0xf2 } }; +// {526C6F4A-C979-4d6a-B8ED-1A90F5A26F7B} +const GUID MLIF_BLENDONBK_UID = { 0x526c6f4a, 0xc979, 0x4d6a, { 0xb8, 0xed, 0x1a, 0x90, 0xf5, 0xa2, 0x6f, 0x7b } }; +// {E61C5E67-B2CC-4f89-9C95-40D18FCAF1F8} +const GUID MLIF_BUTTONBLEND_UID = { 0xe61c5e67, 0xb2cc, 0x4f89, { 0x9c, 0x95, 0x40, 0xd1, 0x8f, 0xca, 0xf1, 0xf8 } }; +// {3619BA52-5088-4f21-9AF1-C5FCFE5AAA99} +const GUID MLIF_BUTTONBLENDPLUSCOLOR_UID = { 0x3619ba52, 0x5088, 0x4f21, { 0x9a, 0xf1, 0xc5, 0xfc, 0xfe, 0x5a, 0xaa, 0x99 } }; +// {F1DD3228-7DA8-4524-B7D3-46F651BFB680} +const GUID MLIF_FILTER1_PRESERVE_ALPHA_UID = { 0xf1dd3228, 0x7da8, 0x4524, { 0xb7, 0xd3, 0x46, 0xf6, 0x51, 0xbf, 0xb6, 0x80 } }; + +#ifdef __cplusplus +} +#endif // __cplusplus + +#endif //_ML_HEADER_IMPMLEMENT \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/ml_lib.cpp b/Src/Plugins/General/gen_ml/ml_lib.cpp new file mode 100644 index 00000000..6fdcb00a --- /dev/null +++ b/Src/Plugins/General/gen_ml/ml_lib.cpp @@ -0,0 +1,586 @@ +/* +** Copyright © 2003-2014 Winamp SA +** +** This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held +** liable for any damages arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, including commercial applications, and to +** alter it and redistribute it freely, subject to the following restrictions: +** +** 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. +** If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +** +** 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +** +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#include +#include "ml.h" + +void freeRecord(itemRecord *p) +{ + if (p) + { + free(p->title); + free( p->artist ); + free( p->ext ); + free(p->comment); + free(p->album); + free(p->genre); + free(p->filename); + if (p->extended_info) + { + for (int x = 0; p->extended_info[x]; x ++) + free(p->extended_info[x]); + free(p->extended_info); + p->extended_info = 0; + } + } +} + +void freeRecordList(itemRecordList *obj) +{ + if (obj) + { + emptyRecordList(obj); + free(obj->Items); + obj->Items=0; + obj->Alloc=obj->Size=0; + } +} + +void emptyRecordList(itemRecordList *obj) +{ + if (obj) + { + itemRecord *p=obj->Items; + while (obj->Size-->0) + { + freeRecord(p); + p++; + } + obj->Size=0; + } +} + +void allocRecordList(itemRecordList *obj, int newsize, int granularity) +{ + if (newsize < obj->Alloc || newsize < obj->Size) return; + + int old_Alloc = obj->Alloc; + obj->Alloc=newsize+granularity; + + itemRecord *data = (itemRecord*)realloc(obj->Items, sizeof(itemRecord) * obj->Alloc); + if (!data) + { + data = (itemRecord*)malloc(sizeof(itemRecord) * obj->Alloc); + if (data) + { + memcpy(data, obj->Items, sizeof(itemRecord) * old_Alloc); + free(obj->Items); + obj->Items = data; + } + else + obj->Alloc = old_Alloc; + } + else + obj->Items = data; + + if (!old_Alloc) memset(data, 0, sizeof(itemRecordW) * obj->Alloc); + if (!obj->Items) obj->Alloc = 0; +} + +void compactRecordList(itemRecordListW *obj) +{ + if (obj->Size && obj->Size < obj->Alloc - 1024) + { + size_t old_Alloc = obj->Size; + obj->Alloc = obj->Size; + + itemRecordW *data = (itemRecordW *)realloc(obj->Items, sizeof(itemRecordW) * obj->Alloc); + if (!data) + { + data = (itemRecordW *)malloc(sizeof(itemRecordW) * obj->Alloc); + if (data) + { + memcpy(data, obj->Items, sizeof(itemRecordW) * old_Alloc); + free(obj->Items); + obj->Items = data; + } + else + obj->Alloc = (int)old_Alloc; + } + else + obj->Items = data; + } +} + +void copyRecord(itemRecord *out, const itemRecord *in) +{ +#define COPYSTR(FOO) out->FOO = in->FOO ? _strdup(in->FOO) : 0; + COPYSTR(filename) + COPYSTR( title ) + COPYSTR( ext ) + COPYSTR(album) + COPYSTR(artist) + COPYSTR(comment) + COPYSTR(genre) + out->year=in->year; + out->track=in->track; + out->length=in->length; +#undef COPYSTR + out->extended_info=0; + + if (in->extended_info) + { + for (int y = 0; in->extended_info[y]; y ++) + { + char *p=in->extended_info[y]; + if (*p) setRecordExtendedItem(out,p,p+strlen(p)+1); + } + } +} + +void copyRecordList(itemRecordList *out, const itemRecordList *in) +{ + allocRecordList(out,out->Size+in->Size,0); + if (!out->Items) return; + for (int x = 0; x < in->Size; x ++) + { + copyRecord(&out->Items[out->Size++],&in->Items[x]); + } +} + +char *getRecordExtendedItem(const itemRecord *item, const char *name) +{ + if (item->extended_info) + { + for (int x = 0; item->extended_info[x]; x ++) + { + if (!_stricmp(item->extended_info[x],name)) + return item->extended_info[x]+strlen(name)+1; + } + } + return NULL; +} + +void setRecordExtendedItem(itemRecord *item, const char *name, char *value) +{ + int x=0; + if (item->extended_info) + { + for (x = 0; item->extended_info[x]; x ++) + { + if (!_stricmp(item->extended_info[x],name)) + { + size_t name_len = strlen(name), value_len = strlen(value); + if (value_len>strlen(item->extended_info[x]+name_len+1)) + { + free(item->extended_info[x]); + item->extended_info[x]=(char*)malloc(name_len+value_len+2); + } + strncpy(item->extended_info[x], name, name_len); + strncpy(item->extended_info[x]+strlen(name)+1, value, value_len); + return; + } + } + } + + // x=number of valid items. + char **data = (char**)realloc(item->extended_info,sizeof(char*) * (x+2)); + if (data) + { + // if we could allocate then add, otherwise we'll have to skip + item->extended_info = data; + + size_t name_len = strlen(name), value_len = strlen(value); + item->extended_info[x]=(char*)malloc(name_len+value_len+2); + strncpy(item->extended_info[x], name, name_len); + strncpy(item->extended_info[x]+name_len+1, value, value_len); + + item->extended_info[x+1]=0; + } + else + { + data=(char**)_aligned_malloc(sizeof(char*) * (x+2), 16); + if (data) + { + memcpy(data, item->extended_info, sizeof(char*) * x); + free(item->extended_info); + + // if we could allocate then add, otherwise we'll have to skip + item->extended_info = data; + + size_t name_len = strlen(name), value_len = strlen(value); + item->extended_info[x]=(char*)malloc(name_len+value_len+2); + strncpy(item->extended_info[x], name, name_len); + strncpy(item->extended_info[x]+name_len+1, value, value_len); + + item->extended_info[x+1]=0; + } + } +} + +/* +---------------------------------- +wide version starts here +---------------------------------- +*/ +void freeRecord(itemRecordW *p) +{ + if (p) + { + free(p->title); + free(p->artist); + free(p->comment); + free(p->album); + free(p->genre); + free(p->filename); + free(p->albumartist); + free(p->replaygain_album_gain); + free(p->replaygain_track_gain); + free(p->publisher); + free(p->composer); + if (p->extended_info) + { + for (int x = 0; p->extended_info[x].key; x ++) + { + free(p->extended_info[x].key); + free(p->extended_info[x].value); + } + free(p->extended_info); + p->extended_info = 0; + } + } +} + +void freeRecordList(itemRecordListW *obj) +{ + if (obj) + { + emptyRecordList(obj); + free(obj->Items); + obj->Items=0; + obj->Alloc=obj->Size=0; + } +} + +void emptyRecordList(itemRecordListW *obj) +{ + if (obj) + { + itemRecordW *p=obj->Items; + while (obj->Size-->0) + { + freeRecord(p); + p++; + } + obj->Size=0; + } +} + +void allocRecordList(itemRecordListW *obj, int newsize, int granularity) +{ + if (newsize < obj->Alloc || newsize < obj->Size) return; + + int old_Alloc = obj->Alloc; + obj->Alloc=newsize+granularity; + itemRecordW *data = (itemRecordW*)realloc(obj->Items,sizeof(itemRecordW)*obj->Alloc); + if (!data) + { + data = (itemRecordW*)malloc(sizeof(itemRecordW) * obj->Alloc); + if (data) + { + memcpy(data, obj->Items, sizeof(itemRecordW) * obj->Alloc); + free(obj->Items); + obj->Items = data; + } + else + obj->Alloc = old_Alloc; + } + else + obj->Items = data; + + if (!old_Alloc) memset(data, 0, sizeof(itemRecordW) * obj->Alloc); + if (!obj->Items) obj->Alloc = 0; +} + +void copyRecord(itemRecordW *out, const itemRecordW *in) +{ +#define COPYSTR(FOO) out->FOO = in->FOO ? _wcsdup(in->FOO) : 0; +#define COPY(FOO) out->FOO = in->FOO; + COPYSTR(filename); + COPYSTR(title); + COPYSTR(album); + COPYSTR(artist); + COPYSTR(comment); + COPYSTR(genre); + COPYSTR(albumartist); + COPYSTR(replaygain_album_gain); + COPYSTR(replaygain_track_gain); + COPYSTR(publisher); + COPYSTR(composer); + COPY(year); + COPY(track); + COPY(tracks); + COPY(length); + COPY(rating); + COPY(playcount); + COPY(lastplay); + COPY(lastupd); + COPY(filetime); + COPY(filesize); + COPY(bitrate); + COPY(type); + COPY(disc); + COPY(discs); + COPY(bpm); + COPYSTR(category); +#undef COPYSTR + out->extended_info=0; + + if (in->extended_info) + { + for (int y = 0; in->extended_info[y].key; y ++) + { + setRecordExtendedItem(out,in->extended_info[y].key,in->extended_info[y].value); + } + } +} + +void copyRecordList(itemRecordListW *out, const itemRecordListW *in) +{ + allocRecordList(out,out->Size+in->Size,0); + if (!out->Items) return; + for (int x = 0; x < in->Size; x ++) + { + copyRecord(&out->Items[out->Size++],&in->Items[x]); + } +} + +wchar_t *getRecordExtendedItem(const itemRecordW *item, const wchar_t *name) +{ + if (item->extended_info) + { + for (size_t x = 0; item->extended_info[x].key; x ++) + { + if (!_wcsicmp(item->extended_info[x].key,name)) + return item->extended_info[x].value; + } + } + return NULL; +} + +wchar_t *getRecordExtendedItem_fast(const itemRecordW *item, const wchar_t *name) +{ + if (item->extended_info) + { + for (size_t x = 0; item->extended_info[x].key; x ++) + { + if (item->extended_info[x].key == name) + return item->extended_info[x].value; + } + } + return NULL; +} + +void setRecordExtendedItem(itemRecordW *item, const wchar_t *name, const wchar_t *value) +{ + size_t x=0; + if (item->extended_info) for (x = 0; item->extended_info[x].key; x ++) + { + if (!_wcsicmp(item->extended_info[x].key,name)) + { + free(item->extended_info[x].value); + item->extended_info[x].value = _wcsdup(value); + return; + } + } + + // x=number of valid items. + extendedInfoW *data = (extendedInfoW *)realloc(item->extended_info, sizeof(extendedInfoW) * (x+2)); + if (data) + { + item->extended_info = data; + + item->extended_info[x].key = _wcsdup(name); + item->extended_info[x].value = _wcsdup(value); + + item->extended_info[x+1].key=0; + item->extended_info[x+1].value=0; + } + else + { + data=(extendedInfoW *)_aligned_malloc(sizeof(extendedInfoW) * (x+2), 16); + if (data) + { + item->extended_info=data; + + item->extended_info[x].key = _wcsdup(name); + item->extended_info[x].value = _wcsdup(value); + + item->extended_info[x+1].key=0; + item->extended_info[x+1].value=0; + } + } +} + +// this version assumes that the 'name' won't already be in the itemRecord +void setRecordExtendedItem_fast(itemRecordW *item, const wchar_t *name, const wchar_t *value) +{ + int x=0; + if (item->extended_info) + { + for (x = 0; item->extended_info[x].key; x ++) + { + } + } + + // x=number of valid items. + extendedInfoW *data=(extendedInfoW *)_aligned_realloc(item->extended_info,sizeof(extendedInfoW) * (x+2), 16); + if (data) + { + item->extended_info=data; + + item->extended_info[x].key = _wcsdup(name); + item->extended_info[x].value = _wcsdup(value); + + item->extended_info[x+1].key=0; + item->extended_info[x+1].value=0; + } + else + { + data=(extendedInfoW *)_aligned_malloc(sizeof(extendedInfoW) * (x+2), 16); + if (data) + { + item->extended_info=data; + + item->extended_info[x].key = _wcsdup(name); + item->extended_info[x].value = _wcsdup(value); + + item->extended_info[x+1].key=0; + item->extended_info[x+1].value=0; + } + } +} + +// TODO: redo this without AutoChar +#include "../nu/AutoChar.h" +#define COPY_EXTENDED_STR(field) if (input-> ## field && input-> ## field ## [0]) setRecordExtendedItem(output, #field, AutoChar(input-> ## field)); +#define COPY_EXTENDED_INT(field) if (input->##field > 0) { char temp[64] = {0}; _itoa(input->##field, temp, 10); setRecordExtendedItem(output, #field, temp); } +#define COPY_EXTENDED_INT64(field) if (input->##field > 0) { char temp[64] = {0}; _i64toa(input->##field, temp, 10); setRecordExtendedItem(output, #field, temp); } +#define COPY_EXTENDED_INT0(field) if (input->##field >= 0) { char temp[64] = {0}; _itoa(input->##field, temp, 10); setRecordExtendedItem(output, #field, temp); } +void convertRecord(itemRecord *output, const itemRecordW *input) +{ + output->filename=AutoCharDup(input->filename); + output->title = AutoCharDup( input->title ); + output->ext = AutoCharDup( input->ext ); + output->album=AutoCharDup(input->album); + output->artist=AutoCharDup(input->artist); + output->comment=AutoCharDup(input->comment); + output->genre=AutoCharDup(input->genre); + output->year=input->year; + output->track=input->track; + output->length=input->length; + output->extended_info=0; + COPY_EXTENDED_STR(albumartist); + COPY_EXTENDED_STR(replaygain_album_gain); + COPY_EXTENDED_STR(replaygain_track_gain); + COPY_EXTENDED_STR(publisher); + COPY_EXTENDED_STR(composer); + COPY_EXTENDED_INT(tracks); + COPY_EXTENDED_INT(rating); + COPY_EXTENDED_INT(playcount); + COPY_EXTENDED_INT64(lastplay); + COPY_EXTENDED_INT64(lastupd); + COPY_EXTENDED_INT64(filetime); + COPY_EXTENDED_INT(filesize); + COPY_EXTENDED_INT(bitrate); + COPY_EXTENDED_INT0(type); + COPY_EXTENDED_INT(disc); + COPY_EXTENDED_INT(discs); + COPY_EXTENDED_INT(bpm); + COPY_EXTENDED_STR(category); + + if (input->extended_info) + { + for (int y = 0; input->extended_info[y].key; y ++) + { + setRecordExtendedItem(output, AutoChar(input->extended_info[y].key), AutoChar(input->extended_info[y].value)); + } + } +} +#undef COPY_EXTENDED_STR +#undef COPY_EXTENDED_INT +#undef COPY_EXTENDED_INT0 + +#include "../nu/AutoWide.h" +#define COPY_EXTENDED_STR(field) output->##field = AutoWideDup(getRecordExtendedItem(input, #field)); +#define COPY_EXTENDED_INT(field) { char *x = getRecordExtendedItem(input, #field); output->##field=x?atoi(x):-1; } + +void convertRecord(itemRecordW *output, const itemRecord *input) +{ + output->filename=AutoWideDup(input->filename); + output->title=AutoWideDup(input->title); + output->ext = AutoWideDup( input->ext ); + output->album=AutoWideDup(input->album); + output->artist=AutoWideDup(input->artist); + output->comment=AutoWideDup(input->comment); + output->genre=AutoWideDup(input->genre); + output->year=input->year; + output->track=input->track; + output->length=input->length; + output->extended_info=0; + COPY_EXTENDED_STR(albumartist); + COPY_EXTENDED_STR(replaygain_album_gain); + COPY_EXTENDED_STR(replaygain_track_gain); + COPY_EXTENDED_STR(publisher); + COPY_EXTENDED_STR(composer); + COPY_EXTENDED_INT(tracks); + COPY_EXTENDED_INT(rating); + COPY_EXTENDED_INT(playcount); + COPY_EXTENDED_INT(lastplay); + COPY_EXTENDED_INT(lastupd); + COPY_EXTENDED_INT(filetime); + COPY_EXTENDED_INT(filesize); + COPY_EXTENDED_INT(type); + COPY_EXTENDED_INT(disc); + COPY_EXTENDED_INT(discs); + COPY_EXTENDED_INT(bpm); + COPY_EXTENDED_INT(bitrate); + COPY_EXTENDED_STR(composer); + COPY_EXTENDED_STR(category); + // TODO: copy input's extended fields +} +#undef COPY_EXTENDED_STR +#undef COPY_EXTENDED_INT + +void convertRecordList(itemRecordList *output, const itemRecordListW *input) +{ + output->Alloc = output->Size = input->Size; + output->Items = (itemRecord*)calloc(sizeof(itemRecord),input->Size); + if (output->Items) + { + for(int i=0; i < input->Size; i++) + { + convertRecord(&output->Items[i],&input->Items[i]); + } + } + else + output->Alloc = output->Size = 0; +} + +void convertRecordList(itemRecordListW *output, const itemRecordList *input) +{ + output->Alloc = output->Size = input->Size; + output->Items = (itemRecordW*)calloc(sizeof(itemRecordW),input->Size); + if (output->Items) + { + for(int i=0; i < input->Size; i++) + { + convertRecord(&output->Items[i],&input->Items[i]); + } + } +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/ml_rating.cpp b/Src/Plugins/General/gen_ml/ml_rating.cpp new file mode 100644 index 00000000..b8e711d2 --- /dev/null +++ b/Src/Plugins/General/gen_ml/ml_rating.cpp @@ -0,0 +1,125 @@ +#include "./ml_rating.h" +#include + +#define IMAGE_PARTSCOUNT 4 + +BOOL MLRatingI_Draw(HDC hdc, INT maxValue, INT value, INT trackingVal, HMLIMGLST hmlil, INT index, RECT *prc, UINT fStyle) +{ + INT ilIndex; + if (!hdc || !hmlil || !prc) return FALSE; + + if (RDS_OPAQUE_I & fStyle) ExtTextOutW(hdc, 0, 0, ETO_OPAQUE, prc, L"", 0, 0); + + ilIndex = MLImageListI_GetRealIndex(hmlil, index, GetBkColor(hdc), GetTextColor(hdc)); + + if (-1 != ilIndex) + { + INT val, count; + static IMAGELISTDRAWPARAMS ildp = { 56/*sizeof(IMAGELISTDRAWPARAMS)*/, 0, }; + + ildp.hdcDst = hdc; + ildp.himl = MLImageListI_GetRealList(hmlil); + ildp.i = ilIndex; + ildp.x = prc->left; + ildp.y = prc->top; + ildp.rgbBk = CLR_DEFAULT; + ildp.rgbFg = CLR_DEFAULT; + ildp.fStyle = ILD_NORMAL; + ildp.dwRop = SRCCOPY; + + MLImageListI_GetImageSize(hmlil, &ildp.cx, &ildp.cy); + + val = (value < 0) ? 0 : value; + + count = ((RDS_SHOWEMPTY_I & fStyle) ? maxValue : (((RDS_HOT_I & fStyle) && trackingVal > val) ? trackingVal : val)); + if (count < 0) count = 0; + + ildp.cx = ildp.cx/IMAGE_PARTSCOUNT; + ildp.xBitmap = ((RDS_HOT_I & fStyle) ? (ildp.cx*2) : (RDS_INACTIVE_HOT_I & fStyle) ? ildp.cx*2 : 0) + ildp.cx; + + if(RDS_RIGHT_I & fStyle) ildp.x = prc->right - maxValue*ildp.cx; + if(RDS_HCENTER_I & fStyle) ildp.x = prc->left + (prc->right - prc->left - maxValue*ildp.cx)/2; + + if(RDS_BOTTOM_I & fStyle) ildp.y = prc->bottom - ildp.cy; + if(RDS_VCENTER_I & fStyle) ildp.y = prc->top + (prc->bottom - prc->top - ildp.cy)/2; + + if (ildp.y < prc->top) + { + ildp.yBitmap = prc->top - ildp.y; + ildp.y = prc->top; + } + else ildp.yBitmap = 0; + + if (ildp.cy > (prc->bottom - ildp.y)) ildp.cy = prc->bottom - ildp.y; + + for (INT i = 0; ildp.x < prc->right && i < count; i++, ildp.x += ildp.cx) + { + if (RDS_HOT_I & fStyle) + { + if (i == trackingVal) ildp.xBitmap -= ildp.cx * ((val > trackingVal) ? 2 : 1); + else if (i == val && val > trackingVal) ildp.xBitmap += ildp.cx; + } + else + { + if (i == val) ildp.xBitmap -= ildp.cx; + } + if (ildp.x < (prc->left - ildp.cx)) continue; + if (prc->right < (ildp.x + ildp.cx)) ildp.cx = prc->right - ildp.x; + ImageList_DrawIndirect(&ildp); + } + } + + return TRUE; +} + +LONG MLRatingI_HitTest(POINT pt, INT maxValue, HMLIMGLST hmlil, RECT *prc, UINT fStyle) +{ + INT imageCX, imageCY, imageX, imageY; + WORD index, flags; + + if (!hmlil || !prc) return MAKELONG(0,0); + + if (!PtInRect(prc, pt)) return MAKELONG(0, RHT_NOWHERE_I); + + MLImageListI_GetImageSize(hmlil, &imageCX, &imageCY); + + imageCX = imageCX/IMAGE_PARTSCOUNT; + + imageX = prc->left; + if(RDS_RIGHT_I & fStyle) imageX = prc->right - maxValue*imageCX; + if(RDS_HCENTER_I & fStyle) imageX = prc->left + (prc->right - prc->left - maxValue*imageCX)/2; + + imageY = prc->top; + if(RDS_BOTTOM_I & fStyle) imageY = prc->bottom - imageCY; + if(RDS_VCENTER_I & fStyle) imageY = prc->top + (prc->bottom - prc->top - imageCY)/2; + + if (imageY < prc->top) imageY = prc->top; + if (imageCY > (prc->bottom - imageY)) imageCY = prc->bottom - imageY; + + flags = 0; + index = 0; + + if (pt.x < imageX) flags |= RHT_TOLEFT_I; + else if (pt.x > (imageX + imageCX*maxValue)) flags |= RHT_TORIGHT_I; + else + { + flags |= RHT_ONVALUE_I; + + if (pt.y < imageY) flags |= RHT_ONVALUEABOVE_I; + else if (pt.y > (imageY + imageCY)) flags |= RHT_ONVALUEBELOW_I; + + index = (WORD)(pt.x - imageX)/imageCX + 1; + if (index > maxValue) index = maxValue; + } + return MAKELONG(index, flags); +} + +BOOL MLRatingI_CalcMinRect(INT maxValue, HMLIMGLST hmlil, RECT *prc) +{ + INT imageCX, imageCY; + + if (!hmlil || !prc || !MLImageListI_GetImageSize(hmlil, &imageCX, &imageCY)) return FALSE; + SetRect(prc, 0, 0, maxValue*imageCX/IMAGE_PARTSCOUNT, imageCY); + + return TRUE; +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/ml_rating.h b/Src/Plugins/General/gen_ml/ml_rating.h new file mode 100644 index 00000000..037c5475 --- /dev/null +++ b/Src/Plugins/General/gen_ml/ml_rating.h @@ -0,0 +1,39 @@ +#ifndef NULLOSFT_MEDIALIBRARY_RATING_HEADER +#define NULLOSFT_MEDIALIBRARY_RATING_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include +#include "./ml_imagelist.h" + +/// Rating_Draw Styles +#define RDS_SHOWEMPTY_I 0x00000001 // Draw elements that not set. +#define RDS_OPAQUE_I 0x00000002 // Fill rest of the rectangle with rgbBk. +#define RDS_HOT_I 0x00000004 // Draw elements as "hot". +#define RDS_LEFT_I 0x00000000 // Aligns elements to the left. +#define RDS_TOP_I 0x00000000 // Justifies elements to the top of the rectangle. +#define RDS_RIGHT_I 0x00000010 // Aligns elements to the right. +#define RDS_BOTTOM_I 0x00000020 // Justifies elements to the bottom of the rectangle. +#define RDS_HCENTER_I 0x00000040 // Centers elements horizontally in the rectangle. +#define RDS_VCENTER_I 0x00000080 // Centers elements horizontally in the rectangle. +#define RDS_INACTIVE_HOT_I 0x00000100 // Draw elements as "hot" when inactive + +#define RDS_NORMAL_I (RDS_SHOWEMPTY_I | RDS_OPAQUE_I | RDS_LEFT | RDS_TOP) + +// Rating_HitTest hitFlags +#define RHT_NOWHERE_I 0x0001 +#define RHT_ONVALUE_I 0x0002 +#define RHT_ONVALUEABOVE_I 0x0004 +#define RHT_ONVALUEBELOW_I 0x0008 +#define RHT_TOLEFT_I 0x0100 +#define RHT_TORIGHT_I 0x0200 + +// Draws Rating based on RATINGDRAWPARAMS +BOOL MLRatingI_Draw(HDC hdc, INT maxValue, INT value, INT trackingVal, HMLIMGLST hmlil, INT index, RECT *prc, UINT fStyle); +// HIWORD - hitFlags LOWORD - index if any +LONG MLRatingI_HitTest(POINT pt, INT maxValue, HMLIMGLST hmlil, RECT *prc, UINT fStyle); +BOOL MLRatingI_CalcMinRect(INT maxValue, HMLIMGLST hmlil, RECT *prc); + +#endif //NULLOSFT_MEDIALIBRARY_RATING_HEADER \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/ml_ratingcolumn.cpp b/Src/Plugins/General/gen_ml/ml_ratingcolumn.cpp new file mode 100644 index 00000000..473a9aca --- /dev/null +++ b/Src/Plugins/General/gen_ml/ml_ratingcolumn.cpp @@ -0,0 +1,874 @@ +#include "main.h" +#include "./ml_ratingcolumn.h" +#include "./ml_rating.h" +#include "api__gen_ml.h" +#include "./ml.h" +#include "./ml_IPC_0313.h" +#include "./resource.h" +#include "../winamp/gen.h" +#include "./stockobjects.h" +#include +#include + +extern HMLIMGLST hmlilRating; +extern UINT ratingGlobalStyle; + +#define RATING_IMAGELIST hmlilRating +#define RATING_IMAGEINDEX 0 +#define RATING_MAXVALUE 5 + +#define RATING_LEFTPADDING 5 +#define RATING_RIGHTPADDING 2 + +#define RATING_AUTOUNHOVERDELAY 80 +#define RATING_ANIMATIONINTERVAL 200 +#define RAITNG_ANIMATIONMAX 1000 + +#define RATING_DRAGFORGIVENESS_LEFT 0xFFFF +#define RATING_DRAGFORGIVENESS_RIGHT 0xFFFF +#define RATING_DRAGFORGIVENESS_TOP 12 +#define RATING_DRAGFORGIVENESS_BOTTOM 12 + +#define RATING_FILLCHAR L'x' + +typedef struct _RATINGTRACKING +{ + HWND hwndList; + UINT iItem; + UINT iSubItem; + RECT rc; // trackin cell + INT value; + UINT_PTR timerId; +} RATINGTRACKING; + +typedef struct _RATINGANIMATION +{ + HWND hwndList; + UINT iItem; + UINT_PTR timerId; + UINT durationMs; + UINT startedMs; + WORD stage; +} RATINGANIMATION; + +typedef struct _RATINGDRAG +{ + HWND hwndList; + UINT iItem; + UINT iSubItem; + RECT rc; + INT value; + BOOL update; + BOOL outside; + UINT fStyle; +} RATINGDRAG; + +typedef struct _TWEAKPARAM +{ + ONRATINGTWEAKAPLLY fnApply; + UINT fStyle; +} TWEAKPARAM; + +static INT ratingMinWidth = 65; +static INT ratingCharWidth = 8; + +static RATINGTRACKING ratingTracking; +static RATINGANIMATION ratingAnimation; +static RATINGDRAG ratingDrag; + +static void CALLBACK Timer_AutoUnhover(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime); +static void CALLBACK Timer_Animation(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime); +static void CorrectDragPoint(CONST RECT *prc, POINT *ppt); +static BOOL IsTrakingAllowed(HWND hwndList, UINT fStyle); +static BOOL IsItemTrackable(HWND hwndList, UINT iItem, UINT fStyle); +static INT_PTR WINAPI TweakDialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + +BOOL MLRatingColumnI_Initialize(void) +{ + RECT rc; + + ZeroMemory(&ratingTracking, sizeof(RATINGTRACKING)); + ratingTracking.iItem = (UINT)-1; + + ZeroMemory(&ratingAnimation, sizeof(RATINGANIMATION)); + ratingAnimation.iItem = (UINT)-1; + + ratingMinWidth = ((MLRatingI_CalcMinRect(RATING_MAXVALUE, RATING_IMAGELIST, &rc)) ? (rc.right - rc.left) : 0); + + return TRUE; +} + +BOOL MLRatingColumnI_Update(void) +{ + HDC hdc; + HFONT hFontOld, hFont; + INT length; + WCHAR test[6], *p; + + hdc = (HDC)MlStockObjects_Get(CACHED_DC); + if (!hdc) return FALSE; + + hFont = (HFONT)MlStockObjects_Get(SKIN_FONT); + + length = sizeof(test)/sizeof(WCHAR); + p = test + length; + while (p-- != test) *p = RATING_FILLCHAR; + hFontOld = (HFONT)SelectObject(hdc, hFont); + + SIZE sz; + + if (GetTextExtentPoint32W(hdc, test, length, &sz)) ratingCharWidth = sz.cx/length; + else ratingCharWidth = 8; + + SelectObject(hdc, hFontOld); + return TRUE; +} + +INT MLRatingColumnI_GetMinWidth(void) +{ + return ratingMinWidth + RATING_LEFTPADDING + RATING_RIGHTPADDING; +} + +LPCWSTR MLRatingColumnI_FillBackString(LPWSTR pszText, INT cchTextMax, INT nColumnWidth, UINT fStyle) +{ + INT width, count; + LPWSTR p; + if (!pszText) return NULL; + pszText[0] = 0x00; + + fStyle = (RCS_DEFAULT == fStyle) ? ratingGlobalStyle : fStyle; + + width = MLRatingColumnI_GetMinWidth(); + if (nColumnWidth < width) return pszText; + + if (RCS_ALLIGN_RIGHT_I & fStyle) width = nColumnWidth; + else if (RCS_ALLIGN_CENTER_I & fStyle) width += (nColumnWidth - width)/2; + if (width <= 0) return pszText; + count = (width - 4)/ratingCharWidth; + if (count >= cchTextMax) count = cchTextMax -1; + for (p = pszText; count--; p++) *p = RATING_FILLCHAR; + *p = 0x00; + return pszText; +} + +BOOL MLRatingColumnI_Paint(RATINGCOLUMNPAINT_I *pRCPaint) +{ + RECT rc; + UINT fStyle, fRCStyle; + + fRCStyle = (RCS_DEFAULT == pRCPaint->fStyle) ? ratingGlobalStyle : pRCPaint->fStyle; + + rc.left = LVIR_BOUNDS; + rc.top = pRCPaint->iSubItem; + if (SendMessageW(pRCPaint->hwndList, LVM_GETSUBITEMRECT, pRCPaint->iItem, (LPARAM)&rc)) + { + if ((rc.right - rc.left - RATING_LEFTPADDING - RATING_RIGHTPADDING) >= ratingMinWidth && + (rc.left + RATING_LEFTPADDING) < (rc.right - RATING_RIGHTPADDING) && rc.top < rc.bottom) + { + INT left; + COLORREF rgbBkOld; + BOOL tracking; + + if (rc.right <= pRCPaint->prcView->left || rc.left >= pRCPaint->prcView->right) return TRUE; + + tracking = (pRCPaint->hwndList == ratingTracking.hwndList && pRCPaint->iItem == ratingTracking.iItem); + + rgbBkOld = SetBkColor(pRCPaint->hdc, pRCPaint->rgbBk); + + left = rc.left; + if (0 == rc.left) rc.left = 3; + ExtTextOutW(pRCPaint->hdc, 0, 0, ETO_OPAQUE, &rc, L"", 0, 0); + + fStyle = RDS_VCENTER_I; + if (RCS_ALLIGN_RIGHT_I & fRCStyle) fStyle |= RDS_RIGHT_I; + else if (RCS_ALLIGN_CENTER_I & fRCStyle) fStyle |= RDS_HCENTER_I; + + INT value = pRCPaint->value, trackingValue; + + if (tracking) + { + fStyle |= RDS_HOT_I; + if(RCS_SHOWEMPTY_HOT_I & fRCStyle) fStyle |= RDS_SHOWEMPTY_I; + trackingValue = ratingTracking.value; + } + else if (pRCPaint->hwndList == ratingDrag.hwndList && pRCPaint->iItem == ratingDrag.iItem && !ratingDrag.outside) + { + value = 0; + trackingValue = 0; + fStyle |= (RDS_SHOWEMPTY_I | RDS_HOT_I); + } + else + { + if(RCS_SHOWEMPTY_NORMAL_I & fRCStyle) fStyle |= RDS_SHOWEMPTY_I; + trackingValue = 0; + } + + if(RCS_SHOWINACTIVE_HOT_I & fRCStyle) fStyle |= RDS_INACTIVE_HOT_I; + + if (pRCPaint->hwndList == ratingAnimation.hwndList && pRCPaint->iItem == ratingAnimation.iItem) + { + if (RCS_SHOWEMPTY_ANIMATION_I & fRCStyle) fStyle |= RDS_SHOWEMPTY_I; + switch(ratingAnimation.stage) + { + case 1: + rc.top -= 1; + rc.bottom -= 1; + break; + } + } + + if (value || (RDS_SHOWEMPTY_I & fStyle)) + { + COLORREF rgbFgOld = SetTextColor(pRCPaint->hdc, pRCPaint->rgbFg); + rc.left = left + RATING_LEFTPADDING; + rc.right -= RATING_RIGHTPADDING; + MLRatingI_Draw(pRCPaint->hdc, RATING_MAXVALUE, value, trackingValue, RATING_IMAGELIST, RATING_IMAGEINDEX, &rc, fStyle); + if (pRCPaint->rgbFg != rgbFgOld) SetTextColor(pRCPaint->hdc, rgbFgOld); + } + if (pRCPaint->rgbBk != rgbBkOld) SetBkColor(pRCPaint->hdc, rgbBkOld); + return TRUE; + } + } + + return FALSE; +} + +BOOL MLRatingColumnI_Click(RATINGCOLUMN_I *pRating) +{ + UINT fRCStyle; + LVHITTESTINFO lvhit; + + fRCStyle = (RCS_DEFAULT == pRating->fStyle) ? ratingGlobalStyle : pRating->fStyle; + + lvhit.pt = pRating->ptAction; + SendMessageW(pRating->hwndList, LVM_SUBITEMHITTEST, 0, (LPARAM)&lvhit); + pRating->iItem = lvhit.iItem; + + if (ratingTracking.hwndList && ratingTracking.hwndList != pRating->hwndList || + !IsTrakingAllowed(pRating->hwndList, fRCStyle)) MLRatingColumnI_CancelTracking(TRUE); + + if (-1 != ratingTracking.iItem && ratingTracking.iItem != (UINT)lvhit.iItem) + { + if (-1 == lvhit.iItem) MLRatingColumnI_CancelTracking(TRUE); + else + { + ratingTracking.iItem = (UINT)lvhit.iItem; + ratingTracking.iSubItem = lvhit.iSubItem; + } + } + + if (-1 != lvhit.iItem && (0 == (RCS_BLOCKCLICK_I & fRCStyle))) + { + RECT rc; + UINT fStyle; + rc.left = LVIR_BOUNDS; + rc.top = lvhit.iSubItem; + fStyle = RDS_VCENTER_I; + + if (RCS_ALLIGN_RIGHT_I & fRCStyle) fStyle |= RDS_RIGHT_I; + if (RCS_ALLIGN_CENTER_I & fRCStyle) fStyle |= RDS_HCENTER_I; + + if (SendMessageW(pRating->hwndList, LVM_GETSUBITEMRECT, lvhit.iItem, (LPARAM)&rc)) + { + if (0 == lvhit.iSubItem) + { + HWND hwndHeader; + RECT rh; + hwndHeader = (HWND)SendMessageW(pRating->hwndList, LVM_GETHEADER, 0, 0L); + if (hwndHeader && SendMessageW(hwndHeader, HDM_GETITEMRECT, lvhit.iSubItem, (LPARAM)&rh)) + { + rc.left = rh.left; + rc.right = rh.right; + } + else SetRect(&rc, 0, 0, 0, 0); + } + + rc.left += RATING_LEFTPADDING; + rc.right -= RATING_RIGHTPADDING; + + if ((rc.right - rc.left) >= ratingMinWidth) + { + pRating->value = MLRatingI_HitTest(pRating->ptAction, RATING_MAXVALUE, RATING_IMAGELIST, &rc, fStyle); + pRating->value = LOWORD(pRating->value); + if (0 == pRating->value && (RCS_BLOCKUNRATECLICK_I & fRCStyle)) return FALSE; + if (ratingTracking.iItem == (UINT)lvhit.iItem && ratingTracking.value != pRating->value) + { + ratingTracking.value = pRating->value; + SendMessageW(pRating->hwndList, LVM_REDRAWITEMS, lvhit.iItem, lvhit.iItem); + if (pRating->bRedrawNow) UpdateWindow(pRating->hwndList); + } + return TRUE; + } + } + } + return FALSE; +} + +void MLRatingColumnI_Track(RATINGCOLUMN_I *pRating) +{ + UINT fRCStyle; + BOOL trackingOk; + + fRCStyle = (RCS_DEFAULT == pRating->fStyle) ? ratingGlobalStyle : pRating->fStyle; + + if (ratingTracking.hwndList != pRating->hwndList) MLRatingColumnI_CancelTracking(pRating->bRedrawNow); + trackingOk = (IsTrakingAllowed(pRating->hwndList, fRCStyle) && IsItemTrackable(pRating->hwndList, pRating->iItem, fRCStyle)); + + if (ratingTracking.iItem != pRating->iItem || ratingTracking.iSubItem != pRating->iSubItem || (-1 != ratingTracking.iItem && !trackingOk)) + { + if (-1 != ratingTracking.iItem) MLRatingColumnI_CancelTracking(FALSE); + if (trackingOk) + { + ratingTracking.rc.left = LVIR_BOUNDS; + ratingTracking.rc.top = pRating->iSubItem; + + if (SendMessageW(pRating->hwndList, LVM_GETSUBITEMRECT, pRating->iItem, (LPARAM)&ratingTracking.rc)) + { + if (0 == pRating->iSubItem) + { + HWND hwndHeader; + RECT rh; + + hwndHeader = (HWND)SendMessageW(pRating->hwndList, LVM_GETHEADER, 0, 0L); + if (hwndHeader && SendMessageW(hwndHeader, HDM_GETITEMRECT, pRating->iSubItem, (LPARAM)&rh)) + { + ratingTracking.rc.left = rh.left; + ratingTracking.rc.right = rh.right; + } + else SetRect(&ratingTracking.rc, 0, 0, 0, 0); + } + ratingTracking.rc.left += RATING_LEFTPADDING; + ratingTracking.rc.right -= RATING_RIGHTPADDING; + if ((ratingTracking.rc.right - ratingTracking.rc.left) < ratingMinWidth) + { + SetRect(&ratingTracking.rc, 0, 0, 0, 0); + } + } + else SetRect(&ratingTracking.rc, 0, 0, 0, 0); + + if (ratingTracking.rc.left != ratingTracking.rc.right) + { + ratingTracking.hwndList = pRating->hwndList; + ratingTracking.iItem = pRating->iItem; + ratingTracking.iSubItem = pRating->iSubItem; + ratingTracking.timerId = SetTimer(NULL, NULL, RATING_AUTOUNHOVERDELAY, Timer_AutoUnhover); + } + else + { + ratingTracking.hwndList = NULL; + ratingTracking.iItem = -1; + ratingTracking.iSubItem = 0; + } + } + } + + if (-1 != ratingTracking.iItem) + { + UINT fStyle; + INT value; + + fStyle = RDS_VCENTER_I; + if (RCS_ALLIGN_RIGHT_I & fRCStyle) fStyle |= RDS_RIGHT_I; + if (RCS_ALLIGN_CENTER_I & fRCStyle) fStyle |= RDS_HCENTER_I; + + value = LOWORD(MLRatingI_HitTest(pRating->ptAction, RATING_MAXVALUE, RATING_IMAGELIST, &ratingTracking.rc, fStyle)); + + if (ratingTracking.value != value) + { + ratingTracking.value = value; + SendMessageW(pRating->hwndList, LVM_REDRAWITEMS, ratingTracking.iItem, ratingTracking.iItem); + if (pRating->bRedrawNow) UpdateWindow(pRating->hwndList); + } + } +} + +BOOL MLRatingColumnI_BeginDrag(RATINGCOLUMN_I *pRating) +{ + POINT pt; + + if (ratingDrag.hwndList) + { + RATINGCOLUMN_I rcol; + rcol.bCanceled = TRUE; + rcol.bRedrawNow = TRUE; + MLRatingColumnI_EndDrag(&rcol); + } + + ratingDrag.fStyle = (RCS_DEFAULT == pRating->fStyle) ? ratingGlobalStyle : pRating->fStyle; + ratingDrag.rc.left = LVIR_BOUNDS; + ratingDrag.rc.top = pRating->iSubItem; + ratingDrag.fStyle = ratingDrag.fStyle & + ~(RCS_TRACKITEM_SELECTED_I | RCS_TRACKITEM_FOCUSED_I | RCS_BLOCKCLICK_I | RCS_BLOCKUNRATECLICK_I) | + (RCS_TRACKITEM_ALL_I | RCS_TRACK_ALWAYS_I); + + if ((RCS_BLOCKDRAG_I & ratingDrag.fStyle ) || !pRating->hwndList || !IsWindow(pRating->hwndList) || + (UINT)-1 == pRating->iItem || (UINT)-1 == pRating->iSubItem || + !SendMessageW(pRating->hwndList, LVM_GETSUBITEMRECT, pRating->iItem, (LPARAM)&ratingDrag.rc)) return FALSE; + + if (0 == pRating->iSubItem) + { + HWND hwndHeader; + RECT rh; + hwndHeader = (HWND)SendMessageW(pRating->hwndList, LVM_GETHEADER, 0, 0L); + if (!hwndHeader || !SendMessageW(hwndHeader, HDM_GETITEMRECT, pRating->iSubItem, (LPARAM)&rh)) return FALSE; + ratingDrag.rc.left = rh.left; + ratingDrag.rc.right = rh.right; + } + ratingDrag.rc.left += RATING_LEFTPADDING; + ratingDrag.rc.right -= RATING_RIGHTPADDING; + + if ((ratingDrag.rc.right - ratingDrag.rc.left) < ratingMinWidth) + { + SetRect(&ratingDrag.rc, 0, 0, 0, 0); + return FALSE; + } + + if (RCS_ALLIGN_RIGHT_I & ratingDrag.fStyle) ratingDrag.rc.left = ratingDrag.rc.right - ratingMinWidth; + else + { + if (RCS_ALLIGN_CENTER_I & ratingDrag.fStyle) ratingDrag.rc.left += ((ratingDrag.rc.right - ratingDrag.rc.left) - ratingMinWidth)/2; + ratingDrag.rc.right = ratingDrag.rc.left + ratingMinWidth; + } + + GetCursorPos(&pt); + MapWindowPoints(HWND_DESKTOP, pRating->hwndList, &pt, 1); + if (ratingDrag.rc.left <= pt.x && pt.x <= ratingDrag.rc.right) + { + ratingDrag.hwndList = pRating->hwndList; + ratingDrag.iItem = pRating->iItem; + ratingDrag.iSubItem = pRating->iSubItem; + ratingDrag.value = pRating->value; + ratingDrag.update = TRUE; + ratingDrag.outside = TRUE; + return TRUE; + } + return FALSE; +} + +BOOL MLRatingColumnI_Drag(POINT pt) +{ + if (ratingDrag.hwndList && ratingDrag.hwndList == WindowFromPoint(pt)) + { + SendMessageW(ratingDrag.hwndList, WM_SETCURSOR, (WPARAM)ratingDrag.hwndList, MAKELPARAM(HTCLIENT, WM_MOUSEMOVE)); + ratingDrag.outside = FALSE; + + MapWindowPoints(HWND_DESKTOP, ratingDrag.hwndList, &pt, 1); + CorrectDragPoint(&ratingDrag.rc, &pt); + + if (PtInRect(&ratingDrag.rc, pt)) + { + RATINGCOLUMN_I rcol; + + ratingDrag.update = TRUE; + rcol.hwndList = ratingDrag.hwndList; + rcol.iItem = ratingDrag.iItem; + rcol.iSubItem = ratingDrag.iSubItem; + rcol.value = ratingDrag.value; + rcol.ptAction = pt; + rcol.bRedrawNow = FALSE; + rcol.fStyle = ratingDrag.fStyle; + MLRatingColumnI_Track(&rcol); + KillTimer(NULL, ratingTracking.timerId); + + if (-1 != ratingTracking.iItem && ratingTracking.value) + { + UpdateWindow(ratingDrag.hwndList); + return TRUE; + } + } + + if (ratingDrag.update) + { + MLRatingColumnI_CancelTracking(FALSE); + SendMessageW(ratingDrag.hwndList, LVM_REDRAWITEMS, ratingDrag.iItem, ratingDrag.iItem); + UpdateWindow(ratingDrag.hwndList); + ratingDrag.update = FALSE; + } + return TRUE; + } + + if (ratingTracking.hwndList) + { + MLRatingColumnI_CancelTracking(FALSE); + ratingDrag.update = FALSE; + } + + if (!ratingDrag.update) + { + ratingDrag.outside = TRUE; + if (NULL != ratingDrag.hwndList) + { + SendMessageW(ratingDrag.hwndList, LVM_REDRAWITEMS, ratingDrag.iItem, ratingDrag.iItem); + UpdateWindow(ratingDrag.hwndList); + } + ratingDrag.update = TRUE; + } + return FALSE; +} + +BOOL MLRatingColumnI_EndDrag(RATINGCOLUMN_I *pRating) +{ + BOOL result; + RATINGCOLUMN_I rcol; + RECT rc; + + rcol.hwndList = ratingDrag.hwndList; + rcol.iItem = ratingDrag.iItem; + rcol.iSubItem = ratingDrag.iSubItem; + rcol.fStyle = ratingDrag.fStyle; + rcol.bRedrawNow = FALSE; + CopyRect(&rc, &ratingDrag.rc); + + ZeroMemory(&ratingDrag, sizeof(RATINGDRAG)); + ratingDrag.iItem = (UINT)-1; + + result = FALSE; + + if (rcol.hwndList) SendMessageW(rcol.hwndList, LVM_REDRAWITEMS, rcol.iItem, rcol.iItem); + + if (rcol.hwndList && rcol.hwndList == WindowFromPoint(pRating->ptAction)) + { + if (!pRating->bCanceled) + { + rcol.ptAction = pRating->ptAction; + MapWindowPoints(HWND_DESKTOP, rcol.hwndList, &rcol.ptAction, 1); + CorrectDragPoint(&rc, &rcol.ptAction); + + pRating->value = (PtInRect(&rc, rcol.ptAction) && MLRatingColumnI_Click(&rcol)) ? rcol.value : 0; + pRating->hwndList = rcol.hwndList; + pRating->iItem = rcol.iItem; + pRating->iSubItem = rcol.iSubItem; + result = TRUE; + } + } + if (pRating->bRedrawNow && NULL != rcol.hwndList) + UpdateWindow(rcol.hwndList); + + return result; +} + +void MLRatingColumnI_Animate(HWND hwndList, UINT iItem, UINT durationMs) +{ + if (ratingAnimation.timerId) + { + HWND ratingList; + KillTimer(NULL, ratingAnimation.timerId); + ratingList = ratingAnimation.hwndList; + ratingAnimation.hwndList = NULL; + if (ratingList && IsWindow(ratingList)) + { + SendMessageW(ratingList, LVM_REDRAWITEMS, ratingAnimation.iItem, ratingAnimation.iItem); + UpdateWindow(ratingList); + } + } + + if ((UINT)-1 != iItem && IsWindow(hwndList)) + { + ratingAnimation.hwndList = hwndList; + ratingAnimation.durationMs = (durationMs > RAITNG_ANIMATIONMAX) ? RAITNG_ANIMATIONMAX : durationMs; + ratingAnimation.stage = 1; + ratingAnimation.iItem = iItem; + ratingAnimation.startedMs = GetTickCount(); + + ratingAnimation.timerId = SetTimer(NULL, NULL, RATING_ANIMATIONINTERVAL, Timer_Animation); + } + else ratingAnimation.timerId = 0; + + if (ratingAnimation.timerId) + { + SendMessageW(ratingAnimation.hwndList, LVM_REDRAWITEMS, ratingAnimation.iItem, ratingAnimation.iItem); + UpdateWindow(ratingAnimation.hwndList); + } + else + { + ZeroMemory(&ratingAnimation, sizeof(RATINGANIMATION)); + ratingAnimation.iItem = (UINT)-1; + } +} + +void MLRatingColumnI_CancelTracking(BOOL bRedrawNow) +{ + HWND hwndList; + UINT iItem; + + if (ratingTracking.timerId) + { + KillTimer(NULL, ratingTracking.timerId); + ratingTracking.timerId = 0; + } + + if (ratingTracking.hwndList && IsWindow(ratingTracking.hwndList) && -1 != ratingTracking.iItem) + { + hwndList = ratingTracking.hwndList; + iItem = ratingTracking.iItem; + } + else + { + hwndList = NULL; + iItem = (UINT)-1; + } + + ratingTracking.hwndList = NULL; + ratingTracking.iItem = (UINT)-1; + ratingTracking.iSubItem = (UINT)-1; + ratingTracking.value = -1; + + if(hwndList) + { + SendMessageW(hwndList, LVM_REDRAWITEMS, (WPARAM)iItem, (LPARAM)iItem); + if(bRedrawNow) UpdateWindow(hwndList); + } +} + +INT MLRatingColumnI_GetWidth(INT width, UINT fStyle) +{ + INT minWidth; + if (RCS_DEFAULT == fStyle) fStyle = ratingGlobalStyle; + minWidth = MLRatingColumnI_GetMinWidth(); + if (width < minWidth && 0 == (RCS_SIZE_ALLOWDECREASE_I & fStyle)) width = minWidth; + if (width > minWidth && 0 == (RCS_SIZE_ALLOWINCREASE_I & fStyle)) width = minWidth; + return width; +} + +HWND MLRatingColumnI_TweakDialog(HWND hwndParent, UINT fStyle, ONRATINGTWEAKAPLLY fnApply, BOOL bVisible) +{ + HWND hwndDlg; + TWEAKPARAM param; + + if (!hwndParent || !IsWindow(hwndParent) || !fnApply) return NULL; + + param.fnApply = fnApply; + param.fStyle = fStyle; + hwndDlg = WASABI_API_CREATEDIALOGPARAMW(IDD_RATINGTWEAK, hwndParent, TweakDialogProc, (LPARAM)¶m); + if (IsWindow(hwndDlg)) + { + RECT rw, rc; + GetWindowRect((IsWindowVisible(hwndParent)?hwndParent:prefsWnd), &rc); + GetWindowRect(hwndDlg, &rw); + rc.left += ((rc.right - rc.left) - (rw.right - rw.left))/2; + rc.top += ((rc.bottom - rc.top) - (rw.bottom - rw.top))/2; + SetWindowPos(hwndDlg, NULL, rc.left, rc.top, 0, 0, SWP_NOSIZE | SWP_NOZORDER | ((bVisible) ? SWP_SHOWWINDOW : SWP_NOACTIVATE)); + } + return hwndDlg; +} + +static void CorrectDragPoint(CONST RECT *prc, POINT *ppt) +{ + if (ppt->x <= prc->left && (prc->left - ppt->x) < RATING_DRAGFORGIVENESS_LEFT) ppt->x = prc->left + 1; + if (ppt->x >= prc->right && (ppt->x - prc->right) < RATING_DRAGFORGIVENESS_RIGHT) ppt->x = prc->right - 1; + if (ppt->y <= prc->top && (prc->top - ppt->y) < RATING_DRAGFORGIVENESS_TOP) ppt->y = prc->top + 1; + if (ppt->y >= prc->bottom && (ppt->y - prc->bottom) < RATING_DRAGFORGIVENESS_BOTTOM) ppt->y = prc->bottom -1; +} + +static BOOL IsTrakingAllowed(HWND hwndList, UINT fStyle) +{ + if (RCS_TRACK_ALWAYS_I & fStyle) return TRUE; + if (RCS_TRACK_WNDFOCUSED_I & fStyle) + { + return (hwndList == GetFocus()); + } + if (RCS_TRACK_ANCESTORACITVE_I & fStyle) + { + HWND hwndActive; + hwndActive = GetActiveWindow(); + return (hwndList == hwndActive || IsChild(hwndActive, hwndList)); + } + if (RCS_TRACK_PROCESSACTIVE_I & fStyle) + { + GUITHREADINFO gui; + gui.cbSize = sizeof(GUITHREADINFO); + return (!GetGUIThreadInfo(GetWindowThreadProcessId(hwndList, NULL), &gui) || gui.hwndActive); + } + return FALSE; +} + +static BOOL IsItemTrackable(HWND hwndList, UINT iItem, UINT fStyle) +{ + if (RCS_TRACKITEM_SELECTED_I & fStyle) return (LVIS_SELECTED == (LVIS_SELECTED & SendMessageW(hwndList, LVM_GETITEMSTATE, (WPARAM)iItem, (LPARAM)LVIS_SELECTED))); + if (RCS_TRACKITEM_FOCUSED_I & fStyle) return (LVIS_FOCUSED == (LVIS_FOCUSED & SendMessageW(hwndList, LVM_GETITEMSTATE, (WPARAM)iItem, (LPARAM)LVIS_FOCUSED))); + return TRUE; +} + +static void CALLBACK Timer_AutoUnhover(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime) +{ + if (-1 == ratingTracking.iItem || NULL == ratingTracking.hwndList) KillTimer(NULL, idEvent); + else + { + POINT pt; + GetCursorPos(&pt); + MapWindowPoints(HWND_DESKTOP, ratingTracking.hwndList, &pt, 1); + + if (pt.x > (ratingTracking.rc.right + RATING_RIGHTPADDING) || pt.x < (ratingTracking.rc.left - RATING_LEFTPADDING) || + pt.y > ratingTracking.rc.bottom || pt.y < ratingTracking.rc.top) + { + MLRatingColumnI_CancelTracking(FALSE); + } + } +} + +static void CALLBACK Timer_Animation(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime) +{ + if (-1 == ratingAnimation.iItem || !ratingAnimation.hwndList) KillTimer(NULL, idEvent); + else + { + UINT iItem; + HWND hwndList; + + iItem = ratingAnimation.iItem; + hwndList = ratingAnimation.hwndList; + + if ((GetTickCount() - ratingAnimation.startedMs) > ratingAnimation.durationMs) + { + KillTimer(NULL, idEvent); + ZeroMemory(&ratingAnimation, sizeof(RATINGANIMATION)); + ratingAnimation.iItem = (UINT)-1; + } + else if (++ratingAnimation.stage > 1) ratingAnimation.stage = 0; + + SendMessageW(hwndList, LVM_REDRAWITEMS, iItem, iItem); + UpdateWindow(hwndList); + } +} + +static void TweakDialog_ApplyStyle(HWND hwndDlg, TWEAKPARAM *pTweak, UINT newStyle, BOOL bClosing) +{ + if (!pTweak->fnApply(newStyle, bClosing)) + { + wchar_t title[32] = {0}; + MessageBoxW(hwndDlg, WASABI_API_LNGSTRINGW(IDS_UNABLE_TO_APPLY_NEW_STYLE), + WASABI_API_LNGSTRINGW_BUF(IDS_TWEAK_ERROR,title,32), MB_OK); + newStyle = pTweak->fStyle; + } + else pTweak->fStyle = newStyle; +} + +static void TweakDialog_InitializeControls(HWND hwndDlg, UINT fStyle) +{ + INT i; + HWND hwndCtrl; + + if (NULL != (hwndCtrl = GetDlgItem(hwndDlg, IDC_CMB_TRACKWHEN))) + { + int pszTrackWhen[] = { IDS_ALWAYS, IDS_PROCESS_ACTIVE, IDS_ANCESTOR_ACTIVE, IDS_WINDOW_FOCUSED, IDS_NEVER}; + for (i = 0; i < sizeof(pszTrackWhen)/sizeof(char*); i++) SendMessageW(hwndCtrl, CB_ADDSTRING, 0, (LPARAM)WASABI_API_LNGSTRINGW(pszTrackWhen[i])); + if (RCS_TRACK_ALWAYS_I & fStyle) i = 0; + else if (RCS_TRACK_PROCESSACTIVE_I & fStyle) i = 1; + else if (RCS_TRACK_ANCESTORACITVE_I & fStyle) i = 2; + else if (RCS_TRACK_WNDFOCUSED_I & fStyle) i = 3; + else i = 4; + SendMessage(hwndCtrl, CB_SETCURSEL, (WPARAM)i, 0L); + } + + if (NULL != (hwndCtrl = GetDlgItem(hwndDlg, IDC_CMB_TRACKWHAT))) + { + int pszTrackWhat[] = { IDS_ALL, IDS_SELECTED, IDS_FOCUSED,}; + for (i = 0; i < sizeof(pszTrackWhat)/sizeof(char*); i++) SendMessageW(hwndCtrl, CB_ADDSTRING, 0, (LPARAM)WASABI_API_LNGSTRINGW(pszTrackWhat[i])); + if (RCS_TRACKITEM_SELECTED_I & fStyle) i = 1; + else if (RCS_TRACKITEM_FOCUSED_I & fStyle) i = 2; + else i = 0; + SendMessage(hwndCtrl, CB_SETCURSEL, (WPARAM)i, 0L); + } + + if (NULL != (hwndCtrl = GetDlgItem(hwndDlg, IDC_CMB_ALIGNMENT))) + { + int pszAlignment[] = { IDS_LEFT, IDS_CENTER, IDS_RIGHT,}; + for (i = 0; i < sizeof(pszAlignment)/sizeof(char*); i++) SendMessageW(hwndCtrl, CB_ADDSTRING, 0, (LPARAM)WASABI_API_LNGSTRINGW(pszAlignment[i])); + if (RCS_ALLIGN_CENTER_I & fStyle) i = 1; + else if (RCS_ALLIGN_RIGHT_I & fStyle) i = 2; + else i = 0; + SendMessage(hwndCtrl, CB_SETCURSEL, (WPARAM)i, 0L); + } + + CheckDlgButton(hwndDlg, IDC_CHK_SHOWEMPTY_NORMAL, (RCS_SHOWEMPTY_NORMAL_I & fStyle) ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hwndDlg, IDC_CHK_SHOWEMPTY_HOT, (RCS_SHOWEMPTY_HOT_I & fStyle) ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hwndDlg, IDC_CHK_SHOWEMPTY_ANIMATION, (RCS_SHOWEMPTY_ANIMATION_I & fStyle) ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hwndDlg, IDC_CHK_SHOWINACTIVE_HOT, (RCS_SHOWINACTIVE_HOT_I & fStyle) ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hwndDlg, IDC_CHK_BLOCKUNRATE, (RCS_BLOCKUNRATECLICK_I & fStyle) ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hwndDlg, IDC_CHK_BLOCKCLICK, (RCS_BLOCKCLICK_I & fStyle) ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hwndDlg, IDC_CHK_BLOCKDRAG, (RCS_BLOCKDRAG_I & fStyle) ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hwndDlg, IDC_CHK_SIZEDECREASE, (RCS_SIZE_ALLOWDECREASE_I & fStyle) ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hwndDlg, IDC_CHK_SIZEINCREASE, (RCS_SIZE_ALLOWINCREASE_I & fStyle) ? BST_CHECKED : BST_UNCHECKED); +} + +static UINT TweakDialog_GetStyle(HWND hwndDlg) +{ + INT i; + HWND hwndCtrl; + UINT fStyle; + fStyle = 0; + + if (BST_CHECKED == IsDlgButtonChecked(hwndDlg, IDC_CHK_SHOWEMPTY_NORMAL)) fStyle |= RCS_SHOWEMPTY_NORMAL_I; + if (BST_CHECKED == IsDlgButtonChecked(hwndDlg, IDC_CHK_SHOWEMPTY_HOT)) fStyle |= RCS_SHOWEMPTY_HOT_I; + if (BST_CHECKED == IsDlgButtonChecked(hwndDlg, IDC_CHK_SHOWEMPTY_ANIMATION)) fStyle |= RCS_SHOWEMPTY_ANIMATION_I; + if (BST_CHECKED == IsDlgButtonChecked(hwndDlg, IDC_CHK_SHOWINACTIVE_HOT)) fStyle |= RCS_SHOWINACTIVE_HOT_I; + if (BST_CHECKED == IsDlgButtonChecked(hwndDlg, IDC_CHK_BLOCKUNRATE)) fStyle |= RCS_BLOCKUNRATECLICK_I; + if (BST_CHECKED == IsDlgButtonChecked(hwndDlg, IDC_CHK_BLOCKCLICK)) fStyle |= RCS_BLOCKCLICK_I; + if (BST_CHECKED == IsDlgButtonChecked(hwndDlg, IDC_CHK_BLOCKDRAG)) fStyle |= RCS_BLOCKDRAG_I; + if (BST_CHECKED == IsDlgButtonChecked(hwndDlg, IDC_CHK_SIZEDECREASE)) fStyle |= RCS_SIZE_ALLOWDECREASE_I; + if (BST_CHECKED == IsDlgButtonChecked(hwndDlg, IDC_CHK_SIZEINCREASE)) fStyle |= RCS_SIZE_ALLOWINCREASE_I; + + i = (NULL != (hwndCtrl = GetDlgItem(hwndDlg, IDC_CMB_TRACKWHEN))) ? (INT)SendMessage(hwndCtrl, CB_GETCURSEL, 0, 0L) : CB_ERR; + switch(i) + { + case 0: fStyle |= RCS_TRACK_ALWAYS_I; break; + case 1: fStyle |= RCS_TRACK_PROCESSACTIVE_I; break; + case 2: fStyle |= RCS_TRACK_ANCESTORACITVE_I; break; + case 3: fStyle |= RCS_TRACK_WNDFOCUSED_I; break; + } + i = (NULL != (hwndCtrl = GetDlgItem(hwndDlg, IDC_CMB_TRACKWHAT))) ? (INT)SendMessage(hwndCtrl, CB_GETCURSEL, 0,0L) : CB_ERR; + switch(i) + { + case 1: fStyle |= RCS_TRACKITEM_SELECTED_I; break; + case 2: fStyle |= RCS_TRACKITEM_FOCUSED_I; break; + } + i = (NULL != (hwndCtrl = GetDlgItem(hwndDlg, IDC_CMB_ALIGNMENT))) ? (INT)SendMessage(hwndCtrl, CB_GETCURSEL, 0,0L) : CB_ERR; + switch(i) + { + case 1: fStyle |= RCS_ALLIGN_CENTER_I; break; + case 2: fStyle |= RCS_ALLIGN_RIGHT_I; break; + } + + return fStyle; +} + +static INT_PTR WINAPI TweakDialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + static TWEAKPARAM tweak; + static UINT fStyle; + switch(uMsg) + { + case WM_INITDIALOG: + + tweak.fnApply = (lParam) ? ((TWEAKPARAM*)lParam)->fnApply : NULL; + tweak.fStyle = (lParam) ? ((TWEAKPARAM*)lParam)->fStyle : 0x000; + if (RCS_DEFAULT == tweak.fStyle) tweak.fStyle = ratingGlobalStyle; + fStyle = tweak.fStyle; + TweakDialog_InitializeControls(hwnd, fStyle); + break; + case WM_COMMAND: + switch(LOWORD(wParam)) + { + case IDCANCEL: + { + wchar_t title[32] = {0}; + fStyle = TweakDialog_GetStyle(hwnd); + TweakDialog_ApplyStyle(hwnd, &tweak, + (tweak.fStyle != fStyle && + IDYES == MessageBoxW(hwnd, WASABI_API_LNGSTRINGW(IDS_DO_YOU_WANT_TO_SAVE_CHANGES_FIRST), + WASABI_API_LNGSTRINGW_BUF(IDS_CONFIRM_CLOSE,title,32), MB_YESNO)) ? + fStyle :tweak.fStyle, TRUE); + DestroyWindow(hwnd); + break; + } + case IDOK: + if (BN_CLICKED == HIWORD(wParam)) + { + fStyle = TweakDialog_GetStyle(hwnd); + TweakDialog_ApplyStyle(hwnd, &tweak, fStyle, FALSE); + } + DestroyWindow(hwnd); + break; + } + } + return 0; +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/ml_ratingcolumn.h b/Src/Plugins/General/gen_ml/ml_ratingcolumn.h new file mode 100644 index 00000000..a9dc356e --- /dev/null +++ b/Src/Plugins/General/gen_ml/ml_ratingcolumn.h @@ -0,0 +1,90 @@ +#ifndef NULLOSFT_MEDIALIBRARY_RATING_COLUMN_HEADER +#define NULLOSFT_MEDIALIBRARY_RATING_COLUMN_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif +#include + +// Styles +#define RCS_DEFAULT_I 0xFFFFFFFF // use default gen_ml style +// layout +#define RCS_ALLIGN_LEFT_I 0x00000000 +#define RCS_ALLIGN_CENTER_I 0x00000001 +#define RCS_ALLIGN_RIGHT_I 0x00000002 +// showepmty +#define RCS_SHOWEMPTY_NEVER_I 0x00000000 +#define RCS_SHOWEMPTY_NORMAL_I 0x00000010 +#define RCS_SHOWEMPTY_HOT_I 0x00000020 +#define RCS_SHOWEMPTY_ANIMATION_I 0x00000040 +#define RCS_SHOWEMPTY_ALWAYS_I 0x00000070 +#define RCS_SHOWINACTIVE_HOT_I 0x00000080 +// traking (when) +#define RCS_TRACK_NEVER_I 0x00000000 +#define RCS_TRACK_WNDFOCUSED_I 0x00000100 +#define RCS_TRACK_ANCESTORACITVE_I 0x00000200 +#define RCS_TRACK_PROCESSACTIVE_I 0x00000400 +#define RCS_TRACK_ALWAYS_I 0x00000800 +// traking (what) +#define RCS_TRACKITEM_ALL_I 0x00000000 +#define RCS_TRACKITEM_SELECTED_I 0x00100000 +#define RCS_TRACKITEM_FOCUSED_I 0x00200000 + +#define RCS_BLOCKCLICK_I 0x01000000 +#define RCS_BLOCKUNRATECLICK_I 0x02000000 +#define RCS_BLOCKDRAG_I 0x04000000 + +#define RCS_SIZE_ALLOWDECREASE_I 0x10000000 +#define RCS_SIZE_ALLOWINCREASE_I 0x20000000 + +#define RATING_DEFAULT_STYLE (RCS_ALLIGN_LEFT_I | \ + RCS_SHOWEMPTY_HOT_I | \ + RCS_SHOWEMPTY_ANIMATION_I | \ + RCS_TRACK_PROCESSACTIVE_I | \ + RCS_TRACKITEM_ALL_I | \ + /*RCS_BLOCKUNRATECLICK_I |*/ \ + 0) + +typedef struct _RATINGCOLUMNPAINT_I +{ + HWND hwndList; // hwnd of the listview + HDC hdc; // hdc + UINT iItem; // item index + UINT iSubItem; // subitem index + INT value; // database rating value + RECT *prcItem; // whole item rect (plvcd->nmcd.rc) + RECT *prcView; // client area size (you can get it at CDDS_PREPAINT in plvcd->nmcd.rc) + COLORREF rgbBk; // color to use as background (plvcd->clrTextBk) + COLORREF rgbFg; // color to use as foreground (plvcd->clrText) + UINT fStyle; // style to use RCS_XXX +} RATINGCOLUMNPAINT_I; + +typedef struct _RATINGCOLUMN_I +{ + HWND hwndList; + UINT iItem; + UINT iSubItem; + INT value; + POINT ptAction; // + BOOL bRedrawNow; // You want list to be redrawn immediatly + BOOL bCanceled; // Used with EndDrag - i + UINT fStyle; // RCS_XXX +} RATINGCOLUMN_I; + +BOOL MLRatingColumnI_Initialize(void); // call it before any other. You can call it any time something changed +BOOL MLRatingColumnI_Update(void); // call this when any skin / font changed happend +BOOL MLRatingColumnI_Paint(RATINGCOLUMNPAINT_I *pRCPaint); +BOOL MLRatingColumnI_Click(RATINGCOLUMN_I *pRating); // Set: hwndList, ptAction, bRedrawNow, fStyle. // Returns TRUE if rating was clicked. Sets: iItem, iSubItem, value. +void MLRatingColumnI_Track(RATINGCOLUMN_I *pRating); // Set: hwndList, iItem, iSubItem, value, ptAction, bRedrawNow, fStyle. No Return. +BOOL MLRatingColumnI_BeginDrag(RATINGCOLUMN_I *pRating); // Set: hwndList, iItem, iSubItem, value, fStyle. Return TRUE if handled. +BOOL MLRatingColumnI_Drag(POINT pt); // Return TRUE if handled. +BOOL MLRatingColumnI_EndDrag(RATINGCOLUMN_I *pRating); // Set: ptAction, bCanceled, bRedrawNow. Return TRUE if handled. Sets: iItem, value. +void MLRatingColumnI_Animate(HWND hwndList, UINT iItem, UINT durationMs); +void MLRatingColumnI_CancelTracking(BOOL bRedrawNow); +INT MLRatingColumnI_GetMinWidth(void); +INT MLRatingColumnI_GetWidth(INT width, UINT fStyle); // will return you new width according to the policies +LPCWSTR MLRatingColumnI_FillBackString(LPWSTR pszText, INT cchTextMax, INT nColumnWidth, UINT fStyle); + +typedef BOOL (CALLBACK *ONRATINGTWEAKAPLLY)(UINT /*newStyle*/, BOOL /*bClosing*/); +HWND MLRatingColumnI_TweakDialog(HWND hwndParent, UINT fStyle, ONRATINGTWEAKAPLLY fnApply, BOOL bVisible); +#endif // NULLOSFT_MEDIALIBRARY_RATING_COLUMN_HEADER \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/mldwm.cpp b/Src/Plugins/General/gen_ml/mldwm.cpp new file mode 100644 index 00000000..14cdd65f --- /dev/null +++ b/Src/Plugins/General/gen_ml/mldwm.cpp @@ -0,0 +1,46 @@ +#include "./mldwm.h" + +typedef HRESULT (WINAPI *DWMSETWINDOWATTRIBUTE)(HWND /*hwnd*/, DWORD /*dwAttribute*/, LPCVOID /*pvAttribute*/, DWORD /*cbAttribute*/); +typedef HRESULT (WINAPI *DWMISCOMPOSITIONENABLED)(BOOL* /*pfEnabled*/); + +static HMODULE hDwmModule = NULL; +static HRESULT loadResult = E_MLDWM_NOTLOADED; + +static DWMSETWINDOWATTRIBUTE fnSetWindowAttribute = NULL; +static DWMISCOMPOSITIONENABLED fnIsCompositionEnabled = NULL; + +HRESULT MlDwm_IsLoaded() +{ + return loadResult; +} + +HRESULT MlDwm_LoadLibrary(void) +{ + if (E_MLDWM_NOTLOADED == loadResult) + { + hDwmModule = LoadLibraryW(L"dwmapi.dll"); + if (!hDwmModule) loadResult = E_MLDWM_LOADFAILED; + else + { + fnSetWindowAttribute = (DWMSETWINDOWATTRIBUTE)GetProcAddress(hDwmModule, "DwmSetWindowAttribute"); + fnIsCompositionEnabled = (DWMISCOMPOSITIONENABLED)GetProcAddress(hDwmModule, "DwmIsCompositionEnabled"); + loadResult = S_OK; + } + } + return loadResult; +} + + +HRESULT MlDwm_SetWindowAttribute(HWND hwnd, DWORD dwAttribute, LPCVOID pvAttribute, DWORD cbAttribute) +{ + if (!hDwmModule) return loadResult; + if (!fnSetWindowAttribute) return E_MLDWM_BADFUNCTION; + return fnSetWindowAttribute(hwnd, dwAttribute, pvAttribute, cbAttribute); +} + +HRESULT MlDwm_IsCompositionEnabled(BOOL *pfEnabled) +{ + if (!hDwmModule) return loadResult; + if (!fnIsCompositionEnabled) return E_MLDWM_BADFUNCTION; + return fnIsCompositionEnabled(pfEnabled); +} diff --git a/Src/Plugins/General/gen_ml/mldwm.h b/Src/Plugins/General/gen_ml/mldwm.h new file mode 100644 index 00000000..ab54a6c4 --- /dev/null +++ b/Src/Plugins/General/gen_ml/mldwm.h @@ -0,0 +1,47 @@ +#ifndef NULLOSFT_MEDIALIBRARY_MS_DWM_HEADER +#define NULLOSFT_MEDIALIBRARY_MS_DWM_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include + +#ifndef WM_DWMCOMPOSITIONCHANGED +#define WM_DWMCOMPOSITIONCHANGED 0x031E +#define WM_DWMNCRENDERINGCHANGED 0x031F +#define WM_DWMCOLORIZATIONCOLORCHANGED 0x0320 +#define WM_DWMWINDOWMAXIMIZEDCHANGE 0x0321 +#endif + +typedef enum _DWMWINDOWATTRIBUTE { + DWMWA_NCRENDERING_ENABLED = 1, + DWMWA_NCRENDERING_POLICY, + DWMWA_TRANSITIONS_FORCEDISABLED, + DWMWA_ALLOW_NCPAINT, + DWMWA_CAPTION_BUTTON_BOUNDS, + DWMWA_NONCLIENT_RTL_LAYOUT, + DWMWA_FORCE_ICONIC_REPRESENTATION, + DWMWA_FLIP3D_POLICY, + DWMWA_EXTENDED_FRAME_BOUNDS, + DWMWA_LAST +} DWMWINDOWATTRIBUTE; + +typedef enum _DWMNCRENDERINGPOLICY { + DWMNCRP_USEWINDOWSTYLE, + DWMNCRP_DISABLED, + DWMNCRP_ENABLED, + DWMNCRP_LAST +} DWMNCRENDERINGPOLICY; + +#define E_MLDWM_NOTLOADED MAKE_HRESULT(0, FACILITY_ITF, 0x0201) // library not loaded (you just need to call MlDwm_LoadLibrary) +#define E_MLDWM_LOADFAILED MAKE_HRESULT(1, FACILITY_ITF, 0x0202) // library load failed (probably not vista?) +#define E_MLDWM_BADFUNCTION MAKE_HRESULT(1, FACILITY_ITF, 0x0203) // function was not loaded + +HRESULT MlDwm_LoadLibrary(void); +HRESULT MlDwm_GetLoadResult(void); +HRESULT MlDwm_SetWindowAttribute(HWND hwnd, DWORD dwAttribute, LPCVOID pvAttribute, DWORD cbAttribute); +HRESULT MlDwm_IsCompositionEnabled(BOOL *pfEnabled); + + +#endif // NULLOSFT_MEDIALIBRARY_MS_DWM_HEADER \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/navigation.cpp b/Src/Plugins/General/gen_ml/navigation.cpp new file mode 100644 index 00000000..fb01abd3 --- /dev/null +++ b/Src/Plugins/General/gen_ml/navigation.cpp @@ -0,0 +1,2733 @@ +#include "./navigation.h" + +#include "./ml.h" +#include "./ml_ipc_0313.h" + +#include "../nu/AutoChar.h" +#include "../nu/AutoWide.h" + +#include "./skinning.h" +#include "./stockobjects.h" + +#include "../winamp/wa_dlg.h" +#include "../winamp/gen.h" + +#include "resource.h" +#include "api__gen_ml.h" +#include "../nu/CGlobalAtom.h" +#include "../nu/trace.h" +#include +#include + +extern "C" winampGeneralPurposePlugin plugin; + +#ifndef LONGX86 +#ifdef _WIN64 + #define LONGX86 LONG_PTR +#else /*_WIN64*/ + #define LONGX86 LONG +#endif /*_WIN64*/ +#endif // LONGX86 + +#define PACKVERSION(major,minor) MAKELONG(minor,major) + +#define SEPARATOR L'/' +#define MLV_PREFIX_A L"mlv10_" +#define EMPTY_ITEM_NAME L"<>" +#define ITEM_SHORTNAME_MAX 512 + +#define SAVEVIEW_BUFFER_MAX 1024 +#define ML_VIEW_MAX 32 + +#define NAVITEM_RANGE_MIN 8000 +static CGlobalAtom NAVMGR_HWNDPROPW(L"NAVMNGR"); +static CGlobalAtom WNDPROP_SCCTRLW(L"SCCTRL"); + +#define IREC_COUNT 3 + +#define IREC_NORMAL 0x00 +#define IREC_SELECTED 0x01 +#define IREC_INACTIVE 0x02 + +#define DRAGIMAGE_OFFSET_X 8 +#define DRAGIMAGE_OFFSET_Y 0 + +typedef struct _SCEDIT +{ + WNDPROC fnOldProc; + BOOL fUnicode; +}SCEDIT; + +typedef struct _SCTREE +{ + WNDPROC fnOldProc; + BOOL fUnicode; + DWORD focused; + BOOL supressEdit; +}SCTREE; + +typedef struct _NAVITEMSTATEREC +{ + LPWSTR pszFullName; + INT fCollapsed; + INT nOrder; +} NAVITEMSTATEREC; + +typedef struct _NAVMNGR +{ + HWND hwndHost; + INT lastUsedId; + C_Config *pConfig; + HMLIMGLST hmlilImages; + INT lockUpdate; + HTREEITEM lockFirst; + HTREEITEM lockSelected; + INT lockSelPos; + INT lastOrderIndex; + NAVITEMSTATEREC *pStates; + INT statesCount; + UINT style; + NAVITEMDRAW_I drawStruct; + HFONT hfontOld; + UINT drawInternal; + + // callbacks + ONNAVITEMCLICK_I fnOnItemClick; + ONNAVITEMSELECTED_I fnOnItemSelected; + ONNAVCTRLKEYDOWN_I fnOnKeyDown; + ONNAVCTRLBEGINDRAG_I fnOnBeginDrag; + ONNAVITEMGETIMAGEINDEX_I fnOnItemGetImageIndex; + ONNAVITEMBEGINTITLEEDIT_I fnOnBeginTitleEdit; + ONNAVCTRLENDTITLEEDIT_I fnOnEndTitleEdit; + ONNAVITEMDELETE_I fnOnItemDelete; + ONNAVITEMDRAW_I fnOnCustomDraw; + ONNAVITEMSETCURSOR_I fnOnSetCursor; + ONNAVITEMHITTEST_I fnOnHitTest; + ONNAVCTRLDESTROY_I fnOnDestroy; +} NAVMNGR, *PNAVMNGR; + +typedef struct _NAVITM +{ + INT id; + INT iImage; + INT iSelectedImage; + WORD sortOrder; + UINT style; + HWND hwndTree; + HTREEITEM hTreeItem; + LPWSTR pszText; + INT cchTextMax; + LPWSTR pszInvariant; + INT cchInvariantMax; + HFONT hFont; + BOOL fBlockInvalid; // if set to TRUE operation do not need to inavlidate item it will be invalidated later; + LPARAM lParam; +} NAVITM, *PNAVITM; + +typedef struct _TREEITEMSEARCH +{ + TVITEMEXW item; + INT_PTR itemData; + BOOL fFound; + UINT flags; +} TREEITEMSEARCH; + + +typedef struct _SAVEVIEWDATA +{ + TVITEMEXW item; + WCHAR buffer[SAVEVIEW_BUFFER_MAX]; + WCHAR mlview[ML_VIEW_MAX]; + C_Config *pConfig; + INT counter_root; + INT counter_write; +}SAVEVIEWDATA; + +typedef struct _NAVENUMSTRUCT +{ + TVITEMW item; + NAVENUMPROC_I callback; + LPARAM lParam; + HNAVCTRL hNav; +} NAVENUMSTRUCT; + +typedef BOOL (CALLBACK *TREEITEMCB)(HWND, HTREEITEM, LPVOID); + +#define ABS(x) (((x) > 0) ? (x) : (-x)) + +#define NAVSTYLE_DEFAULT 0x0000 +#define NAVSTYLE_MOUSEDOWNSELECT 0x0001 +#define NAVSTYLE_FOCUSED 0x0002 +#define NAVSTYLE_EDITMODE 0x0004 +// helpers + +static BOOL GetItemColors(HNAVCTRL hNav, UINT itemState, COLORREF *rgbBk, COLORREF *rgbFg) +{ + INT rgbBkIndex, rgbFgIndex; + switch(itemState) + { + case (NIS_SELECTED_I | NIS_FOCUSED_I): rgbBkIndex = WADLG_SELBAR_BGCOLOR; rgbFgIndex = WADLG_SELBAR_FGCOLOR; break; + case NIS_SELECTED_I: rgbBkIndex = WADLG_INACT_SELBAR_BGCOLOR; rgbFgIndex = WADLG_INACT_SELBAR_FGCOLOR; break; + case NIS_DROPHILITED_I: rgbBkIndex = WADLG_INACT_SELBAR_BGCOLOR; rgbFgIndex = WADLG_INACT_SELBAR_FGCOLOR; break; + default: rgbBkIndex = WADLG_ITEMBG; rgbFgIndex = WADLG_ITEMFG; break; + } + if (rgbBk) *rgbBk = WADlg_getColor(rgbBkIndex); + if (rgbFg) *rgbFg = WADlg_getColor(rgbFgIndex); + return TRUE; +} + +static BOOL EnumerateTreeItems(HWND hwndTree, HTREEITEM hStart, TREEITEMCB pfnItemCallback, LPVOID user) +{ + HTREEITEM hChild; + if (!pfnItemCallback) return FALSE; + + if (NULL == hStart || TVI_ROOT == hStart) + hStart = (HTREEITEM)SendMessageW(hwndTree, TVM_GETNEXTITEM, (WPARAM)TVGN_ROOT, (LPARAM)0); + + while(NULL != hStart) + { + if (!pfnItemCallback(hwndTree, hStart, user)) + return FALSE; + hChild = (HTREEITEM)SendMessageW(hwndTree, TVM_GETNEXTITEM, (WPARAM)TVGN_CHILD, (LPARAM)hStart); + if (NULL != hChild) + { + if (!EnumerateTreeItems(hwndTree, hChild, pfnItemCallback, user)) + return FALSE; + } + hStart = (HTREEITEM)SendMessageW(hwndTree, TVM_GETNEXTITEM, (WPARAM)TVGN_NEXT, (LPARAM)hStart); + } + return TRUE; +} + +static BOOL CALLBACK FindTreeItemByIdCB(HWND hwndTree, HTREEITEM hItem, LPVOID user) +{ + if (!user) return FALSE; + + ((TREEITEMSEARCH*)user)->item.hItem = hItem; + if ((BOOL)SendMessageW(hwndTree, TVM_GETITEMW, (WPARAM)0, (LPARAM)&((TREEITEMSEARCH*)user)->item)) + { + if (((TREEITEMSEARCH*)user)->item.lParam && + (INT)((TREEITEMSEARCH*)user)->itemData == ((NAVITM*)((TREEITEMSEARCH*)user)->item.lParam)->id) + { + ((TREEITEMSEARCH*)user)->fFound = TRUE; + return FALSE; + } + } + return TRUE; +} + +static INT CompareItemName(LCID Locale, UINT compFlags, LPCWSTR pszString, INT cchLength, HNAVITEM hItem) +{ + INT result; + if (!hItem || !pszString) return 0; // error + + if (NICF_INVARIANT_I & compFlags) + { + result = (((NAVITM*)hItem)->pszInvariant) ? + CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, ((NAVITM*)hItem)->pszInvariant, -1, pszString, cchLength) : + CSTR_LESS_THAN; + if (CSTR_EQUAL == result) return CSTR_EQUAL; + } + if (NICF_DISPLAY_I & compFlags || 0 == (compFlags & ~NICF_IGNORECASE_I)) + { + result = (((NAVITM*)hItem)->pszText) ? + CompareStringW(Locale, (NICF_IGNORECASE_I & compFlags) ? NORM_IGNORECASE : 0, ((NAVITM*)hItem)->pszText, -1, pszString, cchLength) : + CSTR_LESS_THAN; + } + return result; +} + +static BOOL CALLBACK FindTreeItemByNameCB(HWND hwndTree, HTREEITEM hItem, LPVOID user) +{ + if (!user) return FALSE; + + ((TREEITEMSEARCH*)user)->item.hItem = hItem; + + if ((BOOL)SendMessageW(hwndTree, TVM_GETITEMW, (WPARAM)0, (LPARAM)&((TREEITEMSEARCH*)user)->item)) + { + if (CSTR_EQUAL == CompareItemName(((TREEITEMSEARCH*)user)->item.state, ((TREEITEMSEARCH*)user)->flags, + (LPCWSTR)((TREEITEMSEARCH*)user)->itemData, ((TREEITEMSEARCH*)user)->item.cchTextMax, + (NAVITM*)((TREEITEMSEARCH*)user)->item.lParam)) + { + ((TREEITEMSEARCH*)user)->fFound = TRUE; + return FALSE; + } + } + return TRUE; +} + +static BOOL CALLBACK ItemSizedCB(HWND hwndTree, HTREEITEM hItem, LPVOID user) +{ + ((TVITEMW*)user)->hItem = hItem; + if (!SendMessageW(hwndTree, TVM_GETITEMW, (WPARAM)0, (LPARAM)user)) return FALSE; + if (((TVITEMW*)user)->lParam) + { + if (NIS_CUSTOMDRAW_I & ((NAVITM*)((TVITEMW*)user)->lParam)->style) + { + *(HTREEITEM*)((TVITEMW*)user)->pszText = hItem; + if (SendMessageW(hwndTree, TVM_GETITEMRECT, FALSE, (LPARAM)((TVITEMW*)user)->pszText)) + { + InvalidateRect(hwndTree, (RECT*)((TVITEMW*)user)->pszText, TRUE); + } + } + } + return TRUE; +} + +static HNAVITEM FindNavItemByNavIdEx(HWND hwndTree, INT itemId, HTREEITEM hStart) +{ + TREEITEMSEARCH search; + search.fFound = FALSE; + search.itemData = (INT_PTR)itemId; + search.item.mask = TVIF_PARAM; + search.flags = 0; + + EnumerateTreeItems(hwndTree, hStart, FindTreeItemByIdCB, &search); + return (search.fFound) ? (HNAVITEM)search.item.lParam : NULL; +} + +static INT GetNextFreeItemId(HNAVCTRL hNav) +{ + if (!hNav) return 0; + while (FindNavItemByNavIdEx(((NAVMNGR*)hNav)->hwndHost, ++((NAVMNGR*)hNav)->lastUsedId, TVI_ROOT)); + return ((NAVMNGR*)hNav)->lastUsedId; +} + +static INT GetRealImageIndex(HNAVCTRL hNav, HNAVITEM hItem, INT imageType, COLORREF rgbBk, COLORREF rgbFg) +{ + INT mlilIndex; + if (!hNav) return -1; + mlilIndex = NavItemI_GetImageIndex(hItem, imageType); + if (-1 == mlilIndex ) + { + if (((NAVMNGR*)hNav)->fnOnItemGetImageIndex) mlilIndex = ((NAVMNGR*)hNav)->fnOnItemGetImageIndex(hNav, hItem, imageType); + if (-1 == mlilIndex) return -1; + } + return MLImageListI_GetRealIndex(((NAVMNGR*)hNav)->hmlilImages, mlilIndex, rgbBk, rgbFg); +} + +static BOOL CALLBACK EnumNavItemCB(HWND hwndTree, HTREEITEM hItem, LPVOID user) +{ + NAVENUMSTRUCT *pData; + if (!user) return FALSE; + + pData = (NAVENUMSTRUCT*)user; + pData->item.hItem = hItem; + + return (BOOL)SendMessageW(hwndTree, TVM_GETITEMW, (WPARAM)0, (LPARAM)&(pData->item)) ? + pData->callback((HNAVITEM)pData->item.lParam, pData->lParam) : TRUE; +} + +static BOOL CALLBACK SaveNavigationCB(HWND hwndTree, HTREEITEM hItem, LPVOID user) +{ + if (!user) return FALSE; // very bad + + ((SAVEVIEWDATA*)user)->item.hItem = hItem; + + if ((BOOL)SendMessageW(hwndTree, TVM_GETITEMW, (WPARAM)0, (LPARAM)&((SAVEVIEWDATA*)user)->item)) + { + INT order; + BOOL bCollapsed = (SendMessageW(hwndTree, TVM_GETNEXTITEM, (WPARAM)TVGN_CHILD, (LPARAM)hItem) && !(TVIS_EXPANDED & ((SAVEVIEWDATA*)user)->item.state)); + if (!SendMessageW(hwndTree, TVM_GETNEXTITEM, (WPARAM)TVGN_PARENT, (LPARAM)hItem)) + { + order = ++((SAVEVIEWDATA*)user)->counter_root; + } + else order = -1; + if (order != -1 || bCollapsed || (TVIS_SELECTED & ((SAVEVIEWDATA*)user)->item.state)) + { + INT remain = NavItemI_GetFullName((HNAVITEM)((SAVEVIEWDATA*)user)->item.lParam, ((SAVEVIEWDATA*)user)->buffer, SAVEVIEW_BUFFER_MAX); + if (remain) + { + if ((order != -1 || bCollapsed) && remain < (SAVEVIEW_BUFFER_MAX - 8) ) + { + if (S_OK == StringCchPrintfW(((SAVEVIEWDATA*)user)->buffer + remain, SAVEVIEW_BUFFER_MAX - remain, L",%d,%d", bCollapsed, order) && + S_OK == StringCchPrintfW(((SAVEVIEWDATA*)user)->mlview, ML_VIEW_MAX, MLV_PREFIX_A L"%02d", (((SAVEVIEWDATA*)user)->counter_write + 1))) + { + ((SAVEVIEWDATA*)user)->pConfig->WriteString(((SAVEVIEWDATA*)user)->mlview, ((SAVEVIEWDATA*)user)->buffer); + ((SAVEVIEWDATA*)user)->counter_write++; + } + } + } + } + } + + return TRUE; +} + +static HNAVITEM GetNavItemFromTreeItem(HNAVCTRL hNav, HTREEITEM hTreeItem) +{ + TVITEMW item; + item.mask = TVIF_PARAM; + item.hItem = hTreeItem; + + if (!hNav) return NULL; + + if (!SendMessageW(((NAVMNGR*)hNav)->hwndHost, TVM_GETITEMW, (WPARAM)0, (LPARAM)&item)) return NULL; + return (HNAVITEM)item.lParam; +} + +static HNAVITEM NavItemI_GetNextEx(HNAVITEM hItem, UINT flag) // use TVGN_ as flags +{ + TVITEMW treeItem; + + if (!hItem) return NULL; + + treeItem.hItem = (HTREEITEM)SendMessageW(((NAVITM*)hItem)->hwndTree, TVM_GETNEXTITEM, (WPARAM)flag, (LPARAM)((NAVITM*)hItem)->hTreeItem); + + if (!treeItem.hItem) return NULL; + + treeItem.mask = TVIF_PARAM; + return (SendMessageW(((NAVITM*)hItem)->hwndTree, TVM_GETITEMW, (WPARAM)0, (LPARAM)&treeItem)) ? (HNAVITEM)treeItem.lParam : NULL; +} + +static void PerformCustomHitTest(HNAVCTRL hNav, POINT pt, UINT *pHitFlags, HNAVITEM *phItem) // returns nav hit test result +{ + UINT flags, test; + + flags = 0; + test = (pHitFlags) ? *pHitFlags : 1; + + if (TVHT_NOWHERE & test) flags |= NAVHT_NOWHERE_I; + if (TVHT_ONITEMLABEL & test) flags |= NAVHT_ONITEM_I; + if (TVHT_ONITEMINDENT & test) flags |= NAVHT_ONITEMINDENT_I; + if (TVHT_ONITEMRIGHT & test) flags |= NAVHT_ONITEMRIGHT_I; + if (TVHT_ABOVE & test) flags |= NAVHT_ABOVE_I; + if (TVHT_BELOW & test) flags |= NAVHT_BELOW_I; + if (TVHT_TORIGHT & test) flags |= NAVHT_TORIGHT_I; + if (TVHT_TOLEFT & test) flags |= NAVHT_TOLEFT_I; + + if (pHitFlags) *pHitFlags = flags; + + if (hNav && phItem && *phItem && pHitFlags) + { + if (TVHT_ONITEMICON & test) *pHitFlags |= (SendMessageW(((NAVMNGR*)hNav)->hwndHost, TVM_GETNEXTITEM, + (WPARAM)TVGN_CHILD, (LPARAM)((NAVITM*)(*phItem))->hTreeItem)) ? + NAVHT_ONITEMBUTTON_I : NAVHT_ONITEM_I; + + if ((NIS_WANTHITTEST_I & ((NAVITM*)(*phItem))->style) && ((NAVMNGR*)hNav)->fnOnHitTest) + { + ((NAVMNGR*)hNav)->fnOnHitTest(hNav, pt, pHitFlags, phItem, ((NAVITM*)(*phItem))->lParam); + } + } +} + +static HTREEITEM TreeItemFromCursor(HNAVCTRL hNav, HTREEITEM *phtiWantExpand) +{ + HTREEITEM hTreeItem; + TVHITTESTINFO hit; + + if (phtiWantExpand) *phtiWantExpand = NULL; + + if(!GetCursorPos(&hit.pt)) return NULL; + + MapWindowPoints(HWND_DESKTOP, ((NAVMNGR*)hNav)->hwndHost, &hit.pt, 1); + hTreeItem = (HTREEITEM)SendMessageW(((NAVMNGR*)hNav)->hwndHost, TVM_HITTEST, (WPARAM)0, (LPARAM)&hit); + + if (0 == (TVHT_ONITEM & hit.flags)) + { + if ((TVHT_ONITEMRIGHT | TVHT_ONITEMINDENT) & hit.flags) + { + if (0 == (TVS_FULLROWSELECT & GetWindowLongPtrW(((NAVMNGR*)hNav)->hwndHost, GWL_STYLE))) hTreeItem = NULL; + } + } + + if(hTreeItem) + { + HNAVITEM hItem; + hItem = GetNavItemFromTreeItem(hNav, hTreeItem); + PerformCustomHitTest(hNav, hit.pt, &hit.flags, &hItem); + + hTreeItem = (hItem) ? ((NAVITM*)hItem)->hTreeItem : NULL; + if (NAVHT_ONITEMBUTTON_I & hit.flags) + { + if (phtiWantExpand) *phtiWantExpand = hTreeItem; + hTreeItem = NULL; + } + } + return hTreeItem; +} + +static NAVITEMSTATEREC *NavCtrlI_FindItemStateRec(HNAVCTRL hNav, HNAVITEM hItem, BOOL fClearOnSuccess) +{ + wchar_t name[1024] = {0}; + + if (!hNav || !hItem) return NULL; + if (NavItemI_GetFullName(hItem, name, 1024)) + { + for (INT idx = 0; idx < ((NAVMNGR*)hNav)->statesCount; idx++) + { + if (((NAVMNGR*)hNav)->pStates[idx].pszFullName && + CSTR_EQUAL == CompareStringW(LOCALE_USER_DEFAULT, 0, name, -1, + ((NAVMNGR*)hNav)->pStates[idx].pszFullName, -1)) + { + if (fClearOnSuccess) + { + free(((NAVMNGR*)hNav)->pStates[idx].pszFullName); + ((NAVMNGR*)hNav)->pStates[idx].pszFullName = NULL; + } + return &((NAVMNGR*)hNav)->pStates[idx]; + } + } + } + return NULL; +} + +static LRESULT EditCtrlWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + SCEDIT *pEdit; + pEdit = (SCEDIT*)GetPropW(hwnd, WNDPROP_SCCTRLW); + if (!pEdit) return (IsWindowUnicode(hwnd)) ? DefWindowProcW(hwnd, uMsg, wParam, lParam) : DefWindowProcA(hwnd, uMsg, wParam, lParam); + + switch(uMsg) + { + case WM_NCDESTROY: // detach + RemovePropW(hwnd, WNDPROP_SCCTRLW); + if (pEdit->fUnicode) + { + CallWindowProcW(pEdit->fnOldProc, hwnd, uMsg, wParam, lParam); + SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (LONGX86)(LONG_PTR)pEdit->fnOldProc); + } + else + { + CallWindowProcA(pEdit->fnOldProc, hwnd, uMsg, wParam, lParam); + SetWindowLongPtrA(hwnd, GWLP_WNDPROC, (LONGX86)(LONG_PTR)pEdit->fnOldProc); + } + free(pEdit); + return 0; + case WM_ERASEBKGND: return 1; + case WM_NCPAINT: + { + HDC hdc; + + hdc = GetDCEx(hwnd, NULL, DCX_CACHE | DCX_WINDOW | DCX_INTERSECTUPDATE | DCX_CLIPSIBLINGS); + if (hdc) + { + RECT rc; + GetClientRect(hwnd, &rc); + FrameRect(hdc, &rc, (HBRUSH)MlStockObjects_Get(ITEMTEXT_BRUSH)); + ReleaseDC(hwnd, hdc); + } + return 0; + } + break; + case WM_PAINT: + { + RECT rc, rv; + GetClientRect(hwnd, &rc); + SetRect(&rv, 0, 0, rc.right, 1); + ValidateRect(hwnd, &rv); + SetRect(&rv, 0, rc.bottom - 1, rc.right, rc.bottom); + ValidateRect(hwnd, &rv); + SetRect(&rv, 0, 1, 1, rc.bottom); + ValidateRect(hwnd, &rv); + SetRect(&rv, rc.right-1, 1, rc.right, rc.bottom); + ValidateRect(hwnd, &rv); + break; + } + case WM_GETDLGCODE: + switch(wParam) + { + case VK_RETURN: + SendMessageW(GetParent(hwnd), TVM_ENDEDITLABELNOW, FALSE, 0L); return 0; // tell trew-view that we done and return 0 + case VK_ESCAPE: + SendMessageW(GetParent(hwnd), TVM_ENDEDITLABELNOW, TRUE, 0L); return 0; // tell trew-view that we done and return 0 + } + break; + case WM_SETCURSOR: + if (HTCLIENT == LOWORD(lParam)) + { + SetCursor(LoadCursor(NULL, IDC_IBEAM)); + return 0; + } + break; + } + return (pEdit->fUnicode) ? CallWindowProcW(pEdit->fnOldProc, hwnd, uMsg, wParam, lParam) : + CallWindowProcA(pEdit->fnOldProc, hwnd, uMsg, wParam, lParam); +} + +static LRESULT TreeViewWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + SCTREE *pTree; + pTree = (SCTREE*)GetPropW(hwnd, WNDPROP_SCCTRLW); + if (!pTree) + return (IsWindowUnicode(hwnd)) ? DefWindowProcW(hwnd, uMsg, wParam, lParam) : DefWindowProcA(hwnd, uMsg, wParam, lParam); + + switch(uMsg) + { + case WM_NCDESTROY: // detach + RemovePropW(hwnd, WNDPROP_SCCTRLW); + if (pTree->fUnicode) + { + CallWindowProcW(pTree->fnOldProc, hwnd, uMsg, wParam, lParam); + SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (LONGX86)(LONG_PTR)pTree->fnOldProc); + } + else + { + CallWindowProcA(pTree->fnOldProc, hwnd, uMsg, wParam, lParam); + SetWindowLongPtrA(hwnd, GWLP_WNDPROC, (LONGX86)(LONG_PTR)pTree->fnOldProc); + } + free(pTree); + return 0; + case WM_ERASEBKGND: + if (wParam && (TVS_FULLROWSELECT & GetWindowLongPtrW(hwnd, GWL_STYLE))) + { + RECT rc; + HTREEITEM hItem; + + GetClientRect(hwnd, &rc); + + hItem = (HTREEITEM)SendMessageW(hwnd, TVM_GETNEXTITEM, TVGN_LASTVISIBLE, 0L); + + if (hItem) + { + RECT ri; + *(HTREEITEM*)&ri = hItem; + if (SendMessageW(hwnd, TVM_GETITEMRECT, FALSE, (LPARAM)&ri)) rc.top = ri.bottom; + } + if (rc.top < rc.bottom) + { + SetBkColor((HDC)wParam, WADlg_getColor(WADLG_ITEMBG)); + ExtTextOutW((HDC)wParam, 0, 0, ETO_OPAQUE, &rc, L"", 0, 0); + } + return 1; + } + return 0; + + case WM_LBUTTONDOWN: + case WM_RBUTTONDOWN: + { + TVHITTESTINFO hit; + HNAVITEM hItem; + NAVMNGR *pMngr; + + hit.pt.x = GET_X_LPARAM(lParam); + hit.pt.y = GET_Y_LPARAM(lParam); + SendMessageW(hwnd, TVM_HITTEST, (WPARAM)0, (LPARAM)&hit); + + pMngr = (NAVMNGR*)GetPropW(hwnd, NAVMGR_HWNDPROPW); + + if (hit.hItem) + { + hItem = GetNavItemFromTreeItem(pMngr, hit.hItem); + PerformCustomHitTest(pMngr, hit.pt, &hit.flags, &hItem); + if (hItem && pMngr && (NAVSTYLE_MOUSEDOWNSELECT & pMngr->style)) + { + SendMessageW(hwnd, TVM_SELECTITEM, TVGN_CARET, (LPARAM)((NAVITM*)hItem)->hTreeItem); + } + } + else + hItem = NULL; + + if (!hItem) + { + SendMessageW(hwnd, TVM_ENDEDITLABELNOW, (WPARAM)TRUE, 0L); + return 0; + } + else + { + HWND hEdit = FindWindowExW(hwnd, NULL, L"Edit", NULL); + if (NULL != hEdit && IsWindowVisible(hEdit)) + { + PostMessageW(hwnd, uMsg, wParam, lParam); + } + else + { + pTree->supressEdit = FALSE; + if (0 != pTree->focused) + { + DWORD clicked = GetTickCount(); + if (clicked >= pTree->focused && (clicked - pTree->focused) < 200) + pTree->supressEdit = TRUE; + + pTree->focused = 0; + } + } + } + } + break; + + case WM_LBUTTONDBLCLK: + if (TVS_FULLROWSELECT == (TVS_FULLROWSELECT & (UINT)GetWindowLongPtrW(hwnd, GWL_STYLE))) + { + TVHITTESTINFO test; + test.pt.x = GET_X_LPARAM(lParam); + test.pt.y = GET_Y_LPARAM(lParam); + if(SendMessageW(hwnd, TVM_HITTEST, 0, (LPARAM)&test) && (TVHT_ONITEMRIGHT & test.flags)) + { + HWND hParent; + NMHDR hdr; + hdr.code = NM_DBLCLK; + hdr.hwndFrom = hwnd; + hdr.idFrom = GetDlgCtrlID(hwnd); + hParent = GetParent(hwnd); + if (hParent) SendMessageW(hParent, WM_NOTIFY, hdr.idFrom, (LPARAM)&hdr); + return 0; + } + } + break; + + case WM_CTLCOLOREDIT: + SetTextColor((HDC)wParam, WADlg_getColor(WADLG_ITEMFG)); + SetBkColor((HDC)wParam, WADlg_getColor(WADLG_ITEMBG)); + return (LRESULT)MlStockObjects_Get(ITEMBCK_BRUSH); + + case WM_SIZE: + { + TVITEMW item; + RECT ri; + + item.mask = TVIF_PARAM; + item.pszText = (LPWSTR)&ri; + EnumerateTreeItems(hwnd, NULL, ItemSizedCB, &item); + } + break; + case WM_CHAR: + pTree->supressEdit = FALSE; + return (VK_RETURN == wParam) ? 0 : + ((pTree->fUnicode) ? CallWindowProcW(pTree->fnOldProc, hwnd, uMsg, wParam, lParam) : + CallWindowProcA(pTree->fnOldProc, hwnd, uMsg, wParam, lParam)); + + case WM_GETDLGCODE: + { + LRESULT r = (pTree->fUnicode) ? CallWindowProcW(pTree->fnOldProc, hwnd, uMsg, wParam, lParam) : + CallWindowProcA(pTree->fnOldProc, hwnd, uMsg, wParam, lParam); + if (lParam) + { + MSG *pMsg = (LPMSG)lParam; + if (pMsg->message == WM_KEYDOWN || pMsg->message == WM_KEYUP) + { + switch(pMsg->wParam) + { + case VK_RETURN: r |= DLGC_WANTMESSAGE; break; + case VK_TAB: + case VK_ESCAPE: SendMessageW(hwnd, TVM_ENDEDITLABELNOW, TRUE, 0L); break; + } + } + } + return r; + } + + case WM_SETFOCUS: + pTree->focused = GetTickCount(); + break; + + case WM_TIMER: + if (42 == wParam && FALSE != pTree->supressEdit) + { + KillTimer(hwnd, wParam); + pTree->supressEdit = FALSE; + return 0; + } + break; + } + return (pTree->fUnicode) ? CallWindowProcW(pTree->fnOldProc, hwnd, uMsg, wParam, lParam) : + CallWindowProcA(pTree->fnOldProc, hwnd, uMsg, wParam, lParam); +} + +static BOOL SubclassEditControl(HWND hwndEdit) +{ + if (!hwndEdit || !IsWindow(hwndEdit)) return FALSE; + SCEDIT *pEdit = (SCEDIT*)calloc(1, sizeof(SCEDIT)); + if (!pEdit) return FALSE; + + pEdit->fUnicode = IsWindowUnicode(hwndEdit); + pEdit->fnOldProc = (WNDPROC)(LONG_PTR)((pEdit->fUnicode) ? SetWindowLongPtrW(hwndEdit, GWLP_WNDPROC, (LONGX86)(LONG_PTR)EditCtrlWndProc) : + SetWindowLongPtrA(hwndEdit, GWLP_WNDPROC, (LONGX86)(LONG_PTR)EditCtrlWndProc)); + if (!pEdit->fnOldProc || !SetPropW(hwndEdit, WNDPROP_SCCTRLW, pEdit)) + { + if (pEdit->fnOldProc) + { + if (pEdit->fUnicode) SetWindowLongPtrW(hwndEdit, GWLP_WNDPROC, (LONGX86)(LONG_PTR)pEdit->fnOldProc); + else SetWindowLongPtrA(hwndEdit, GWLP_WNDPROC, (LONGX86)(LONG_PTR)pEdit->fnOldProc); + } + free(pEdit); + return FALSE; + } + + return TRUE; +} + +static BOOL SubclassTreeView(HWND hwndTree) +{ + if (!hwndTree || !IsWindow(hwndTree)) return FALSE; + SCTREE *pTree = (SCTREE*)calloc(1, sizeof(SCTREE)); + if (!pTree) return FALSE; + + pTree->fUnicode = IsWindowUnicode(hwndTree); + pTree->fnOldProc = (WNDPROC)(LONG_PTR)((pTree->fUnicode) ? SetWindowLongPtrW(hwndTree, GWLP_WNDPROC, (LONGX86)(LONG_PTR)TreeViewWndProc) : + SetWindowLongPtrA(hwndTree, GWLP_WNDPROC, (LONGX86)(LONG_PTR)TreeViewWndProc)); + if (!pTree->fnOldProc || !SetPropW(hwndTree, WNDPROP_SCCTRLW, pTree)) + { + if (pTree->fnOldProc) + { + if (pTree->fUnicode) SetWindowLongPtrW(hwndTree, GWLP_WNDPROC, (LONGX86)(LONG_PTR)pTree->fnOldProc); + else SetWindowLongPtrA(hwndTree, GWLP_WNDPROC, (LONGX86)(LONG_PTR)pTree->fnOldProc); + } + free(pTree); + return FALSE; + } + return TRUE; +} + +static BOOL NavCtrlI_SaveStates(HNAVCTRL hNav) +{ + BOOL result; + SAVEVIEWDATA save; + + if (!hNav || !((NAVMNGR*)hNav)->pConfig) return FALSE; + + save.item.mask = TVIF_STATE | TVIF_PARAM; + save.item.stateMask = TVIS_EXPANDED | TVIS_SELECTED; + save.pConfig = ((NAVMNGR*)hNav)->pConfig; + save.counter_root = 0; + save.counter_write = 0; + + result = EnumerateTreeItems(((NAVMNGR*)hNav)->hwndHost, TVI_ROOT, SaveNavigationCB, &save); + if (!result) save.counter_write = 0; + ((NAVMNGR*)hNav)->pConfig->WriteInt(MLV_PREFIX_A L"count", save.counter_write); + + return result; +} + +static BOOL NavCtrlI_DeleteStates(HNAVCTRL hNav) +{ + if (!hNav) return FALSE; + if (((NAVMNGR*)hNav)->pStates) + { + INT i; + for (i =0; i < ((NAVMNGR*)hNav)->statesCount; i++) + { + if (((NAVMNGR*)hNav)->pStates[i].pszFullName) free(((NAVMNGR*)hNav)->pStates[i].pszFullName); + } + free(((NAVMNGR*)hNav)->pStates); + ((NAVMNGR*)hNav)->pStates = NULL; + } + ((NAVMNGR*)hNav)->statesCount = 0; + return TRUE; +} + +static BOOL NavCtrlI_LoadStates(HNAVCTRL hNav) +{ + INT index, count, len; + wchar_t mlview[ML_VIEW_MAX] = {0}; + const wchar_t *data; + if (!hNav || !((NAVMNGR*)hNav)->pConfig) return FALSE; + + if (!NavCtrlI_DeleteStates(hNav)) return FALSE; + + count = ((NAVMNGR*)hNav)->pConfig->ReadInt(MLV_PREFIX_A L"count", 0); + if (count < 1) return TRUE; + + ((NAVMNGR*)hNav)->pStates = (NAVITEMSTATEREC*)calloc(count, sizeof(NAVITEMSTATEREC)); + if (!((NAVMNGR*)hNav)->pStates) return FALSE; + + for (index = 0; index < count; index++) + { + StringCchPrintfW(mlview, ML_VIEW_MAX, MLV_PREFIX_A L"%02d", index + 1); + //AutoWide aw(((NAVMNGR*)hNav)->pConfig->ReadString(mlview, ""), CP_UTF8); + data = ((NAVMNGR*)hNav)->pConfig->ReadString(mlview, L"");//(LPCWSTR)aw; + if (data && *data) + { + len = lstrlenW(data); + if (len > 4 && len < 4096) + { + while (len > 0 && data[len-1] != L',') len--; + if (len) + { + ((NAVMNGR*)hNav)->pStates[index].nOrder = _wtoi(&data[len]); + len--; + while (len > 0 && data[len-1] != L',')len--; + if (len) ((NAVMNGR*)hNav)->pStates[index].fCollapsed = ( 0 != _wtoi(&data[len])); + if (len > 1) + { + ((NAVMNGR*)hNav)->pStates[index].pszFullName = (LPWSTR)calloc(len, sizeof(wchar_t)); + if (!((NAVMNGR*)hNav)->pStates[index].pszFullName) continue; + if (S_OK == StringCchCopyNW(((NAVMNGR*)hNav)->pStates[index].pszFullName, len, data, len -1)) + { + ((NAVMNGR*)hNav)->statesCount++; + } + } + } + } + } + } + + ((NAVMNGR*)hNav)->lastOrderIndex = ((NAVMNGR*)hNav)->statesCount; + return (count == ((NAVMNGR*)hNav)->statesCount); +} + +static BOOL OnNavTree_Click(HNAVCTRL hNav, INT actionId, LRESULT *pResult) +{ + if (hNav && ((NAVMNGR*)hNav)->fnOnItemClick) + { + HTREEITEM hTreeItem, hTreeExpand = 0; + + if (ACTION_ENTER_I == actionId) + { + hTreeExpand = hTreeItem = (HTREEITEM)SendMessageW(((NAVMNGR*)hNav)->hwndHost, TVM_GETNEXTITEM, (WPARAM)TVGN_CARET, (LPARAM)0L); + } + else + { + hTreeItem = TreeItemFromCursor(hNav, &hTreeExpand); + } + + if (hTreeExpand || ((ACTION_DBLCLICKL_I == actionId) && hTreeItem && !hTreeExpand)) + { + if (ACTION_CLICKL_I == actionId || (ACTION_DBLCLICKL_I == actionId && hTreeItem && !hTreeExpand)) + { + if (ACTION_DBLCLICKL_I == actionId) hTreeExpand = hTreeItem; + hTreeItem = (HTREEITEM)SendMessageW(((NAVMNGR*)hNav)->hwndHost, TVM_GETNEXTITEM, (WPARAM)TVGN_CARET, (LPARAM)0L); + while (hTreeItem) + { + hTreeItem = (HTREEITEM)SendMessageW(((NAVMNGR*)hNav)->hwndHost, TVM_GETNEXTITEM, (WPARAM)TVGN_PARENT, (LPARAM)hTreeItem); + if (hTreeItem == hTreeExpand) break; + } + + if (hTreeItem == hTreeExpand && + (TVIS_EXPANDED == (TVIS_EXPANDED & SendMessageW(((NAVMNGR*)hNav)->hwndHost, TVM_GETITEMSTATE, (WPARAM)hTreeExpand, (LPARAM)TVIS_EXPANDED)))) + { + SendMessageW(((NAVMNGR*)hNav)->hwndHost, TVM_SELECTITEM, (WPARAM)TVGN_CARET, (LPARAM)hTreeExpand); + } + + SendMessageW(((NAVMNGR*)hNav)->hwndHost, TVM_EXPAND, (WPARAM)TVE_TOGGLE, (LPARAM)hTreeExpand); + InvalidateRect(((NAVMNGR*)hNav)->hwndHost, NULL, TRUE); + + if (ACTION_DBLCLICKL_I == actionId) + { + HNAVITEM hItem; + hItem = GetNavItemFromTreeItem(hNav, hTreeExpand); + if (hItem) ((NAVMNGR*)hNav)->fnOnItemClick(hNav, hItem, actionId); + } + + *pResult = TRUE; + return (NULL == hTreeItem); + } + else if (ACTION_DBLCLICKL_I != actionId) hTreeItem = hTreeExpand; + } + + if (hTreeItem) + { + HNAVITEM hItem; + hItem = GetNavItemFromTreeItem(hNav, hTreeItem); + if (hItem) + { + *pResult = ((NAVMNGR*)hNav)->fnOnItemClick(hNav, hItem, actionId); + return (BOOL)*pResult; + } + return FALSE; + } + } + return FALSE; +} + +static BOOL OnTV_DeleteItem(HNAVCTRL hNav, TVITEMW *pItem) +{ + if (pItem->lParam) + { + if (((NAVMNGR*)hNav)->fnOnItemDelete) ((NAVMNGR*)hNav)->fnOnItemDelete(hNav, (HNAVITEM)pItem->lParam); + NavItemI_SetText((HNAVITEM)pItem->lParam, NULL); + NavItemI_SetInvariantText((HNAVITEM)pItem->lParam, NULL); + free((NAVITM*)pItem->lParam); + } + return FALSE; +} + +static BOOL OnTV_CustomDraw(HNAVCTRL hNav, NMTVCUSTOMDRAW *cd, LRESULT *pResult) +{ + NAVMNGR *manager; + + manager = (NAVMNGR*)hNav; + + *pResult = CDRF_DODEFAULT; + + switch (cd->nmcd.dwDrawStage) + { + case CDDS_PREPAINT: + manager->drawStruct.hdc = cd->nmcd.hdc; + manager->drawInternal = 0; + if ((TVS_FULLROWSELECT & GetWindowLongPtrW(manager->hwndHost, GWL_STYLE))) manager->drawInternal |= 0x0001; + if (0x8000 & GetAsyncKeyState( GetSystemMetrics(SM_SWAPBUTTON) ? VK_RBUTTON : VK_LBUTTON)) manager->drawInternal |= 0x0010; + *pResult = CDRF_NOTIFYITEMDRAW; + break; + + case CDDS_ITEMPREPAINT: + manager->drawStruct.drawStage = NIDS_PREPAINT_I; + manager->drawStruct.prc = &cd->nmcd.rc; + manager->drawStruct.iLevel = cd->iLevel; + + if (CDIS_SELECTED & cd->nmcd.uItemState) + { + manager->drawStruct.itemState = NIS_SELECTED_I; + + if (0 != (CDIS_FOCUS & cd->nmcd.uItemState) || + 0 != (NAVSTYLE_FOCUSED & manager->style)) + { + manager->drawStruct.itemState |= NIS_FOCUSED_I; + } + } + else + { + HTREEITEM hTreeItem; + + manager->drawStruct.itemState = NIS_NORMAL_I; + + if (0x0010 & manager->drawInternal) TreeItemFromCursor(hNav, &hTreeItem); + else hTreeItem = NULL; + + if (!hTreeItem) + { + UINT state; + state = (UINT)SendMessageW(cd->nmcd.hdr.hwndFrom, TVM_GETITEMSTATE, + (WPARAM)cd->nmcd.dwItemSpec, TVIS_DROPHILITED); + + if (TVIS_DROPHILITED & state) + manager->drawStruct.itemState = NIS_DROPHILITED_I; + } + } + + GetItemColors(hNav, manager->drawStruct.itemState, &cd->clrTextBk, &cd->clrText); + + if (0 == (0x0001 & manager->drawInternal)) + { + COLORREF rgbBk, rgbFg; + GetItemColors(hNav, NIS_NORMAL_I, &rgbBk, &rgbFg); + SetBkColor(cd->nmcd.hdc, rgbBk); + SetTextColor(cd->nmcd.hdc, rgbFg); + } + else + { + SetBkColor(cd->nmcd.hdc, cd->clrTextBk); + SetTextColor(cd->nmcd.hdc, cd->clrText); + } + + if (cd->nmcd.lItemlParam) + { + NAVITM *pItem; + + pItem = ((NAVITM*)cd->nmcd.lItemlParam); + + if (pItem->hFont) manager->drawStruct.hFont = pItem->hFont; + else if ((NIS_BOLD_I | NIS_ITALIC_I | NIS_UNDERLINE_I) & pItem->style) + { + LOGFONTW lf = { 0 }; + + manager->drawStruct.hFont = (HFONT)::SendMessageW(cd->nmcd.hdr.hwndFrom, WM_GETFONT, 0, 0); + GetObjectW(manager->drawStruct.hFont, sizeof(LOGFONTW), &lf); + if (NIS_BOLD_I & pItem->style) lf.lfWeight = FW_BOLD; + if (NIS_ITALIC_I & pItem->style) lf.lfItalic = TRUE; + if (NIS_UNDERLINE_I & pItem->style) lf.lfUnderline = TRUE; + manager->drawStruct.hFont = CreateFontIndirectW(&lf); + } + else manager->drawStruct.hFont = NULL; + + if (manager->drawStruct.hFont) + { + manager->hfontOld = (HFONT)SelectObject(cd->nmcd.hdc, manager->drawStruct.hFont); + *pResult |= CDRF_NEWFONT; + if (pItem->hFont != manager->drawStruct.hFont) *pResult |= CDRF_NOTIFYPOSTPAINT; + } + + if((NIS_CUSTOMDRAW_I & pItem->style) && manager->fnOnCustomDraw) + { + INT result; + manager->drawStruct.clrTextBk = cd->clrTextBk; + manager->drawStruct.clrText = cd->clrText; + result = manager->fnOnCustomDraw(hNav, pItem, &manager->drawStruct, pItem->lParam); + if (NICDRF_SKIPDEFAULT_I & result) + { + SelectObject(cd->nmcd.hdc, manager->hfontOld); + DeleteObject(manager->drawStruct.hFont); + manager->drawStruct.hFont = NULL; + *pResult |= CDRF_SKIPDEFAULT; + } + if (NICDRF_NEWFONT_I & result) *pResult |= CDRF_NEWFONT; + if (NICDRF_NOTIFYPOSTPAINT_I & result) + { + pItem->style |= NIS_WANTPOSTPAINT_I; + *pResult |= CDRF_NOTIFYPOSTPAINT; + } + else pItem->style &= ~NIS_WANTPOSTPAINT_I; + } + } + break; + + case CDDS_ITEMPOSTPAINT: + + if(NIS_WANTPOSTPAINT_I & ((NAVITM*)cd->nmcd.lItemlParam)->style) + { + INT result; + manager->drawStruct.drawStage = NIDS_POSTPAINT_I; + result = manager->fnOnCustomDraw(hNav, (HNAVITEM)cd->nmcd.lItemlParam, &manager->drawStruct, ((NAVITM*)cd->nmcd.lItemlParam)->lParam); + if (NICDRF_SKIPDEFAULT_I & result) *pResult |= CDRF_SKIPDEFAULT; + } + + if (manager->drawStruct.hFont && manager->drawStruct.hFont != ((NAVITM*)cd->nmcd.lItemlParam)->hFont) + { + SelectObject(cd->nmcd.hdc, manager->hfontOld); + DeleteObject(manager->drawStruct.hFont); + manager->drawStruct.hFont = NULL; + } + break; + } + return TRUE; +} + +static BOOL OnTV_GetDispInfo(HNAVCTRL hNav, NMTVDISPINFOW *di, LRESULT *pResult) +{ + BOOL handled; + NAVMNGR *manager; + + manager = (NAVMNGR*)hNav; + + handled = FALSE; + if((TVIF_IMAGE | TVIF_SELECTEDIMAGE) & di->item.mask) + { + if (di->item.lParam) + { + INT itemState; + COLORREF rgbBk, rgbFg; + + itemState = NIS_NORMAL_I; + if (TVS_FULLROWSELECT == (TVS_FULLROWSELECT & (UINT)GetWindowLongPtrW(manager->hwndHost, GWL_STYLE))) + { + itemState = di->item.state; + if (TVIS_DROPHILITED & di->item.state) + { + if (0x8000 & GetAsyncKeyState( GetSystemMetrics(SM_SWAPBUTTON) ? VK_RBUTTON : VK_LBUTTON)) + { + HTREEITEM hTreeItem; + TreeItemFromCursor(hNav, &hTreeItem); + itemState = (hTreeItem) ? NIS_NORMAL_I : NIS_DROPHILITED_I; + } + else + itemState = NIS_DROPHILITED_I; + } + else if (TVIS_SELECTED & di->item.state) + { + itemState = NIS_SELECTED_I; + if (0 != ((NAVSTYLE_FOCUSED | NAVSTYLE_EDITMODE) & manager->style)) + itemState |= NIS_FOCUSED_I; + } + } + + GetItemColors(hNav, itemState, &rgbBk, &rgbFg); + if (TVIF_IMAGE & di->item.mask) + { + di->item.iImage = GetRealImageIndex(hNav, (NAVITM*)di->item.lParam, IMAGE_NORMAL_I, rgbBk, rgbFg); + } + + if (TVIF_SELECTEDIMAGE & di->item.mask) + { + di->item.iSelectedImage = ((TVIF_IMAGE & di->item.mask) && + ((NAVITM*)(di->item.lParam))->iImage == ((NAVITM*)(di->item.lParam))->iSelectedImage) ? + di->item.iImage : + GetRealImageIndex(hNav, (NAVITM*)di->item.lParam, IMAGE_SELECTED_I, rgbBk, rgbFg); + } + handled = TRUE; + + } + } + if (TVIF_CHILDREN & di->item.mask) + { + di->item.cChildren = (di->item.lParam) ? (NIS_HASCHILDREN_I & ((NAVITM*)di->item.lParam)->style) : 0; + handled = TRUE; + } + if (TVIF_TEXT & di->item.mask) + { + di->item.pszText = (di->item.lParam) ? + ((NAVITM*)di->item.lParam)->pszText : WASABI_API_LNGSTRINGW(IDS_BAD_ITEM); + handled = TRUE; + } + return handled; +} + +static BOOL OnTV_Click(HNAVCTRL hNav, NMHDR *pnmh, LRESULT *pResult) +{ + return OnNavTree_Click(hNav, ACTION_CLICKL_I, pResult); +} + +static BOOL OnTV_BeginDrag(HNAVCTRL hNav, NMTREEVIEWW *pnmtv) +{ + if (hNav && ((NAVMNGR*)hNav)->fnOnBeginDrag) + { + ((NAVMNGR*)hNav)->fnOnBeginDrag(hNav, (HNAVITEM)pnmtv->itemNew.lParam, pnmtv->ptDrag); + } + return FALSE; +} + +static BOOL OnTV_SelectionChanged(HNAVCTRL hNav, NMTREEVIEWW *pnmtv) +{ + if (hNav && ((NAVMNGR*)hNav)->fnOnItemSelected) + { + ((NAVMNGR*)hNav)->fnOnItemSelected(hNav, (HNAVITEM)pnmtv->itemOld.lParam, (HNAVITEM)pnmtv->itemNew.lParam); + MLSkinnedScrollWnd_UpdateBars(((NAVMNGR*)hNav)->hwndHost, TRUE); + } + return FALSE; +} + +static BOOL OnTV_RightClick(HNAVCTRL hNav, NMHDR *pnmh, LRESULT *pResult) +{ + return OnNavTree_Click(hNav, ACTION_CLICKR_I, pResult); +} + +static BOOL OnTV_DoubleClick(HNAVCTRL hNav, NMHDR *pnmh, LRESULT *pResult) +{ + return OnNavTree_Click(hNav, ACTION_DBLCLICKL_I, pResult); +} + +static BOOL OnTV_KeyDown(HNAVCTRL hNav, NMTVKEYDOWN *ptvkd, LRESULT *pResult) +{ + if (VK_RETURN == ptvkd->wVKey) return OnNavTree_Click(hNav, ACTION_ENTER_I, pResult); + + if (hNav && ((NAVMNGR*)hNav)->fnOnKeyDown) + { + HNAVITEM hItem; + hItem = NavCtrlI_GetSelection(hNav); + + if (hItem) + { + *pResult = ((NAVMNGR*)hNav)->fnOnKeyDown(hNav, hItem, ptvkd); + return (BOOL)*pResult; + } + } + return FALSE; +} + +static BOOL OnTV_BeginLabelEdit(HNAVCTRL hNav, NMTVDISPINFOW *di, LRESULT *pResult) +{ + NAVMNGR *manager; + NAVITM *item; + manager = (NAVMNGR*)hNav; + + if (NULL == di) + return FALSE; + + item = (NAVITM*)di->item.lParam; + *pResult = TRUE; + + if (NULL != item && + 0 != (NIS_ALLOWEDIT_I & item->style)) + { + if (NULL == manager->fnOnBeginTitleEdit || + FALSE != manager->fnOnBeginTitleEdit(hNav, (HNAVITEM)item)) + { + HWND treeWindow, editWindow; + + treeWindow = manager->hwndHost; + + editWindow = (HWND)SendMessageW(treeWindow, TVM_GETEDITCONTROL, 0, 0); + if (NULL != editWindow) + SubclassEditControl(editWindow); + + manager->style |= NAVSTYLE_EDITMODE; + *pResult = FALSE; + } + } + + return TRUE; +} + +static BOOL OnTV_EndLabelEdit(HNAVCTRL hNav, NMTVDISPINFOW *di, LRESULT *pResult) +{ + NAVMNGR *manager; + manager = (NAVMNGR*)hNav; + + manager->style &= ~NAVSTYLE_EDITMODE; + + if (NULL != manager->fnOnEndTitleEdit && + FALSE == manager->fnOnEndTitleEdit(hNav, (HNAVITEM)di->item.lParam, di->item.pszText)) + { + *pResult = FALSE; + } + else + { + *pResult = (NULL != di->item.pszText) ? + NavItemI_SetText((HNAVITEM)di->item.lParam, di->item.pszText) : + FALSE; + } + + return TRUE; +} + +static BOOL OnTV_SetCursor(HNAVCTRL hNav, NMMOUSE *pm, LRESULT *pResult) +{ + if (!pm->dwItemData) + { + TVHITTESTINFO hit; + + if(GetCursorPos(&hit.pt)) + { + HTREEITEM hTreeItem; + MapWindowPoints(HWND_DESKTOP, ((NAVMNGR*)hNav)->hwndHost, &hit.pt, 1); + hTreeItem = (HTREEITEM)SendMessageW(((NAVMNGR*)hNav)->hwndHost, TVM_HITTEST, (WPARAM)0, (LPARAM)&hit); + if (hTreeItem) pm->dwItemData = (DWORD_PTR)GetNavItemFromTreeItem(hNav, hTreeItem); + } + } + + if (pm->dwItemData) + { + if ((NIS_WANTSETCURSOR_I & ((NAVITM*)pm->dwItemData)->style) && ((NAVMNGR*)hNav)->fnOnSetCursor) + { + *pResult = ((NAVMNGR*)hNav)->fnOnSetCursor(hNav, (HNAVITEM)pm->dwItemData, ((NAVITM*)pm->dwItemData)->lParam); + } + } + + return TRUE; +} + +static BOOL OnTV_SetFocus(HNAVCTRL hNav, NMHDR *pnmh) +{ + NAVMNGR *manager = (NAVMNGR*)hNav; + + if (NULL == manager) + return FALSE; + + if (0 == (NAVSTYLE_FOCUSED & manager->style)) + { + manager->style |= NAVSTYLE_FOCUSED; + + HNAVITEM selectedItem = NavCtrlI_GetSelection(hNav); + if (NULL != selectedItem) + NavItemI_Invalidate(selectedItem, NULL, FALSE); + } + + return TRUE; +} + +static BOOL OnTV_KillFocus(HNAVCTRL hNav, NMHDR *pnmh) +{ + NAVMNGR *manager = (NAVMNGR*)hNav; + + if (NULL == manager) + return FALSE; + + if (0 != (NAVSTYLE_FOCUSED & manager->style)) + { + manager->style &= ~NAVSTYLE_FOCUSED; + + HNAVITEM selectedItem = NavCtrlI_GetSelection(hNav); + if (NULL != selectedItem) + NavItemI_Invalidate(selectedItem, NULL, FALSE); + } + + return TRUE; +} + +// manager +HNAVCTRL NavCtrlI_Create(HWND hwndParent) +{ + PNAVMNGR pMngr = (PNAVMNGR)calloc(1, sizeof(NAVMNGR)); + if (!pMngr) return NULL; + + pMngr->hwndHost = CreateWindowExW(WS_EX_NOPARENTNOTIFY | WS_EX_CLIENTEDGE, + WC_TREEVIEWW, L"", + WS_CHILD | WS_TABSTOP | TVS_SHOWSELALWAYS | TVS_FULLROWSELECT | TVS_NOHSCROLL | TVS_EDITLABELS, + 0, 0, 1, 1, hwndParent, NULL, NULL, NULL); + + if (!IsWindow(pMngr->hwndHost) || !SetPropW(pMngr->hwndHost, NAVMGR_HWNDPROPW, pMngr)) + { + NavCtrlI_Destroy(pMngr); + return NULL; + } + + pMngr->lastUsedId = NAVITEM_RANGE_MIN; + pMngr->style = NAVSTYLE_DEFAULT; + + SubclassTreeView(pMngr->hwndHost); // need to be before Skinning + + if (SkinWindowEx(pMngr->hwndHost, SKINNEDWND_TYPE_SCROLLWND, SWS_USESKINFONT | SWS_USESKINCOLORS)) + { + MLSkinnedScrollWnd_SetMode(pMngr->hwndHost, SCROLLMODE_TREEVIEW); + HWND hTooltip = (HWND)SendMessageW(pMngr->hwndHost, TVM_GETTOOLTIPS, 0, 0L); + if (NULL != hTooltip) + { + SkinWindowEx(hTooltip, SKINNEDWND_TYPE_TOOLTIP, SWS_USESKINFONT | SWS_USESKINCOLORS); + } + } + SendMessageW(pMngr->hwndHost, (TV_FIRST + 44)/*TVM_SETEXTENDEDSTYLE*/, + (WPARAM)pMngr->hwndHost, (LPARAM)0x0004/*TVS_EX_DOUBLEBUFFER*/); + + SendMessageW(pMngr->hwndHost, CCM_SETVERSION, 6, 0); + SendMessageW(pMngr->hwndHost, TVM_SETSCROLLTIME, 200, 0); + + return (HNAVCTRL)pMngr; +} + +BOOL NavCtrlI_SetRect(HNAVCTRL hNav, RECT *prc) +{ + return (hNav && prc) ? SetWindowPos(((NAVMNGR*)hNav)->hwndHost, NULL, + prc->left, prc->top, prc->right - prc->left, prc->bottom - prc->top, + SWP_NOACTIVATE | SWP_NOZORDER) : FALSE; +} + +BOOL NavCtrlI_Show(HNAVCTRL hNav, INT nCmdShow) +{ + return (hNav) ? ShowWindow(((NAVMNGR*)hNav)->hwndHost, nCmdShow) : FALSE; +} + +BOOL NavCtrlI_Enable(HNAVCTRL hNav, BOOL fEnable) +{ + return (hNav) ? EnableWindow(((NAVMNGR*)hNav)->hwndHost, fEnable) : FALSE; +} + +BOOL NavCtrlI_Destroy(HNAVCTRL hNav) +{ + if (!hNav) return FALSE; + + if (NULL != ((NAVMNGR*)hNav)->fnOnDestroy) + { + ((NAVMNGR*)hNav)->fnOnDestroy(hNav); + } + + NavCtrlI_SaveStates(hNav); + + if (((NAVMNGR*)hNav)->hwndHost) + { + RemovePropW(((NAVMNGR*)hNav)->hwndHost, NAVMGR_HWNDPROPW); + DestroyWindow(((NAVMNGR*)hNav)->hwndHost); + } + + NavCtrlI_DeleteStates(hNav); + + free(hNav); + return TRUE; +} + +BOOL NavCtrlI_Update(HNAVCTRL hNav) +{ + return (hNav) ? UpdateWindow(((NAVMNGR*)hNav)->hwndHost) : FALSE; +} + +C_Config *NavCtrlI_SetConfig(HNAVCTRL hNav, C_Config *pConfig) +{ + C_Config *pTemp; + if (!hNav) return NULL; + pTemp = ((NAVMNGR*)hNav)->pConfig; + ((NAVMNGR*)hNav)->pConfig = pConfig; + + if (pConfig) NavCtrlI_LoadStates(hNav); + + return pTemp; +} + +HWND NavCtrlI_GetHWND(HNAVCTRL hNav) +{ + return (hNav) ? ((NAVMNGR*)hNav)->hwndHost : NULL; +} + +BOOL NavCtrlI_ProcessNotifications(HNAVCTRL hNav, LPNMHDR pnmh, LRESULT *pResult) +{ + if (!hNav || pnmh->hwndFrom != ((PNAVMNGR)hNav)->hwndHost) return FALSE; + switch(pnmh->code) + { + case TVN_KEYDOWN: return OnTV_KeyDown(hNav, (NMTVKEYDOWN*)pnmh, pResult); + case TVN_SELCHANGEDW: return OnTV_SelectionChanged(hNav, (NMTREEVIEWW*)pnmh); + case NM_RCLICK: return OnTV_RightClick(hNav, pnmh, pResult); + case NM_DBLCLK: return OnTV_DoubleClick(hNav, pnmh, pResult); + case TVN_BEGINDRAGW: return OnTV_BeginDrag(hNav, (NMTREEVIEWW*)pnmh); + case NM_CLICK: return OnTV_Click(hNav, pnmh, pResult); + case TVN_GETDISPINFOW: return OnTV_GetDispInfo(hNav, (NMTVDISPINFOW*)pnmh, pResult); + case NM_CUSTOMDRAW: return OnTV_CustomDraw(hNav, (NMTVCUSTOMDRAW*)pnmh, pResult); + case TVN_DELETEITEMW: return OnTV_DeleteItem(hNav, &((NMTREEVIEWW*)pnmh)->itemOld); + case TVN_BEGINLABELEDITW: return OnTV_BeginLabelEdit(hNav, (NMTVDISPINFOW*)pnmh, pResult); + case TVN_ENDLABELEDITW: return OnTV_EndLabelEdit(hNav, (NMTVDISPINFOW*)pnmh, pResult); + case NM_SETCURSOR: return OnTV_SetCursor(hNav, (NMMOUSE*)pnmh, pResult); + case NM_SETFOCUS: return OnTV_SetFocus(hNav, pnmh); + case NM_KILLFOCUS: return OnTV_KillFocus(hNav, pnmh); + } + return FALSE; +} + +LPVOID NavCtrlI_RegisterCallback(HNAVCTRL hNav, LPVOID fnCallback, INT cbType) +{ + LPVOID temp; + if (!hNav) return NULL; + switch(cbType) + { + case CALLBACK_ONCLICK_I: + temp = ((NAVMNGR*)hNav)->fnOnItemClick; + ((NAVMNGR*)hNav)->fnOnItemClick = (ONNAVITEMCLICK_I)fnCallback; + return temp; + case CALLBACK_ONSELECTED_I: + temp = ((NAVMNGR*)hNav)->fnOnItemSelected; + ((NAVMNGR*)hNav)->fnOnItemSelected = (ONNAVITEMSELECTED_I)fnCallback; + return temp; + case CALLBACK_ONKEYDOWN_I: + temp = ((NAVMNGR*)hNav)->fnOnKeyDown; + ((NAVMNGR*)hNav)->fnOnKeyDown = (ONNAVCTRLKEYDOWN_I)fnCallback; + return temp; + case CALLBACK_ONBEGINDRAG_I: + temp = ((NAVMNGR*)hNav)->fnOnBeginDrag; + ((NAVMNGR*)hNav)->fnOnBeginDrag = (ONNAVCTRLBEGINDRAG_I)fnCallback; + return temp; + case CALLBACK_ONDESTROY_I: + temp = ((NAVMNGR*)hNav)->fnOnDestroy; + ((NAVMNGR*)hNav)->fnOnDestroy = (ONNAVCTRLDESTROY_I)fnCallback; + return temp; + case CALLBACK_ONGETIMAGEINDEX_I: + temp = ((NAVMNGR*)hNav)->fnOnItemGetImageIndex; + ((NAVMNGR*)hNav)->fnOnItemGetImageIndex = (ONNAVITEMGETIMAGEINDEX_I)fnCallback; + return temp; + case CALLBACK_ONBEGINTITLEEDIT_I: + temp = ((NAVMNGR*)hNav)->fnOnBeginTitleEdit; + ((NAVMNGR*)hNav)->fnOnBeginTitleEdit = (ONNAVITEMBEGINTITLEEDIT_I)fnCallback; + return temp; + case CALLBACK_ONENDTITLEEDIT_I: + temp = ((NAVMNGR*)hNav)->fnOnEndTitleEdit; + ((NAVMNGR*)hNav)->fnOnEndTitleEdit = (ONNAVCTRLENDTITLEEDIT_I)fnCallback; + return temp; + case CALLBACK_ONITEMDELETE_I: + temp = ((NAVMNGR*)hNav)->fnOnItemDelete; + ((NAVMNGR*)hNav)->fnOnItemDelete = (ONNAVITEMDELETE_I)fnCallback; + return temp; + case CALLBACK_ONITEMDRAW_I: + temp = ((NAVMNGR*)hNav)->fnOnCustomDraw; + ((NAVMNGR*)hNav)->fnOnCustomDraw = (ONNAVITEMDRAW_I)fnCallback; + return temp; + case CALLBACK_ONSETCURSOR_I: + temp = ((NAVMNGR*)hNav)->fnOnSetCursor; + ((NAVMNGR*)hNav)->fnOnSetCursor = (ONNAVITEMSETCURSOR_I)fnCallback; + return temp; + case CALLBACK_ONHITTEST_I: + temp = ((NAVMNGR*)hNav)->fnOnHitTest; + ((NAVMNGR*)hNav)->fnOnHitTest = (ONNAVITEMHITTEST_I)fnCallback; + return temp; + } + + return NULL; + +} + +BOOL NavCtrlI_UpdateLook(HNAVCTRL hNav) +{ + INT minHeight; + + if (!hNav || !((NAVMNGR*)hNav)->hwndHost) return FALSE; + + minHeight = -1; + + SendMessageW(((NAVMNGR*)hNav)->hwndHost, TVM_SETBKCOLOR, (WPARAM)0, (LPARAM)WADlg_getColor(WADLG_ITEMBG)); + SendMessageW(((NAVMNGR*)hNav)->hwndHost, TVM_SETTEXTCOLOR, (WPARAM)0, (LPARAM)WADlg_getColor(WADLG_ITEMFG)); + SendMessageW(((NAVMNGR*)hNav)->hwndHost, TVM_SETINSERTMARKCOLOR, (WPARAM)0, (LPARAM)WADlg_getColor(WADLG_ITEMFG)); + + MLSkinnedWnd_SkinChanged(((NAVMNGR*)hNav)->hwndHost, FALSE, FALSE); + + HFONT hFont = (HFONT)SendMessageW(((NAVMNGR*)hNav)->hwndHost, WM_GETFONT, 0, 0L); + + HDC hdc; + hdc = GetDCEx(((NAVMNGR*)hNav)->hwndHost, NULL, DCX_CACHE); + if (hdc) + { + if (NULL == hFont) + hFont = (HFONT)MlStockObjects_Get(DEFAULT_FONT); + TEXTMETRICW tm; + if (hFont) + { + HFONT hfntOld = (HFONT)SelectObject(hdc, hFont); + + if (GetTextMetricsW(hdc, &tm)) minHeight = tm.tmHeight + 2/*Borders*/; + SelectObject(hdc, hfntOld); + } + ReleaseDC(((NAVMNGR*)hNav)->hwndHost, hdc); + } + + if (((NAVMNGR*)hNav)->pConfig) + { + BOOL fFullRowSelect; + DWORD dwWndStyle; + INT height; + + height = ((NAVMNGR*)hNav)->pConfig->ReadInt(L"Navigation_ItemHeight", 18); + if (minHeight > 0 && height < minHeight) height = minHeight; + + SendMessageW(((NAVMNGR*)hNav)->hwndHost, TVM_SETITEMHEIGHT, (WPARAM)height, (LPARAM)0); + ((NAVMNGR*)hNav)->style = (((NAVMNGR*)hNav)->style & ~NAVSTYLE_MOUSEDOWNSELECT) | ((0 != ((NAVMNGR*)hNav)->pConfig->ReadInt(L"Navigation_MouseDownSel", 0)) ? NAVSTYLE_MOUSEDOWNSELECT : 0); + fFullRowSelect = (((NAVMNGR*)hNav)->pConfig->ReadInt(L"Navigation_FullRowSel", 1) != 0); + dwWndStyle = (DWORD)GetWindowLongPtrW(((NAVMNGR*)hNav)->hwndHost, GWL_STYLE); + if (fFullRowSelect != (TVS_FULLROWSELECT == (TVS_FULLROWSELECT & dwWndStyle))) + { + SetWindowLongPtrW(((NAVMNGR*)hNav)->hwndHost, GWL_STYLE, + (dwWndStyle & ~TVS_FULLROWSELECT) | ((fFullRowSelect) ? TVS_FULLROWSELECT : 0)); + } + NavCtrlI_SetImageList(hNav, ((NAVMNGR*)hNav)->hmlilImages); + } + + return TRUE; +} + +HMLIMGLST NavCtrlI_SetImageList(HNAVCTRL hNav, HMLIMGLST hmlil) +{ + HMLIMGLST hmlilOld; + HIMAGELIST hilTree, hilNew; + + if (!hNav) return NULL; + + hmlilOld = ((NAVMNGR*)hNav)->hmlilImages; + ((NAVMNGR*)hNav)->hmlilImages = hmlil; + + hilNew = MLImageListI_GetRealList(hmlil); + hilTree = (HIMAGELIST)SendMessageW(((NAVMNGR*)hNav)->hwndHost, TVM_GETIMAGELIST, (WPARAM)TVSIL_NORMAL, (LPARAM)0); + + if (!((NAVMNGR*)hNav)->pConfig || ((NAVMNGR*)hNav)->pConfig->ReadInt(L"Navigation_ShowIcons", 1)) + { + if (hilTree != hilNew) SendMessageW(((NAVMNGR*)hNav)->hwndHost, TVM_SETIMAGELIST, (WPARAM)TVSIL_NORMAL, (LPARAM)hilNew); + } + else if (hilTree) SendMessageW(((NAVMNGR*)hNav)->hwndHost, TVM_SETIMAGELIST, (WPARAM)TVSIL_NORMAL, (LPARAM)NULL); + return hmlilOld; +} + +HMLIMGLST NavCtrlI_GetImageList(HNAVCTRL hNav) +{ + return (hNav) ? ((NAVMNGR*)hNav)->hmlilImages : NULL; +} + +HNAVITEM NavCtrlI_FindItem(HNAVCTRL hNav, INT itemId) +{ + return (hNav) ? FindNavItemByNavIdEx(((NAVMNGR*)hNav)->hwndHost, itemId, TVI_ROOT) : NULL; +} + +HNAVITEM NavCtrlI_FindItemByName(HNAVCTRL hNav, LCID Locale, UINT compFlags, LPCWSTR pszName, INT cchLength) +{ + TREEITEMSEARCH search; + + if (!hNav || !pszName || !((NAVMNGR*)hNav)->hwndHost) return NULL; + + search.fFound = FALSE; + search.itemData = (INT_PTR)pszName; + search.item.mask = TVIF_PARAM; + search.flags = compFlags; + // pack extra info + search.item.cchTextMax = cchLength; + search.item.state = Locale; + + EnumerateTreeItems(((NAVMNGR*)hNav)->hwndHost, TVI_ROOT, FindTreeItemByNameCB, &search); + return (search.fFound) ? (HNAVITEM)search.item.lParam : NULL; +} + +HNAVITEM NavCtrlI_FindItemByFullName(HNAVCTRL hNav, LCID Locale, UINT compFlags, LPCWSTR pszFullName, INT cchLength, BOOL fAncestorOk) +{ + INT len, separatorCount; + WCHAR shortname[ITEM_SHORTNAME_MAX] = {0}; + LPCWSTR end; + TVITEMW treeItem; + HNAVITEM hNavParent; + + if (!hNav || !pszFullName || !((NAVMNGR*)hNav)->hwndHost) return NULL; + + hNavParent = NULL; + + len = (cchLength < 0)? lstrlenW(pszFullName) : cchLength; + if (!len) return NULL; + end = pszFullName + len; + + ZeroMemory(&treeItem, sizeof(TVITEMW)); + treeItem.mask = TVIF_PARAM; + + len = 0; + separatorCount = 0; + + while (pszFullName != end + 1) + { + if (pszFullName == end) { separatorCount++; } + + if (SEPARATOR == *pszFullName) separatorCount++; + else + { + if (separatorCount) + { + INT i; + for (i = separatorCount/2; i > 0; i--) + { + if (len == ITEM_SHORTNAME_MAX -1) return NULL; // ugh... + shortname[len++] = SEPARATOR; + } + + if (separatorCount%2 && len) + { + if (CSTR_EQUAL != CompareStringW(LOCALE_INVARIANT, NORM_IGNORECASE, EMPTY_ITEM_NAME, -1, shortname, len)) + { + treeItem.hItem = (HTREEITEM)((NULL == hNavParent) ? + SendMessageW(((NAVMNGR*)hNav)->hwndHost, TVM_GETNEXTITEM, (WPARAM)TVGN_ROOT, (LPARAM)0) : + SendMessageW(((NAVMNGR*)hNav)->hwndHost, TVM_GETNEXTITEM, (WPARAM)TVGN_CHILD, (LPARAM)((NAVITM*)hNavParent)->hTreeItem)); + while(treeItem.hItem) + { + if (SendMessageW(((NAVMNGR*)hNav)->hwndHost, TVM_GETITEMW, (WPARAM)0, (LPARAM)&treeItem)) + { + if (CSTR_EQUAL == CompareItemName(Locale, compFlags, shortname, len, (HNAVITEM)treeItem.lParam)) + { // found!!! + if (pszFullName == end) return (HNAVITEM)treeItem.lParam; + break; + } + } + treeItem.hItem = (HTREEITEM)SendMessageW(((NAVMNGR*)hNav)->hwndHost, TVM_GETNEXTITEM, (WPARAM)TVGN_NEXT, (LPARAM)treeItem.hItem); + } + if (treeItem.hItem) + { + hNavParent = (HNAVITEM)treeItem.lParam; + if (!hNavParent) return NULL; // thats bad + } + else return (fAncestorOk) ? hNavParent : NULL; + } + len = 0; + } + separatorCount = 0; + } + if (len == ITEM_SHORTNAME_MAX -1) return NULL; // ugh... + shortname[len++] = *pszFullName; + } + pszFullName++; + } + + return NULL; +} + +HNAVITEM NavCtrlI_InsertItem(HNAVCTRL hNav, HNAVITEM hInsertAfter, HNAVITEM hParent, NAVITEM_I *pnis) +{ + TVINSERTSTRUCTW is = {0}; + NAVITM *pNavItem = 0; + HTREEITEM hItem = 0; + + if (!pnis) return NULL; + + if (NIMF_ITEMID_I & pnis->mask) { if (NavCtrlI_FindItem(hNav, pnis->id)) return NULL; } + else pnis->id = GetNextFreeItemId(hNav); + + if (!pnis->id) return NULL; + + pNavItem = (NAVITM*)calloc(1, sizeof(NAVITM)); + if (!pNavItem) return NULL; + + is.hParent = (hParent) ? ((NAVITM*)hParent)->hTreeItem : TVI_ROOT; + is.hInsertAfter = TVI_LAST; + + if (NCI_LAST_I == hInsertAfter) is.hInsertAfter = TVI_LAST; + else if (NCI_FIRST_I == hInsertAfter) is.hInsertAfter = TVI_FIRST; + else if (hInsertAfter && !IS_NAVITEMSORTORDER(hInsertAfter)) is.hInsertAfter = ((NAVITM*)hInsertAfter)->hTreeItem; + + if (is.hInsertAfter == is.hParent) is.hInsertAfter = TVI_FIRST; + + pNavItem->id = pnis->id; + pNavItem->hwndTree = ((NAVMNGR*)hNav)->hwndHost; + + is.item.mask = TVIF_PARAM | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_CHILDREN | TVIF_TEXT; + is.item.lParam = (LPARAM)pNavItem; + is.item.pszText = LPSTR_TEXTCALLBACKW; + is.item.iImage = I_IMAGECALLBACK; + is.item.iSelectedImage = I_IMAGECALLBACK; + is.item.cChildren = I_CHILDRENCALLBACK; + + if (NIMF_STYLE_I & pnis->mask) NavItemI_SetStyle(pNavItem, pnis->style, pnis->styleMask); + if (NIMF_TEXT_I & pnis->mask) NavItemI_SetText(pNavItem, pnis->pszText); + if (NIMF_TEXTINVARIANT_I & pnis->mask) NavItemI_SetInvariantText(pNavItem, pnis->pszInvariant); + if (NIMF_FONT_I & pnis->mask) NavItemI_SetFont(pNavItem, pnis->hFont); + if (NIMF_PARAM_I & pnis->mask) pNavItem->lParam = pnis->lParam; + + NavItemI_SetImageIndex(pNavItem, (NIMF_IMAGE_I & pnis->mask) ? pnis->iImage : -1, IMAGE_NORMAL_I); + NavItemI_SetImageIndex(pNavItem, (NIMF_IMAGESEL_I & pnis->mask) ? pnis->iSelectedImage : -1, IMAGE_SELECTED_I); + + NavCtrlI_BeginUpdate(hNav, NUF_LOCK_SELECTED_I); + + hItem = (HTREEITEM)SendMessageW(((NAVMNGR*)hNav)->hwndHost, TVM_INSERTITEMW, (WPARAM)0, (LPARAM)&is); + + if (hItem) + { + UINT state, stateMask; + NAVITEMSTATEREC *pRec; + BOOL itemRecSearched; + WORD order; + UINT flags; + + pNavItem->hTreeItem = hItem; + itemRecSearched = FALSE; + order = (WORD)-1; + flags = NOF_MOVEAFTER_I; + + if (hInsertAfter) + { + if (IS_NAVITEMSORTORDER(hInsertAfter)) { order = (WORD)hInsertAfter; flags = NOF_MOVEONEAFTER_I; } + else + { + HTREEITEM hTreePrev; + hTreePrev = (HTREEITEM)SendMessageW(((NAVMNGR*)hNav)->hwndHost, TVM_GETNEXTITEM, (WPARAM)TVGN_PREVIOUS, (LPARAM)hItem); + if (!hTreePrev) { order = 1; flags = NOF_MOVEONEBEFORE_I; } + else + { + HNAVITEM hPrev; + hPrev = GetNavItemFromTreeItem(hNav, hTreePrev); + order = (hPrev) ? ((NAVITM*)hPrev)->sortOrder : (WORD)-1; + flags = NOF_MOVEONEAFTER_I; + } + } + } + else + { + if (TVI_ROOT == is.hParent || !is.hParent) + { + pRec = NavCtrlI_FindItemStateRec(hNav, pNavItem, TRUE); + order = (pRec) ? pRec->nOrder : ++((NAVMNGR*)hNav)->lastOrderIndex; + flags = NOF_MOVEAFTER_I; + itemRecSearched = TRUE; + } + } + NavItemI_SetOrder(pNavItem, order, flags); + + if (NIMF_STATE_I & pnis->mask) + { + state = pnis->state; + stateMask = pnis->stateMask; + } + else + { + state = 0; + stateMask = 0; + } + + if (0 == (NIS_EXPANDED_I & stateMask)) + { + if (!itemRecSearched) pRec = NavCtrlI_FindItemStateRec(hNav, pNavItem, TRUE); + state |= (pRec && pRec->fCollapsed) ? 0 : NIS_EXPANDED_I; + stateMask |= NIS_EXPANDED_I; + } + NavItemI_SetState(pNavItem, state, stateMask); + } + else + { + NavItemI_SetText(pNavItem, NULL); + NavItemI_SetInvariantText(pNavItem, NULL); + free(pNavItem); + pNavItem = NULL; + } + NavCtrlI_EndUpdate(hNav); + + return (HNAVITEM)pNavItem; +} + +BOOL NavCtrlI_DeleteItem(HNAVCTRL hNav, HNAVITEM hItem) +{ + return (hNav && hItem) ? (BOOL)SendMessageW(((NAVMNGR*)hNav)->hwndHost, TVM_DELETEITEM, 0, (LPARAM)((NAVITM*)hItem)->hTreeItem) : FALSE; +} + +BOOL NavCtrlI_DeleteAll(HNAVCTRL hNav) +{ + return (hNav) ? (BOOL)SendMessageW(((NAVMNGR*)hNav)->hwndHost, TVM_DELETEITEM, 0, (LPARAM)TVI_ROOT) : FALSE; +} + +HNAVITEM NavCtrlI_GetRoot(HNAVCTRL hNav) +{ + HTREEITEM hTreeChild; + if (!hNav) return NULL; + hTreeChild = (HTREEITEM)SendMessageW(((NAVMNGR*)hNav)->hwndHost, TVM_GETNEXTITEM, (WPARAM)TVGN_ROOT, (LPARAM)0L); + return (hTreeChild) ? GetNavItemFromTreeItem(hNav, hTreeChild) : NULL; +} + +HNAVITEM NavCtrlI_GetSelection(HNAVCTRL hNav) +{ + HTREEITEM hTreeChild; + if (!hNav) return NULL; + hTreeChild = (HTREEITEM)SendMessageW(((NAVMNGR*)hNav)->hwndHost, TVM_GETNEXTITEM, (WPARAM)TVGN_CARET, (LPARAM)0L); + return (hTreeChild) ? GetNavItemFromTreeItem(hNav, hTreeChild) : NULL; +} + +HNAVITEM NavCtrlI_GetFirstVisible(HNAVCTRL hNav) +{ + HTREEITEM hTreeChild; + if (!hNav) return NULL; + hTreeChild = (HTREEITEM)SendMessageW(((NAVMNGR*)hNav)->hwndHost, TVM_GETNEXTITEM, (WPARAM)TVGN_FIRSTVISIBLE, (LPARAM)0L); + return (hTreeChild) ? GetNavItemFromTreeItem(hNav, hTreeChild) : NULL; +} + +HNAVITEM NavCtrlI_GetLastVisible(HNAVCTRL hNav) +{ + HTREEITEM hTreeChild; + if (!hNav) return NULL; + hTreeChild = (HTREEITEM)SendMessageW(((NAVMNGR*)hNav)->hwndHost, TVM_GETNEXTITEM, (WPARAM)TVGN_LASTVISIBLE, (LPARAM)0L); + return (hTreeChild) ? GetNavItemFromTreeItem(hNav, hTreeChild) : NULL; +} + +HNAVITEM NavCtrlI_HitTest(HNAVCTRL hNav, POINT *ppt, UINT *pFlags) +{ + TVHITTESTINFO tvhi; + HNAVITEM hItem; + + if (!hNav || !((NAVMNGR*)hNav)->hwndHost || !ppt) + { + if (pFlags) *pFlags = NAVHT_NOWHERE_I; + return NULL; + } + tvhi.pt = *ppt; + SendMessageW(((NAVMNGR*)hNav)->hwndHost, TVM_HITTEST, (WPARAM)0, (LPARAM)&tvhi); + + hItem = (tvhi.hItem) ? GetNavItemFromTreeItem(hNav, tvhi.hItem) : NULL; + + PerformCustomHitTest(hNav, tvhi.pt, &tvhi.flags, (hItem) ? &hItem : NULL); + if (pFlags) *pFlags = tvhi.flags; + + return hItem; +} + +BOOL NavCtrlI_SetInsertMark(HNAVCTRL hNav, HNAVITEM hItem, BOOL fAfter) +{ + return (hNav) ? (BOOL)SendMessageW(((NAVMNGR*)hNav)->hwndHost, TVM_SETINSERTMARK, + (WPARAM)fAfter, + (hItem) ? (LPARAM)((NAVITM*)hItem)->hTreeItem : NULL) : FALSE; +} + +BOOL NavCtrlI_EnumItems(HNAVCTRL hNav, NAVENUMPROC_I pEnumFunc, HNAVITEM hItemStart, LPARAM lParam) +{ + NAVENUMSTRUCT navenum; + if (!hNav || !pEnumFunc) return FALSE; + + navenum.hNav = hNav; + navenum.callback = pEnumFunc; + navenum.lParam = lParam; + navenum.item.mask = TVIF_PARAM; + + return EnumerateTreeItems(((NAVMNGR*)hNav)->hwndHost, (hItemStart) ? ((NAVITM*)hItemStart)->hTreeItem : TVI_ROOT, EnumNavItemCB, &navenum); +} + +INT NavCtrlI_BeginUpdate(HNAVCTRL hNav, UINT fRememberPos) +{ + if (!hNav || !((NAVMNGR*)hNav)->hwndHost || !IsWindow(((NAVMNGR*)hNav)->hwndHost)) return -1; + if (!((NAVMNGR*)hNav)->lockUpdate) + { + ((NAVMNGR*)hNav)->lockFirst = NULL; + ((NAVMNGR*)hNav)->lockSelected = NULL; + if (fRememberPos) + { + ((NAVMNGR*)hNav)->lockFirst = (HTREEITEM)SendMessageW(((NAVMNGR*)hNav)->hwndHost, TVM_GETNEXTITEM, TVGN_FIRSTVISIBLE, (LPARAM)TVI_ROOT); + if (NUF_LOCK_SELECTED_I & fRememberPos) + { + ((NAVMNGR*)hNav)->lockSelected = (HTREEITEM)SendMessageW(((NAVMNGR*)hNav)->hwndHost, TVM_GETNEXTITEM, TVGN_CARET, (LPARAM)TVI_ROOT); + if (((NAVMNGR*)hNav)->lockSelected) + { + RECT rc; + *(HTREEITEM*)&rc = ((NAVMNGR*)hNav)->lockSelected; + if (!SendMessageW(((NAVMNGR*)hNav)->hwndHost, TVM_GETITEMRECT, FALSE, (LPARAM)&rc)) ((NAVMNGR*)hNav)->lockSelected = NULL; + else ((NAVMNGR*)hNav)->lockSelPos = rc.top; + } + } + } + UpdateWindow(((NAVMNGR*)hNav)->hwndHost); + SendMessageW(((NAVMNGR*)hNav)->hwndHost, WM_SETREDRAW, (WPARAM)FALSE, 0L); + } + return ++((NAVMNGR*)hNav)->lockUpdate; +} + +INT NavCtrlI_EndUpdate(HNAVCTRL hNav) +{ + if (!hNav || !((NAVMNGR*)hNav)->hwndHost || !IsWindow(((NAVMNGR*)hNav)->hwndHost)) return -1; + if (((NAVMNGR*)hNav)->lockUpdate) + { + ((NAVMNGR*)hNav)->lockUpdate--; + if (!((NAVMNGR*)hNav)->lockUpdate) + { + SCROLLINFO si; + si.cbSize = sizeof(SCROLLINFO); + si.fMask = SIF_POS | SIF_RANGE; + + if (((NAVMNGR*)hNav)->lockSelected) + { + RECT rc; + *(HTREEITEM*)&rc = ((NAVMNGR*)hNav)->lockSelected; + if (!SendMessageW(((NAVMNGR*)hNav)->hwndHost, TVM_GETITEMRECT, FALSE, (LPARAM)&rc)) + { + SendMessageW(((NAVMNGR*)hNav)->hwndHost, TVM_SELECTITEM, (WPARAM)TVGN_FIRSTVISIBLE, (LPARAM)((NAVMNGR*)hNav)->lockSelected); + } + else if (((NAVMNGR*)hNav)->lockSelPos != rc.top) + { + INT iHeight = (INT)SendMessageW(((NAVMNGR*)hNav)->hwndHost, TVM_GETITEMHEIGHT , 0, 0L); + if (iHeight) + { + INT pos, oldPos; + if (((NAVMNGR*)hNav)->lockSelPos > rc.top) iHeight = 0 - iHeight; + WPARAM wCmd = MAKEWPARAM((((NAVMNGR*)hNav)->lockSelPos > rc.top) ? SB_LINEUP : SB_LINEDOWN, 0); + oldPos = 0xFFFFFF; + for(pos = ((NAVMNGR*)hNav)->lockSelPos; pos != rc.top; pos += iHeight) + { + if (ABS((oldPos - rc.top)) <= ABS((pos - rc.top))) break; + oldPos = pos; + SendMessageW(((NAVMNGR*)hNav)->hwndHost, WM_VSCROLL, wCmd, 0L); + SendMessageW(((NAVMNGR*)hNav)->hwndHost, WM_VSCROLL, MAKEWPARAM(SB_ENDSCROLL, 0), 0L); + } + } + } + } + else if (((NAVMNGR*)hNav)->lockFirst) + { + SendMessageW(((NAVMNGR*)hNav)->hwndHost, TVM_ENSUREVISIBLE, (WPARAM)0, (LPARAM)((NAVMNGR*)hNav)->lockFirst); + } + + ((NAVMNGR*)hNav)->lockFirst = NULL; + ((NAVMNGR*)hNav)->lockSelected = NULL; + + if(GetScrollInfo(((NAVMNGR*)hNav)->hwndHost, SB_HORZ, &si) && (si.nMin != si.nPos)) + { + SendMessageW(((NAVMNGR*)hNav)->hwndHost, WM_HSCROLL, MAKEWPARAM(SB_LEFT, 0), NULL); + SendMessageW(((NAVMNGR*)hNav)->hwndHost, WM_HSCROLL, MAKEWPARAM(SB_ENDSCROLL, 0), NULL); + } + SendMessageW(((NAVMNGR*)hNav)->hwndHost, WM_SETREDRAW, (WPARAM)TRUE, 0L); + } + } + return ((NAVMNGR*)hNav)->lockUpdate; +} + +INT NavCtrlI_MapPointsTo(HNAVCTRL hNav, HWND hwndTo, POINT *ppt, UINT cPoints) +{ + return (hNav) ? MapWindowPoints(((NAVMNGR*)hNav)->hwndHost, hwndTo, ppt, cPoints) : 0; +} + +INT NavCtrlI_MapPointsFrom(HNAVCTRL hNav, HWND hwndFrom, POINT *ppt, UINT cPoints) +{ + return (hNav) ? MapWindowPoints(hwndFrom, ((NAVMNGR*)hNav)->hwndHost, ppt, cPoints) : 0; +} + +BOOL NavCtrlI_EndEditTitle(HNAVCTRL hNav, BOOL fCancel) +{ + return (hNav) ? (BOOL)SendMessageW(((NAVMNGR*)hNav)->hwndHost, TVM_ENDEDITLABELNOW, (WPARAM)fCancel, 0L) : FALSE; +} + +INT NavCtrlI_GetIndent(HNAVCTRL hNav) +{ + return (hNav) ? (BOOL)SendMessageW(((NAVMNGR*)hNav)->hwndHost, TVM_GETINDENT, (WPARAM)0, 0L) : 0; +} + +DWORD NavCtrlI_GetStyle(HNAVCTRL hNav) +{ + DWORD style; + style = NCS_NORMAL_I; + if (hNav) + { + if (TVS_FULLROWSELECT == (TVS_FULLROWSELECT & (DWORD)GetWindowLongPtrW(((NAVMNGR*)hNav)->hwndHost, GWL_STYLE))) + style |= NCS_FULLROWSELECT_I; + if (NULL != (HIMAGELIST)SendMessageW(((NAVMNGR*)hNav)->hwndHost, TVM_GETIMAGELIST, (WPARAM)TVSIL_NORMAL, (LPARAM)0)) + style |= NCS_SHOWICONS_I; + } + return style; +} + +BOOL NavItemI_EditTitle(HNAVITEM hItem) +{ + NAVITM *item; + HWND editWindow; + + if (NULL == hItem) + return FALSE; + + item = (NAVITM*)hItem; + if (0 == (NIS_ALLOWEDIT_I & item->style)) + return FALSE; + + SendMessageW(item->hwndTree, TVM_ENSUREVISIBLE, 0, (LPARAM)item->hTreeItem); + + editWindow = (HWND)SendMessageW(item->hwndTree, TVM_EDITLABELW, 0, (LPARAM)item->hTreeItem); + return (NULL != editWindow); +} + +INT NavItemI_GetId(HNAVITEM hItem) +{ + return (hItem) ? ((NAVITM*)hItem)->id : 0; +} + +HNAVITEM NavItemI_GetChild(HNAVITEM hItem) +{ + return NavItemI_GetNextEx(hItem, TVGN_CHILD); +} + +HNAVITEM NavItemI_GetNext(HNAVITEM hItem) +{ + return NavItemI_GetNextEx(hItem, TVGN_NEXT); +} + +HNAVITEM NavItemI_GetRoot(HNAVITEM hItem) +{ + return NavItemI_GetNextEx(hItem, TVGN_ROOT); +} + +HNAVITEM NavItemI_GetParent(HNAVITEM hItem) +{ + return NavItemI_GetNextEx(hItem, TVGN_PARENT); +} + +HNAVITEM NavItemI_GetPrevious(HNAVITEM hItem) +{ + return NavItemI_GetNextEx(hItem, TVGN_PREVIOUS); +} + +INT NavItemI_GetChildrenCount(HNAVITEM hItem) +{ + HTREEITEM hChild; + int counter; + + if (!hItem) return -1; + + hChild = (HTREEITEM)SendMessageW(((NAVITM*)hItem)->hwndTree, TVM_GETNEXTITEM, (WPARAM)TVGN_CHILD, (LPARAM)((NAVITM*)hItem)->hTreeItem); + if (!hChild) return 0; + counter = 1; + while(NULL != (hChild = (HTREEITEM)SendMessageW(((NAVITM*)hItem)->hwndTree, TVM_GETNEXTITEM, (WPARAM)TVGN_NEXT, (LPARAM)hChild))) counter++; + return counter; +} + +BOOL NavItemI_IsExpanded(HNAVITEM hItem) +{ + return (hItem && (TVIS_EXPANDED == (TVIS_EXPANDED & (UINT)SendMessageW(((NAVITM*)hItem)->hwndTree, TVM_GETITEMSTATE, + (WPARAM)((NAVITM*)hItem)->hTreeItem, (LPARAM)TVIS_EXPANDED)))); +} + +INT NavItemI_GetImageIndex(HNAVITEM hItem, INT imageType) +{ + NAVITM *item; + item = (NAVITM*)hItem; + + if (NULL == item) + return -1; + + if (0 != (NIS_DEFAULTIMAGE & item->style)) + return -1; + + switch(imageType) + { + case IMAGE_NORMAL_I: return ((NAVITM*)hItem)->iImage; + case IMAGE_SELECTED_I: return ((NAVITM*)hItem)->iSelectedImage; + } + return -1; +} + +BOOL NavItemI_GetIndirect(HNAVITEM hItem, NAVITEM_I *pnis) +{ + NAVITM *pItem; + if (!hItem || !pnis) return FALSE; + pItem = (NAVITM*)hItem; + + if (NIMF_ITEMID_I & pnis->mask) pnis->id = pItem->id; + if (NIMF_IMAGE_I & pnis->mask) pnis->iImage = pItem->iImage; + if (NIMF_IMAGESEL_I & pnis->mask) pnis->iSelectedImage = pItem->iSelectedImage; + if (NIMF_STYLE_I & pnis->mask) pnis->style = (pnis->styleMask & pItem->style); + if (NIMF_TEXT_I & pnis->mask && !NavItemI_GetText(pItem, pnis->pszText, pnis->cchTextMax)) return FALSE; + if (NIMF_TEXTINVARIANT_I & pnis->mask && !NavItemI_GetInvariantText(pItem, pnis->pszInvariant, pnis->cchInvariantMax)) return FALSE; + if (NIMF_STATE_I & pnis->mask) pnis->state = NavItemI_GetState(pItem, 0xFFFFFFFF); + if (NIMF_FONT_I & pnis->mask) pnis->hFont = NavItemI_GetFont(pItem); + if (NIMF_PARAM_I & pnis->mask) pnis->lParam = pItem->lParam; + + return TRUE; +} + +BOOL NavItemI_GetText(HNAVITEM hItem, LPWSTR pszText, INT cchMaxLen) +{ + if (!hItem || !pszText) return FALSE; + return (S_OK == StringCchCopyW(pszText, cchMaxLen, (((NAVITM*)hItem)->pszText) ? ((NAVITM*)hItem)->pszText : L"")); +} + +BOOL NavItemI_GetInvariantText(HNAVITEM hItem, LPWSTR pszText, INT cchMaxLen) +{ + if (!hItem || !pszText) return FALSE; + return (S_OK == StringCchCopyW(pszText, cchMaxLen, (((NAVITM*)hItem)->pszInvariant) ? ((NAVITM*)hItem)->pszInvariant : L"")); +} + +BOOL NavItemI_HasChildren(HNAVITEM hItem) +{ + return (hItem && (0 != (NIS_HASCHILDREN_I & ((NAVITM*)hItem)->style))); +} + +BOOL NavItemI_HasChildrenReal(HNAVITEM hItem) +{ + return (hItem && SendMessageW(((NAVITM*)hItem)->hwndTree, TVM_GETNEXTITEM, (WPARAM)TVGN_CHILD, (LPARAM)((NAVITM*)hItem)->hTreeItem)); +} + +BOOL NavItemI_IsSelected(HNAVITEM hItem) +{ + return (NIS_SELECTED_I == NavItemI_GetState(hItem, NIS_SELECTED_I)); +} + +BOOL NavItemI_SetId(HNAVITEM hItem, INT itemId) +{ + if (!hItem || itemId < 0) return FALSE; + if (((NAVITM*)hItem)->id == itemId) return TRUE; + if (FindNavItemByNavIdEx(((NAVITM*)hItem)->hwndTree, itemId, TVI_ROOT)) return FALSE; + ((NAVITM*)hItem)->id = itemId; + return TRUE; +} + +BOOL NavItemI_SetImageIndex(HNAVITEM hItem, INT mlilIndex, INT imageType) +{ + if (!hItem) return FALSE; + + switch(imageType) + { + case IMAGE_NORMAL_I: ((NAVITM*)hItem)->iImage = mlilIndex; break; + case IMAGE_SELECTED_I: ((NAVITM*)hItem)->iSelectedImage = mlilIndex; break; + default: return FALSE; + } + NavItemI_Invalidate(hItem, NULL, FALSE); + return TRUE; +} + +BOOL NavItemI_SetStyle(HNAVITEM hItem, UINT style, UINT mask) +{ + UINT newStyle; + if (!hItem) return FALSE; + + newStyle = (((NAVITM*)hItem)->style & ~mask) | style; + if (((NAVITM*)hItem)->style != newStyle) + { + ((NAVITM*)hItem)->style = newStyle; + NavItemI_Invalidate(hItem, NULL, FALSE); + } + return TRUE; +} + +BOOL NavItemI_SetText(HNAVITEM hItem, LPCWSTR pszText) +{ + if (!hItem) return FALSE; + if (!pszText) + { + free(((NAVITM*)hItem)->pszText); + ((NAVITM*)hItem)->pszText = NULL; + ((NAVITM*)hItem)->cchTextMax = 0; + } + else + { + INT len = lstrlenW(pszText); + if (len >= ((NAVITM*)hItem)->cchTextMax) + { + LPVOID data; + data = realloc(((NAVITM*)hItem)->pszText, sizeof(WCHAR)*(len + 4)); + if (!data) return FALSE; + ((NAVITM*)hItem)->pszText = (LPWSTR)data; + ((NAVITM*)hItem)->cchTextMax = len + 4; + } + if (S_OK != StringCchCopyW(((NAVITM*)hItem)->pszText, ((NAVITM*)hItem)->cchTextMax, pszText)) return FALSE; + } + NavItemI_Invalidate(hItem, NULL, FALSE); + return TRUE; +} + +BOOL NavItemI_SetInvariantText(HNAVITEM hItem, LPCWSTR pszText) +{ + if (!hItem) return FALSE; + if (!pszText) + { + free(((NAVITM*)hItem)->pszInvariant); + ((NAVITM*)hItem)->pszInvariant = NULL; + ((NAVITM*)hItem)->cchInvariantMax = 0; + } + else + { + INT len = lstrlenW(pszText); + if (len >= ((NAVITM*)hItem)->cchInvariantMax) + { + LPVOID data; + data = realloc(((NAVITM*)hItem)->pszInvariant, sizeof(WCHAR)*(len + 4)); + if (!data) return FALSE; + ((NAVITM*)hItem)->pszInvariant = (LPWSTR)data; + ((NAVITM*)hItem)->cchInvariantMax = len + 4; + } + if (S_OK != StringCchCopyW(((NAVITM*)hItem)->pszInvariant, ((NAVITM*)hItem)->cchInvariantMax, pszText)) return FALSE; + } + return TRUE; +} + +BOOL NavItemI_SetState(HNAVITEM hItem, UINT state, UINT stateMask) +{ + TVITEMW item; + if (!hItem) return FALSE; + item.hItem = ((NAVITM*)hItem)->hTreeItem; + item.mask = TVIF_STATE; + item.state = 0; + item.stateMask = 0; + switch(state) + { + case NIS_SELECTED_I: item.state |= TVIS_SELECTED; break; + case NIS_EXPANDED_I: item.state |= TVIS_EXPANDED; break; + case NIS_DROPHILITED_I: item.state |= TVIS_DROPHILITED; break; + } + switch(stateMask) + { + case NIS_SELECTED_I: item.stateMask |= TVIS_SELECTED; break; + case NIS_EXPANDED_I: item.stateMask |= TVIS_EXPANDED; break; + case NIS_DROPHILITED_I: item.stateMask |= TVIS_DROPHILITED; break; + } + return (BOOL)SendMessageW(((NAVITM*)hItem)->hwndTree, TVM_SETITEMW, (WPARAM)0, (LPARAM)&item); +} + +UINT NavItemI_GetState(HNAVITEM hItem, UINT stateMask) +{ + UINT treeState, navState; + if (!hItem) return 0; + + treeState = (UINT)SendMessageW(((NAVITM*)hItem)->hwndTree, TVM_GETITEMSTATE, (WPARAM)((NAVITM*)hItem)->hTreeItem, (LPARAM)0xFFFFFFFF); + navState = 0; + if (TVIS_SELECTED & treeState) navState |= NIS_SELECTED_I; + if (TVIS_EXPANDED & treeState) navState |= NIS_EXPANDED_I; + if (TVIS_DROPHILITED & treeState) navState |= NIS_DROPHILITED_I; + return (navState & stateMask); +} + +UINT NavItemI_GetStyle(HNAVITEM hItem, UINT styleMask) +{ + return (hItem) ? (((NAVITM*)hItem)->style & styleMask) : 0; +} + +BOOL NavItemI_SetIndirect(HNAVITEM hItem, NAVITEM_I *pnis) +{ + if (!hItem || !pnis) return FALSE; + + BOOL fResult = TRUE; + ((NAVITM*)hItem)->fBlockInvalid = TRUE; + if (NIMF_ITEMID_I & pnis->mask && !NavItemI_SetId(hItem, pnis->id)) fResult = FALSE; + if (NIMF_IMAGE_I & pnis->mask && !NavItemI_SetImageIndex(hItem, pnis->iImage, IMAGE_NORMAL_I)) fResult = FALSE; + if (NIMF_IMAGESEL_I & pnis->mask && !NavItemI_SetImageIndex(hItem, pnis->iSelectedImage, IMAGE_SELECTED_I)) fResult = FALSE; + if (NIMF_STYLE_I & pnis->mask && !NavItemI_SetStyle(hItem, pnis->style, pnis->styleMask)) fResult = FALSE; + if (NIMF_TEXT_I & pnis->mask && !NavItemI_SetText(hItem, pnis->pszText)) fResult = FALSE; + if (NIMF_TEXTINVARIANT_I & pnis->mask && !NavItemI_SetText(hItem, pnis->pszInvariant)) fResult = FALSE; + if (NIMF_STATE_I & pnis->mask && !NavItemI_SetState(hItem, pnis->state, pnis->stateMask)) fResult = FALSE; + if (NIMF_FONT_I & pnis->mask && !NavItemI_SetFont(hItem, pnis->hFont)) fResult = FALSE; + if (NIMF_PARAM_I & pnis->mask) ((NAVITM*)hItem)->lParam = pnis->lParam; + + ((NAVITM*)hItem)->fBlockInvalid = FALSE; + if (!NavItemI_Invalidate(hItem, NULL, FALSE)) fResult = FALSE; + + return fResult; +} + +BOOL NavItemI_GetRect(HNAVITEM hItem, RECT *prc, BOOL fItemRect) +{ + if (!hItem || !prc) return FALSE; + *((HTREEITEM*)prc) = ((NAVITM*)hItem)->hTreeItem; + return (BOOL)SendMessageW(((NAVITM*)hItem)->hwndTree, TVM_GETITEMRECT, (WPARAM)fItemRect, (LPARAM)prc); +} + +BOOL NavItemI_Invalidate(HNAVITEM hItem, RECT *prc, BOOL fErase) +{ + if (!hItem) return FALSE; + if (!((NAVITM*)hItem)->fBlockInvalid) + { + RECT rc; + if (NavItemI_GetRect(hItem, &rc, FALSE)) + { + if (prc) IntersectRect(&rc, &rc, prc); + InvalidateRect(((NAVITM*)hItem)->hwndTree, &rc, fErase); + return TRUE; + } + } + return TRUE; +} + +BOOL NavItemI_Expand(HNAVITEM hItem, UINT flag) +{ + UINT tiFlag; + if (!hItem) return FALSE; + + switch(flag) + { + case NAVITEM_TOGGLE_I: tiFlag = TVE_TOGGLE; break; + case NAVITEM_EXPAND_I: tiFlag = TVE_EXPAND; break; + case NAVITEM_COLLAPSE_I: tiFlag = TVE_COLLAPSE; break; + default: return FALSE; + } + return (BOOL)SendMessageW(((NAVITM*)hItem)->hwndTree, TVM_EXPAND, (WPARAM)tiFlag, (LPARAM)((NAVITM*)hItem)->hTreeItem); +} + +INT NavItemI_GetFullName(HNAVITEM hItem, LPWSTR pszFullName, INT cchMaxLen) +{ + wchar_t *current, *scanner, *text; + INT remaining; + + if (!pszFullName || !hItem) return 0; + pszFullName[0] = 0x00; + remaining = cchMaxLen; + current = pszFullName; + + while(hItem && remaining) + { + text = (((NAVITM*)hItem)->pszInvariant) ? ((NAVITM*)hItem)->pszInvariant : ((NAVITM*)hItem)->pszText; + if (!text) text = EMPTY_ITEM_NAME; + + if (current != pszFullName) + { + *current = SEPARATOR; + current++; + remaining--; + } + scanner = text + lstrlenW(text) - 1; + BOOL second = FALSE; + + while (scanner >= text && remaining) + { + *current = *scanner; + current++; + remaining--; + if (SEPARATOR == *scanner && !second) { second = TRUE; continue; } + second = FALSE; + scanner--; + } + + hItem = NavItemI_GetParent(hItem); + } + + if (remaining) + { + *current = 0x00; + current--; + scanner = pszFullName; + while (scanner < current) + { + wchar_t tmp = *scanner; + *scanner = *current; + *current = tmp; + scanner++; + current--; + } + } + else + { + *pszFullName = 0x00; + return 0; + } + + return cchMaxLen - remaining; +} + +BOOL NavItemI_Move(HNAVITEM hItem, HNAVITEM hItemDest, BOOL fAfter) +{ + WORD order; + UINT flags; + HNAVCTRL hNav; + if (!hItem) return FALSE; + + if (hItemDest) + { + order = ((NAVITM*)hItemDest)->sortOrder; + flags = (fAfter) ? NOF_MOVEONEAFTER_I : NOF_MOVEONEBEFORE_I; + } + else + { + order = 1; + flags = NOF_MOVEONEBEFORE_I; + } + + hNav = (HNAVCTRL)GetPropW(((NAVITM*)hItem)->hwndTree, NAVMGR_HWNDPROPW); + NavCtrlI_BeginUpdate(hNav, NUF_LOCK_NONE_I); + order = NavItemI_SetOrder(hItem, order, flags); + NavItemI_EnsureVisible(hItem); + NavCtrlI_EndUpdate(hNav); + + return ((WORD)-1 != order); +} + +static INT CALLBACK CommpareByOrderCB(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort) +{ + if (lParam1 == lParam2) return 0; + if (NULL == lParam1) return 1; + return (((NAVITM*)lParam1)->sortOrder - ((NAVITM*)lParam2)->sortOrder); +} + +static BOOL NavItemI_SetOrderWorker(HNAVITEM hItem, HTREEITEM hTreeFirst, WORD order, UINT flags) +{ + bool bRecurse; + + if (!hItem) return FALSE; + + bRecurse = false; + + if (0 == order) + { + order = 1; + flags = NOF_MOVEONEBEFORE_I; + } + + if (((WORD)-1) == order || ((NAVITM*)hItem)->sortOrder != order) + { + TVITEMW treeItem; // keep it here so it will be released prior to recurtion + ((NAVITM*)hItem)->sortOrder = (((WORD)-1) == order) ? 1 : order; + + treeItem.mask = TVIF_HANDLE | TVIF_PARAM; + treeItem.hItem = hTreeFirst; + + while (treeItem.hItem) + { + if (treeItem.hItem != ((NAVITM*)hItem)->hTreeItem) + { + if (!SendMessageW(((NAVITM*)hItem)->hwndTree, TVM_GETITEMW, (WPARAM)0, (LPARAM)&treeItem)) return FALSE; + if (((WORD)-1) == order) + { + if (treeItem.lParam && ((NAVITM*)hItem)->sortOrder <= ((NAVITM*)treeItem.lParam)->sortOrder) + ((NAVITM*)hItem)->sortOrder = ((NAVITM*)treeItem.lParam)->sortOrder + 1; + } + else + { + if (treeItem.lParam && ((NAVITM*)treeItem.lParam)->sortOrder == order) + { + switch(flags) + { + case NOF_MOVEONEBEFORE_I: + hItem = (HNAVITEM)treeItem.lParam; + order = ((NAVITM*)hItem)->sortOrder + 1; + break; + case NOF_MOVEONEAFTER_I: + order++; + flags = NOF_MOVEONEBEFORE_I; + break; + case NOF_MOVEBEFORE_I: + if (1 == order) + { + hItem = (HNAVITEM)treeItem.lParam; + order = ((NAVITM*)hItem)->sortOrder + 1; + flags = NOF_MOVEONEBEFORE_I; + } + else order--; + break; + case NOF_MOVEAFTER_I: + order++; + break; + default: return FALSE; + } + bRecurse = true; + break; + } + } + } + treeItem.hItem = (HTREEITEM)SendMessageW(((NAVITM*)hItem)->hwndTree, TVM_GETNEXTITEM, (WPARAM)TVGN_NEXT, (LPARAM)treeItem.hItem); + } + } + + return (bRecurse) ? NavItemI_SetOrderWorker(hItem, hTreeFirst, order, flags) : TRUE; +} + +WORD NavItemI_SetOrder(HNAVITEM hItem, WORD order, UINT flags) +{ + HTREEITEM hTreeParent; + if (!hItem) return (WORD)-1; + + hTreeParent = (HTREEITEM)SendMessageW(((NAVITM*)hItem)->hwndTree, TVM_GETNEXTITEM, (WPARAM)TVGN_PARENT, (LPARAM)((NAVITM*)hItem)->hTreeItem); + + if (NavItemI_SetOrderWorker(hItem, + (HTREEITEM)SendMessageW(((NAVITM*)hItem)->hwndTree, TVM_GETNEXTITEM, + (WPARAM)((hTreeParent) ? TVGN_CHILD : TVGN_ROOT), (LPARAM)hTreeParent), + order, flags)) + { + HNAVCTRL hNav; + TVSORTCB sort; + + sort.hParent = hTreeParent; + sort.lpfnCompare = CommpareByOrderCB; + sort.lParam = 0; + + hNav = (HNAVCTRL)GetPropW(((NAVITM*)hItem)->hwndTree, NAVMGR_HWNDPROPW); + NavCtrlI_BeginUpdate(hNav, NUF_LOCK_NONE_I); + SendMessageW(((NAVITM*)hItem)->hwndTree, TVM_SORTCHILDRENCB, (WPARAM)FALSE, (LPARAM)&sort); + NavCtrlI_EndUpdate(hNav); + } + return ((NAVITM*)hItem)->sortOrder; +} + +WORD NavItemI_GetOrder(HNAVITEM hItem) +{ + return (hItem) ? ((NAVITM*)hItem)->sortOrder : 0xFFFF; +} + +BOOL NavItemI_Select(HNAVITEM hItem) +{ + BOOL fResult; + if (!hItem) return FALSE; + fResult = (BOOL)SendMessageW(((NAVITM*)hItem)->hwndTree, TVM_SELECTITEM, (WPARAM)TVGN_CARET, (LPARAM)((NAVITM*)hItem)->hTreeItem); + SendMessageW(((NAVITM*)hItem)->hwndTree, WM_HSCROLL, MAKEWPARAM(SB_LEFT, 0), NULL); + SendMessageW(((NAVITM*)hItem)->hwndTree, WM_HSCROLL, MAKEWPARAM(SB_ENDSCROLL, 0), NULL); + return fResult; +} + +BOOL NavItemI_EnsureVisible(HNAVITEM hItem) +{ + BOOL fResult; + + if (!hItem) return FALSE; + fResult = (BOOL)SendMessageW(((NAVITM*)hItem)->hwndTree, TVM_ENSUREVISIBLE, (WPARAM)0, (LPARAM)((NAVITM*)hItem)->hTreeItem); + if (fResult) + { + SCROLLINFO si; + si.cbSize = sizeof(SCROLLINFO); + si.fMask = SIF_POS | SIF_RANGE; + if(GetScrollInfo(((NAVITM*)hItem)->hwndTree, SB_HORZ, &si) && (si.nMin != si.nPos)) + { + SendMessageW(((NAVITM*)hItem)->hwndTree, WM_HSCROLL, MAKEWPARAM(SB_LEFT, 0), NULL); + SendMessageW(((NAVITM*)hItem)->hwndTree, WM_HSCROLL, MAKEWPARAM(SB_ENDSCROLL, 0), NULL); + } + } + + return fResult; +} + +HFONT NavItemI_GetFont(HNAVITEM hItem) +{ + return (hItem) ? ((NAVITM*)hItem)->hFont : NULL; +} + +HFONT NavItemI_SetFont(HNAVITEM hItem, HFONT hFont) +{ + HFONT hFontOld; + if (!hItem) return NULL; + hFontOld = ((NAVITM*)hItem)->hFont; + ((NAVITM*)hItem)->hFont = hFont; + NavItemI_Invalidate(hItem, NULL, FALSE); + return hFontOld; +} + +HIMAGELIST NavItemI_CreateDragImage(HNAVITEM hItem, LPCWSTR pszTipText) +{ + HIMAGELIST hIL; + HNAVCTRL hNav; + HBITMAP hbmp; + INT cy, cx, ilIndex; + RECT rcImage, rcText, rcTip; + HDC hdcMem, hdcScreen; + + HFONT hFont, hFontOld, hFontTip; + BOOL fDestroyFont; + + fDestroyFont = FALSE; + hFont = NULL; + hFontTip = NULL; + + if (!hItem) return NULL; + + hNav = (HNAVCTRL)GetPropW(((NAVITM*)hItem)->hwndTree, NAVMGR_HWNDPROPW); + if (!hNav) return NULL; + + cx = DRAGIMAGE_OFFSET_X; + cy = (INT)SendMessageW(((NAVITM*)hItem)->hwndTree, TVM_GETITEMHEIGHT, (WPARAM)0, (LPARAM)0L) + 2*DRAGIMAGE_OFFSET_Y; + if (!cy) return NULL; + + hdcScreen = GetDCEx(((NAVITM*)hItem)->hwndTree, NULL, DCX_WINDOW | DCX_CACHE); + hdcMem = (hdcScreen) ? CreateCompatibleDC(NULL) : NULL; + if (!hdcMem) + { + if (hdcScreen) ReleaseDC(((NAVITM*)hItem)->hwndTree, hdcScreen); + return NULL; + } + + SetRect(&rcText, 0,0,0,0); + SetRect(&rcTip, 0,0,0,0); + + if (((NAVITM*)hItem)->pszText) + { + if (((NAVITM*)hItem)->hFont) hFont = ((NAVITM*)hItem)->hFont; + else + { + hFont = (HFONT)::SendMessageW(((NAVITM*)hItem)->hwndTree, WM_GETFONT, 0, 0); + if (NULL == hFont) + hFont = (HFONT)MlStockObjects_Get(DEFAULT_FONT); + + if ((NIS_BOLD_I | NIS_ITALIC_I | NIS_UNDERLINE_I) & ((NAVITM*)hItem)->style) + { + LOGFONTW lf = { 0 }; + GetObjectW(hFont, sizeof(LOGFONTW), &lf); + if (NIS_BOLD_I & ((NAVITM*)hItem)->style) lf.lfWeight = FW_BOLD; + if (NIS_ITALIC_I & ((NAVITM*)hItem)->style) lf.lfItalic = TRUE; + if (NIS_UNDERLINE_I & ((NAVITM*)hItem)->style) lf.lfUnderline = TRUE; + hFont = CreateFontIndirectW(&lf); + fDestroyFont = TRUE; + } + } + + if (hFont) hFontOld = (HFONT)SelectObject(hdcMem, hFont); + + DrawTextW(hdcMem, ((NAVITM*)hItem)->pszText, -1, &rcText, DT_CALCRECT | DT_NOPREFIX); + if(rcText.bottom - rcText.top > cy) cy = (rcText.bottom - rcText.top) + 2; + cx += ((rcText.right - rcText.left) + DRAGIMAGE_OFFSET_X); + } + + ilIndex = -1; + + if (((NAVMNGR*)hNav)->hmlilImages) + { + hIL = (HIMAGELIST)SendMessageW(((NAVMNGR*)hNav)->hwndHost, TVM_GETIMAGELIST, (WPARAM)TVSIL_NORMAL, (LPARAM)0); + if (hIL && MLImageListI_GetRealList(((NAVMNGR*)hNav)->hmlilImages) == hIL) + { + COLORREF rgbBk, rgbFg; + GetItemColors(hNav, NIS_SELECTED_I | NIS_FOCUSED_I, &rgbBk, &rgbFg); + ilIndex = GetRealImageIndex(hNav, (NAVITM*)hItem, IMAGE_SELECTED_I, rgbBk, rgbFg); + } + } + else + hIL = NULL; + + if (-1 != ilIndex && NULL != hIL) + { + IMAGEINFO ii; + ImageList_GetImageInfo(hIL, ilIndex, &ii); + cx += (ii.rcImage.right - ii.rcImage.left) + 5; + SetRect(&rcImage, 0, 0, ii.rcImage.right - ii.rcImage.left, ii.rcImage.bottom - ii.rcImage.top); + } + else SetRect(&rcImage, 0,0,0,0); + + OffsetRect(&rcImage, DRAGIMAGE_OFFSET_X, ((cy - DRAGIMAGE_OFFSET_Y) - (rcImage.bottom - rcImage.top))/2); + OffsetRect(&rcText, rcImage.right + ((rcImage.right != rcImage.left) ? 5 : 0), ((cy - DRAGIMAGE_OFFSET_Y) - (rcText.bottom - rcText.top))/2); + + if (pszTipText && *pszTipText) + { + HFONT hFontTmp; + hFontTip = (HFONT)::SendMessageW(((NAVITM*)hItem)->hwndTree, WM_GETFONT, 0, 0); + if (!hFontTip) hFontTip = (HFONT)MlStockObjects_Get(DEFAULT_FONT); + + hFontTmp = (HFONT)SelectObject(hdcMem, hFontTip); + DrawTextW(hdcMem, pszTipText, -1, &rcTip, DT_CALCRECT | DT_NOPREFIX); + SelectObject(hdcMem, hFontTmp); + if (rcTip.right - rcTip.left > (cx - rcText.left + 3)) cx = rcText.left + (rcTip.right - rcTip.left) + 3; + OffsetRect(&rcTip, rcText.left, cy + 1); + cy += ((rcTip.bottom - rcTip.top) + 3); + } + + hbmp = (3 != cx) ? CreateCompatibleBitmap(hdcScreen, cx, cy) : NULL; + + if (hbmp) + { + HGDIOBJ hgdiOld; + RECT rc; + + hgdiOld = SelectObject(hdcMem, hbmp); + + SetBkColor(hdcMem, WADlg_getColor(WADLG_SELBAR_BGCOLOR)); + SetTextColor(hdcMem, WADlg_getColor(WADLG_SELBAR_FGCOLOR)); + + SetRect(&rc, 0, 0, cx, cy); + ExtTextOutW(hdcMem, 0, 0, ETO_OPAQUE, &rc, L"", 0, 0); + + if (hIL && -1 != ilIndex) + { + ImageList_DrawEx(hIL, ilIndex, hdcMem, rcImage.left, rcImage.top, + (rcImage.right - rcImage.left), (rcImage.bottom - rcImage.top), CLR_DEFAULT, CLR_DEFAULT, ILD_NORMAL); + } + + if (((NAVITM*)hItem)->pszText) DrawTextW(hdcMem, ((NAVITM*)hItem)->pszText, -1, &rcText, DT_NOPREFIX); + if (pszTipText && *pszTipText) + { + HFONT hFontTmp; + hFontTmp = (HFONT)SelectObject(hdcMem, hFontTip); + DrawTextW(hdcMem, pszTipText, -1, &rcTip, DT_NOPREFIX); + SelectObject(hdcMem, hFontTmp); + } + + SelectObject(hdcMem, hgdiOld); + + hIL = ImageList_Create(cx, cy, ILC_COLOR24 | ILC_MASK, 0, 1); + if (hIL && -1 == ImageList_Add(hIL, hbmp, NULL)) + { + ImageList_Destroy(hIL); + hIL = NULL; + } + + DeleteObject(hbmp); + } + + if (hFont) + { + SelectObject(hdcMem, hFontOld); + if (fDestroyFont) DeleteObject(hFont); + } + +// if (hFontTip) DeleteObject(hFontTip); // we not creating it... + + DeleteDC(hdcMem); + ReleaseDC(((NAVITM*)hItem)->hwndTree, hdcScreen); + + return hIL; +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/navigation.h b/Src/Plugins/General/gen_ml/navigation.h new file mode 100644 index 00000000..7ea68059 --- /dev/null +++ b/Src/Plugins/General/gen_ml/navigation.h @@ -0,0 +1,267 @@ +#ifndef NULLOSFT_MEDIALIBRARY_NAVIGATION_HEADER +#define NULLOSFT_MEDIALIBRARY_NAVIGATION_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include +#include "./config.h" +#include "./ml_imagelist.h" + +typedef LPVOID HNAVCTRL; +typedef LPVOID HNAVITEM; + +typedef struct _NAVITEM_I +{ + UINT mask; + INT id; + LPWSTR pszText; + INT cchTextMax; + LPWSTR pszInvariant; + INT cchInvariantMax; + INT iImage; + INT iSelectedImage; + UINT state; + UINT stateMask; + UINT style; + UINT styleMask; + HFONT hFont; + LPARAM lParam; +} NAVITEM_I; + +typedef struct _NAVITEMDRAW_I +{ + HDC hdc; // + COLORREF clrText; // + COLORREF clrTextBk; // + HFONT hFont; // + RECT *prc; // + UINT itemState; // NIS_XXX + UINT drawStage; // NIDS_XXX + INT iLevel; // Zero-based level of the item being drawn. +} NAVITEMDRAW_I; + + +// navigation control styles +#define NCS_NORMAL_I 0x0000 +#define NCS_FULLROWSELECT_I 0x0001 +#define NCS_SHOWICONS_I 0x0002 + +// nav item draw stage +#define NIDS_PREPAINT_I 0x0001 +#define NIDS_POSTPAINT_I 0x0002 + +// item custom draw callback return values +#define NICDRF_DODEFAULT_I 0x0001 +#define NICDRF_SKIPDEFAULT_I 0x0002 +#define NICDRF_NOTIFYPOSTPAINT_I 0x0004 +#define NICDRF_NEWFONT_I 0x0008 + + +// hit test flags +#define NAVHT_NOWHERE_I 0x0001 +#define NAVHT_ONITEM_I 0x0002 +#define NAVHT_ONITEMBUTTON_I 0x0004 // only if item currently has children +#define NAVHT_ONITEMINDENT_I 0x0010 +#define NAVHT_ONITEMRIGHT_I 0x0020 +#define NAVHT_ABOVE_I 0x0100 +#define NAVHT_BELOW_I 0x0200 +#define NAVHT_TORIGHT_I 0x0400 +#define NAVHT_TOLEFT_I 0x0800 + +// navigation item masks +#define NIMF_ITEMID_I 0x0001 +#define NIMF_TEXT_I 0x0002 +#define NIMF_TEXTINVARIANT_I 0x0004 +#define NIMF_IMAGE_I 0x0008 +#define NIMF_IMAGESEL_I 0x0010 +#define NIMF_STATE_I 0x0020 +#define NIMF_STYLE_I 0x0040 +#define NIMF_FONT_I 0x0080 +#define NIMF_PARAM_I 0x0100 + +// states +#define NIS_NORMAL_I 0x0000 +#define NIS_SELECTED_I 0x0001 +#define NIS_EXPANDED_I 0x0002 +#define NIS_DROPHILITED_I 0x0004 +#define NIS_FOCUSED_I 0x0008 // used with draw item + +// styles +#define NIS_HASCHILDREN_I 0x0001 // item has children +#define NIS_ALLOWCHILDMOVE_I 0x0002 // allow children to be moved (re-ordered) +#define NIS_ALLOWEDIT_I 0x0004 // allow title edit +#define NIS_ITALIC_I 0x0100 // when displaying item text draw it with italic style +#define NIS_BOLD_I 0x0200 // when displaying item text draw it with bold style +#define NIS_UNDERLINE_I 0x0400 // when displaying item text draw it with underline style +#define NIS_CUSTOMDRAW_I 0x0010 // custom draw calback +#define NIS_WANTSETCURSOR_I 0x0020 // item want to recive set cursor notification +#define NIS_WANTHITTEST_I 0x0040 // item want to monitor/modify hittest results +/// internal style - do not use +#define NIS_WANTPOSTPAINT_I 0x8000 // custom draw calback + + +// image types +#define IMAGE_NORMAL_I 0x0000 +#define IMAGE_SELECTED_I 0x0001 + +// item expand command flags +#define NAVITEM_TOGGLE_I 0x0000 +#define NAVITEM_EXPAND_I 0x0001 +#define NAVITEM_COLLAPSE_I 0x0002 + +// action codes +#define ACTION_CLICKL_I 0x0000 +#define ACTION_CLICKR_I 0x0001 +#define ACTION_ENTER_I 0x0002 +#define ACTION_DBLCLICKL_I 0x0003 +#define ACTION_DBLCLICKR_I 0x0004 + +// navigation callbacks ident +#define CALLBACK_ONCLICK_I 0x0001 +#define CALLBACK_ONSELECTED_I 0x0002 +#define CALLBACK_ONKEYDOWN_I 0x0003 +#define CALLBACK_ONBEGINDRAG_I 0x0004 +#define CALLBACK_ONGETIMAGEINDEX_I 0x0005 +#define CALLBACK_ONBEGINTITLEEDIT_I 0x0006 +#define CALLBACK_ONENDTITLEEDIT_I 0x0007 +#define CALLBACK_ONITEMDELETE_I 0x0008 +#define CALLBACK_ONITEMDRAW_I 0x0009 +#define CALLBACK_ONSETCURSOR_I 0x000A +#define CALLBACK_ONHITTEST_I 0x000B +#define CALLBACK_ONDESTROY_I 0x000C + +typedef BOOL (CALLBACK *ONNAVITEMCLICK_I)(HNAVCTRL /*hNav*/, HNAVITEM /*hItem*/, INT /*actionId*/); // return TRUE if you want to prevent further processing +typedef void (CALLBACK *ONNAVITEMSELECTED_I)(HNAVCTRL /*hNav*/, HNAVITEM /*hItemOld*/, HNAVITEM /*hItemNew*/); +typedef BOOL (CALLBACK *ONNAVCTRLKEYDOWN_I)(HNAVCTRL /*hNav*/, HNAVITEM /*hItem*/, NMTVKEYDOWN* /*ptvkd*/); +typedef void (CALLBACK *ONNAVCTRLBEGINDRAG_I)(HNAVCTRL /*hNav*/, HNAVITEM /*hItem*/, POINT /*pt*/); +typedef INT (CALLBACK *ONNAVITEMGETIMAGEINDEX_I)(HNAVCTRL /*hNav*/, HNAVITEM /*hItem*/, INT /*imageType*/); // if item image index == -1 you can provide some some other index +typedef BOOL (CALLBACK *ONNAVITEMBEGINTITLEEDIT_I)(HNAVCTRL /*hNav*/, HNAVITEM /*hItem*/); // return TRUE to allow edit +typedef BOOL (CALLBACK *ONNAVCTRLENDTITLEEDIT_I)(HNAVCTRL /*hNav*/, HNAVITEM /*hItem*/, LPCWSTR /*pszNewTitle*/); // if pszNewTitle == NULL edit wa canceled. Return TRUE to accept new title +typedef void (CALLBACK *ONNAVITEMDELETE_I)(HNAVCTRL /*hNav*/, HNAVITEM /*hItem*/); // +typedef UINT (CALLBACK *ONNAVITEMDRAW_I)(HNAVCTRL /*hNav*/, HNAVITEM /*hItem*/, NAVITEMDRAW_I* /*pnicd*/, LPARAM /*lParam*/); // custom draw +typedef BOOL (CALLBACK *ONNAVITEMSETCURSOR_I)(HNAVCTRL /*hNav*/, HNAVITEM /*hItem*/, LPARAM /*lParam*/); // set cursor +typedef void (CALLBACK *ONNAVITEMHITTEST_I)(HNAVCTRL /*hNav*/, POINT /*pt*/, UINT* /*pFlags*/, HNAVITEM* /*phItem*/, LPARAM /*lParam*/); +typedef void (CALLBACK *ONNAVCTRLDESTROY_I)(HNAVCTRL /*hNav*/); + +// name compare flags +#define NICF_DISPLAY_I 0x0001 // compare display name (if specified in combination with othe flags will be used last) +#define NICF_INVARIANT_I 0x0002 // compare invariant name (if specified in combination with other flags will be used after FULL and before DISPLAY +#define NICF_IGNORECASE_I 0x0004 // ignore case (always used when comparing invariant names) + +// NavItemI_SetOrder flags +#define NOF_MOVEONEBEFORE_I 0x00 // if order inded already used set order for this item and move old item on one position after until all items rearranged +#define NOF_MOVEONEAFTER_I 0x01 // +#define NOF_MOVEBEFORE_I 0x02 // +#define NOF_MOVEAFTER_I 0x03 // + +// NavCtrlI_EnumItems callback +typedef BOOL (CALLBACK *NAVENUMPROC_I)(HNAVITEM /*hItem*/, LPARAM /*lParam*/); // return FALSE to stop enumeration + +// Use this to in NavCtrlI_InertItem to create hInsertAfter +#define MAKE_NAVITEMSORTORDER(o) ((HNAVITEM)((ULONG_PTR)((WORD)(o)))) +#define IS_NAVITEMSORTORDER(_i) ((((ULONG_PTR)(_i)) >> 16) == 0) + +#define NUF_LOCK_NONE_I 0x00 +#define NUF_LOCK_SELECTED_I 0x01 // try to remember selected position if no selected items in the view it will fallback to NUF_REMEMBER_TOP_I +#define NUF_LOCK_TOP_I 0x02 // try to remeber top item... + +// insert item Insert after flags +#define NCI_FIRST_I ((HNAVITEM)(ULONG_PTR)-0x0FFFF) // insert first item +#define NCI_LAST_I ((HNAVITEM)(ULONG_PTR)-0x00000) // insert as last item + +// Navigation Control + +// NavCtrlI_Create - create new navigation manager +HNAVCTRL NavCtrlI_Create(HWND hwndParent); +BOOL NavCtrlI_SetRect(HNAVCTRL hNav, RECT *prc); +BOOL NavCtrlI_Show(HNAVCTRL hNav, INT nCmdShow); +BOOL NavCtrlI_Enable(HNAVCTRL hNav, BOOL fEnable); +BOOL NavCtrlI_Destroy(HNAVCTRL hNav); +BOOL NavCtrlI_Update(HNAVCTRL hNav); // calls UpdateWindow for the host; +BOOL NavCtrlI_ProcessNotifications(HNAVCTRL hNav, LPNMHDR pnmh, LRESULT *pResult); +C_Config *NavCtrlI_SetConfig(HNAVCTRL hNav, C_Config *pConfig); + +BOOL NavCtrlI_BeginUpdate(HNAVCTRL hNav, UINT fRememberPos); // call to prevent redraw (use NUF_REMEMBER_XXX) +BOOL NavCtrlI_EndUpdate(HNAVCTRL hNav); // call to allow redraw + +BOOL NavCtrlI_DeleteItem(HNAVCTRL hNav, HNAVITEM hItem); +BOOL NavCtrlI_DeleteAll(HNAVCTRL hNav); +HNAVITEM NavCtrlI_FindItem(HNAVCTRL hNav, INT itemId); +HNAVITEM NavCtrlI_FindItemByName(HNAVCTRL hNav, LCID Locale, UINT compFlags, LPCWSTR pszName, INT cchLength); // use one of the NICF_* flags +HNAVITEM NavCtrlI_FindItemByFullName(HNAVCTRL hNav, LCID Locale, UINT compFlags, LPCWSTR pszName, INT cchLength, BOOL fAncestorOk); // use one of the NICF_* flags. if fAncestor is set and item with this name not exist - will fall back to closest ancestor + +HNAVITEM NavCtrlI_InsertItem(HNAVCTRL hNav, HNAVITEM hInsertAfter, HNAVITEM hParent, NAVITEM_I *pnis); // if hInsertAfter > 0xFFFF it is consider as an item otherwise it is sort order, Use MAKE_NAVITEMSORTORDER macro +HNAVITEM NavCtrlI_GetRoot(HNAVCTRL hNav); +HNAVITEM NavCtrlI_GetSelection(HNAVCTRL hNav); +HNAVITEM NavCtrlI_GetFirstVisible(HNAVCTRL hNav); +HNAVITEM NavCtrlI_GetLastVisible(HNAVCTRL hNav); +BOOL NavCtrlI_UpdateLook(HNAVCTRL hNav); + +HMLIMGLST NavCtrlI_SetImageList(HNAVCTRL hNav, HMLIMGLST hmlil); +HMLIMGLST NavCtrlI_GetImageList(HNAVCTRL hNav); + +HWND NavCtrlI_GetHWND(HNAVCTRL hNav); +LPVOID NavCtrlI_RegisterCallback(HNAVCTRL hNav, LPVOID fnCallback, INT cbType); +HNAVITEM NavCtrlI_HitTest(HNAVCTRL hNav, POINT *ppt, UINT *pFlags); +BOOL NavCtrlI_SetInsertMark(HNAVCTRL hNav, HNAVITEM hItem, BOOL fAfter); + +BOOL NavCtrlI_EnumItems(HNAVCTRL hNav, NAVENUMPROC_I pEnumFunc, HNAVITEM hItemStart, LPARAM lParam); +INT NavCtrlI_MapPointsTo(HNAVCTRL hNav, HWND hwndTo, POINT *ppt, UINT cPoints); +INT NavCtrlI_MapPointsFrom(HNAVCTRL hNav, HWND hwndFrom, POINT *ppt, UINT cPoints); + +BOOL NavCtrlI_EndEditTitle(HNAVCTRL hNav, BOOL fCancel); +INT NavCtrlI_GetIndent(HNAVCTRL hNav); +DWORD NavCtrlI_GetStyle(HNAVCTRL hNav); // NCS_XXX + +// Navigation Item + +BOOL NavItemI_EditTitle(HNAVITEM hItem); +HNAVITEM NavItemI_GetChild(HNAVITEM hItem); +INT NavItemI_GetChildrenCount(HNAVITEM hItem); +HFONT NavItemI_GetFont(HNAVITEM hItem); +INT NavItemI_GetId(HNAVITEM hItem); +INT NavItemI_GetImageIndex(HNAVITEM hItem, INT imageType); +BOOL NavItemI_GetIndirect(HNAVITEM hItem, NAVITEM_I *pnis); +BOOL NavItemI_GetText(HNAVITEM hItem, LPWSTR pszText, INT cchMaxLen); +BOOL NavItemI_GetInvariantText(HNAVITEM hItem, LPWSTR pszText, INT cchMaxLen); +HNAVITEM NavItemI_GetNext(HNAVITEM hItem); +WORD NavItemI_GetOrder(HNAVITEM hItem); +HNAVITEM NavItemI_GetParent(HNAVITEM hItem); +HNAVITEM NavItemI_GetPrevious(HNAVITEM hItem); +BOOL NavItemI_GetRect(HNAVITEM hItem, RECT *prc, BOOL fItemRect); +HNAVITEM NavItemI_GetRoot(HNAVITEM hItem); +UINT NavItemI_GetState(HNAVITEM hItem, UINT stateMask); +UINT NavItemI_GetStyle(HNAVITEM hItem, UINT styleMask); +INT NavItemI_GetFullName(HNAVITEM hItem, LPWSTR pszFullName, INT cchMaxLen); + + +BOOL NavItemI_HasChildren(HNAVITEM hItem); // checks if item style for NIS_HASCHILDREN_I +BOOL NavItemI_HasChildrenReal(HNAVITEM hItem); // cheks if item actually has at least one child +BOOL NavItemI_IsSelected(HNAVITEM hItem); +BOOL NavItemI_IsExpanded(HNAVITEM hItem); +BOOL NavItemI_Expand(HNAVITEM hItem, UINT flag); +BOOL NavItemI_Move(HNAVITEM hItem, HNAVITEM hItemDest, BOOL fAfter); +HFONT NavItemI_SetFont(HNAVITEM hItem, HFONT hFont); // you stil own this font !!! (set hFont to NULL if you want to use treeview font). Returns previously set font. +BOOL NavItemI_SetId(HNAVITEM hItem, INT itemId); +BOOL NavItemI_SetImageIndex(HNAVITEM hItem, INT mlilIndex, INT imageType); +BOOL NavItemI_SetIndirect(HNAVITEM hItem, NAVITEM_I *pnis); +BOOL NavItemI_SetState(HNAVITEM hItem, UINT state, UINT stateMask); +BOOL NavItemI_SetStyle(HNAVITEM hItem, UINT style, UINT mask); +BOOL NavItemI_SetText(HNAVITEM hItem, LPCWSTR pszText); +BOOL NavItemI_SetInvariantText(HNAVITEM hItem, LPCWSTR pszText); + + +// Sets Item order and modifies all items oreder after it if required. +// if oder == 0xFFFF order will be set to max + 1 for this group +// returns new item order or 0xFFFF if error; +// minimal order value 1. +// flags one of the NOF_XXX +WORD NavItemI_SetOrder(HNAVITEM hItem, WORD order, UINT flags); +BOOL NavItemI_EnsureVisible(HNAVITEM hItem); +BOOL NavItemI_Select(HNAVITEM hItem); +HIMAGELIST NavItemI_CreateDragImage(HNAVITEM hItem, LPCWSTR pszTipText); +BOOL NavItemI_Invalidate(HNAVITEM hItem, RECT *prc, BOOL fErase); + +#endif //NULLOSFT_MEDIALIBRARY_NAVIGATION_HEADER \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/plugin.cpp b/Src/Plugins/General/gen_ml/plugin.cpp new file mode 100644 index 00000000..894402fd --- /dev/null +++ b/Src/Plugins/General/gen_ml/plugin.cpp @@ -0,0 +1,719 @@ +#include "main.h" +#include "ml.h" +#include "itemlist.h" +#include "../winamp/gen.h" +#include "config.h" +#include "../winamp/wa_ipc.h" +#include "../winamp/ipc_pe.h" +#include "resource.h" +#include "comboskin.h" +#include "../winamp/wa_dlg.h" +#include "childwnd.h" +#include "sendto.h" +#include "api__gen_ml.h" +#include "../nu/autowide.h" +#include "../nu/autochar.h" +#if defined(_WIN64) +#include "../Elevator/IFileTypeRegistrar_64.h" +#else +#include "../Elevator/IFileTypeRegistrar_32.h" +#endif +#include +#include +#include + +#include "..\WAT\wa_logger.h" + +#define ListView_InsertColumnW(hwnd, iCol, pcol) \ + (int)SNDMSG((hwnd), LVM_INSERTCOLUMNW, (WPARAM)(int)(iCol), (LPARAM)(const LV_COLUMNW *)(pcol)) + +#define ListView_InsertItemW(hwnd, pitem) \ + (int)SNDMSG((hwnd), LVM_INSERTITEMW, 0, (LPARAM)(const LV_ITEMW *)(pitem)) + +#define ListView_SetItemTextW(hwndLV, i, iSubItem_, pszText_) \ +{ LV_ITEMW _macro_lvi;\ + _macro_lvi.iSubItem = (iSubItem_);\ + _macro_lvi.pszText = (pszText_);\ + SNDMSG((hwndLV), LVM_SETITEMTEXTW, (WPARAM)(i), (LPARAM)(LV_ITEMW *)&_macro_lvi);\ +} + +#define ListView_SetItemW(hwnd, pitem) \ + (BOOL)SNDMSG((hwnd), LVM_SETITEMW, 0, (LPARAM)(LV_ITEMW *)(pitem)) + +#define ListView_GetItemW(hwnd, pitem) \ + (BOOL)SNDMSG((hwnd), LVM_GETITEMW, 0, (LPARAM)(LV_ITEMW *)(pitem)) + +extern "C" winampGeneralPurposePlugin plugin; +C_ItemList m_plugins; + +static HCURSOR link_hand_cursor; +LRESULT link_handlecursor(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + LRESULT ret = CallWindowProcW((WNDPROC)GetPropW(hwndDlg, L"link_proc"), hwndDlg, uMsg, wParam, lParam); + // override the normal cursor behaviour so we have a hand to show it is a link + if ( uMsg == WM_SETCURSOR ) + { + if ( (HWND)wParam == hwndDlg ) + { + if ( !link_hand_cursor ) + { + link_hand_cursor = LoadCursor(NULL, IDC_HAND); + } + SetCursor(link_hand_cursor); + return TRUE; + } + } + return ret; +} + +void link_startsubclass(HWND hwndDlg, UINT id) +{ + HWND ctrl = GetDlgItem(hwndDlg, id); + SetPropW(ctrl, L"link_proc", + (HANDLE)(LONG_PTR)SetWindowLongPtrW(ctrl, GWLP_WNDPROC, (LONGX86)(LONG_PTR)link_handlecursor)); +} + +void link_handledraw(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + if ( uMsg == WM_DRAWITEM ) + { + DRAWITEMSTRUCT* di = (DRAWITEMSTRUCT*)lParam; + if ( di->CtlType == ODT_BUTTON ) + { + wchar_t wt[123] = { 0 }; + int y; + RECT r; + HPEN hPen, hOldPen; + GetDlgItemTextW(hwndDlg, (INT)wParam, wt, 123); + + // draw text + SetTextColor(di->hDC, (di->itemState & ODS_SELECTED) ? RGB(220, 0, 0) : RGB(0, 0, 220)); + r = di->rcItem; + r.left += 2; + DrawTextW(di->hDC, wt, -1, &r, DT_VCENTER | DT_SINGLELINE); + + memset(&r, 0, sizeof(r)); + DrawTextW(di->hDC, wt, -1, &r, DT_SINGLELINE | DT_CALCRECT); + + // draw underline + y = di->rcItem.bottom - ((di->rcItem.bottom - di->rcItem.top) - (r.bottom - r.top)) / 2 - 1; + hPen = CreatePen(PS_SOLID, 0, (di->itemState & ODS_SELECTED) ? RGB(220, 0, 0) : RGB(0, 0, 220)); + hOldPen = (HPEN)SelectObject(di->hDC, hPen); + MoveToEx(di->hDC, di->rcItem.left + 2, y, NULL); + LineTo(di->hDC, di->rcItem.right + 2 - ((di->rcItem.right - di->rcItem.left) - (r.right - r.left)), y); + SelectObject(di->hDC, hOldPen); + DeleteObject(hPen); + + } + } +} + +/* In Winamp's preferences, Plugins->Media Library */ +static bool pluginsLoaded; +INT_PTR CALLBACK PluginsProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + if ( uMsg == WM_INITDIALOG ) + { + pluginsLoaded = false; + + extern BOOL init2(); + if ( !g_hwnd ) init2(); + WIN32_FIND_DATAW d = { 0 }; + + link_startsubclass(hwndDlg, IDC_PLUGINVERS); + + HWND listWindow = GetDlgItem(hwndDlg, IDC_GENLIB); + if ( NULL != listWindow ) + { + RECT r = { 0 }, rc = { 0 }; + GetWindowRect(listWindow, &r); + GetClientRect(listWindow, &r); + MapWindowPoints(listWindow, hwndDlg, (LPPOINT)&r, 2); + InflateRect(&r, 2, 2); + DestroyWindow(listWindow); + listWindow = CreateWindowExW(WS_EX_NOPARENTNOTIFY | WS_EX_CLIENTEDGE, WC_LISTVIEWW, L"", + WS_CHILD | WS_VISIBLE | WS_TABSTOP | LVS_REPORT | LVS_SINGLESEL | + LVS_SHOWSELALWAYS | LVS_SORTASCENDING | LVS_NOCOLUMNHEADER | LVS_NOSORTHEADER, + r.left, r.top, r.right - r.left, r.bottom - r.top, + hwndDlg, (HMENU)IDC_GENLIB, NULL, NULL); + SetWindowPos(listWindow, HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOSENDCHANGING); + ListView_SetExtendedListViewStyleEx(listWindow, LVS_EX_FULLROWSELECT | LVS_EX_INFOTIP, LVS_EX_FULLROWSELECT | LVS_EX_INFOTIP); + SendMessage(listWindow, WM_SETFONT, SendMessage(hwndDlg, WM_GETFONT, 0, 0), FALSE); + + LVCOLUMNW lvc = { 0 }; + ListView_InsertColumnW(listWindow, 0, &lvc); + ListView_InsertColumnW(listWindow, 1, &lvc); + + wchar_t filename[MAX_PATH] = { 0 }, description[512] = { 0 }; + for ( int x = 0; x < m_plugins.GetSize(); x++ ) + { + winampMediaLibraryPlugin* mlplugin = (winampMediaLibraryPlugin*)m_plugins.Get(x); + + if ( mlplugin && mlplugin->hDllInstance ) + { + wchar_t buf[512] = { 0 }; + LVITEMW lvi = { LVIF_TEXT | LVIF_PARAM, x, 0 }; + lstrcpynW(buf, (mlplugin->version >= MLHDR_VER_U ? (wchar_t*)mlplugin->description : AutoWide(mlplugin->description)), 512); + lvi.pszText = buf; + lvi.lParam = x; + lvi.iItem = ListView_InsertItemW(listWindow, &lvi); + + GetModuleFileNameW(mlplugin->hDllInstance, filename, ARRAYSIZE(filename)); + PathStripPathW(filename); + + lvi.mask = LVIF_TEXT; + lvi.iSubItem = 1; + lvi.pszText = filename; + ListView_SetItemW(listWindow, &lvi); + } + } + + wchar_t dirstr[MAX_PATH] = { 0 }; + PathCombineW(dirstr, pluginPath, L"ML_*.DLL"); + HANDLE h = FindFirstFileW(dirstr, &d); + if ( h != INVALID_HANDLE_VALUE ) + { + do + { + PathCombineW(dirstr, pluginPath, d.cFileName); + + HMODULE b = LoadLibraryExW(dirstr, NULL, LOAD_LIBRARY_AS_DATAFILE); + + int x = 0; + for ( x = 0; b && (x != m_plugins.GetSize()); x++ ) + { + winampMediaLibraryPlugin* mlplugin = (winampMediaLibraryPlugin*)m_plugins.Get(x); + if ( mlplugin->hDllInstance == b ) + { + break; + } + } + + if ( x == m_plugins.GetSize() || !b ) + { + LVITEMW lvi = { LVIF_TEXT | LVIF_PARAM, x, 0 }; + lvi.pszText = d.cFileName; + lvi.lParam = -2; + lvi.iItem = ListView_InsertItemW(listWindow, &lvi); + + lvi.mask = LVIF_TEXT; + lvi.iSubItem = 1; + lvi.pszText = WASABI_API_LNGSTRINGW(IDS_NOT_LOADED); + ListView_SetItemW(listWindow, &lvi); + } + + FreeLibrary(b); + } while ( FindNextFileW(h, &d) ); + + FindClose(h); + } + + GetClientRect(listWindow, &r); + ListView_SetColumnWidth(listWindow, 1, LVSCW_AUTOSIZE); + ListView_SetColumnWidth(listWindow, 0, (r.right - r.left) - ListView_GetColumnWidth(listWindow, 1)); + + if ( NULL != WASABI_API_APP ) + WASABI_API_APP->DirectMouseWheel_EnableConvertToMouseWheel(listWindow, TRUE); + + pluginsLoaded = true; + } + } + else if ( uMsg == WM_NOTIFY ) + { + LPNMHDR p = (LPNMHDR)lParam; + if ( p->idFrom == IDC_GENLIB ) + { + if ( p->code == LVN_ITEMCHANGED ) + { + LPNMLISTVIEW pnmv = (LPNMLISTVIEW)lParam; + LVITEM lvi = { LVIF_PARAM, pnmv->iItem }; + if ( ListView_GetItem(p->hwndFrom, &lvi) && (pnmv->uNewState & LVIS_SELECTED) ) + { + int loaded = (lvi.lParam != -2); + if ( loaded ) + { + winampMediaLibraryPlugin* mlplugin; + if ( lvi.lParam >= 0 && lvi.lParam < m_plugins.GetSize() && (mlplugin = (winampMediaLibraryPlugin*)m_plugins.Get(lvi.lParam)) ) + { + EnableWindow(GetDlgItem(hwndDlg, IDC_UNINST), !!mlplugin->hDllInstance); + + // enables / disables the config button as applicable instead of the + // "This plug-in has no configuration implemented" message (opt-in) + EnableWindow(GetDlgItem(hwndDlg, IDC_GENCONF), (!mlplugin->MessageProc(ML_MSG_NO_CONFIG, 0, 0, 0))); + } + } + else + { + EnableWindow(GetDlgItem(hwndDlg, IDC_GENCONF), 0); + } + EnableWindow(GetDlgItem(hwndDlg, IDC_UNINST), 1); + } + else + { + EnableWindow(GetDlgItem(hwndDlg, IDC_GENCONF), 0); + EnableWindow(GetDlgItem(hwndDlg, IDC_UNINST), 0); + } + } + else if ( p->code == NM_DBLCLK ) + { + PostMessage(hwndDlg, WM_COMMAND, MAKEWPARAM(IDC_GENCONF, 0), (LPARAM)GetDlgItem(hwndDlg, IDC_GENCONF)); + } + } + else if ( p->code == HDN_ITEMCHANGINGW ) + { + if ( pluginsLoaded ) + { +#if defined(_WIN64) + SetWindowLong(hwndDlg, DWLP_MSGRESULT, TRUE); +#else + SetWindowLong(hwndDlg, DWL_MSGRESULT, TRUE); +#endif + return TRUE; + } + } + } + else if ( uMsg == WM_DESTROY ) + { + HWND listWindow = GetDlgItem(hwndDlg, IDC_GENLIB); + if ( IsWindow(listWindow) && (NULL != WASABI_API_APP) ) + WASABI_API_APP->DirectMouseWheel_EnableConvertToMouseWheel(listWindow, FALSE); + } + else if ( uMsg == WM_COMMAND ) + { + switch ( LOWORD(wParam) ) + { + case IDC_GENCONF: + { + if ( IsWindowEnabled(GetDlgItem(hwndDlg, IDC_GENCONF)) ) + { + HWND listWindow = GetDlgItem(hwndDlg, IDC_GENLIB); + LVITEM lvi = { LVIF_PARAM, ListView_GetSelectionMark(listWindow) }; + if ( ListView_GetItem(listWindow, &lvi) ) + { + winampMediaLibraryPlugin* mlplugin; + if ( lvi.lParam >= 0 && lvi.lParam < m_plugins.GetSize() && (mlplugin = (winampMediaLibraryPlugin*)m_plugins.Get(lvi.lParam)) ) + { + if ( mlplugin->MessageProc && mlplugin->MessageProc(ML_MSG_CONFIG, (INT_PTR)hwndDlg, 0, 0) ) + { + } + else + { + wchar_t title[128] = { 0 }; + MessageBoxW(hwndDlg, WASABI_API_LNGSTRINGW(IDS_NO_CONFIG_PRESENT), + WASABI_API_LNGSTRINGW_BUF(IDS_ML_PLUGIN_INFO, title, 128), MB_OK); + } + } + } + } + } + return FALSE; + + case IDC_UNINST: + { + if ( IsWindowEnabled(GetDlgItem(hwndDlg, IDC_UNINST)) ) + { + HWND listWindow = GetDlgItem(hwndDlg, IDC_GENLIB); + int which_sel = ListView_GetSelectionMark(listWindow); + LVITEM lvi = { LVIF_PARAM, which_sel }; + if ( ListView_GetItem(listWindow, &lvi) ) + { + winampMediaLibraryPlugin* mlplugin; + wchar_t title[32] = { 0 }; + int msgBox = MessageBoxW(hwndDlg, WASABI_API_LNGSTRINGW(IDS_UNINSTALL_PROMPT), + WASABI_API_LNGSTRINGW_BUF(IDS_UINSTALL_CONFIRMATION, title, 32), + MB_YESNO | MB_ICONEXCLAMATION); + + if ( lvi.lParam >= 0 && lvi.lParam < m_plugins.GetSize() && (mlplugin = (winampMediaLibraryPlugin*)m_plugins.Get(lvi.lParam)) && msgBox == IDYES ) + { + int ret = 0; + int (*pr)(HINSTANCE hDllInst, HWND hwndDlg, int param); + + *(void**)&pr = (void*)GetProcAddress(mlplugin->hDllInstance, "winampUninstallPlugin"); + if ( pr )ret = pr(mlplugin->hDllInstance, hwndDlg, 0); + // ok to uninstall but do with a full restart (default/needed in subclassing cases) + if ( ret == ML_PLUGIN_UNINSTALL_REBOOT ) + { + wchar_t buf[MAX_PATH] = { 0 }; + GetModuleFileNameW(mlplugin->hDllInstance, buf, MAX_PATH); + WritePrivateProfileStringW(L"winamp", L"remove_genplug", buf, WINAMP_INI); + WritePrivateProfileStringW(L"winamp", L"show_prefs", L"-1", WINAMP_INI); + PostMessage(plugin.hwndParent, WM_WA_IPC, 0, IPC_RESTARTWINAMP); + } + // added from 5.37+ so we can do true on-the-fly removals (will fall back to default if fails) + else if ( ret == ML_PLUGIN_UNINSTALL_NOW ) + { + // get the filename before we free the dll otherwise things may go boom + wchar_t buf[MAX_PATH] = { 0 }; + GetModuleFileNameW(mlplugin->hDllInstance, buf, MAX_PATH); + + mlplugin->quit(); + //if (mlplugin->hDllInstance) FreeLibrary(mlplugin->hDllInstance); + m_plugins.Del(lvi.lParam); + + // try to use the elevator to do this + IFileTypeRegistrar* registrar = (IFileTypeRegistrar*)SendMessage(plugin.hwndParent, WM_WA_IPC, 0, IPC_GET_FILEREGISTRAR_OBJECT); + if ( registrar && (registrar != (IFileTypeRegistrar*)1) ) + { + if ( registrar->DeleteItem(buf) != S_OK ) + { + WritePrivateProfileStringW(L"winamp", L"remove_genplug", buf, WINAMP_INI); + WritePrivateProfileStringW(L"winamp", L"show_prefs", L"-1", WINAMP_INI); + PostMessage(plugin.hwndParent, WM_WA_IPC, 0, IPC_RESTARTWINAMP); + } + else + ListView_DeleteItem(listWindow, which_sel); + registrar->Release(); + } + } + } + // will cope with not loaded plug-ins so we can still remove them, etc + else if ( lvi.lParam == -2 && msgBox == IDYES ) + { + wchar_t buf[1024] = { 0 }, base[1024] = { 0 }; + GetModuleFileNameW(plugin.hDllInstance, base, sizeof(base) / sizeof(wchar_t)); + + LVITEMW lvi = { LVIF_TEXT, which_sel }; + lvi.pszText = buf; + lvi.cchTextMax = ARRAYSIZE(buf); + ListView_GetItemW(listWindow, &lvi); + + wchar_t* p = wcschr(buf, L'.'); + if ( p && *p == L'.' ) + { + p += 4; + *p = 0; + PathRemoveFileSpecW(base); + PathAppendW(base, buf); + } + + // try to use the elevator to do this + IFileTypeRegistrar* registrar = (IFileTypeRegistrar*)SendMessage(plugin.hwndParent, WM_WA_IPC, 0, IPC_GET_FILEREGISTRAR_OBJECT); + if ( registrar && (registrar != (IFileTypeRegistrar*)1) ) + { + if ( registrar->DeleteItem(base) != S_OK ) + { + WritePrivateProfileStringW(L"winamp", L"remove_genplug", base, WINAMP_INI); + WritePrivateProfileStringW(L"winamp", L"show_prefs", L"-1", WINAMP_INI); + PostMessage(plugin.hwndParent, WM_WA_IPC, 0, IPC_RESTARTWINAMP); + } + else + ListView_DeleteItem(listWindow, which_sel); + registrar->Release(); + } + // otherwise revert to a standard method + else + { + WritePrivateProfileStringW(L"winamp", L"remove_genplug", base, WINAMP_INI); + WritePrivateProfileStringW(L"winamp", L"show_prefs", L"-1", WINAMP_INI); + PostMessage(plugin.hwndParent, WM_WA_IPC, 0, IPC_RESTARTWINAMP); + } + } + + // resets the focus to the listbox so it'll keep ui response working + SetFocus(GetDlgItem(hwndDlg, IDC_GENLIB)); + } + } + } + return FALSE; + + case IDC_PLUGINVERS: + myOpenURLWithFallback(hwndDlg, L"http://www.google.com/search?q=Winamp+Library+Plugins", L"http://www.google.com/search?q=Winamp+Library+Plugins"); + return TRUE; + } + } + + link_handledraw(hwndDlg, uMsg, wParam, lParam); + return 0; +} + +typedef struct _PLUGINORDER +{ + LPCWSTR name; + bool found; +} PLUGINORDER; + +static PLUGINORDER preload[] = +{ + { L"ml_local.dll", false }, + { L"ml_downloads.dll", false }, + { L"ml_playlists.dll", false }, + { L"ml_wire.dll", false }, + { L"ml_online.dll", false }, + { L"ml_history.dll", false }, + { L"ml_rg.dll", false }, + { L"ml_bookmarks.dll", false }, + { L"ml_disc.dll", false }, + { L"ml_nowplaying2.dll", false }, + { L"ml_devices.dll", false }, + { L"ml_pmp.dll", false }, +}; + +static HANDLE hProfile = INVALID_HANDLE_VALUE; +HANDLE GetProfileFileHandle(int mode) +{ + if ( profile & mode ) + { + if ( hProfile == INVALID_HANDLE_VALUE ) + { + wchar_t profileFile[MAX_PATH] = { 0 }; + PathCombineW(profileFile, WINAMP_INI_DIR, ((mode == 2) ? L"profile_load.txt" : L"profile.txt")); + hProfile = CreateFileW(profileFile, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if ( hProfile != INVALID_HANDLE_VALUE ) + { + // just to make sure we don't over-write things + SetFilePointer(hProfile, NULL, NULL, FILE_END); + } + } + return hProfile; + } + return INVALID_HANDLE_VALUE; +} + +void LoadPlugin(const wchar_t* filename) +{ + wsprintfW( _log_message_w, L"filename( %s )", filename ); + + LOG_DEBUG( _log_message_w ); + + wchar_t profile[MAX_PATH * 2] = { 0 }; + LARGE_INTEGER starttime, endtime; + if ( hProfile != INVALID_HANDLE_VALUE ) + { + QueryPerformanceCounter(&starttime); + } + + wchar_t file[MAX_PATH] = { 0 }; + PathCombineW(file, pluginPath, filename); + + if ( !wa::files::file_exists( file ) ) + { + wsprintfW( _log_message_w, L"The plugin '%s' is not found in the \"Plugins\" folder!", filename ); + + LOG_ERROR( _log_message_w ); + + + return; + } + + + HMODULE hLib = LoadLibraryW(file); + if (hLib == NULL) + { + DWORD l_error_code = ::GetLastError(); + + wsprintfW( _log_message_w, L"Error when loading the plugin '%s'! Error code : %d!", filename, l_error_code ); + + LOG_ERROR( _log_message_w ); + + + return; + } + + winampMediaLibraryPlugin* (*pr)(); + pr = (winampMediaLibraryPlugin * (__cdecl*)(void)) GetProcAddress(hLib, "winampGetMediaLibraryPlugin"); + if (pr == NULL) + { + wsprintfW( _log_message_w, L"No entry point found for the plugin '%s'!", filename ); + + LOG_ERROR( _log_message_w ); + + + FreeLibrary(hLib); + return; + } + + winampMediaLibraryPlugin* mlplugin = pr(); + if ( !mlplugin || (mlplugin->version > MLHDR_VER && mlplugin->version < MLHDR_VER_OLD) ) + { + wsprintfW( _log_message_w, L"Either the plugin '%s' can't be loaded, or its version is incorrect!", filename ); + + LOG_ERROR( _log_message_w ); + + + FreeLibrary(hLib); + return; + } + + wsprintfW( _log_message_w, L"The plugin '%s' is correctly loaded!", filename ); + + LOG_DEBUG( _log_message_w ); + + + if ( g_safeMode != 1 ) + { + if ( g_safeMode == 2 ) + { + FreeModule(hLib); + return; + } + + char desc[128] = { 0 }; + lstrcpynA(desc, mlplugin->description, sizeof(desc)); + if ( desc[0] && !memcmp(desc, "nullsoft(", 9) ) + { + char* p = strrchr(desc, ')'); + if ( p ) + { + *p = 0; + if ( stricmp(AutoChar(filename), (desc + 9)) ) + { + FreeModule(hLib); + return; + } + } + } + else + { + FreeModule(hLib); + return; + } + } + + mlplugin->hwndLibraryParent = g_hwnd; + mlplugin->hwndWinampParent = plugin.hwndParent; + mlplugin->hDllInstance = hLib; + + int index = m_plugins.GetSize(); + + + if ( mlplugin->version >= MLHDR_VER ) + { + mlplugin->service = WASABI_API_SVC; + } + + if ( mlplugin->init() == ML_INIT_SUCCESS ) + { + wsprintfW( _log_message_w, L"The plugin '%s' is initialized!", filename ); + + LOG_DEBUG( _log_message_w ); + + + m_plugins.Add( (void *)mlplugin ); + } + else + { + wsprintfW( _log_message_w, L"An error occurs when initializing the plugin '%s'!", filename ); + + LOG_ERROR( _log_message_w ); + + + FreeLibrary( hLib ); + + return; + } + + if ( hProfile != INVALID_HANDLE_VALUE && mlplugin->hDllInstance ) + { + QueryPerformanceCounter(&endtime); + + DWORD written = 0; + unsigned int ms = (UINT)((endtime.QuadPart - starttime.QuadPart) * 1000 / freq.QuadPart); + int len = swprintf(profile, L"Library\t%s\t[%s]\t%dms\r\n", file, + (mlplugin->version >= MLHDR_VER_U ? (wchar_t*)mlplugin->description : AutoWide(mlplugin->description)), ms); + SetFilePointer(hProfile, NULL, NULL, FILE_END); + WriteFile(hProfile, profile, len * sizeof(wchar_t), &written, NULL); + } +} + +void loadMlPlugins() +{ + HANDLE hProfile = GetProfileFileHandle(2); + WIN32_FIND_DATAW d = { 0 }; + wchar_t tofind[MAX_PATH] = { 0 }; + + int i, count = ARRAYSIZE(preload); + for ( i = 0; i < count; i++ ) + LoadPlugin(preload[i].name); + + PathCombineW(tofind, pluginPath, L"ML_*.DLL"); + + HANDLE h = FindFirstFileW(tofind, &d); + if ( h != INVALID_HANDLE_VALUE ) + { + do + { + for ( i = 0; i < count && (preload[i].found || lstrcmpiW(preload[i].name, d.cFileName)); i++ ); + + if ( i == count ) + LoadPlugin(d.cFileName); + else + preload[i].found = true; + } while ( FindNextFileW(h, &d) ); + + FindClose(h); + } + + if ( hProfile != INVALID_HANDLE_VALUE ) + { + DWORD written = 0; + WriteFile(hProfile, L"\r\n", 2, &written, NULL); + CloseHandle(hProfile); + hProfile = INVALID_HANDLE_VALUE; + } + + if ( !m_plugins.GetSize() ) + ShowWindow(GetDlgItem(g_hwnd, IDC_NO_VIEW), SW_SHOW); +} + +void unloadMlPlugins() +{ + HANDLE hProfile = GetProfileFileHandle(1); + if ( hProfile != INVALID_HANDLE_VALUE ) + { + DWORD written = 0; + WriteFile(hProfile, L"\r\n", 2, &written, NULL); + } + + int i = m_plugins.GetSize(); + while ( i-- > 0 ) // reverse order to aid in not fucking up subclassing shit + { + winampMediaLibraryPlugin* mlplugin = (winampMediaLibraryPlugin*)m_plugins.Get(i); + wchar_t profile[MAX_PATH * 2] = { 0 }, filename[MAX_PATH] = { 0 }; + LARGE_INTEGER starttime, endtime; + if ( hProfile != INVALID_HANDLE_VALUE ) + { + GetModuleFileNameW(mlplugin->hDllInstance, filename, MAX_PATH); + QueryPerformanceCounter(&starttime); + } + + if ( mlplugin->quit ) + mlplugin->quit(); // deals with 'virtual' ml items on restart + + if ( hProfile != INVALID_HANDLE_VALUE && mlplugin->hDllInstance ) + { + QueryPerformanceCounter(&endtime); + + DWORD written = 0; + unsigned int ms = (UINT)((endtime.QuadPart - starttime.QuadPart) * 1000 / freq.QuadPart); + int len = swprintf(profile, L"Library\t%s\t[%s]\t%dms\r\n", filename, + (mlplugin->version >= MLHDR_VER_U ? (wchar_t*)mlplugin->description : AutoWide(mlplugin->description)), ms); + SetFilePointer(hProfile, NULL, NULL, FILE_END); + WriteFile(hProfile, profile, len * sizeof(wchar_t), &written, NULL); + } + + m_plugins.Del(i); + } + + if ( hProfile != INVALID_HANDLE_VALUE ) + { + DWORD written = 0; + WriteFile(hProfile, L"\r\n", 2, &written, NULL); + CloseHandle(hProfile); + hProfile = INVALID_HANDLE_VALUE; + } +} + +INT_PTR plugin_SendMessage(int message_type, INT_PTR param1, INT_PTR param2, INT_PTR param3) +{ + for ( int i = 0; i < m_plugins.GetSize(); i++ ) + { + winampMediaLibraryPlugin* mlplugin = (winampMediaLibraryPlugin*)m_plugins.Get(i); + if ( mlplugin && mlplugin->MessageProc ) + { + INT_PTR h = mlplugin->MessageProc(message_type, param1, param2, param3); + if ( h ) + return h; + } + } + return 0; +} diff --git a/Src/Plugins/General/gen_ml/png.rc b/Src/Plugins/General/gen_ml/png.rc new file mode 100644 index 00000000..1d14d459 --- /dev/null +++ b/Src/Plugins/General/gen_ml/png.rc @@ -0,0 +1,57 @@ +#include "resource.h" +///////////////////////////////////////////////////////////////////////////// +// +// Data +// +IDB_SORTARROW RCDATA +".\\resources\\sortarrow.png" +IDB_RATING RCDATA +".\\resources\\rating.png" +IDB_SPLITARROW RCDATA +".\\resources\\splitarrow.png" +IDB_SPLITARROW_PRESSED RCDATA +".\\resources\\splitarrow_pressed.png" +IDB_MENU_CHECKMARK RCDATA +".\\resources\\menu_check.png" +IDB_MENU_RADIOMARK RCDATA +".\\resources\\menu_dot.png" +IDB_MENU_EXPANDARROW RCDATA +".\\resources\\menu_arrow.png" +IDB_MENU_SCROLLARROW RCDATA +".\\resources\\menu_scrollarrow.png" +IDB_MENU_SCROLLARROW_DISABLED RCDATA +".\\resources\\menu_scrollarrow_disabled.png" +IDB_FILETYPE_AUDIO_SMALL RCDATA +".\\resources\\filetype_audio_16.png" +IDB_FILETYPE_AUDIO_LARGE RCDATA +".\\resources\\filetype_audio_32.png" +IDB_FILETYPE_VIDEO_SMALL RCDATA +".\\resources\\filetype_video_16.png" +IDB_FILETYPE_VIDEO_LARGE RCDATA +".\\resources\\filetype_video_32.png" +IDB_FILETYPE_UNKNOWN_SMALL RCDATA +".\\resources\\filetype_unknown_16.png" +IDB_FILETYPE_UNKNOWN_LARGE RCDATA +".\\resources\\filetype_unknown_32.png" +IDB_FILETYPE_PLAYLIST_SMALL RCDATA +".\\resources\\filetype_playlist_16.png" +IDB_FILETYPE_PLAYLIST_LARGE RCDATA +".\\resources\\filetype_playlist_32.png" +IDB_FILEVIEW_LIST RCDATA +".\\resources\\view_mode_list.png" +IDB_FILEVIEW_ICON RCDATA +".\\resources\\view_mode_icon.png" +IDB_FILEVIEW_DETAIL RCDATA +".\\resources\\view_mode_detail.png" +/* +IDB_CLOUD_IS_IN RCDATA +".\\resources\\cloud_16_incloud.png" +IDB_CLOUD_PARTIAL RCDATA +".\\resources\\cloud_16_partial.png" +IDB_CLOUD_UNAVAIL RCDATA +".\\resources\\cloud_16_unavail.png" +IDB_CLOUD_UPLOAD RCDATA +".\\resources\\cloud_16_upload.png" +IDB_CLOUD_UPLOADING RCDATA +".\\resources\\cloud_16_uploading.png" +*/ \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/prefs.cpp b/Src/Plugins/General/gen_ml/prefs.cpp new file mode 100644 index 00000000..786b950e --- /dev/null +++ b/Src/Plugins/General/gen_ml/prefs.cpp @@ -0,0 +1,787 @@ +#include "main.h" +#include "config.h" +#include "api__gen_ml.h" +#include "resource.h" +#include "./navigation.h" +#include "./ml_ipc_0313.h" + +#include +#include + +extern HNAVCTRL hNavigation; + +#define PASSWORD_MAXLEN 256 + +void encode_mimestr(char *in, char *out) +{ + char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + int shift = 0; + int accum = 0; + + while (in && *in) + { + if (*in) + { + accum <<= 8; + shift += 8; + accum |= *in++; + } + while (shift >= 6) + { + shift -= 6; + *out++ = alphabet[(accum >> shift) & 0x3F]; + } + } + + if (shift == 4) + { + *out++ = alphabet[(accum & 0xF) << 2]; + *out++ = '='; + } + + else if (shift == 2) + { + *out++ = alphabet[(accum & 0x3) << 4]; + *out++ = '='; + *out++ = '='; + } + *out++ = 0; +} + +int ResizeComboBoxDropDown(HWND hwndDlg, UINT id, const wchar_t * str, int width){ + SIZE size = {0}; + HWND control = GetDlgItem(hwndDlg, id); + HDC hdc = GetDC(control); + // get and select parent dialog's font so that it'll calculate things correctly + HFONT font = (HFONT)SendMessage(hwndDlg,WM_GETFONT,0,0), oldfont = (HFONT)SelectObject(hdc,font); + GetTextExtentPoint32W(hdc, str, lstrlenW(str)+1, &size); + + int ret = width; + if(size.cx > width) + { + SendDlgItemMessage(hwndDlg, id, CB_SETDROPPEDWIDTH, size.cx, 0); + ret = size.cx; + } + + SelectObject(hdc, oldfont); + ReleaseDC(control, hdc); + return ret; +} + +static void CenterPopup(HWND hwnd, HWND hCenter) +{ + if (NULL == hwnd || NULL == hCenter) + return; + + RECT centerRect, windowRect; + if (!GetWindowRect(hwnd, &windowRect) || !GetWindowRect(hCenter, ¢erRect)) + return; + windowRect.left = centerRect.left + ((centerRect.right - centerRect.left) - (windowRect.right - windowRect.left))/2; + windowRect.top = centerRect.top + ((centerRect.bottom - centerRect.top) - (windowRect.bottom - windowRect.top))/2; + + SetWindowPos(hwnd, NULL, windowRect.left, windowRect.top, 0, 0, SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOZORDER); +} + +static INT MessageBoxWA(HWND hwnd, LPCWSTR pszText, LPCWSTR pszCaption, UINT uType) +{ + WCHAR szText[128] = {0}, szCaption[2048] = {0}; + if (IS_INTRESOURCE(pszText)) + { + if (NULL != pszText) + { + WASABI_API_LNGSTRINGW_BUF((INT)(INT_PTR)pszText, szText, ARRAYSIZE(szText)); + pszText = szText; + } + } + if (IS_INTRESOURCE(pszCaption)) + { + if (NULL != pszCaption) + { + WASABI_API_LNGSTRINGW_BUF((INT)(INT_PTR)pszCaption, szCaption, ARRAYSIZE(szCaption)); + pszCaption = szCaption; + } + } + return MessageBoxW(hwnd, pszText, pszCaption, uType); +} + +static INT_PTR CALLBACK Prefs1Proc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch (uMsg) + { + case WM_INITDIALOG: + { + CheckDlgButton(hwndDlg, IDC_PLPLAYLIST, !!g_config->ReadInt(L"plplaymode", 1)); + CheckDlgButton(hwndDlg, IDC_VIEWPLAYMODE, !!g_config->ReadInt(L"viewplaymode", 1)); + + wchar_t* str = WASABI_API_LNGSTRINGW(IDS_PLAY_SELECTED); + SendDlgItemMessageW(hwndDlg, IDC_COMBO1, CB_ADDSTRING, 0, (LPARAM)str); + int width = ResizeComboBoxDropDown(hwndDlg, IDC_COMBO1, str, 0); + SendDlgItemMessageW(hwndDlg, IDC_COMBO1, CB_ADDSTRING, 0, (LPARAM)(str = WASABI_API_LNGSTRINGW(IDS_ENQUEUE_SELECTED))); + ResizeComboBoxDropDown(hwndDlg, IDC_COMBO1, str, width); + SendDlgItemMessage(hwndDlg, IDC_COMBO1, CB_SETCURSEL, g_config->ReadInt(L"enqueuedef", 0) ? 1 : 0, 0); + CheckDlgButton(hwndDlg, IDC_CHECK1, !!g_config->ReadInt(L"attachlbolt", 0)); + CheckDlgButton(hwndDlg, IDC_PL_SEND_TO, !!g_config->ReadInt(L"pl_send_to", 1)); + CheckDlgButton(hwndDlg, IDC_PMP_SEND_TO, !!g_config->ReadInt(L"pmp_send_to", 1)); + if (!GetModuleHandleW(L"ml_pmp.dll")) EnableWindow(GetDlgItem(hwndDlg, IDC_PMP_SEND_TO), FALSE); + CheckDlgButton(hwndDlg, IDC_WRITE_RATINGS, !!g_config->ReadInt(L"writeratings", 0)); + CheckDlgButton(hwndDlg, IDC_GROUP_BUTTONS, !!g_config->ReadInt(L"groupbtn", 1)); + + int colresmode = g_config->ReadInt(L"column_resize_mode", 0); + width = 0; + SendDlgItemMessageW(hwndDlg, IDC_COMBO2, CB_ADDSTRING, 0, (LPARAM)(str = WASABI_API_LNGSTRINGW(IDS_COL_NORMAL))); + width = ResizeComboBoxDropDown(hwndDlg, IDC_COMBO2, str, width); + SendDlgItemMessageW(hwndDlg, IDC_COMBO2, CB_ADDSTRING, 0, (LPARAM)(str = WASABI_API_LNGSTRINGW(IDS_COL_SELONLY))); + ResizeComboBoxDropDown(hwndDlg, IDC_COMBO2, str, width); + SendDlgItemMessageW(hwndDlg, IDC_COMBO2, CB_ADDSTRING, 0, (LPARAM)(str = WASABI_API_LNGSTRINGW(IDS_COL_PROP))); + ResizeComboBoxDropDown(hwndDlg, IDC_COMBO2, str, 0); + SendDlgItemMessageW(hwndDlg, IDC_COMBO2, CB_SETCURSEL, colresmode, 0); + } + break; + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDC_COMBO1: + if (HIWORD(wParam) == CBN_SELCHANGE) + { + int a = (INT)SendDlgItemMessage(hwndDlg, IDC_COMBO1, CB_GETCURSEL, 0, 0), + mode = g_config->ReadInt(L"enqueuedef", 0); + if ((a == 1) != mode) + { + mode = (a == 1); + g_config->WriteInt(L"enqueuedef", mode); + HWND hView = (HWND)SENDMLIPC(g_hwnd, ML_IPC_GETCURRENTVIEW, 0); + if (IsWindow(hView)) PostMessageW(hView, WM_DISPLAYCHANGE, 0, 0); + } + + // v5.66+ - send a general notification so plug-ins can (if needed update on + // the play / enqueue mode change (mainly added for ml_playlists) + pluginMessage p = {ML_MSG_VIEW_PLAY_ENQUEUE_CHANGE, (INT_PTR)!!mode, + (INT_PTR)!!g_config->ReadInt(L"groupbtn", 1), 0}; + SENDMLIPC(g_hwnd, ML_IPC_SEND_PLUGIN_MESSAGE, (WPARAM)&p); + } + break; + case IDC_PL_SEND_TO: + g_config->WriteInt(L"pl_send_to", !!IsDlgButtonChecked(hwndDlg, IDC_PL_SEND_TO)); + break; + case IDC_PMP_SEND_TO: + g_config->WriteInt(L"pmp_send_to", !!IsDlgButtonChecked(hwndDlg, IDC_PMP_SEND_TO)); + break; + case IDC_WRITE_RATINGS: + g_config->WriteInt(L"writeratings", !!IsDlgButtonChecked(hwndDlg, IDC_WRITE_RATINGS)); + break; + case IDC_PLPLAYLIST: + g_config->WriteInt(L"plplaymode", !!IsDlgButtonChecked(hwndDlg, IDC_PLPLAYLIST)); + break; + case IDC_VIEWPLAYMODE: + g_config->WriteInt(L"viewplaymode", !!IsDlgButtonChecked(hwndDlg, IDC_VIEWPLAYMODE)); + break; + case IDC_COMBO2: + if (HIWORD(wParam) == CBN_SELCHANGE) + { + int colresmode = (INT)SendDlgItemMessage(hwndDlg, IDC_COMBO2, CB_GETCURSEL, 0, 0); + g_config->WriteInt(L"column_resize_mode", colresmode); + } + break; + case IDC_CHECK1: + g_config->WriteInt(L"attachlbolt", !!IsDlgButtonChecked(hwndDlg, IDC_CHECK1)); + break; + case IDC_GROUP_BUTTONS: + { + g_config->WriteInt(L"groupbtn", !!IsDlgButtonChecked(hwndDlg, IDC_GROUP_BUTTONS)); + // refresh the current view as otherwise is a pain to dynamically update as needed + PostMessage(g_hwnd, WM_USER + 30, 0, 0); + break; + } + } + break; + } + return 0; +} + +static INT_PTR CALLBACK GetTVPassProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch (uMsg) + { + case WM_INITDIALOG: + { + PostMessage(hwndDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hwndDlg, IDC_GPASS), TRUE); + } + break; + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDOK: + { + char pass1[64] = {0}; + char password[4096] = {0}; + GetDlgItemTextA(hwndDlg, IDC_GPASS, pass1, sizeof(pass1) - 1); + encode_mimestr(pass1, password); + if (strcmp(password, g_config->ReadString("stctka", "none")) != 0) + { + wchar_t titleStr[32] = {0}; + MessageBoxW(NULL, + WASABI_API_LNGSTRINGW(IDS_INVALID_PASSWORD), + WASABI_API_LNGSTRINGW_BUF(IDS_INTERNET_ACCESS,titleStr,32), + MB_OK); + EndDialog(hwndDlg, 0); + break; + } + else + { + EndDialog(hwndDlg, 1); + break; + } + + } + case IDCANCEL: + { + EndDialog(hwndDlg, 0); + break; + } + } + break; + } + return 0; +} + +static INT_PTR InternetPassword_OnInitDialog(HWND hwnd, HWND hFocus, LPARAM lParam) +{ + HWND hControl = GetDlgItem(hwnd, IDC_EDIT_PASSWORD); + if (NULL != hControl) + PostMessage(hwnd, WM_NEXTDLGCTL, (WPARAM)hControl, TRUE); + + hControl = GetDlgItem(hwnd, IDOK); + if (NULL != hControl) + EnableWindow(hControl, FALSE); + + HWND hCenter = (HWND)lParam; + if (NULL != hCenter && IsWindow(hCenter)) + CenterPopup(hwnd, hCenter); + + SendMessage(hwnd, DM_REPOSITION, 0, 0L); + return FALSE; +} + +static BOOL InternetPassword_GetPassword(HWND hwnd, LPWSTR pszBuffer, INT cchBufferMax) +{ + + HWND hEdit1 = GetDlgItem(hwnd, IDC_EDIT_PASSWORD); + HWND hEdit2 = GetDlgItem(hwnd, IDC_EDIT_PASSWORD_VERIFY); + if (NULL != hEdit1 && NULL != hEdit2) + { + WCHAR szPwd1[PASSWORD_MAXLEN] = {0}, szPwd2[PASSWORD_MAXLEN] = {0}; + + INT cchPwd1 = GetWindowTextW(hEdit1, szPwd1, ARRAYSIZE(szPwd1)); + INT cchPwd2 = GetWindowTextW(hEdit2, szPwd2, ARRAYSIZE(szPwd2)); + + if (0 != cchPwd1 && + cchPwd1 == cchPwd2 && + CSTR_EQUAL == CompareStringW(LOCALE_USER_DEFAULT, 0, szPwd1, cchPwd1, szPwd2, cchPwd2)) + { + if (NULL != pszBuffer && cchBufferMax > 0) + { + if (FAILED(StringCchCopyW(pszBuffer, cchBufferMax, szPwd1))) + return FALSE; + } + return TRUE; + } + } + return FALSE; +} + +static void InternetPassword_OnCommand(HWND hwnd, INT commandId, INT eventId, HWND hControl) +{ + switch (commandId) + { + case IDC_EDIT_PASSWORD: + case IDC_EDIT_PASSWORD_VERIFY: + switch(eventId) + { + case EN_UPDATE: + + HWND hButton = GetDlgItem(hwnd, IDOK); + if (NULL != hButton) + { + EnableWindow(hButton, InternetPassword_GetPassword(hwnd, NULL, 0)); + } + break; + } + break; + case IDOK: + { + WCHAR szPassword[PASSWORD_MAXLEN] = {0}; + BOOL passwordOk = InternetPassword_GetPassword(hwnd, szPassword, ARRAYSIZE(szPassword)); + + if (FALSE == passwordOk) + { + MessageBoxWA(hwnd, MAKEINTRESOURCEW(IDS_PASSWORD_NO_MATCH), MAKEINTRESOURCEW(IDS_INTERNET_ACCESS), + MB_OK | MB_ICONEXCLAMATION); + } + else + { + char szPasswordAnsi[PASSWORD_MAXLEN * 2] = {0}; + BOOL fInvalidChar; + if (0 == WideCharToMultiByte(CP_ACP, 0, szPassword, -1, szPasswordAnsi, ARRAYSIZE(szPasswordAnsi), NULL, &fInvalidChar) || + FALSE != fInvalidChar) + { + // TODO: put better error description + LPCWSTR pszMessage = MAKEINTRESOURCEW(IDS_INVALID_PASSWORD); + MessageBoxWA(hwnd, pszMessage, MAKEINTRESOURCEW(IDS_INTERNET_ACCESS), MB_OK | MB_ICONERROR); + + } + else + { + char szEncoded[PASSWORD_MAXLEN * 2] = {0}; + encode_mimestr(szPasswordAnsi, szEncoded); + g_config->WriteString("stctka", szEncoded); + EndDialog(hwnd, IDOK); + } + } + break; + } + case IDCANCEL: + EndDialog(hwnd, IDCANCEL); + break; + } +} + +static INT_PTR CALLBACK InternetPassword_DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch (uMsg) + { + case WM_INITDIALOG: return InternetPassword_OnInitDialog(hwnd, (HWND)wParam, lParam); + case WM_COMMAND: InternetPassword_OnCommand(hwnd, LOWORD(wParam), HIWORD(wParam), (HWND)lParam); break; + } + return 0; +} + +static INT_PTR CALLBACK Prefs2Proc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch (uMsg) + { + case WM_INITDIALOG: + { + int passprompt = g_config->ReadInt(L"tvpp", 0); + if (passprompt && strcmp("none", g_config->ReadString("stctka", "none")) == 0) + { + wchar_t titleStr[32] = {0}; + MessageBoxW(hwndDlg, + WASABI_API_LNGSTRINGW(IDS_RATINGS_PASSWORD_MISSING), + WASABI_API_LNGSTRINGW_BUF(IDS_SERCURITY_ALERT,titleStr,32), + MB_OK); + passprompt = 0; + } + + CheckDlgButton(hwndDlg, IDC_CHECK_PASSPROMPT, passprompt); + EnableWindow(GetDlgItem(hwndDlg, IDC_ITV_CHANGEPASS), passprompt); + + { + int rating = g_config->ReadInt(L"tvrating", 7); + CheckDlgButton(hwndDlg, IDC_CHECK_RATING0, !!(rating&1) || !!(rating&2)); + CheckDlgButton(hwndDlg, IDC_CHECK_RATING1, !!(rating&4)); + CheckDlgButton(hwndDlg, IDC_CHECK_RATING2, !!(rating&8)); + CheckDlgButton(hwndDlg, IDC_CHECK_RATING3, !!(rating&16) || !!(rating&32)); + CheckDlgButton(hwndDlg, IDC_CHECK_RATING4, !!(rating&64)); + } + + if (passprompt && strcmp("none", g_config->ReadString("stctka", "none")) != 0) + { + INT_PTR result = WASABI_API_DIALOGBOX(IDD_PREFS_ITV_GETPASS, hwndDlg, GetTVPassProc); + if (!result) + { + HWND next = GetWindow(hwndDlg,GW_CHILD); + HWND warning = GetDlgItem(hwndDlg,IDC_STATIC_INFO); + while(IsWindow(next)) + { + if(next != warning) + { + HWND remove = next; + next = GetWindow(next, GW_HWNDNEXT); + DestroyWindow(remove); + } + else + { + next = GetWindow(next, GW_HWNDNEXT); + } + } + break; + } + else + { + DestroyWindow(GetDlgItem(hwndDlg,IDC_STATIC_INFO)); + } + } + else + { + DestroyWindow(GetDlgItem(hwndDlg,IDC_STATIC_INFO)); + } + } + break; + + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDC_CHECK_PASSPROMPT: + { + int passprompt = 0; + if (IsDlgButtonChecked(hwndDlg, IDC_CHECK_PASSPROMPT)) passprompt = 1; + else g_config->WriteString("stctka", 0); + EnableWindow(GetDlgItem(hwndDlg, IDC_ITV_CHANGEPASS), passprompt); + break; + } + case IDC_ITV_CHANGEPASS: + { + WASABI_API_DIALOGBOXPARAMW(IDD_PREFS_ITV_ASSIGNPASS, hwndDlg, InternetPassword_DialogProc, (LPARAM)hwndDlg); + break; + } + } + break; + + case WM_DESTROY: + { + if(!IsWindow(GetDlgItem(hwndDlg,IDC_STATIC_INFO))) + { + int rating = 0; + if (IsDlgButtonChecked(hwndDlg, IDC_CHECK_RATING0)) rating |= (1 | 2); + if (IsDlgButtonChecked(hwndDlg, IDC_CHECK_RATING1)) rating |= 4; + if (IsDlgButtonChecked(hwndDlg, IDC_CHECK_RATING2)) rating |= 8; + if (IsDlgButtonChecked(hwndDlg, IDC_CHECK_RATING3)) rating |= (16 | 32); + if (IsDlgButtonChecked(hwndDlg, IDC_CHECK_RATING4)) rating |= 64; + g_config->WriteInt(L"tvrating", rating); + int passprompt = 0; + if (IsDlgButtonChecked(hwndDlg, IDC_CHECK_PASSPROMPT)) passprompt = 1; + g_config->WriteInt(L"tvpp", passprompt); + if (passprompt) + { + if (strcmp("none", g_config->ReadString("stctka", "none")) == 0) + { + WASABI_API_DIALOGBOX(IDD_PREFS_ITV_ASSIGNPASS, hwndDlg, InternetPassword_DialogProc); + } + } +#if 0 // no radio + radio_updateTvView(1); +#endif + } + } + break; + } + return 0; +} + +C_Config *g_view_metaconf = NULL; +static UINT msgNotify = 0; +static HINSTANCE cloud_hinst; +static int IPC_GET_CLOUD_HINST = -1, last_pos; +static INT_PTR CALLBACK Prefs3Proc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + static int edit_inited; + + switch (uMsg) + { + case WM_INITDIALOG: + { + wchar_t buffer[MAX_PATH] = {0}; + edit_inited = 0; + + LPCWSTR pszPath = (LPCWSTR)SendMessageW(plugin.hwndParent, WM_WA_IPC, 0, IPC_GETPLUGINDIRECTORYW); + if(PathCombineW(buffer, pszPath, L"ml_disc.dll") && PathFileExistsW(buffer)) + { + PathCombineW(buffer, WINAMP_INI_DIR, L"Plugins\\ml\\cdrom.vmd"); + g_view_metaconf = new C_Config(buffer); + + CheckDlgButton(hwndDlg, IDC_SHOW_EJECT_ICONS, g_view_metaconf->ReadInt(L"showeject", 1)); + CheckDlgButton(hwndDlg, IDC_GROUP_DRIVES, g_view_metaconf->ReadInt(L"showparent", 0)); + if(!msgNotify) msgNotify = RegisterWindowMessageW(L"ripburn_nav_update"); + } + else + { + DestroyWindow(GetDlgItem(hwndDlg,IDC_CD_DVD_ITEMS)); + DestroyWindow(GetDlgItem(hwndDlg,IDC_SHOW_EJECT_ICONS)); + DestroyWindow(GetDlgItem(hwndDlg,IDC_GROUP_DRIVES)); + RECT r; + HWND frame = GetDlgItem(hwndDlg,IDC_ML_TREE_OPTS); + GetWindowRect(frame, &r); + SetWindowPos(frame, 0, 0, 0, (r.right - r.left), (r.bottom - r.top) - 72, + SWP_NOZORDER|SWP_NOMOVE|SWP_NOACTIVATE); + } + + CheckDlgButton(hwndDlg, IDC_SHOW_ICONS, !!g_config->ReadInt(L"Navigation_ShowIcons", 1)); + CheckDlgButton(hwndDlg, IDC_HIGHLIGHT_FULL_TREE_ITEM, !!g_config->ReadInt(L"Navigation_FullRowSel", 1)); + CheckDlgButton(hwndDlg, IDC_RCLICK_TO_SELECT, g_config->ReadInt(L"Navigation_MouseDownSel", 0)); + SetDlgItemInt(hwndDlg, IDC_ITEM_HEIGHT, g_config->ReadInt(L"Navigation_ItemHeight", 18), 0); + + CheckDlgButton(hwndDlg, IDC_PARENT_PODCASTS, g_config->ReadInt(L"podcast_parent", 0)); + + /*if (IPC_GET_CLOUD_HINST == -1) IPC_GET_CLOUD_HINST = (INT)SendMessage(plugin.hwndParent, WM_WA_IPC, (WPARAM)&"WinampCloud", IPC_REGISTER_WINAMP_IPCMESSAGE); + if (!cloud_hinst) cloud_hinst = (HINSTANCE)SendMessage(plugin.hwndParent, WM_WA_IPC, 0, IPC_GET_CLOUD_HINST); + + SendDlgItemMessageW(hwndDlg, IDC_TRANSFERS_COMBO, CB_SETITEMDATA, SendDlgItemMessageW(hwndDlg, IDC_TRANSFERS_COMBO, CB_ADDSTRING, 0, (LPARAM)L"as a top level tree item (default)"), 0); + if (cloud_hinst && cloud_hinst != (HINSTANCE)1) + SendDlgItemMessageW(hwndDlg, IDC_TRANSFERS_COMBO, CB_SETITEMDATA, SendDlgItemMessageW(hwndDlg, IDC_TRANSFERS_COMBO, CB_ADDSTRING, 0, (LPARAM)L"under the 'Cloud Library' item"), 1); + SendDlgItemMessageW(hwndDlg, IDC_TRANSFERS_COMBO, CB_SETITEMDATA, SendDlgItemMessageW(hwndDlg, IDC_TRANSFERS_COMBO, CB_ADDSTRING, 0, (LPARAM)L"under the 'Devices' item"), 2); + + last_pos = g_config->ReadInt(L"txviewpos", 0); + int i = 0, count = SendDlgItemMessageW(hwndDlg, IDC_TRANSFERS_COMBO, CB_GETCOUNT, 0, 0); + for (; i < count; i++) + { + int item = SendDlgItemMessageW(hwndDlg, IDC_TRANSFERS_COMBO, CB_GETITEMDATA, i, 0); + if (item == last_pos) + { + SendDlgItemMessageW(hwndDlg, IDC_TRANSFERS_COMBO, CB_SETCURSEL, i, 0); + break; + } + } + if (i == count) + SendDlgItemMessageW(hwndDlg, IDC_TRANSFERS_COMBO, CB_SETCURSEL, 0, 0);*/ + + edit_inited = 1; + } + break; + + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDC_SHOW_ICONS: + g_config->WriteInt(L"Navigation_ShowIcons", !!IsDlgButtonChecked(hwndDlg, LOWORD(wParam))); + NavCtrlI_UpdateLook(hNavigation); + // tell ml_disc to update if present + if(g_view_metaconf)SendMessage(plugin.hwndParent, msgNotify, 0, 0); + break; + + case IDC_HIGHLIGHT_FULL_TREE_ITEM: + g_config->WriteInt(L"Navigation_FullRowSel", !!IsDlgButtonChecked(hwndDlg, LOWORD(wParam))); + NavCtrlI_UpdateLook(hNavigation); + break; + + case IDC_RCLICK_TO_SELECT: + g_config->WriteInt(L"Navigation_MouseDownSel", !!IsDlgButtonChecked(hwndDlg, LOWORD(wParam))); + NavCtrlI_UpdateLook(hNavigation); + break; + + case IDC_ITEM_HEIGHT: + if(HIWORD(wParam) == EN_CHANGE && edit_inited) + { + BOOL success = 0; + UINT val = GetDlgItemInt(hwndDlg, LOWORD(wParam), &success, 0); + if(success) + { + g_config->WriteInt(L"Navigation_ItemHeight", val); + NavCtrlI_UpdateLook(hNavigation); + } + } + break; + + case IDC_SHOW_EJECT_ICONS: + g_view_metaconf->WriteInt(L"showeject", !!IsDlgButtonChecked(hwndDlg, LOWORD(wParam))); + SendMessage(plugin.hwndParent, msgNotify, 0, 0); + NavCtrlI_UpdateLook(hNavigation); + break; + + case IDC_GROUP_DRIVES: + g_view_metaconf->WriteInt(L"showparent", !!IsDlgButtonChecked(hwndDlg, LOWORD(wParam))); + SendMessage(plugin.hwndParent, msgNotify, 1, 0); + NavCtrlI_UpdateLook(hNavigation); + break; + + case IDC_PARENT_PODCASTS: + { + int parent = !!IsDlgButtonChecked(hwndDlg, LOWORD(wParam)); + g_config->WriteInt(L"podcast_parent", parent); + + pluginMessage p = {ML_MSG_DOWNLOADS_VIEW_POSITION, (INT_PTR)parent, (INT_PTR)1, 0}; + SENDMLIPC(g_hwnd, ML_IPC_SEND_PLUGIN_MESSAGE, (WPARAM)&p); + NavCtrlI_UpdateLook(hNavigation); + break; + } + case IDC_TRANSFERS_COMBO: + if (HIWORD(wParam) == CBN_SELCHANGE) + { + int a = (INT)SendDlgItemMessage(hwndDlg, IDC_TRANSFERS_COMBO, CB_GETCURSEL, 0, 0); + if (a != CB_ERR) + { + g_config->WriteInt(L"txviewpos", SendDlgItemMessageW(hwndDlg, IDC_TRANSFERS_COMBO, CB_GETITEMDATA, a, 0)); + wchar_t titleStr[32] = {0}; + if ((a != last_pos) && + MessageBoxW(hwndDlg, WASABI_API_LNGSTRINGW(IDS_RESTART_MESSAGE), + WASABI_API_LNGSTRINGW_BUF(IDS_RESTART, titleStr, 32), + MB_ICONQUESTION | MB_YESNO) == IDYES) + { + WritePrivateProfileStringW(L"winamp", L"show_prefs", L"-1", WINAMP_INI); + PostMessage(plugin.hwndParent, WM_WA_IPC, 0, IPC_RESTARTWINAMP); + } + } + } + break; + } + break; + + case WM_DESTROY: + if(g_view_metaconf) + { + delete(g_view_metaconf); + g_view_metaconf = 0; + } + break; + } + return 0; +} + +static INT_PTR CALLBACK Prefs4Proc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch (uMsg) + { + case WM_INITDIALOG: + { + CheckDlgButton(hwndDlg, IDC_CHECK_USEPLFONT, !!g_config->ReadInt(L"plfont_everywhere", 1)); + CheckDlgButton(hwndDlg, IDC_FF_SCROLLBARS, config_use_ff_scrollbars); + CheckDlgButton(hwndDlg, IDC_ALTERNATEITEMS, config_use_alternate_colors); + CheckDlgButton(hwndDlg, IDC_SKINNED_MENUS, IsSkinnedPopupEnabled(FALSE)); + CheckDlgButton(hwndDlg, IDC_GENO, !!GetPrivateProfileIntW(L"winamp", L"geno", 1, WINAMP_INI)); + } + break; + + case WM_COMMAND: + switch (LOWORD(wParam)) + { + case IDC_CHECK_USEPLFONT: + g_config->WriteInt(L"plfont_everywhere", !!IsDlgButtonChecked(hwndDlg, IDC_CHECK_USEPLFONT)); + PostMessage(plugin.hwndParent, WM_DISPLAYCHANGE, 0, 0); + break; + case IDC_FF_SCROLLBARS: + config_use_ff_scrollbars = !!IsDlgButtonChecked(hwndDlg, IDC_FF_SCROLLBARS); + g_config->WriteInt(L"ffsb", config_use_ff_scrollbars); + PostMessage(plugin.hwndParent, WM_DISPLAYCHANGE, 0, 0); + break; + case IDC_ALTERNATEITEMS: + config_use_alternate_colors = !!IsDlgButtonChecked(hwndDlg, IDC_ALTERNATEITEMS); + g_config->WriteInt(L"alternate_items", config_use_alternate_colors); + PostMessage(plugin.hwndParent, WM_DISPLAYCHANGE, 0, 0); + break; + case IDC_SKINNED_MENUS: + EnableSkinnedPopup(BST_CHECKED == IsDlgButtonChecked(hwndDlg, IDC_SKINNED_MENUS)); + break; + case IDC_GENO: + { + wchar_t buf[64] = {L"1"}; + StringCchPrintfW(buf, 64, L"%d", !!IsDlgButtonChecked(hwndDlg, IDC_GENO)); + WritePrivateProfileStringW(L"winamp", L"geno", buf, WINAMP_INI); + break; + } + case IDC_RATING_COLUMN: + SendMessage(g_hwnd,WM_COMMAND,MAKEWPARAM(ID_SHOW_RATINGTWEAK,0),0); + break; + } + break; + } + return 0; +} + +HWND subWnd = 0, prefsWnd = 0; + +static void _dosetsel(HWND hwndDlg) +{ + HWND tabwnd = GetDlgItem(hwndDlg, IDC_TAB1); + int sel = TabCtrl_GetCurSel(tabwnd); + + if (sel >= 0 && (sel != g_config->ReadInt(L"lastprefp", 0) || !subWnd)) + { + g_config->WriteInt(L"lastprefp", sel); + if (subWnd) DestroyWindow(subWnd); + subWnd = 0; + + UINT t = 0; + DLGPROC p; + switch (sel) + { + case 0: t = IDD_PREFS1; p = Prefs1Proc; break; + case 1: t = IDD_PREFS2; p = Prefs2Proc; break; + case 2: t = IDD_PREFS3; p = Prefs3Proc; break; + case 3: t = IDD_PREFS4; p = Prefs4Proc; break; + } + if (t) subWnd = WASABI_API_CREATEDIALOGW(t, hwndDlg, p); + + if (subWnd) + { + RECT r; + GetClientRect(tabwnd, &r); + TabCtrl_AdjustRect(tabwnd, FALSE, &r); + SetWindowPos(subWnd, HWND_TOP, r.left, r.top, r.right - r.left, r.bottom - r.top, SWP_NOACTIVATE); + ShowWindow(subWnd, SW_SHOWNA); + } + + if (!SendMessage(plugin.hwndParent,WM_WA_IPC,IPC_ISWINTHEMEPRESENT,IPC_USE_UXTHEME_FUNC)) + { + SendMessage(plugin.hwndParent,WM_WA_IPC,(WPARAM)tabwnd,IPC_USE_UXTHEME_FUNC); + SendMessage(plugin.hwndParent,WM_WA_IPC,(WPARAM)subWnd,IPC_USE_UXTHEME_FUNC); + } + } +} + +#define TabCtrl_InsertItemW(hwnd, iItem, pitem) \ + (int)SNDMSG((hwnd), TCM_INSERTITEMW, (WPARAM)(int)(iItem), (LPARAM)(const TC_ITEMW *)(pitem)) + +// frame proc +INT_PTR CALLBACK PrefsProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch (uMsg) + { + case WM_INITDIALOG: + { + TCITEMW item; + HWND tabwnd = GetDlgItem(hwndDlg, IDC_TAB1); + item.mask = TCIF_TEXT; + item.pszText = WASABI_API_LNGSTRINGW(IDS_LIBRARY_OPTIONS); + TabCtrl_InsertItemW(tabwnd, 0, &item); + item.pszText = WASABI_API_LNGSTRINGW(IDS_ONLINE_MEDIA); + TabCtrl_InsertItemW(tabwnd, 1, &item); + item.pszText = WASABI_API_LNGSTRINGW(IDS_TREE_OPTIONS); + TabCtrl_InsertItemW(tabwnd, 2, &item); + item.pszText = WASABI_API_LNGSTRINGW(IDS_APPEARANCE); + TabCtrl_InsertItemW(tabwnd, 3, &item); + TabCtrl_SetCurSel(tabwnd, g_config->ReadInt(L"lastprefp", 0)); + _dosetsel(hwndDlg); + + prefsWnd = hwndDlg; + } + return 0; + case WM_NOTIFY: + { + LPNMHDR p = (LPNMHDR) lParam; + if (p->idFrom == IDC_TAB1 && p->code == TCN_SELCHANGE) _dosetsel(hwndDlg); + } + return 0; + case WM_DESTROY: + subWnd = NULL; + prefsWnd = NULL; + return 0; + } + return 0; +} + +void refreshPrefs(INT_PTR screen) +{ + if (subWnd && g_config->ReadInt(L"lastprefp", -1) == screen) + { + if (screen == 4) SendMessage(subWnd, WM_INITDIALOG, 0, 0); + } +} + +extern prefsDlgRecW myPrefsItem; + +void openPrefs(INT_PTR screen) +{ + if (!subWnd) + { + if (screen != -1) g_config->WriteInt(L"lastprefp", (INT)screen); + } + else + { + if (screen != -1) + { + HWND tabwnd = GetDlgItem(prefsWnd, IDC_TAB1); + TabCtrl_SetCurSel(tabwnd, screen); + _dosetsel(prefsWnd); + } + } + SendMessage(plugin.hwndParent, WM_WA_IPC, (WPARAM)&myPrefsItem, IPC_OPENPREFSTOPAGE); +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/reflectmsg.cpp b/Src/Plugins/General/gen_ml/reflectmsg.cpp new file mode 100644 index 00000000..984db922 --- /dev/null +++ b/Src/Plugins/General/gen_ml/reflectmsg.cpp @@ -0,0 +1,188 @@ +#include "main.h" +#include "./reflectmsg.h" +#include "./skinnedwnd.h" +#include "./skinnedmenu.h" +#include "./skinnedmenuthreadinfo.h" + + + +BOOL CanReflect(UINT uMsg) +{ + switch(uMsg) + { + case WM_DRAWITEM: + case WM_NOTIFY: + case WM_COMMAND: + case WM_CTLCOLORBTN: + case WM_CTLCOLOREDIT: + case WM_CTLCOLORLISTBOX: + case WM_CTLCOLORSCROLLBAR: + case WM_CTLCOLORSTATIC: + case WM_MEASUREITEM: + return TRUE; + } + return FALSE; +} + +BOOL ReflectMessage(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL bDialog, LRESULT *pResult) +{ + HWND hwndReflect = NULL; + + switch(uMsg) + { + case WM_DRAWITEM: + hwndReflect = ((LPDRAWITEMSTRUCT)lParam)->hwndItem; + if (NULL != hwndReflect && ODT_MENU == ((LPDRAWITEMSTRUCT)lParam)->CtlType) + hwndReflect = SkinnedMenu::WindowFromHandle((HMENU)hwndReflect); + break; + case WM_NOTIFY: + hwndReflect = ((LPNMHDR)lParam)->hwndFrom; + break; + case WM_COMMAND: + case WM_CTLCOLORBTN: + case WM_CTLCOLOREDIT: + case WM_CTLCOLORLISTBOX: + case WM_CTLCOLORSCROLLBAR: + case WM_CTLCOLORSTATIC: + hwndReflect =(HWND)lParam; + break; + case WM_MEASUREITEM: + if (0 == wParam && ODT_MENU == ((MEASUREITEMSTRUCT*)lParam)->CtlType) + { + SkinnedMenuThreadInfo *threadInfo; + if (S_OK == SkinnedMenuThreadInfo::GetInstance(FALSE, &threadInfo)) + { + hwndReflect = SkinnedMenu::WindowFromHandle(threadInfo->GetActiveMeasureMenu()); + threadInfo->Release(); + } + } + break; + } + // make sure that hwndReflect is a valid hwnd otherwise it can incorrectly lose valid messages + // when sent as custom version of normally valid ones ie faked wm_command messages where lparam is -1 + if (NULL != hwndReflect && + hwnd != hwndReflect && + IsWindow(hwndReflect)) + { + REFLECTPARAM rParam; + rParam.result = 0; + rParam.lParam = lParam; + rParam.hwndFrom = hwnd; + + if (REFLECTMESSAGE(hwndReflect, uMsg, wParam, (LPARAM)&rParam)) + { + *pResult = rParam.result; + if (bDialog) + { + switch(uMsg) + { + case WM_CTLCOLORBTN: + case WM_CTLCOLOREDIT: + case WM_CTLCOLORLISTBOX: + case WM_CTLCOLORSCROLLBAR: + case WM_CTLCOLORSTATIC: + return TRUE; + } + SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, (LONGX86)(LONG_PTR)*pResult); + *pResult = TRUE; + } + return TRUE; + } + } + *pResult = 0; + return FALSE; +} + + +typedef struct __REFLECTORWND +{ + BOOL bUnicode; + BOOL bDialog; + WNDPROC windowProc; +}REFLECTORWND; + +static ATOM REFLECTOR = 0; + +static LRESULT CALLBACK Reflector_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + +HRESULT InstallReflector(HWND hwnd) +{ + if (0 == REFLECTOR) + { + REFLECTOR = GlobalAddAtomW(L"WAREFLECTOR"); + if (0 == REFLECTOR) return E_UNEXPECTED; + } + + if (NULL == hwnd || !IsWindow(hwnd)) + return E_INVALIDARG; + + if (NULL != GetPropW(hwnd, (LPCWSTR)MAKEINTATOM(REFLECTOR)) || + NULL != SkinnedWnd::GetFromHWND(hwnd)) + return S_FALSE; + + REFLECTORWND *prw = (REFLECTORWND*)calloc(1, sizeof(REFLECTORWND)); + if (NULL == prw) + return E_OUTOFMEMORY; + + ZeroMemory(prw, sizeof(REFLECTORWND)); + + prw->bUnicode = IsWindowUnicode(hwnd); + prw->windowProc = (WNDPROC)(LONG_PTR)SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (LONGX86)(LONG_PTR)Reflector_WindowProc); + + if (NULL == prw->windowProc || + !SetPropW(hwnd, (LPCWSTR)MAKEINTATOM(REFLECTOR), prw)) + { + RemoveReflector(hwnd); + return E_FAIL; + } + + return S_OK; +} + +BOOL RemoveReflector(HWND hwnd) +{ + if (!hwnd || !IsWindow(hwnd)) + return FALSE; + + REFLECTORWND *prw = (REFLECTORWND*)GetPropW(hwnd, (LPCWSTR)MAKEINTATOM(REFLECTOR)); + RemovePropW(hwnd, (LPCWSTR)MAKEINTATOM(REFLECTOR)); + + if (NULL == prw) + return FALSE; + + if (prw->windowProc) + SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (LONGX86)(LONG_PTR)prw->windowProc); + + free(prw); + return TRUE; + +} + +static LRESULT CALLBACK Reflector_WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + REFLECTORWND *prw = (REFLECTORWND*)GetPropW(hwnd, (LPCWSTR)MAKEINTATOM(REFLECTOR)); + if (NULL == prw || NULL == prw->windowProc) + { + return (IsWindowUnicode(hwnd)) ? + DefWindowProcW(hwnd, uMsg, wParam, lParam) : + DefWindowProcA(hwnd, uMsg, wParam, lParam); + } + + if (WM_DESTROY == uMsg) + { + BOOL unicode = prw->bUnicode; + WNDPROC windowProc = prw->windowProc; + RemoveReflector(hwnd); + return (unicode) ? + CallWindowProcW(windowProc, hwnd, uMsg, wParam, lParam) : + CallWindowProcA(windowProc, hwnd, uMsg, wParam, lParam); + } + + LRESULT result; + if (ReflectMessage(hwnd, uMsg, wParam, lParam, prw->bDialog, &result)) + return result; + + return (prw->bUnicode) ? + CallWindowProcW(prw->windowProc, hwnd, uMsg, wParam, lParam) : + CallWindowProcA(prw->windowProc, hwnd, uMsg, wParam, lParam); +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/reflectmsg.h b/Src/Plugins/General/gen_ml/reflectmsg.h new file mode 100644 index 00000000..1e7224c6 --- /dev/null +++ b/Src/Plugins/General/gen_ml/reflectmsg.h @@ -0,0 +1,50 @@ +#ifndef NULLOSFT_MEDIALIBRARY_REFLECTED_MESSAGES_HEADER +#define NULLOSFT_MEDIALIBRARY_REFLECTED_MESSAGES_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include + +// reflected messages will pass this as lParam +typedef struct _REFLECTPARAM +{ + LRESULT result; // return result here. If refleting window is dialog it is responsible to set result using SetWindowlongPtr. + LPARAM lParam; // orginal lParam + HWND hwndFrom; // reflecting window +}REFLECTPARAM, *PREFLECTPARAM; + + + +// reflected messages +// you need to return TRUE if you procesed a message otherwise FALSE +#define REFLECT_BASE (WM_APP + 0x3000) + +#define WM_SUPPORTREFLECT (REFLECT_BASE + 0x0000) // wParam = (WPARM)(UINT)testMessageCode. Return TRUE if you suport message reflecting + +#define REFLECTED_DRAWITEM (REFLECT_BASE + WM_DRAWITEM) +#define REFLECTED_CTLCOLORBTN (REFLECT_BASE + WM_CTLCOLORBTN) +#define REFLECTED_CTLCOLOREDIT (REFLECT_BASE + WM_CTLCOLOREDIT) +#define REFLECTED_CTLCOLORLISTBOX (REFLECT_BASE + WM_CTLCOLORLISTBOX) +#define REFLECTED_CTLCOLORSCROLLBAR (REFLECT_BASE + WM_CTLCOLORSCROLLBAR) +#define REFLECTED_CTLCOLORSTATIC (REFLECT_BASE + WM_CTLCOLORSTATIC) +#define REFLECTED_NOTIFY (REFLECT_BASE + WM_NOTIFY) +#define REFLECTED_COMMAND (REFLECT_BASE + WM_COMMAND) +#define REFLECTED_MEASUREITEM (REFLECT_BASE + WM_MEASUREITEM) + + +#ifdef __cplusplus +#define REFLECTMESSAGE(hwnd, uMsg, wParam, lParam) (BOOL)::SendMessage((hwnd), (REFLECT_BASE + (uMsg)), (wParam), (lParam)) +#else +#define REFLECTMESSAGE(hwnd, uMsg, wParam, lParam) (BOOL)SendMessage((hwnd), (REFLECT_BASE + (uMsg)), (wParam), (lParam)) +#endif + +BOOL CanReflect(UINT uMsg); +BOOL ReflectMessage(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL bDialog, LRESULT *pResult); + +HRESULT InstallReflector(HWND hwnd); // this is installs simple window hook that allows reflection code to run. + // returns , S_OK - hook installed, S_FALSE in case hook already installed, E_XXX - something bad +BOOL RemoveReflector(HWND hwnd); // returns TRUE if window was reflecting + +#endif // NULLOSFT_MEDIALIBRARY_REFLECTED_MESSAGES_HEADER diff --git a/Src/Plugins/General/gen_ml/resource.h b/Src/Plugins/General/gen_ml/resource.h new file mode 100644 index 00000000..b2edb501 --- /dev/null +++ b/Src/Plugins/General/gen_ml/resource.h @@ -0,0 +1,263 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by gen_ml.rc +// +#define IDS_ENQUEUE_IN_WINAMP 0 +#define IDS_LIBRARY_OPTIONS 1 +#define IDS_ONLINE_MEDIA 2 +#define IDC_ITV_CHANGEPASS 3 +#define IDS_RATINGS_PASSWORD_MISSING 3 +#define IDS_SERCURITY_ALERT 4 +#define IDS_MUST_ENTER_PASSWORD 5 +#define IDS_INTERNET_ACCESS 6 +#define IDS_PASSWORD_NO_MATCH 7 +#define IDS_INVALID_PASSWORD 8 +#define IDS_PLAY_SELECTED 9 +#define IDS_ENQUEUE_SELECTED 10 +#define IDS_NO_CONFIG_PRESENT 11 +#define IDS_ML_PLUGIN_INFO 12 +#define IDS_BAD_ITEM 13 +#define IDS_ML_GHK_STR 15 +#define IDS_MEDIA_LIBRARY 16 +#define IDS_ML_ALT_L_SHORTCUT 17 +#define IDS_WINAMP_LIBRARY 18 +#define IDS_ONLINE_SERVICES_NOT_PRESENT 19 +#define IDS_ERROR_SWITCHING_TO_VIEW 20 +#define IDS_CDDB_CONNECTING 22 +#define IDS_CDDB_SENDING 23 +#define IDS_CDDB_RECEIVING 24 +#define IDS_CDDB_COMPLETE 25 +#define IDS_TOGGLE_LIBRARY 26 +#define IDS_UNINSTALL_PROMPT 27 +#define IDS_UINSTALL_CONFIRMATION 28 +#define IDS_DOWNLOADING 29 +#define IDS_WEBINFO_NAVIGATE_ERROR 30 +#define IDS_WEBINFO_MESSAGEBOX_TITLE 31 +#define IDS_LOADING_IN_PROGRESS 32 +#define IDS_UNABLE_TO_APPLY_NEW_STYLE 33 +#define IDS_TWEAK_ERROR 34 +#define IDS_ALWAYS 35 +#define IDS_PROCESS_ACTIVE 36 +#define IDS_ANCESTOR_ACTIVE 37 +#define IDS_WINDOW_FOCUSED 38 +#define IDS_NEVER 39 +#define IDS_ALL 40 +#define IDS_SELECTED 41 +#define IDS_FOCUSED 42 +#define IDS_LEFT 43 +#define IDS_CENTER 44 +#define IDS_RIGHT 45 +#define IDS_DO_YOU_WANT_TO_SAVE_CHANGES_FIRST 46 +#define IDS_CONFIRM_CLOSE 47 +#define IDS_TREE_OPTIONS 48 +#define IDS_FILE_ATTRIBUTES 49 +#define IDS_FILETYPE_AUDIO 50 +#define IDS_FVFT_VIDEO 51 +#define IDS_FILETYPE_VIDEO 51 +#define IDS_FILETYPE_PLAYLIST 52 +#define IDS_FILETYPE_UNKNOWN 53 +#define IDS_FILEVIEW_COL_NAME 54 +#define IDS_FILEVIEW_COL_TYPE 55 +#define IDS_FILEVIEW_COL_SIZE 56 +#define IDS_FILEVIEW_COL_MODIFIED 57 +#define IDS_FILEVIEW_COL_CREATED 58 +#define IDS_FILEVIEW_COL_EXTENSION 59 +#define IDS_FILEVIEW_COL_ATTRIBUTES 60 +#define IDS_FILEVIEW_COL_ARTIST 61 +#define IDS_FILEVIEW_COL_ALBUM 62 +#define IDS_FILEVIEW_COL_TITLE 63 +#define IDS_FILEVIEW_COL_INMLDB 64 +#define IDS_FILEVIEW_COL_GENRE 65 +#define IDS_FILEVIEW_COL_YEAR 66 +#define IDS_FILEVIEW_COL_LENGTH 67 +#define IDS_FILEVIEW_COL_BITRATE 68 +#define IDS_FILEVIEW_COL_TRACK 69 +#define IDS_FILEVIEW_COL_DISC 70 +#define IDS_FILEVIEW_COL_COMMENT 71 +#define IDS_FILEVIEW_COL_PUBLISHER 72 +#define IDS_FILEVIEW_COL_COMPOSER 73 +#define IDS_FILEVIEW_COL_ALBUMARTIST 74 +#define IDS_FILEVIEW_COL_ENTRYCOUNT 75 +#define IDS_FILEVIEW_COL_TOTALLENGTH 76 +#define IDS_KBPS 80 +#define IDS_APPEARANCE 81 +#define IDD_MAIN 101 +#define IDR_CONTEXTMENUS 103 +#define IDR_MENU_FILEVIEW 104 +#define ML_IDC_DRAGDROP 107 +#define IDB_TREEITEM_COLLAPSED 135 +#define IDB_TREEITEM_NOCHILD 136 +#define IDB_TREEITEM_EXPANDED 137 +#define IDB_TREEITEM_DEFAULT 138 +#define IDB_BITMAP1 145 +#define IDB_RATING 145 +#define IDB_SORTARROW 146 +#define IDB_SPLITARROW 147 +#define IDB_SPLITARROW_PRESSED 148 +#define IDB_MENU_CHECKMARK 149 +#define IDB_MENU_RADIOMARK 150 +#define IDD_RATINGTWEAK 151 +#define IDB_MENU_EXPANDARROW 152 +#define IDB_MENU_SCROLLARROW 153 +#define IDB_MENU_SCROLLARROW_DISABLED 154 +#define IDD_PREFS3 157 +#define IDD_FILEVIEW 161 +#define IDD_DIALOG2 162 +#define IDD_FILEVIEW_TOOLBAR 162 +#define IDS_YES 163 +#define IDS_NO 164 +#define IDS_FILEVIEW_EMPTYFOLDER 165 +#define IDS_FILEVIEWSTATUS_GROUPTEMPLATE 167 +#define IDR_ACCELERATOR_GLOBAL 167 +#define IDS_FILEVIEWSTATUS_SELECTEDGROUPTEMPLATE 168 +#define IDR_ACCELERATOR_MAIN 168 +#define IDR_ACCELERATOR_FILEVIEW 169 +#define IDS_RESTART 169 +#define IDS_RESTART_MESSAGE 170 +#define IDR_MENU1 171 +#define IDS_COL_NORMAL 171 +#define IDB_CLOUD_IS_IN 172 +#define IDS_COL_SELONLY 172 +#define IDB_CLOUD_PARTIAL 173 +#define IDS_COL_PROP 173 +#define IDB_CLOUD_UNAVAIL 174 +#define IDS_PLAY 174 +#define IDB_CLOUD_UPLOAD 175 +#define IDS_ENQUEUE 175 +#define IDB_CLOUD_UPLOADING 176 +#define IDS_NOT_LOADED 176 +#define IDB_TREEITEM_LABS 177 +#define IDD_VIEW_EMPTY 224 +#define IDD_PREFSFR 228 +#define IDD_PREFS2 229 +#define IDD_MLPLUGINS 254 +#define IDD_PREFS_ITV_ASSIGNPASS 266 +#define IDD_PREFS_ITV_GETPASS 267 +#define IDD_PREFS4 268 +#define IDD_PREFS1 269 +#define IDC_LIST1 1000 +#define IDC_LIST2 1001 +#define IDC_BTN_LIB 1003 +#define IDC_VDELIM 1014 +#define IDC_COMBO1 1032 +#define IDC_CMB_TRACKWHEN 1033 +#define IDC_COMBO2 1033 +#define IDC_CMB_TRACKWHAT 1034 +#define IDC_CMB_CLICKWHAT 1036 +#define IDC_CHECK1 1052 +#define IDC_CHK_SHOWEMPTY_HOT 1053 +#define IDC_CHECK2 1053 +#define IDC_FF_SCROLLBARS 1053 +#define IDC_WRITE_RATINGS 1053 +#define IDC_CHK_SHOWEMPTY_ANIMATION 1054 +#define IDC_FF_ALTERNATEITEMS 1054 +#define IDC_ALTERNATEITEMS 1054 +#define IDC_GROUP_BUTTONS 1054 +#define IDC_CHK_SIZEDECREASE 1055 +#define IDC_SKINNED_MENUS 1055 +#define IDC_CHK_SIZEINCREASE 1056 +#define IDC_GENO 1056 +#define IDC_BUTTON1 1062 +#define IDC_RATING_COLUMN 1062 +#define IDC_BUTTON3 1063 +#define IDC_BUTTON4 1064 +#define IDC_BUTTON5 1065 +#define IDC_BUTTON2 1066 +#define IDC_PLPLAYLIST 1069 +#define IDC_VIEWPLAYMODE 1070 +#define IDC_TAB1 1079 +#define IDC_GENLIB 1121 +#define IDC_GENCONF 1122 +#define IDB_FILETYPE_AUDIO_SMALL 1126 +#define IDB_FILETYPE_AUDIO_LARGE 1127 +#define IDB_FILETYPE_VIDEO_SMALL 1128 +#define IDB_FILETYPE_VIDEO_LARGE 1129 +#define IDB_FILETYPE_UNKNOWN_SMALL 1130 +#define IDB_FILETYPE_UNKNOWN_LARGE 1131 +#define IDB_FILETYPE_PLAYLIST_SMALL 1132 +#define IDB_FILETYPE_PLAYLIST_LARGE 1133 +#define IDB_FILEVIEW_LIST 1134 +#define IDB_FILEVIEW_ICON 1135 +#define IDB_FILEVIEW_DETAIL 1136 +#define IDC_DEFS 1210 +#define IDC_CHECK_USEPLFONT 1211 +#define IDC_PLUGINVERS 1229 +#define IDC_UNINST 1252 +#define IDC_CHECK_RATING0 1280 +#define IDC_CHECK_RATING1 1281 +#define IDC_CHECK_RATING2 1282 +#define IDC_CHECK_RATING3 1283 +#define IDC_CHECK_RATING4 1284 +#define IDC_CHECK_PASSPROMPT 1285 +#define IDC_EDIT_PASSWORD 1304 +#define IDC_EDIT_PASSWORD_VERIFY 1305 +#define IDC_GPASS 1307 +#define IDC_PL_SEND_TO 1309 +#define IDC_PMP_SEND_TO 1310 +#define IDC_CMB_ALIGNMENT 1311 +#define IDC_CHK_SHOWEMPTY_NORMAL 1312 +#define IDC_CHK_BLOCKCLICK 1313 +#define IDC_CHK_BLOCKUNRATE 1314 +#define IDC_SHOW_ICONS 1314 +#define IDC_CHK_BLOCKDRAG 1315 +#define IDC_HIGHLIGHT_FULL_TREE_ITEM 1315 +#define IDC_RCLICK_TO_SELECT 1316 +#define IDC_CHK_SHOWINACTIVE_HOT 1316 +#define IDC_SHOW_EJECT_ICONS 1317 +#define IDC_ITEM_HEIGHT 1318 +#define IDC_GROUP_DRIVES 1319 +#define IDC_PARENT_PODCASTS 1320 +#define IDC_CD_DVD_ITEMS 1321 +#define IDC_ML_TREE_OPTS 1322 +#define IDC_STATIC_INFO 1323 +#define IDC_DOWNLOADS_ITEMS 1323 +#define IDC_EDT_PATH 1324 +#define IDC_GROUP_DRIVES2 1324 +#define IDC_TRANSFERS_ITEMS 1324 +#define IDC_FOLDER_BROWSER 1325 +#define IDC_LBL_ADDRESS 1326 +#define IDC_HDELIM 1327 +#define IDC_TRANSFERS_COMBO 1327 +#define IDC_BTN_VIEW_ICON 1328 +#define IDC_NO_VIEW 1328 +#define IDC_BTN_VIEW_LIST 1329 +#define IDC_BTN_VIEW_DETAIL 1330 +#define IDC_BTN_ARRANGEBY 1331 +#define IDC_BTN_OPTIONS 1332 +#define IDM_LIBRARY_CONFIG 40050 +#define ID_WINDOW_CLOSE 40155 +#define ID_GO_TO_VIEW_SEARCHBAR 40157 +#define ID_SHOW_HELP 40158 +#define ID_DOSHITMENU_HELP 40159 +#define IDM_LIBRARY_HELP 40161 +#define ID_REFRESH_SEARCH 40165 +#define ID_FILEVIEW_PLAYSELECTION 42001 +#define ID_FILEVIEW_PLAYSELECTION_SHIFT 42002 +#define ID_FILEVIEW_SELECT_ALL 42003 +#define ID_FILEVIEW_SETMODE_ICON 42004 +#define ID_FILEVIEW_SETMODE_LIST 42005 +#define ID_FILEVIEW_SETMODE_DETAIL 42006 +#define ID_FILEVIEW_REFRESH 42007 +#define IDM_FILEVIEW_HIDEEXTENSION 42008 +#define IDM_FILEVIEW_IGNOREHIDDEN 42009 +#define IDM_FILEVIEW_SHOWAUDIO 42010 +#define IDM_FILEVIEW_SHOWVIDEO 42011 +#define IDM_FILEVIEW_SHOWPLAYLIST 42012 +#define IDM_FILEVIEW_SHOWUNKNOWN 42013 +#define IDM_FILEVIEW_SORTASCENDING 42014 +#define ID_SELECTCOLUMNS_CUSTOMIZE 42015 +#define ID_SHOW_RATINGTWEAK 42016 +#define ID_TOGGLE_LIBRARY 42017 +#define ID_NEW_PLAYLIST 42018 +#define IDS_NULLSOFT_ML_STR 65534 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 179 +#define _APS_NEXT_COMMAND_VALUE 40167 +#define _APS_NEXT_CONTROL_VALUE 1329 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/Src/Plugins/General/gen_ml/resources/checkmark.png b/Src/Plugins/General/gen_ml/resources/checkmark.png new file mode 100644 index 00000000..3c9be33d Binary files /dev/null and b/Src/Plugins/General/gen_ml/resources/checkmark.png differ diff --git a/Src/Plugins/General/gen_ml/resources/cloud_16_incloud.png b/Src/Plugins/General/gen_ml/resources/cloud_16_incloud.png new file mode 100644 index 00000000..f6ee332e Binary files /dev/null and b/Src/Plugins/General/gen_ml/resources/cloud_16_incloud.png differ diff --git a/Src/Plugins/General/gen_ml/resources/cloud_16_partial.png b/Src/Plugins/General/gen_ml/resources/cloud_16_partial.png new file mode 100644 index 00000000..e55c170b Binary files /dev/null and b/Src/Plugins/General/gen_ml/resources/cloud_16_partial.png differ diff --git a/Src/Plugins/General/gen_ml/resources/cloud_16_unavail.png b/Src/Plugins/General/gen_ml/resources/cloud_16_unavail.png new file mode 100644 index 00000000..a196af5b Binary files /dev/null and b/Src/Plugins/General/gen_ml/resources/cloud_16_unavail.png differ diff --git a/Src/Plugins/General/gen_ml/resources/cloud_16_upload.png b/Src/Plugins/General/gen_ml/resources/cloud_16_upload.png new file mode 100644 index 00000000..f3e50307 Binary files /dev/null and b/Src/Plugins/General/gen_ml/resources/cloud_16_upload.png differ diff --git a/Src/Plugins/General/gen_ml/resources/cloud_16_uploading.png b/Src/Plugins/General/gen_ml/resources/cloud_16_uploading.png new file mode 100644 index 00000000..c2f49c0c Binary files /dev/null and b/Src/Plugins/General/gen_ml/resources/cloud_16_uploading.png differ diff --git a/Src/Plugins/General/gen_ml/resources/dragdrop.cur b/Src/Plugins/General/gen_ml/resources/dragdrop.cur new file mode 100644 index 00000000..f1264e1d Binary files /dev/null and b/Src/Plugins/General/gen_ml/resources/dragdrop.cur differ diff --git a/Src/Plugins/General/gen_ml/resources/filetype_audio_16.png b/Src/Plugins/General/gen_ml/resources/filetype_audio_16.png new file mode 100644 index 00000000..384cafa7 Binary files /dev/null and b/Src/Plugins/General/gen_ml/resources/filetype_audio_16.png differ diff --git a/Src/Plugins/General/gen_ml/resources/filetype_audio_32.png b/Src/Plugins/General/gen_ml/resources/filetype_audio_32.png new file mode 100644 index 00000000..0f787f3d Binary files /dev/null and b/Src/Plugins/General/gen_ml/resources/filetype_audio_32.png differ diff --git a/Src/Plugins/General/gen_ml/resources/filetype_playlist_16.png b/Src/Plugins/General/gen_ml/resources/filetype_playlist_16.png new file mode 100644 index 00000000..cfa4e58f Binary files /dev/null and b/Src/Plugins/General/gen_ml/resources/filetype_playlist_16.png differ diff --git a/Src/Plugins/General/gen_ml/resources/filetype_playlist_32.png b/Src/Plugins/General/gen_ml/resources/filetype_playlist_32.png new file mode 100644 index 00000000..a1f42bca Binary files /dev/null and b/Src/Plugins/General/gen_ml/resources/filetype_playlist_32.png differ diff --git a/Src/Plugins/General/gen_ml/resources/filetype_unknown_16.png b/Src/Plugins/General/gen_ml/resources/filetype_unknown_16.png new file mode 100644 index 00000000..39b7803e Binary files /dev/null and b/Src/Plugins/General/gen_ml/resources/filetype_unknown_16.png differ diff --git a/Src/Plugins/General/gen_ml/resources/filetype_unknown_32.png b/Src/Plugins/General/gen_ml/resources/filetype_unknown_32.png new file mode 100644 index 00000000..063396a3 Binary files /dev/null and b/Src/Plugins/General/gen_ml/resources/filetype_unknown_32.png differ diff --git a/Src/Plugins/General/gen_ml/resources/filetype_video_16.png b/Src/Plugins/General/gen_ml/resources/filetype_video_16.png new file mode 100644 index 00000000..5d78c705 Binary files /dev/null and b/Src/Plugins/General/gen_ml/resources/filetype_video_16.png differ diff --git a/Src/Plugins/General/gen_ml/resources/filetype_video_32.png b/Src/Plugins/General/gen_ml/resources/filetype_video_32.png new file mode 100644 index 00000000..aaee034b Binary files /dev/null and b/Src/Plugins/General/gen_ml/resources/filetype_video_32.png differ diff --git a/Src/Plugins/General/gen_ml/resources/menu_arrow.png b/Src/Plugins/General/gen_ml/resources/menu_arrow.png new file mode 100644 index 00000000..8fb3e757 Binary files /dev/null and b/Src/Plugins/General/gen_ml/resources/menu_arrow.png differ diff --git a/Src/Plugins/General/gen_ml/resources/menu_check.png b/Src/Plugins/General/gen_ml/resources/menu_check.png new file mode 100644 index 00000000..a615c07d Binary files /dev/null and b/Src/Plugins/General/gen_ml/resources/menu_check.png differ diff --git a/Src/Plugins/General/gen_ml/resources/menu_dot.png b/Src/Plugins/General/gen_ml/resources/menu_dot.png new file mode 100644 index 00000000..a9efbef4 Binary files /dev/null and b/Src/Plugins/General/gen_ml/resources/menu_dot.png differ diff --git a/Src/Plugins/General/gen_ml/resources/menu_scrollarrow.png b/Src/Plugins/General/gen_ml/resources/menu_scrollarrow.png new file mode 100644 index 00000000..70549263 Binary files /dev/null and b/Src/Plugins/General/gen_ml/resources/menu_scrollarrow.png differ diff --git a/Src/Plugins/General/gen_ml/resources/menu_scrollarrow_disabled.png b/Src/Plugins/General/gen_ml/resources/menu_scrollarrow_disabled.png new file mode 100644 index 00000000..91f64bb0 Binary files /dev/null and b/Src/Plugins/General/gen_ml/resources/menu_scrollarrow_disabled.png differ diff --git a/Src/Plugins/General/gen_ml/resources/rating.png b/Src/Plugins/General/gen_ml/resources/rating.png new file mode 100644 index 00000000..0d7ee13d Binary files /dev/null and b/Src/Plugins/General/gen_ml/resources/rating.png differ diff --git a/Src/Plugins/General/gen_ml/resources/sortarrow.png b/Src/Plugins/General/gen_ml/resources/sortarrow.png new file mode 100644 index 00000000..019bf0fb Binary files /dev/null and b/Src/Plugins/General/gen_ml/resources/sortarrow.png differ diff --git a/Src/Plugins/General/gen_ml/resources/splitarrow.png b/Src/Plugins/General/gen_ml/resources/splitarrow.png new file mode 100644 index 00000000..cb7c42c3 Binary files /dev/null and b/Src/Plugins/General/gen_ml/resources/splitarrow.png differ diff --git a/Src/Plugins/General/gen_ml/resources/splitarrow_pressed.png b/Src/Plugins/General/gen_ml/resources/splitarrow_pressed.png new file mode 100644 index 00000000..3fb60ac9 Binary files /dev/null and b/Src/Plugins/General/gen_ml/resources/splitarrow_pressed.png differ diff --git a/Src/Plugins/General/gen_ml/resources/stars_invert.bmp b/Src/Plugins/General/gen_ml/resources/stars_invert.bmp new file mode 100644 index 00000000..0e6a8425 Binary files /dev/null and b/Src/Plugins/General/gen_ml/resources/stars_invert.bmp differ diff --git a/Src/Plugins/General/gen_ml/resources/ti_default_16x16x16.bmp b/Src/Plugins/General/gen_ml/resources/ti_default_16x16x16.bmp new file mode 100644 index 00000000..74889cd0 Binary files /dev/null and b/Src/Plugins/General/gen_ml/resources/ti_default_16x16x16.bmp differ diff --git a/Src/Plugins/General/gen_ml/resources/ti_labs_16x16x16.bmp b/Src/Plugins/General/gen_ml/resources/ti_labs_16x16x16.bmp new file mode 100644 index 00000000..34c8c61f Binary files /dev/null and b/Src/Plugins/General/gen_ml/resources/ti_labs_16x16x16.bmp differ diff --git a/Src/Plugins/General/gen_ml/resources/tree_closed_16x16x16.bmp b/Src/Plugins/General/gen_ml/resources/tree_closed_16x16x16.bmp new file mode 100644 index 00000000..e7063a87 Binary files /dev/null and b/Src/Plugins/General/gen_ml/resources/tree_closed_16x16x16.bmp differ diff --git a/Src/Plugins/General/gen_ml/resources/tree_closed_disabled_16x16x16.bmp b/Src/Plugins/General/gen_ml/resources/tree_closed_disabled_16x16x16.bmp new file mode 100644 index 00000000..86640395 Binary files /dev/null and b/Src/Plugins/General/gen_ml/resources/tree_closed_disabled_16x16x16.bmp differ diff --git a/Src/Plugins/General/gen_ml/resources/tree_open_16x16x16.bmp b/Src/Plugins/General/gen_ml/resources/tree_open_16x16x16.bmp new file mode 100644 index 00000000..3e4f969a Binary files /dev/null and b/Src/Plugins/General/gen_ml/resources/tree_open_16x16x16.bmp differ diff --git a/Src/Plugins/General/gen_ml/resources/view_mode_detail.png b/Src/Plugins/General/gen_ml/resources/view_mode_detail.png new file mode 100644 index 00000000..caf9d4da Binary files /dev/null and b/Src/Plugins/General/gen_ml/resources/view_mode_detail.png differ diff --git a/Src/Plugins/General/gen_ml/resources/view_mode_icon.png b/Src/Plugins/General/gen_ml/resources/view_mode_icon.png new file mode 100644 index 00000000..ea72bcf3 Binary files /dev/null and b/Src/Plugins/General/gen_ml/resources/view_mode_icon.png differ diff --git a/Src/Plugins/General/gen_ml/resources/view_mode_list.png b/Src/Plugins/General/gen_ml/resources/view_mode_list.png new file mode 100644 index 00000000..33bc4313 Binary files /dev/null and b/Src/Plugins/General/gen_ml/resources/view_mode_list.png differ diff --git a/Src/Plugins/General/gen_ml/scrollwnd.cpp b/Src/Plugins/General/gen_ml/scrollwnd.cpp new file mode 100644 index 00000000..61a78761 --- /dev/null +++ b/Src/Plugins/General/gen_ml/scrollwnd.cpp @@ -0,0 +1,2391 @@ +// some code taken from (freeware) Cool ScrollBar library by J Brown +#include "main.h" +#include +#include +#include "scrollwnd.h" +#include "../winamp/wa_dlg.h" + +#ifndef WM_MOUSEWHEEL +#define WM_MOUSEWHEEL 0x020A +#endif + + +extern HRESULT(WINAPI *SetWindowTheme)(HWND hwnd, LPCWSTR pszSubAppName, LPCWSTR pszSubIdList); //xp theme shit +extern HRESULT(WINAPI *IsAppThemed)(void); + +static TCHAR szPropStr[] = _T("CoolSBSubclassPtr"); + +// +// Special thumb-tracking variables +// +// +static UINT uCurrentScrollbar = COOLSB_NONE; //SB_HORZ / SB_VERT +static UINT uCurrentScrollPortion = HTSCROLL_NONE; +static UINT uCurrentButton = 0; + +static RECT rcThumbBounds; //area that the scroll thumb can travel in +static int nThumbSize; //(pixels) +static int nThumbPos; //(pixels) +static int nThumbMouseOffset; //(pixels) +static int nLastPos = -1; //(scrollbar units) +static int nThumbPos0; //(pixels) initial thumb position + +// +// Temporary state used to auto-generate timer messages +// +static UINT uMouseOverId = 0; +static UINT uMouseOverScrollbar = COOLSB_NONE; +static UINT uHitTestPortion = HTSCROLL_NONE; +static UINT uLastHitTestPortion = HTSCROLL_NONE; +static RECT MouseOverRect; + +static UINT uScrollTimerMsg = 0; +static UINT uScrollTimerPortion = HTSCROLL_NONE; +static UINT_PTR uScrollTimerId = 0; +static HWND hwndCurCoolSB = 0; + +ScrollWnd *GetScrollWndFromHwnd(HWND hwnd) +{ + return (ScrollWnd *)GetProp(hwnd, szPropStr); +} + +// +// swap the rectangle's x coords with its y coords +// +static void __stdcall RotateRect(RECT *rect) +{ + int temp; + temp = rect->left; + rect->left = rect->top; + rect->top = temp; + + temp = rect->right; + rect->right = rect->bottom; + rect->bottom = temp; +} + +// +// swap the coords if the scrollbar is a SB_VERT +// +static void __stdcall RotateRect0(SCROLLBAR *sb, RECT *rect) +{ + if (sb->nBarType == SB_VERT) + RotateRect(rect); +} + +// +// Calculate if the SCROLLINFO members produce +// an enabled or disabled scrollbar +// +static BOOL IsScrollInfoActive(SCROLLINFO *si) +{ + if ((si->nPage > (UINT)si->nMax + || si->nMax <= si->nMin || si->nMax == 0)) + return FALSE; + else + return TRUE; +} + +// +// Return if the specified scrollbar is enabled or not +// +static BOOL IsScrollbarActive(SCROLLBAR *sb) +{ + SCROLLINFO *si = &sb->scrollInfo; + if (((sb->fScrollFlags & ESB_DISABLE_BOTH) == ESB_DISABLE_BOTH) || + !(sb->fScrollFlags & CSBS_THUMBALWAYS) && !IsScrollInfoActive(si)) + return FALSE; + else + return TRUE; +} + +BOOL drawFrameControl(HDC hdc, LPRECT lprc, UINT uType, UINT state) +{ + HDC hdcbmp; + HBITMAP hbmpOld, hbmp; + int startx, starty; + + hbmp = WADlg_getBitmap(); + if (!hbmp) return FALSE; + + hdcbmp = CreateCompatibleDC(hdc); + if (!hdcbmp) return FALSE; + + hbmpOld = (HBITMAP)SelectObject(hdcbmp, hbmp); + + startx = 0; + starty = 31; + switch (state&3) + { + case DFCS_SCROLLRIGHT: startx = 14; starty = 45; break; + case DFCS_SCROLLLEFT: startx = 0; starty = 45; break; + case DFCS_SCROLLDOWN: startx = 14; starty = 31; break; + } + if (state&DFCS_PUSHED) startx += 28; + StretchBlt(hdc, lprc->left, lprc->top, lprc->right - lprc->left, lprc->bottom - lprc->top, hdcbmp, startx, starty, 14, 14, SRCCOPY); + + SelectObject(hdcbmp, hbmpOld); + DeleteDC(hdcbmp); + return 1; +} + +// +// Draw a standard scrollbar arrow +// +static int DrawScrollArrow(SCROLLBAR *sbar, HDC hdc, RECT *rect, UINT arrow, BOOL fMouseDown, BOOL fMouseOver) +{ + UINT ret; + UINT flags = arrow; + + //HACKY bit so this routine can be called by vertical and horizontal code + if (sbar->nBarType == SB_VERT) + { + if (flags & DFCS_SCROLLLEFT) flags = flags & ~DFCS_SCROLLLEFT | DFCS_SCROLLUP; + if (flags & DFCS_SCROLLRIGHT) flags = flags & ~DFCS_SCROLLRIGHT | DFCS_SCROLLDOWN; + } + + if (fMouseDown) flags |= (DFCS_FLAT | DFCS_PUSHED); + + ret = drawFrameControl(hdc, rect, DFC_SCROLL, flags); + + return ret; +} + +// +// Return the size in pixels for the specified scrollbar metric, +// for the specified scrollbar +// +static int GetScrollMetric(SCROLLBAR *sbar, int metric) +{ + if (sbar->nBarType == SB_HORZ) + { + if (metric == SM_CXHORZSB) + { + if (sbar->nArrowLength < 0) + return -sbar->nArrowLength * 14; //GetSystemMetrics(SM_CXHSCROLL); + else + return sbar->nArrowLength; + } + else + { + if (sbar->nArrowWidth < 0) + return -sbar->nArrowWidth * 14; //GetSystemMetrics(SM_CYHSCROLL); + else + return sbar->nArrowWidth; + } + } + else if (sbar->nBarType == SB_VERT) + { + if (metric == SM_CYVERTSB) + { + if (sbar->nArrowLength < 0) + return -sbar->nArrowLength * 14;//GetSystemMetrics(SM_CYVSCROLL); + else + return sbar->nArrowLength; + } + else + { + if (sbar->nArrowWidth < 0) + return -sbar->nArrowWidth * 14;//GetSystemMetrics(SM_CXVSCROLL); + else + return sbar->nArrowWidth; + } + } + + return 0; +} + +// +// +// +static COLORREF GetSBForeColor(void) +{ + return WADlg_getColor(WADLG_SCROLLBAR_FGCOLOR); +} + +static COLORREF GetSBBackColor(void) +{ + return WADlg_getColor(WADLG_SCROLLBAR_BGCOLOR); +} + +// +// Paint a checkered rectangle, with each alternate +// pixel being assigned a different colour +// +static void DrawCheckedRect(HDC hdc, RECT *rect, COLORREF fg, COLORREF bg) +{ + static WORD wCheckPat[8] = + { + 0xaaaa, 0x5555, 0xaaaa, 0x5555, 0xaaaa, 0x5555, 0xaaaa, 0x5555 + }; + + HBITMAP hbmp; + HBRUSH hbr, hbrold; + COLORREF fgold, bgold; + + hbmp = CreateBitmap(8, 8, 1, 1, wCheckPat); + hbr = CreatePatternBrush(hbmp); + + UnrealizeObject(hbr); + SetBrushOrgEx(hdc, rect->left, rect->top, 0); + + hbrold = (HBRUSH)SelectObject(hdc, hbr); + + fgold = SetTextColor(hdc, fg); + bgold = SetBkColor(hdc, bg); + + PatBlt(hdc, rect->left, rect->top, + rect->right - rect->left, + rect->bottom - rect->top, + PATCOPY); + + SetBkColor(hdc, bgold); + SetTextColor(hdc, fgold); + + SelectObject(hdc, hbrold); + DeleteObject(hbr); + DeleteObject(hbmp); +} + +// +// Fill the specifed rectangle using a solid colour +// +static void PaintRect(HDC hdc, RECT *rect, COLORREF color) +{ + COLORREF oldcol = SetBkColor(hdc, color); + ExtTextOut(hdc, 0, 0, ETO_OPAQUE, rect, _T(""), 0, 0); + SetBkColor(hdc, oldcol); +} + +// +// Draw a simple blank scrollbar push-button. Can be used +// to draw a push button, or the scrollbar thumb +// drawflag - could set to BF_FLAT to make flat scrollbars +// +void DrawBlankButton(HDC hdc, const RECT *rect, UINT drawflag, int pushed, int vertical) +{ + HBITMAP hbmp, hbmpOld; + hbmp = WADlg_getBitmap(); + if (!hbmp) return; + + HDC hdcbmp = CreateCompatibleDC(hdc); + if (!hdcbmp) return; + + hbmpOld = (HBITMAP)SelectObject(hdcbmp, hbmp); + +#define PART1SIZE 4 //copied top +#define PART2SIZE 5 //stretched top +#define PART3SIZE 10 //copied middle +#define PART4SIZE 5 //stretched bottom +#define PART5SIZE 4 //copied bottom + + if (vertical) + { + int middle = (rect->bottom - rect->top) / 2; + int startx = pushed ? 70 : 56; + //top + StretchBlt(hdc, rect->left, rect->top, rect->right - rect->left, PART1SIZE, hdcbmp, startx, 31, 14, PART1SIZE, SRCCOPY); + int p = PART1SIZE; + //stretched top + int l = middle - PART1SIZE - (PART3SIZE / 2); + if (l > 0) + { + StretchBlt(hdc, rect->left, rect->top + p, rect->right - rect->left, l, hdcbmp, startx, 31 + PART1SIZE, 14, PART2SIZE, SRCCOPY); + p += middle - PART1SIZE - (PART3SIZE / 2); + } + //copied middle + int m = (rect->bottom - rect->top) - PART1SIZE - PART5SIZE; //space that's available for middle + m = min(m, PART3SIZE); + if (m > 0) + { + StretchBlt(hdc, rect->left, rect->top + p, rect->right - rect->left, m, hdcbmp, startx, 31 + PART1SIZE + PART2SIZE, 14, m, SRCCOPY); + p += m; + } + //stretched bottom + l = rect->bottom - rect->top - p - PART5SIZE; + if (l > 0) StretchBlt(hdc, rect->left, rect->top + p, rect->right - rect->left, l, hdcbmp, startx, 31 + PART1SIZE + PART2SIZE + PART3SIZE, 14, PART4SIZE, SRCCOPY); + //bottom + StretchBlt(hdc, rect->left, rect->bottom - PART5SIZE, rect->right - rect->left, PART5SIZE, hdcbmp, startx, 31 + PART1SIZE + PART2SIZE + PART3SIZE + PART4SIZE, 14, PART5SIZE, SRCCOPY); + } + else + { + int middle = (rect->right - rect->left) / 2; + int starty = pushed ? 45 : 31; + //top + StretchBlt(hdc, rect->left, rect->top, PART1SIZE, rect->bottom - rect->top, hdcbmp, 84, starty, PART1SIZE, 14, SRCCOPY); + int p = PART1SIZE; + //stretched top + int l = middle - PART1SIZE - (PART3SIZE / 2); + if (l > 0) + { + StretchBlt(hdc, rect->left + p, rect->top, l, rect->bottom - rect->top, hdcbmp, 84 + PART1SIZE, starty, PART2SIZE, 14, SRCCOPY); + p += middle - PART1SIZE - (PART3SIZE / 2); + } + //copied middle + int m = (rect->right - rect->left) - PART1SIZE - PART5SIZE; //space that's available for middle + m = min(m, PART3SIZE); + if (m > 0) + { + StretchBlt(hdc, rect->left + p, rect->top, m, rect->bottom - rect->top, hdcbmp, 84 + PART1SIZE + PART2SIZE, starty, m, 14, SRCCOPY); + p += m; + } + //stretched bottom + l = rect->right - rect->left - p - PART5SIZE; + if (l > 0) StretchBlt(hdc, rect->left + p, rect->top, l, rect->bottom - rect->top, hdcbmp, 84 + PART1SIZE + PART2SIZE + PART3SIZE, starty, PART4SIZE, 14, SRCCOPY); + //bottom + StretchBlt(hdc, rect->right - PART5SIZE, rect->top, PART5SIZE, rect->bottom - rect->top, hdcbmp, 84 + PART1SIZE + PART2SIZE + PART3SIZE + PART4SIZE, starty, PART5SIZE, 14, SRCCOPY); + } + + SelectObject(hdcbmp, hbmpOld); + DeleteDC(hdcbmp); +} + +// +// Send a WM_VSCROLL or WM_HSCROLL message +// +static void SendScrollMessage(HWND hwnd, UINT scrMsg, UINT scrId, UINT pos) +{ + SendMessage(hwnd, scrMsg, MAKEWPARAM(scrId, pos), 0); +} + +// +// Calculate the screen coordinates of the area taken by +// the horizontal scrollbar. Take into account the size +// of the window borders +// +static BOOL GetHScrollRect(ScrollWnd *sw, HWND hwnd, RECT *rect) +{ + GetWindowRect(hwnd, rect); + + if (sw->fLeftScrollbar) + { + rect->left += sw->cxLeftEdge + (sw->sbarVert.fScrollVisible ? + GetScrollMetric(&sw->sbarVert, SM_CXVERTSB) : 0); + rect->right -= sw->cxRightEdge; + } + else + { + rect->left += sw->cxLeftEdge; //left window edge + + rect->right -= sw->cxRightEdge + //right window edge + (sw->sbarVert.fScrollVisible ? + GetScrollMetric(&sw->sbarVert, SM_CXVERTSB) : 0); + } + + rect->bottom -= sw->cyBottomEdge; //bottom window edge + + rect->top = rect->bottom - + (sw->sbarHorz.fScrollVisible ? + GetScrollMetric(&sw->sbarHorz, SM_CYHORZSB) : 0); + + return TRUE; +} + +// +// Calculate the screen coordinates of the area taken by the +// vertical scrollbar +// +static BOOL GetVScrollRect(ScrollWnd *sw, HWND hwnd, RECT *rect) +{ + GetWindowRect(hwnd, rect); + rect->top += sw->cyTopEdge; //top window edge + + rect->bottom -= sw->cyBottomEdge + + (sw->sbarHorz.fScrollVisible ? //bottom window edge + GetScrollMetric(&sw->sbarHorz, SM_CYHORZSB) : 0); + + if (sw->fLeftScrollbar) + { + rect->left += sw->cxLeftEdge; + rect->right = rect->left + (sw->sbarVert.fScrollVisible ? + GetScrollMetric(&sw->sbarVert, SM_CXVERTSB) : 0); + } + else + { + rect->right -= sw->cxRightEdge; + rect->left = rect->right - (sw->sbarVert.fScrollVisible ? + GetScrollMetric(&sw->sbarVert, SM_CXVERTSB) : 0); + } + + return TRUE; +} + +// Depending on what type of scrollbar nBar refers to, call the +// appropriate Get?ScrollRect function +// +BOOL GetScrollRect(ScrollWnd *sw, UINT nBar, HWND hwnd, RECT *rect) +{ + if (nBar == SB_HORZ) + return GetHScrollRect(sw, hwnd, rect); + else if (nBar == SB_VERT) + return GetVScrollRect(sw, hwnd, rect); + else + return FALSE; +} + +// +// Work out the scrollbar width/height for either type of scrollbar (SB_HORZ/SB_VERT) +// rect - coords of the scrollbar. +// store results into *thumbsize and *thumbpos +// +static int CalcThumbSize(SCROLLBAR *sbar, const RECT *rect, int *pthumbsize, int *pthumbpos) +{ + SCROLLINFO *si; + int scrollsize; //total size of the scrollbar including arrow buttons + int workingsize; //working area (where the thumb can slide) + int siMaxMin; + int butsize; + int startcoord; + int thumbpos = 0, thumbsize = 0; + + //work out the width (for a horizontal) or the height (for a vertical) + //of a standard scrollbar button + butsize = GetScrollMetric(sbar, SM_SCROLL_LENGTH); + + if (1) //sbar->nBarType == SB_HORZ) + { + scrollsize = rect->right - rect->left; + startcoord = rect->left; + } + /*else if(sbar->nBarType == SB_VERT) + { + scrollsize = rect->bottom - rect->top; + startcoord = rect->top; + } + else + { + return 0; + }*/ + + si = &sbar->scrollInfo; + siMaxMin = si->nMax - si->nMin + 1; + workingsize = scrollsize - butsize * 2; + + // + // Work out the scrollbar thumb SIZE + // + if (si->nPage == 0) + { + thumbsize = butsize; + } + else if (siMaxMin > 0) + { + thumbsize = MulDiv(si->nPage, workingsize, siMaxMin); + + if (thumbsize < sbar->nMinThumbSize) + thumbsize = sbar->nMinThumbSize; + } + + // + // Work out the scrollbar thumb position + // + if (siMaxMin > 0) + { + int pagesize = max(1, si->nPage); + thumbpos = MulDiv(si->nPos - si->nMin, workingsize - thumbsize, siMaxMin - pagesize); + + if (thumbpos < 0) + thumbpos = 0; + + if (thumbpos >= workingsize - thumbsize) + thumbpos = workingsize - thumbsize; + } + + thumbpos += startcoord + butsize; + + *pthumbpos = thumbpos; + *pthumbsize = thumbsize; + + return 1; +} + +// +// return a hit-test value for whatever part of the scrollbar x,y is located in +// rect, x, y: SCREEN coordinates +// the rectangle must not include space for any inserted buttons +// (i.e, JUST the scrollbar area) +// +static UINT GetHorzScrollPortion(SCROLLBAR *sbar, HWND hwnd, const RECT *rect, int x, int y) +{ + int thumbwidth, thumbpos; + int butwidth = GetScrollMetric(sbar, SM_SCROLL_LENGTH); + int scrollwidth = rect->right - rect->left; + int workingwidth = scrollwidth - butwidth * 2; + + if (y < rect->top || y >= rect->bottom) + return HTSCROLL_NONE; + + CalcThumbSize(sbar, rect, &thumbwidth, &thumbpos); + + //if we have had to scale the buttons to fit in the rect, + //then adjust the button width accordingly + if (scrollwidth <= butwidth * 2) + { + butwidth = scrollwidth / 2; + } + + //check for left button click + if (x >= rect->left && x < rect->left + butwidth) + { + return HTSCROLL_LEFT; + } + //check for right button click + else if (x >= rect->right - butwidth && x < rect->right) + { + return HTSCROLL_RIGHT; + } + + //if the thumb is too big to fit (i.e. it isn't visible) + //then return a NULL scrollbar area + if (thumbwidth >= workingwidth) + return HTSCROLL_NONE; + + //check for point in the thumbbar + if (x >= thumbpos && x < thumbpos + thumbwidth) + { + return HTSCROLL_THUMB; + } + //check for left margin + else if (x >= rect->left + butwidth && x < thumbpos) + { + return HTSCROLL_PAGELEFT; + } + else if (x >= thumbpos + thumbwidth && x < rect->right - butwidth) + { + return HTSCROLL_PAGERIGHT; + } + + return HTSCROLL_NONE; +} + +// +// For vertical scrollbars, rotate all coordinates by -90 degrees +// so that we can use the horizontal version of this function +// +static UINT GetVertScrollPortion(SCROLLBAR *sb, HWND hwnd, RECT *rect, int x, int y) +{ + UINT r; + + RotateRect(rect); + r = GetHorzScrollPortion(sb, hwnd, rect, y, x); + RotateRect(rect); + return r; +} + +// +// CUSTOM DRAW support +// +static LRESULT PostCustomPrePostPaint0(HWND hwnd, HDC hdc, SCROLLBAR *sb, UINT dwStage) +{ + return 0; +} + +static LRESULT PostCustomDrawNotify0(HWND hwnd, HDC hdc, UINT nBar, RECT *prect, UINT nItem, BOOL fMouseDown, BOOL fMouseOver, BOOL fInactive) +{ + return 0; +} + +// Depending on if we are supporting custom draw, either define +// a macro to the function name, or to nothing at all. If custom draw +// is turned off, then we can save ALOT of code space by binning all +// calls to the custom draw support. +// +#define PostCustomDrawNotify 1 ? (void)0 : PostCustomDrawNotify0 +#define PostCustomPrePostPaint 1 ? (void)0 : PostCustomPrePostPaint0 + +static LRESULT PostMouseNotify0(HWND hwnd, UINT msg, UINT nBar, RECT *prect, UINT nCmdId, POINT pt) +{ + return 0; +} + +#ifdef NOTIFY_MOUSE +#define PostMouseNotify PostMouseNotify0 +#else +#define PostMouseNotify 1 ? (void)0 : PostMouseNotify0 +#endif + + + +// +// Draw a complete HORIZONTAL scrollbar in the given rectangle +// Don't draw any inserted buttons in this procedure +// +// uDrawFlags - hittest code, to say if to draw the +// specified portion in an active state or not. +// +// +static LRESULT NCDrawHScrollbar(SCROLLBAR *sb, HWND hwnd, HDC hdc, const RECT *rect, UINT uDrawFlags) +{ + SCROLLINFO *si; + RECT ctrl, thumb; + RECT sbm; + int butwidth = GetScrollMetric(sb, SM_SCROLL_LENGTH); + int scrollwidth = rect->right - rect->left; + int workingwidth = scrollwidth - butwidth * 2; + int thumbwidth = 0, thumbpos = 0; + BOOL fCustomDraw = 0; + + BOOL fMouseDownL = 0, fMouseOverL = 0; + BOOL fMouseDownR = 0, fMouseOverR = 0; + + COLORREF crCheck1 = GetSBForeColor(); + COLORREF crCheck2 = GetSBBackColor(); + COLORREF crInverse1 = WADlg_getColor(WADLG_SCROLLBAR_INV_FGCOLOR); + COLORREF crInverse2 = WADlg_getColor(WADLG_SCROLLBAR_INV_BGCOLOR); + + UINT uDEFlat = sb->fFlatScrollbar ? BF_FLAT : 0; + + //drawing flags to modify the appearance of the scrollbar buttons + UINT uLeftButFlags = DFCS_SCROLLLEFT; + UINT uRightButFlags = DFCS_SCROLLRIGHT; + + if (scrollwidth <= 0) + return 0; + + si = &sb->scrollInfo; + + if (hwnd != hwndCurCoolSB) + uDrawFlags = HTSCROLL_NONE; + // + // work out the thumb size and position + // + CalcThumbSize(sb, rect, &thumbwidth, &thumbpos); + + if (sb->fScrollFlags & ESB_DISABLE_LEFT) uLeftButFlags |= DFCS_INACTIVE; + if (sb->fScrollFlags & ESB_DISABLE_RIGHT) uRightButFlags |= DFCS_INACTIVE; + + //if we need to grey the arrows because there is no data to scroll + if (!IsScrollInfoActive(si) && !(sb->fScrollFlags & CSBS_THUMBALWAYS)) + { + uLeftButFlags |= DFCS_INACTIVE; + uRightButFlags |= DFCS_INACTIVE; + } + + if (hwnd == hwndCurCoolSB) + { + fMouseDownL = (uDrawFlags == HTSCROLL_LEFT); + fMouseDownR = (uDrawFlags == HTSCROLL_RIGHT); + } + + // + // Draw the scrollbar now + // + if (scrollwidth > butwidth*2) + { + //LEFT ARROW + SetRect(&ctrl, rect->left, rect->top, rect->left + butwidth, rect->bottom); + + RotateRect0(sb, &ctrl); + + if (fCustomDraw) + PostCustomDrawNotify(hwnd, hdc, sb->nBarType, &ctrl, SB_LINELEFT, fMouseDownL, fMouseOverL, uLeftButFlags & DFCS_INACTIVE); + else + DrawScrollArrow(sb, hdc, &ctrl, uLeftButFlags, fMouseDownL, fMouseOverL); + + RotateRect0(sb, &ctrl); + + //MIDDLE PORTION + //if we can fit the thumbbar in, then draw it + if (thumbwidth > 0 && thumbwidth <= workingwidth + && IsScrollInfoActive(si) && ((sb->fScrollFlags & ESB_DISABLE_BOTH) != ESB_DISABLE_BOTH)) + { + //Draw the scrollbar margin above the thumb + SetRect(&sbm, rect->left + butwidth, rect->top, thumbpos, rect->bottom); + + RotateRect0(sb, &sbm); + + if (fCustomDraw) + { + PostCustomDrawNotify(hwnd, hdc, sb->nBarType, &sbm, SB_PAGELEFT, uDrawFlags == HTSCROLL_PAGELEFT, FALSE, FALSE); + } + else + { + if (uDrawFlags == HTSCROLL_PAGELEFT) + DrawCheckedRect(hdc, &sbm, crInverse1, crInverse2); + else + DrawCheckedRect(hdc, &sbm, crCheck1, crCheck2); + + } + + RotateRect0(sb, &sbm); + + //Draw the margin below the thumb + sbm.left = thumbpos + thumbwidth; + sbm.right = rect->right - butwidth; + + RotateRect0(sb, &sbm); + if (fCustomDraw) + { + PostCustomDrawNotify(hwnd, hdc, sb->nBarType, &sbm, SB_PAGERIGHT, uDrawFlags == HTSCROLL_PAGERIGHT, 0, 0); + } + else + { + if (uDrawFlags == HTSCROLL_PAGERIGHT) + DrawCheckedRect(hdc, &sbm, crInverse1, crInverse2); + else + DrawCheckedRect(hdc, &sbm, crCheck1, crCheck2); + + } + RotateRect0(sb, &sbm); + + //Draw the THUMB finally + SetRect(&thumb, thumbpos, rect->top, thumbpos + thumbwidth, rect->bottom); + + RotateRect0(sb, &thumb); + + if (fCustomDraw) + { + PostCustomDrawNotify(hwnd, hdc, sb->nBarType, &thumb, SB_THUMBTRACK, uDrawFlags == HTSCROLL_THUMB, uHitTestPortion == HTSCROLL_THUMB, FALSE); + } + else + { + int track = 0; + if (uCurrentScrollbar == (UINT)sb->nBarType) track = GetScrollWndFromHwnd(hwnd)->fThumbTracking; + DrawBlankButton(hdc, &thumb, uDEFlat, track, sb->nBarType == SB_VERT); + } + RotateRect0(sb, &thumb); + + } + //otherwise, just leave that whole area blank + else + { + OffsetRect(&ctrl, butwidth, 0); + ctrl.right = rect->right - butwidth; + + //if we always show the thumb covering the whole scrollbar, + //then draw it that way + if (!IsScrollInfoActive(si) && (sb->fScrollFlags & CSBS_THUMBALWAYS) + && ctrl.right - ctrl.left > sb->nMinThumbSize) + { + //leave a 1-pixel gap between the thumb + right button + ctrl.right --; + RotateRect0(sb, &ctrl); + + if (fCustomDraw) + PostCustomDrawNotify(hwnd, hdc, sb->nBarType, &ctrl, SB_THUMBTRACK, fMouseDownL, FALSE, FALSE); + else + { + DrawBlankButton(hdc, &ctrl, uDEFlat, 0, sb->nBarType == SB_VERT); + + } + RotateRect0(sb, &ctrl); + + //draw the single-line gap + ctrl.left = ctrl.right; + ctrl.right += 1; + + RotateRect0(sb, &ctrl); + + if (fCustomDraw) + PostCustomDrawNotify(hwnd, hdc, sb->nBarType, &ctrl, SB_PAGERIGHT, 0, 0, 0); + else + PaintRect(hdc, &ctrl, GetSysColor(COLOR_SCROLLBAR)); + + RotateRect0(sb, &ctrl); + } + //otherwise, paint a blank if the thumb doesn't fit in + else + { + RotateRect0(sb, &ctrl); + + if (fCustomDraw) + PostCustomDrawNotify(hwnd, hdc, sb->nBarType, &ctrl, SB_PAGERIGHT, 0, 0, 0); + else + DrawCheckedRect(hdc, &ctrl, crCheck1, crCheck2); + + RotateRect0(sb, &ctrl); + } + } + + //RIGHT ARROW + SetRect(&ctrl, rect->right - butwidth, rect->top, rect->right, rect->bottom); + + RotateRect0(sb, &ctrl); + + if (fCustomDraw) + PostCustomDrawNotify(hwnd, hdc, sb->nBarType, &ctrl, SB_LINERIGHT, fMouseDownR, fMouseOverR, uRightButFlags & DFCS_INACTIVE); + else + DrawScrollArrow(sb, hdc, &ctrl, uRightButFlags, fMouseDownR, fMouseOverR); + + RotateRect0(sb, &ctrl); + } + //not enough room for the scrollbar, so just draw the buttons (scaled in size to fit) + else + { + butwidth = scrollwidth / 2; + + //LEFT ARROW + SetRect(&ctrl, rect->left, rect->top, rect->left + butwidth, rect->bottom); + + RotateRect0(sb, &ctrl); + if (fCustomDraw) + PostCustomDrawNotify(hwnd, hdc, sb->nBarType, &ctrl, SB_LINELEFT, fMouseDownL, fMouseOverL, uLeftButFlags & DFCS_INACTIVE); + else + DrawScrollArrow(sb, hdc, &ctrl, uLeftButFlags, fMouseDownL, fMouseOverL); + RotateRect0(sb, &ctrl); + + //RIGHT ARROW + OffsetRect(&ctrl, scrollwidth - butwidth, 0); + + RotateRect0(sb, &ctrl); + if (fCustomDraw) + PostCustomDrawNotify(hwnd, hdc, sb->nBarType, &ctrl, SB_LINERIGHT, fMouseDownR, fMouseOverR, uRightButFlags & DFCS_INACTIVE); + else + DrawScrollArrow(sb, hdc, &ctrl, uRightButFlags, fMouseDownR, fMouseOverR); + RotateRect0(sb, &ctrl); + + //if there is a gap between the buttons, fill it with a solid color + //if(butwidth & 0x0001) + if (ctrl.left != rect->left + butwidth) + { + ctrl.left --; + ctrl.right -= butwidth; + RotateRect0(sb, &ctrl); + + if (fCustomDraw) + PostCustomDrawNotify(hwnd, hdc, sb->nBarType, &ctrl, SB_PAGERIGHT, 0, 0, 0); + else + DrawCheckedRect(hdc, &ctrl, crCheck1, crCheck2); + + RotateRect0(sb, &ctrl); + } + + } + + return fCustomDraw; +} + +// +// Draw a vertical scrollbar using the horizontal draw routine, but +// with the coordinates adjusted accordingly +// +static LRESULT NCDrawVScrollbar(SCROLLBAR *sb, HWND hwnd, HDC hdc, const RECT *rect, UINT uDrawFlags) +{ + LRESULT ret; + RECT rc; + + rc = *rect; + RotateRect(&rc); + ret = NCDrawHScrollbar(sb, hwnd, hdc, &rc, uDrawFlags); + RotateRect(&rc); + + return ret; +} + +// +// Generic wrapper function for the scrollbar drawing +// +static LRESULT NCDrawScrollbar(SCROLLBAR *sb, HWND hwnd, HDC hdc, const RECT *rect, UINT uDrawFlags) +{ + if (sb->nBarType == SB_HORZ) + return NCDrawHScrollbar(sb, hwnd, hdc, rect, uDrawFlags); + else + return NCDrawVScrollbar(sb, hwnd, hdc, rect, uDrawFlags); +} + +// +// Define these two for proper processing of NCPAINT +// NOT needed if we don't bother to mask the scrollbars we draw +// to prevent the old window procedure from accidently drawing over them +// +HDC CoolSB_GetDC(HWND hwnd, WPARAM wParam) +{ + // I just can't figure out GetDCEx, so I'll just use this: + return GetWindowDC(hwnd); +} + +static LRESULT NCPaint(ScrollWnd *sw, HWND hwnd, WPARAM wParam, LPARAM lParam) +{ + SCROLLBAR *sb; + HDC hdc; + HRGN hrgn; + RECT winrect, rect; + HRGN clip; + BOOL fUpdateAll = ((LONG)wParam == 1); + BOOL fCustomDraw = FALSE; + LRESULT ret; + DWORD dwStyle; + + GetWindowRect(hwnd, &winrect); + + //if entire region needs painting, then make a region to cover the entire window + hrgn = (HRGN)wParam; + + //hdc = GetWindowDC(hwnd); + hdc = CoolSB_GetDC(hwnd, wParam); + + // + // Only draw the horizontal scrollbar if the window is tall enough + // + sb = &sw->sbarHorz; + if (sb->fScrollVisible) + { + //get the screen coordinates of the whole horizontal scrollbar area + GetHScrollRect(sw, hwnd, &rect); + + //make the coordinates relative to the window for drawing + OffsetRect(&rect, -winrect.left, -winrect.top); + + if (uCurrentScrollbar == SB_HORZ) + fCustomDraw |= NCDrawHScrollbar(sb, hwnd, hdc, &rect, uScrollTimerPortion); + else + fCustomDraw |= NCDrawHScrollbar(sb, hwnd, hdc, &rect, HTSCROLL_NONE); + } + + // + // Only draw the vertical scrollbar if the window is wide enough to accomodate it + // + sb = &sw->sbarVert; + if (sb->fScrollVisible) + { + //get the screen cooridinates of the whole horizontal scrollbar area + GetVScrollRect(sw, hwnd, &rect); + + //make the coordinates relative to the window for drawing + OffsetRect(&rect, -winrect.left, -winrect.top); + + if (uCurrentScrollbar == SB_VERT) + fCustomDraw |= NCDrawVScrollbar(sb, hwnd, hdc, &rect, uScrollTimerPortion); + else + fCustomDraw |= NCDrawVScrollbar(sb, hwnd, hdc, &rect, HTSCROLL_NONE); + } + + //Call the default window procedure for WM_NCPAINT, with the + //new window region. ** region must be in SCREEN coordinates ** + dwStyle = GetWindowLongPtr(hwnd, GWL_STYLE); + + // If the window has WS_(H-V)SCROLL bits set, we should reset them + // to avoid windows taking the scrollbars into account. + // We temporarily set a flag preventing the subsecuent + // WM_STYLECHANGING/WM_STYLECHANGED to be forwarded to + // the original window procedure + if (dwStyle & (WS_VSCROLL | WS_HSCROLL)) + { + sw->bPreventStyleChange = TRUE; + SetWindowLongPtr(hwnd, GWL_STYLE, dwStyle & ~(WS_VSCROLL | WS_HSCROLL)); + } + + ret = (sw->fWndUnicode) ? CallWindowProcW(sw->oldproc, hwnd, WM_NCPAINT, (WPARAM)hrgn, lParam) : + CallWindowProcA(sw->oldproc, hwnd, WM_NCPAINT, (WPARAM)hrgn, lParam); + if (dwStyle & (WS_VSCROLL | WS_HSCROLL)) + { + SetWindowLong(hwnd, GWL_STYLE, dwStyle); + sw->bPreventStyleChange = FALSE; + } + + + // DRAW THE DEAD AREA + // only do this if the horizontal and vertical bars are visible + if (sw->sbarHorz.fScrollVisible && sw->sbarVert.fScrollVisible) + { + GetWindowRect(hwnd, &rect); + OffsetRect(&rect, -winrect.left, -winrect.top); + + rect.bottom -= sw->cyBottomEdge; + rect.top = rect.bottom - GetScrollMetric(&sw->sbarHorz, SM_CYHORZSB); + + if (sw->fLeftScrollbar) + { + rect.left += sw->cxLeftEdge; + rect.right = rect.left + GetScrollMetric(&sw->sbarVert, SM_CXVERTSB); + } + else + { + rect.right -= sw->cxRightEdge; + rect.left = rect.right - GetScrollMetric(&sw->sbarVert, SM_CXVERTSB); + } + + if (fCustomDraw) + PostCustomDrawNotify(hwnd, hdc, SB_BOTH, &rect, 32, 0, 0, 0); + else + { + //calculate the position of THIS window's dead area + //with the position of the PARENT window's client rectangle. + //if THIS window has been positioned such that its bottom-right + //corner sits in the parent's bottom-right corner, then we should + //show the sizing-grip. + //Otherwise, assume this window is not in the right place, and + //just draw a blank rectangle + RECT parent; + RECT rect2; + HWND hwndParent = GetParent(hwnd); + + GetClientRect(hwndParent, &parent); + MapWindowPoints(hwndParent, 0, (POINT *)&parent, 2); + + CopyRect(&rect2, &rect); + OffsetRect(&rect2, winrect.left, winrect.top); + + if (!sw->fLeftScrollbar && parent.right == rect2.right + sw->cxRightEdge && parent.bottom == rect2.bottom + sw->cyBottomEdge + || sw->fLeftScrollbar && parent.left == rect2.left - sw->cxLeftEdge && parent.bottom == rect2.bottom + sw->cyBottomEdge) + drawFrameControl(hdc, &rect, DFC_SCROLL, sw->fLeftScrollbar ? DFCS_SCROLLSIZEGRIPRIGHT : DFCS_SCROLLSIZEGRIP); + else + PaintRect(hdc, &rect, WADlg_getColor(WADLG_SCROLLBAR_DEADAREA_COLOR)); + } + } + + UNREFERENCED_PARAMETER(clip); + + ReleaseDC(hwnd, hdc); + return ret; +} + +// +// Need to detect if we have clicked in the scrollbar region or not +// +static LRESULT NCHitTest(ScrollWnd *sw, HWND hwnd, WPARAM wParam, LPARAM lParam) +{ + RECT hrect; + RECT vrect; + POINT pt; + + pt.x = GET_X_LPARAM(lParam); + pt.y = GET_Y_LPARAM(lParam); + + //work out exactly where the Horizontal and Vertical scrollbars are + GetHScrollRect(sw, hwnd, &hrect); + GetVScrollRect(sw, hwnd, &vrect); + + //Clicked in the horizontal scrollbar area + if (sw->sbarHorz.fScrollVisible && PtInRect(&hrect, pt)) + { + return HTHSCROLL; + } + //Clicked in the vertical scrollbar area + else if (sw->sbarVert.fScrollVisible && PtInRect(&vrect, pt)) + { + return HTVSCROLL; + } + //clicked somewhere else + else + { + return (sw->fWndUnicode) ? CallWindowProcW(sw->oldproc, hwnd, WM_NCHITTEST, wParam, lParam) : + CallWindowProcA(sw->oldproc, hwnd, WM_NCHITTEST, wParam, lParam); + + } +} + +// +// Return a HT* value indicating what part of the scrollbar was clicked +// Rectangle is not adjusted +// +static UINT GetHorzPortion(SCROLLBAR *sb, HWND hwnd, RECT *rect, int x, int y) +{ + RECT rc = *rect; + + if (y < rc.top || y >= rc.bottom) return HTSCROLL_NONE; + + //Now we have the rectangle for the scrollbar itself, so work out + //what part we clicked on. + return GetHorzScrollPortion(sb, hwnd, &rc, x, y); +} + +// +// Just call the horizontal version, with adjusted coordinates +// +static UINT GetVertPortion(SCROLLBAR *sb, HWND hwnd, RECT *rect, int x, int y) +{ + UINT ret; + RotateRect(rect); + ret = GetHorzPortion(sb, hwnd, rect, y, x); + RotateRect(rect); + return ret; +} + +// +// Wrapper function for GetHorzPortion and GetVertPortion +// +static UINT GetPortion(SCROLLBAR *sb, HWND hwnd, RECT *rect, int x, int y) +{ + if (sb->nBarType == SB_HORZ) + return GetHorzPortion(sb, hwnd, rect, x, y); + else if (sb->nBarType == SB_VERT) + return GetVertPortion(sb, hwnd, rect, x, y); + else + return HTSCROLL_NONE; +} + +// +// Input: rectangle of the total scrollbar area +// Output: adjusted to take the inserted buttons into account +// +static void GetRealHorzScrollRect(SCROLLBAR *sb, RECT *rect) +{ + if (sb->fButVisibleBefore) rect->left += sb->nButSizeBefore; + if (sb->fButVisibleAfter) rect->right -= sb->nButSizeAfter; +} + +// +// Input: rectangle of the total scrollbar area +// Output: adjusted to take the inserted buttons into account +// +static void GetRealVertScrollRect(SCROLLBAR *sb, RECT *rect) +{ + if (sb->fButVisibleBefore) rect->top += sb->nButSizeBefore; + if (sb->fButVisibleAfter) rect->bottom -= sb->nButSizeAfter; +} + +// +// Decide which type of scrollbar we have before calling +// the real function to do the job +// +static void GetRealScrollRect(SCROLLBAR *sb, RECT *rect) +{ + if (sb->nBarType == SB_HORZ) + { + GetRealHorzScrollRect(sb, rect); + } + else if (sb->nBarType == SB_VERT) + { + GetRealVertScrollRect(sb, rect); + } +} + +// +// Left button click in the non-client area +// +static LRESULT NCLButtonDown(ScrollWnd *sw, HWND hwnd, WPARAM wParam, LPARAM lParam) +{ + RECT rect, winrect; + HDC hdc; + SCROLLBAR *sb; + POINT pt; + + pt.x = GET_X_LPARAM(lParam); + pt.y = GET_Y_LPARAM(lParam); + + hwndCurCoolSB = hwnd; + + // + // HORIZONTAL SCROLLBAR PROCESSING + // + if (wParam == HTHSCROLL) + { + uScrollTimerMsg = WM_HSCROLL; + uCurrentScrollbar = SB_HORZ; + sb = &sw->sbarHorz; + + //get the total area of the normal Horz scrollbar area + GetHScrollRect(sw, hwnd, &rect); + uCurrentScrollPortion = GetHorzPortion(sb, hwnd, &rect, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); + } + // + // VERTICAL SCROLLBAR PROCESSING + // + else if (wParam == HTVSCROLL) + { + uScrollTimerMsg = WM_VSCROLL; + uCurrentScrollbar = SB_VERT; + sb = &sw->sbarVert; + + //get the total area of the normal Horz scrollbar area + GetVScrollRect(sw, hwnd, &rect); + uCurrentScrollPortion = GetVertPortion(sb, hwnd, &rect, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); + } + // + // NORMAL PROCESSING + // + else + { + uCurrentScrollPortion = HTSCROLL_NONE; + return (sw->fWndUnicode) ? CallWindowProcW(sw->oldproc, hwnd, WM_NCLBUTTONDOWN, wParam, lParam) : + CallWindowProcA(sw->oldproc, hwnd, WM_NCLBUTTONDOWN, wParam, lParam); + } + + // + // we can now share the same code for vertical + // and horizontal scrollbars + // + switch (uCurrentScrollPortion) + { + //inserted buttons to the left/right + case HTSCROLL_THUMB: + + //if the scrollbar is disabled, then do no further processing + if (!IsScrollbarActive(sb)) + return 0; + + GetRealScrollRect(sb, &rect); + RotateRect0(sb, &rect); + CalcThumbSize(sb, &rect, &nThumbSize, &nThumbPos); + RotateRect0(sb, &rect); + + //remember the bounding rectangle of the scrollbar work area + rcThumbBounds = rect; + + sw->fThumbTracking = TRUE; + sb->scrollInfo.nTrackPos = sb->scrollInfo.nPos; + + if (wParam == HTVSCROLL) + nThumbMouseOffset = pt.y - nThumbPos; + else + nThumbMouseOffset = pt.x - nThumbPos; + + nLastPos = -sb->scrollInfo.nPos; + nThumbPos0 = nThumbPos; + + //if(sb->fFlatScrollbar) + //{ + GetWindowRect(hwnd, &winrect); + OffsetRect(&rect, -winrect.left, -winrect.top); + hdc = GetWindowDC(hwnd); + NCDrawScrollbar(sb, hwnd, hdc, &rect, HTSCROLL_THUMB); + ReleaseDC(hwnd, hdc); + //} + + break; + + //Any part of the scrollbar + case HTSCROLL_LEFT: + if (sb->fScrollFlags & ESB_DISABLE_LEFT) return 0; + else goto target1; + + case HTSCROLL_RIGHT: + if (sb->fScrollFlags & ESB_DISABLE_RIGHT) return 0; + else goto target1; + + goto target1; + + case HTSCROLL_PAGELEFT: case HTSCROLL_PAGERIGHT: + +target1: + + //if the scrollbar is disabled, then do no further processing + if (!IsScrollbarActive(sb)) + break; + + //ajust the horizontal rectangle to NOT include + //any inserted buttons + GetRealScrollRect(sb, &rect); + + SendScrollMessage(hwnd, uScrollTimerMsg, uCurrentScrollPortion, 0); + + // Check what area the mouse is now over : + // If the scroll thumb has moved under the mouse in response to + // a call to SetScrollPos etc, then we don't hilight the scrollbar margin + if (uCurrentScrollbar == SB_HORZ) + uScrollTimerPortion = GetHorzScrollPortion(sb, hwnd, &rect, pt.x, pt.y); + else + uScrollTimerPortion = GetVertScrollPortion(sb, hwnd, &rect, pt.x, pt.y); + + GetWindowRect(hwnd, &winrect); + OffsetRect(&rect, -winrect.left, -winrect.top); + hdc = GetWindowDC(hwnd); + + //if we aren't hot-tracking, then don't highlight + //the scrollbar thumb unless we click on it + if (uScrollTimerPortion == HTSCROLL_THUMB) + uScrollTimerPortion = HTSCROLL_NONE; + NCDrawScrollbar(sb, hwnd, hdc, &rect, uScrollTimerPortion); + ReleaseDC(hwnd, hdc); + + //Post the scroll message!!!! + uScrollTimerPortion = uCurrentScrollPortion; + + //set a timer going on the first click. + //if this one expires, then we can start off a more regular timer + //to generate the auto-scroll behaviour + uScrollTimerId = SetTimer(hwnd, COOLSB_TIMERID1, COOLSB_TIMERINTERVAL1, 0); + sw->update(); + break; + default: + return (sw->fWndUnicode) ? CallWindowProcW(sw->oldproc, hwnd, WM_NCLBUTTONDOWN, wParam, lParam) : + CallWindowProcA(sw->oldproc, hwnd, WM_NCLBUTTONDOWN, wParam, lParam); + //return 0; + } + + SetCapture(hwnd); + return 0; +} + +// +// Left button released +// +static LRESULT LButtonUp(ScrollWnd *sw, HWND hwnd, WPARAM wParam, LPARAM lParam) +{ + RECT rect; + POINT pt; + RECT winrect; + + //current scrollportion is the button that we clicked down on + if (uCurrentScrollPortion != HTSCROLL_NONE) + { + SCROLLBAR *sb = &sw->sbarHorz; + lParam = GetMessagePos(); + ReleaseCapture(); + + GetWindowRect(hwnd, &winrect); + pt.x = GET_X_LPARAM(lParam); + pt.y = GET_Y_LPARAM(lParam); + + //emulate the mouse input on a scrollbar here... + if (uCurrentScrollbar == SB_HORZ) + { + //get the total area of the normal Horz scrollbar area + sb = &sw->sbarHorz; + GetHScrollRect(sw, hwnd, &rect); + } + else if (uCurrentScrollbar == SB_VERT) + { + //get the total area of the normal Horz scrollbar area + sb = &sw->sbarVert; + GetVScrollRect(sw, hwnd, &rect); + } + + //we need to do different things depending on if the + //user is activating the scrollbar itself, or one of + //the inserted buttons + switch (uCurrentScrollPortion) + { + //The scrollbar is active + case HTSCROLL_LEFT: case HTSCROLL_RIGHT: + case HTSCROLL_PAGELEFT: case HTSCROLL_PAGERIGHT: + case HTSCROLL_NONE: + + KillTimer(hwnd, uScrollTimerId); + + case HTSCROLL_THUMB: + + sw->update(); + + //In case we were thumb tracking, make sure we stop NOW + if (sw->fThumbTracking == TRUE) + { + SendScrollMessage(hwnd, uScrollTimerMsg, SB_THUMBPOSITION, nLastPos); + sw->fThumbTracking = FALSE; + } + + //send the SB_ENDSCROLL message now that scrolling has finished + SendScrollMessage(hwnd, uScrollTimerMsg, SB_ENDSCROLL, 0); + + //adjust the total scroll area to become where the scrollbar + //really is (take into account the inserted buttons) + GetRealScrollRect(sb, &rect); + OffsetRect(&rect, -winrect.left, -winrect.top); + HDC hdc = GetWindowDC(hwnd); + + //draw whichever scrollbar sb is + NCDrawScrollbar(sb, hwnd, hdc, &rect, HTSCROLL_NORMAL); + + ReleaseDC(hwnd, hdc); + break; + } + + //reset our state to default + uCurrentScrollPortion = HTSCROLL_NONE; + uScrollTimerPortion = HTSCROLL_NONE; + uScrollTimerId = 0; + + uScrollTimerMsg = 0; + uCurrentScrollbar = COOLSB_NONE; + + return 0; + } + else + { + /* + // Can't remember why I did this! + if(GetCapture() == hwnd) + { + ReleaseCapture(); + }*/ + } + + //sw->update(); + + return (sw->fWndUnicode) ? CallWindowProcW(sw->oldproc, hwnd, WM_LBUTTONUP, wParam, lParam) : + CallWindowProcA(sw->oldproc, hwnd, WM_LBUTTONUP, wParam, lParam); +} + +// +// This function is called whenever the mouse is moved and +// we are dragging the scrollbar thumb about. +// +static LRESULT ThumbTrackHorz(SCROLLBAR *sbar, HWND hwnd, int x, int y) +{ + POINT pt; + RECT rc, winrect, rc2; + COLORREF crCheck1 = GetSBForeColor(); + COLORREF crCheck2 = GetSBBackColor(); + HDC hdc; + int thumbpos = nThumbPos; + int pos; + int siMaxMin = 0; + UINT flatflag = sbar->fFlatScrollbar ? BF_FLAT : 0; + BOOL fCustomDraw = FALSE; + + SCROLLINFO *si; + si = &sbar->scrollInfo; + + pt.x = x; + pt.y = y; + + //draw the thumb at whatever position + rc = rcThumbBounds; + + SetRect(&rc2, rc.left - THUMBTRACK_SNAPDIST*2, rc.top - THUMBTRACK_SNAPDIST, + rc.right + THUMBTRACK_SNAPDIST*2, rc.bottom + THUMBTRACK_SNAPDIST); + + rc.left += GetScrollMetric(sbar, SM_CXHORZSB); + rc.right -= GetScrollMetric(sbar, SM_CXHORZSB); + + //if the mouse is not in a suitable distance of the scrollbar, + //then "snap" the thumb back to its initial position +#ifdef SNAP_THUMB_BACK + if (!PtInRect(&rc2, pt)) + { + thumbpos = nThumbPos0; + } + //otherwise, move the thumb to where the mouse is + else +#endif //SNAP_THUMB_BACK + { + //keep the thumb within the scrollbar limits + thumbpos = pt.x - nThumbMouseOffset; + if (thumbpos < rc.left) thumbpos = rc.left; + if (thumbpos > rc.right - nThumbSize) thumbpos = rc.right - nThumbSize; + } + + GetWindowRect(hwnd, &winrect); + + if (sbar->nBarType == SB_VERT) + RotateRect(&winrect); + + hdc = GetWindowDC(hwnd); + + OffsetRect(&rc, -winrect.left, -winrect.top); + thumbpos -= winrect.left; + + //draw the margin before the thumb + SetRect(&rc2, rc.left, rc.top, thumbpos, rc.bottom); + RotateRect0(sbar, &rc2); + + if (fCustomDraw) + PostCustomDrawNotify(hwnd, hdc, sbar->nBarType, &rc2, SB_PAGELEFT, 0, 0, 0); + else + DrawCheckedRect(hdc, &rc2, crCheck1, crCheck2); + + RotateRect0(sbar, &rc2); + + //draw the margin after the thumb + SetRect(&rc2, thumbpos + nThumbSize, rc.top, rc.right, rc.bottom); + + RotateRect0(sbar, &rc2); + + if (fCustomDraw) + PostCustomDrawNotify(hwnd, hdc, sbar->nBarType, &rc2, SB_PAGERIGHT, 0, 0, 0); + else + DrawCheckedRect(hdc, &rc2, crCheck1, crCheck2); + + RotateRect0(sbar, &rc2); + + //finally draw the thumb itelf. This is how it looks on win2000, anyway + SetRect(&rc2, thumbpos, rc.top, thumbpos + nThumbSize, rc.bottom); + + RotateRect0(sbar, &rc2); + + if (fCustomDraw) + PostCustomDrawNotify(hwnd, hdc, sbar->nBarType, &rc2, SB_THUMBTRACK, TRUE, TRUE, FALSE); + else + { + DrawBlankButton(hdc, &rc2, flatflag, 1, sbar->nBarType == SB_VERT); + } + + RotateRect0(sbar, &rc2); + ReleaseDC(hwnd, hdc); + + //post a SB_TRACKPOS message!!! + siMaxMin = si->nMax - si->nMin; + + if (siMaxMin > 0) + pos = MulDiv(thumbpos - rc.left, siMaxMin - si->nPage + 1, rc.right - rc.left - nThumbSize); + else + pos = thumbpos - rc.left; + + if (pos != nLastPos) + { + + if (sbar->flags & SCROLLBAR_LISTVIEW) + { + // only for listviews + if (sbar->nBarType == SB_HORZ) + { + SCROLLINFO info; + info.cbSize = sizeof(SCROLLINFO); + info.fMask = SIF_TRACKPOS; + if (GetScrollInfo(hwnd, SB_HORZ, &info)) + { + int nPos = info.nTrackPos; + SendMessage(hwnd, LVM_SCROLL, pos - nPos, 0); + SetScrollInfo(hwnd, sbar->nBarType, &info, FALSE); + } + } + else if (sbar->nBarType == SB_VERT) + { + SCROLLINFO info; + info.cbSize = sizeof(SCROLLINFO); + info.fMask = SIF_TRACKPOS; + if (GetScrollInfo(hwnd, SB_VERT, &info)) + { + int nPos = info.nTrackPos; + SendMessage(hwnd, LVM_SCROLL, 0, (pos - nPos)*14); //BIG FUCKO: get the text height size + SetScrollInfo(hwnd, sbar->nBarType, &info, FALSE); + } + } + } + else + { + si->nTrackPos = pos; + SCROLLINFO info; + info.cbSize = sizeof(SCROLLINFO); + info.fMask = SIF_TRACKPOS; + info.nTrackPos = pos; + + SetScrollInfo(hwnd, sbar->nBarType, &info, FALSE); + SendScrollMessage(hwnd, uScrollTimerMsg, SB_THUMBTRACK, pos); + } + } + + nLastPos = pos; + + return 0; +} + +// +// remember to rotate the thumb bounds rectangle!! +// +static LRESULT ThumbTrackVert(SCROLLBAR *sb, HWND hwnd, int x, int y) +{ + //sw->swapcoords = TRUE; + RotateRect(&rcThumbBounds); + ThumbTrackHorz(sb, hwnd, y, x); + RotateRect(&rcThumbBounds); + //sw->swapcoords = FALSE; + + return 0; +} + +// +// Called when we have set the capture from the NCLButtonDown(...) +// +static LRESULT MouseMove(ScrollWnd *sw, HWND hwnd, WPARAM wParam, LPARAM lParam) +{ + RECT rect; + POINT pt; + RECT winrect; + + if (sw->fThumbTracking == TRUE) + { + int x, y; + lParam = GetMessagePos(); + x = GET_X_LPARAM(lParam); + y = GET_Y_LPARAM(lParam); + + if (uCurrentScrollbar == SB_HORZ) + return ThumbTrackHorz(&sw->sbarHorz, hwnd, x, y); + + + else if (uCurrentScrollbar == SB_VERT) + return ThumbTrackVert(&sw->sbarVert, hwnd, x, y); + } + + if (uCurrentScrollPortion == HTSCROLL_NONE) + { + + return (sw->fWndUnicode) ? CallWindowProcW(sw->oldproc, hwnd, WM_MOUSEMOVE, wParam, lParam) : + CallWindowProcA(sw->oldproc, hwnd, WM_MOUSEMOVE, wParam, lParam); + } + else + { + LPARAM nlParam; + SCROLLBAR *sb = &sw->sbarHorz; + + nlParam = GetMessagePos(); + + GetWindowRect(hwnd, &winrect); + + pt.x = GET_X_LPARAM(nlParam); + pt.y = GET_Y_LPARAM(nlParam); + + //emulate the mouse input on a scrollbar here... + if (uCurrentScrollbar == SB_HORZ) + { + sb = &sw->sbarHorz; + } + else if (uCurrentScrollbar == SB_VERT) + { + sb = &sw->sbarVert; + } + + //get the total area of the normal scrollbar area + GetScrollRect(sw, sb->nBarType, hwnd, &rect); + + //see if we clicked in the inserted buttons / normal scrollbar + //thisportion = GetPortion(sb, hwnd, &rect, LOWORD(lParam), HIWORD(lParam)); + UINT thisportion = GetPortion(sb, hwnd, &rect, pt.x, pt.y); + + //we need to do different things depending on if the + //user is activating the scrollbar itself, or one of + //the inserted buttons + static UINT lastportion = 0; + switch (uCurrentScrollPortion) + { + //The scrollbar is active + case HTSCROLL_LEFT: case HTSCROLL_RIGHT: case HTSCROLL_THUMB: + case HTSCROLL_PAGELEFT: case HTSCROLL_PAGERIGHT: + case HTSCROLL_NONE: + + //adjust the total scroll area to become where the scrollbar + //really is (take into account the inserted buttons) + GetRealScrollRect(sb, &rect); + + OffsetRect(&rect, -winrect.left, -winrect.top); + HDC hdc = GetWindowDC(hwnd); + + if (thisportion != uCurrentScrollPortion) + { + uScrollTimerPortion = HTSCROLL_NONE; + + if (lastportion != thisportion) + NCDrawScrollbar(sb, hwnd, hdc, &rect, HTSCROLL_NORMAL); + } + //otherwise, draw the button in its depressed / clicked state + else + { + uScrollTimerPortion = uCurrentScrollPortion; + + if (lastportion != thisportion) + NCDrawScrollbar(sb, hwnd, hdc, &rect, thisportion); + } + + ReleaseDC(hwnd, hdc); + + break; + } + + + lastportion = thisportion; + + //must return zero here, because we might get cursor anomilies + //CallWindowProc(sw->oldproc, hwnd, WM_MOUSEMOVE, wParam, lParam); + return 0; + + } +} + +// +// We must allocate from in the non-client area for our scrollbars +// Call the default window procedure first, to get the borders (if any) +// allocated some space, then allocate the space for the scrollbars +// if they fit +// +static LRESULT NCCalcSize(ScrollWnd *sw, HWND hwnd, WPARAM wParam, LPARAM lParam) +{ + NCCALCSIZE_PARAMS *nccsp; + RECT *rect; + RECT oldrect; + //BOOL fCalcValidRects = (wParam == TRUE); + SCROLLBAR *sb; + LRESULT ret; + DWORD dwStyle; + + //Regardless of the value of fCalcValidRects, the first rectangle + //in the array specified by the rgrc structure member of the + //NCCALCSIZE_PARAMS structure contains the coordinates of the window, + //so we can use the exact same code to modify this rectangle, when + //wParam is TRUE and when it is FALSE. + nccsp = (NCCALCSIZE_PARAMS *)lParam; + rect = &nccsp->rgrc[0]; + oldrect = *rect; + + dwStyle = GetWindowLong(hwnd, GWL_STYLE); + + // TURN OFF SCROLL-STYLES. + if (dwStyle & (WS_VSCROLL | WS_HSCROLL)) + { + sw->bPreventStyleChange = TRUE; + SetWindowLong(hwnd, GWL_STYLE, dwStyle & ~(WS_VSCROLL | WS_HSCROLL)); + } + + //call the default procedure to get the borders allocated + + ret = (sw->fWndUnicode) ? CallWindowProcW(sw->oldproc, hwnd, WM_NCCALCSIZE, wParam, lParam) : + CallWindowProcA(sw->oldproc, hwnd, WM_NCCALCSIZE, wParam, lParam); + + // RESTORE PREVIOUS STYLES (if present at all) + if (dwStyle & (WS_VSCROLL | WS_HSCROLL)) + { + SetWindowLong(hwnd, GWL_STYLE, dwStyle); + sw->bPreventStyleChange = FALSE; + } + + // calculate what the size of each window border is, + sw->cxLeftEdge = rect->left - oldrect.left; + sw->cxRightEdge = oldrect.right - rect->right; + sw->cyTopEdge = rect->top - oldrect.top; + sw->cyBottomEdge = oldrect.bottom - rect->bottom; + + sb = &sw->sbarHorz; + + //if there is room, allocate some space for the horizontal scrollbar + //NOTE: Change the ">" to a ">=" to make the horz bar totally fill the + //window before disappearing + if ((sb->fScrollFlags & CSBS_VISIBLE) && +#ifdef COOLSB_FILLWINDOW + rect->bottom - rect->top >= GetScrollMetric(sb, SM_CYHORZSB)) +#else + rect->bottom - rect->top > GetScrollMetric(sb, SM_CYHORZSB)) +#endif + { + rect->bottom -= GetScrollMetric(sb, SM_CYHORZSB); + sb->fScrollVisible = TRUE; + } + else + sb->fScrollVisible = FALSE; + + sb = &sw->sbarVert; + + //if there is room, allocate some space for the vertical scrollbar + if ((sb->fScrollFlags & CSBS_VISIBLE) && + rect->right - rect->left >= GetScrollMetric(sb, SM_CXVERTSB)) + { + if (sw->fLeftScrollbar) + rect->left += GetScrollMetric(sb, SM_CXVERTSB); + else + rect->right -= GetScrollMetric(sb, SM_CXVERTSB); + + sb->fScrollVisible = TRUE; + } + else + sb->fScrollVisible = FALSE; + + //don't return a value unless we actually modify the other rectangles + //in the NCCALCSIZE_PARAMS structure. In this case, we return 0 + //no matter what the value of fCalcValidRects is + return ret;//FALSE; +} + +// +// used for hot-tracking over the scroll buttons +// +static LRESULT NCMouseMove(ScrollWnd *sw, HWND hwnd, WPARAM wHitTest, LPARAM lParam) +{ + //install a timer for the mouse-over events, if the mouse moves + //over one of the scrollbars + return (sw->fWndUnicode) ? CallWindowProcW(sw->oldproc, hwnd, WM_NCMOUSEMOVE, wHitTest, lParam) : + CallWindowProcA(sw->oldproc, hwnd, WM_NCMOUSEMOVE, wHitTest, lParam); +} + +// +// Timer routine to generate scrollbar messages +// +static LRESULT CoolSB_Timer(ScrollWnd *swnd, HWND hwnd, WPARAM wTimerId, LPARAM lParam) +{ + //let all timer messages go past if we don't have a timer installed ourselves + if (uScrollTimerId == 0 && uMouseOverId == 0) + { + return (swnd->fWndUnicode) ? CallWindowProcW(swnd->oldproc, hwnd, WM_TIMER, wTimerId, lParam) : + CallWindowProcA(swnd->oldproc, hwnd, WM_TIMER, wTimerId, lParam); + + } + + //if the first timer goes off, then we can start a more + //regular timer interval to auto-generate scroll messages + //this gives a slight pause between first pressing the scroll arrow, and the + //actual scroll starting + if (wTimerId == COOLSB_TIMERID1) + { + KillTimer(hwnd, uScrollTimerId); + uScrollTimerId = SetTimer(hwnd, COOLSB_TIMERID2, COOLSB_TIMERINTERVAL2, 0); + return 0; + } + //send the scrollbar message repeatedly + else if (wTimerId == COOLSB_TIMERID2) + { + //need to process a spoof WM_MOUSEMOVE, so that + //we know where the mouse is each time the scroll timer goes off. + //This is so we can stop sending scroll messages if the thumb moves + //under the mouse. + POINT pt; + GetCursorPos(&pt); + ScreenToClient(hwnd, &pt); + + MouseMove(swnd, hwnd, MK_LBUTTON, MAKELPARAM(pt.x, pt.y)); + + if (uScrollTimerPortion != HTSCROLL_NONE) + SendScrollMessage(hwnd, uScrollTimerMsg, uScrollTimerPortion, 0); + + swnd->update(); + return 0; + } + else + { + return (swnd->fWndUnicode) ? CallWindowProcW(swnd->oldproc, hwnd, WM_TIMER, wTimerId, lParam) : + CallWindowProcA(swnd->oldproc, hwnd, WM_TIMER, wTimerId, lParam); + } +} + +// +// We must intercept any calls to SetWindowLong, to check if +// left-scrollbars are taking effect or not +// +static LRESULT CoolSB_StyleChange(ScrollWnd *swnd, HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + STYLESTRUCT *ss = (STYLESTRUCT *)lParam; + + if (wParam == GWL_EXSTYLE) + { + if (ss->styleNew & WS_EX_LEFTSCROLLBAR) + swnd->fLeftScrollbar = TRUE; + else + swnd->fLeftScrollbar = FALSE; + } + + return (swnd->fWndUnicode) ? CallWindowProcW(swnd->oldproc, hwnd, msg, wParam, lParam) : + CallWindowProcA(swnd->oldproc, hwnd, msg, wParam, lParam); +} + +static UINT curTool = -1; +static LRESULT CoolSB_Notify(ScrollWnd *swnd, HWND hwnd, WPARAM wParam, LPARAM lParam) +{ + return (swnd->fWndUnicode) ? CallWindowProcW(swnd->oldproc, hwnd, WM_NOTIFY, wParam, lParam) : + CallWindowProcA(swnd->oldproc, hwnd, WM_NOTIFY, wParam, lParam); +} + +static LRESULT SendToolTipMessage0(HWND hwndTT, UINT message, WPARAM wParam, LPARAM lParam) +{ + return SendMessage(hwndTT, message, wParam, lParam); +} + +#ifdef COOLSB_TOOLTIPS +#define SendToolTipMessage SendToolTipMessage0 +#else +#define SendToolTipMessage 1 ? (void)0 : SendToolTipMessage0 +#endif + + +// +// We must intercept any calls to SetWindowLong, to make sure that +// the user does not set the WS_VSCROLL or WS_HSCROLL styles +// +static LRESULT CoolSB_SetCursor(ScrollWnd *swnd, HWND hwnd, WPARAM wParam, LPARAM lParam) +{ + return (swnd->fWndUnicode) ? CallWindowProcW(swnd->oldproc, hwnd, WM_SETCURSOR, wParam, lParam) : + CallWindowProcA(swnd->oldproc, hwnd, WM_SETCURSOR, wParam, lParam); +} + +// +// CoolScrollbar subclass procedure. +// Handle all messages needed to mimick normal windows scrollbars +// +LRESULT CALLBACK CoolSBWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + WNDPROC oldproc; + + ScrollWnd *swnd = GetScrollWndFromHwnd(hwnd); + + if (!swnd || !swnd->oldproc) + { + return (IsWindowUnicode(hwnd)) ? DefWindowProcW(hwnd, message, wParam, lParam) : + DefWindowProcA(hwnd, message, wParam, lParam); + } + + switch (message) + { + case WM_NCDESTROY: + //this should NEVER be called, because the user + //should have called Uninitialize() themselves. + + //However, if the user tries to call Uninitialize().. + //after this window is destroyed, this window's entry in the lookup + //table will not be there, and the call will fail + oldproc = swnd->oldproc; + delete(swnd); + + //we must call the original window procedure, otherwise it + //will never get the WM_NCDESTROY message, and it wouldn't + //be able to clean up etc. + return (IsWindowUnicode(hwnd)) ? CallWindowProcW(oldproc, hwnd, message, wParam, lParam) : CallWindowProcA(oldproc, hwnd, message, wParam, lParam); + + case WM_NCCALCSIZE: + return NCCalcSize(swnd, hwnd, wParam, lParam); + + case WM_NCPAINT: + return NCPaint(swnd, hwnd, wParam, lParam); + + case WM_NCHITTEST: + return NCHitTest(swnd, hwnd, wParam, lParam); + + case WM_NCRBUTTONDOWN: case WM_NCRBUTTONUP: + case WM_NCMBUTTONDOWN: case WM_NCMBUTTONUP: + if (wParam == HTHSCROLL || wParam == HTVSCROLL) + return 0; + else + break; + + case WM_NCLBUTTONDBLCLK: + //TRACE("WM_NCLBUTTONDBLCLK %d\n", count++); + if (wParam == HTHSCROLL || wParam == HTVSCROLL) + return NCLButtonDown(swnd, hwnd, wParam, lParam); + else + break; + + case WM_NCLBUTTONDOWN: + //TRACE("WM_NCLBUTTONDOWN%d\n", count++); + return NCLButtonDown(swnd, hwnd, wParam, lParam); + + + case WM_LBUTTONUP: + //TRACE("WM_LBUTTONUP %d\n", count++); + return LButtonUp(swnd, hwnd, wParam, lParam); + + case WM_NOTIFY: + return CoolSB_Notify(swnd, hwnd, wParam, lParam); + + //Mouse moves are received when we set the mouse capture, + //even when the mouse moves over the non-client area + case WM_MOUSEMOVE: + //TRACE("WM_MOUSEMOVE %d\n", count++); + return MouseMove(swnd, hwnd, wParam, lParam); + + case WM_TIMER: + return CoolSB_Timer(swnd, hwnd, wParam, lParam); + + //case WM_STYLECHANGING: + // return CoolSB_StyleChange(swnd, hwnd, WM_STYLECHANGING, wParam, lParam); + case WM_STYLECHANGED: + if (swnd->bPreventStyleChange) + { + // the NCPAINT handler has told us to eat this message! + return 0; + } + else + { + if (message == WM_STYLECHANGED) + return CoolSB_StyleChange(swnd, hwnd, WM_STYLECHANGED, wParam, lParam); + } + break; + + case WM_NCMOUSEMOVE: + { + static LPARAM lastpos = -1; + + //TRACE("WM_NCMOUSEMOVE %d\n", count++); + + //The problem with NCMOUSEMOVE is that it is sent continuously + //even when the mouse is stationary (under win2000 / win98) + // + //Tooltips don't like being sent a continous stream of mouse-moves + //if the cursor isn't moving, because they will think that the mouse + //is moving position, and the internal timer will never expire + // + if (lastpos != lParam) + { + lastpos = lParam; + } + } + + return NCMouseMove(swnd, hwnd, wParam, lParam); + + + case WM_SETCURSOR: + return CoolSB_SetCursor(swnd, hwnd, wParam, lParam); + + case WM_CAPTURECHANGED: + break; + + case WM_ERASEBKGND: + if (swnd && !swnd->fThumbTracking && uCurrentScrollPortion == HTSCROLL_NONE) + { + //disable windows scrollbar painting (fixes gfx repainting weirdness) + int style = GetWindowLong(hwnd, GWL_STYLE); + if (style&(WS_HSCROLL | WS_VSCROLL)) + { + SetWindowLong(hwnd, GWL_STYLE, style&~(WS_HSCROLL | WS_VSCROLL)); + } + + LRESULT ret = (swnd->fWndUnicode) ? CallWindowProcW(swnd->oldproc, hwnd, message, wParam, lParam) : + CallWindowProcA(swnd->oldproc, hwnd, message, wParam, lParam); + swnd->update(); + return ret; + } + break; + + //needed if we want mousewheel to work properly because we disable the styles in WM_ERASEBKGND... + case WM_MOUSEWHEEL: + { + int style = GetWindowLong(hwnd, GWL_STYLE); + swnd->bPreventStyleChange = TRUE; + SetWindowLong(hwnd, GWL_STYLE, style | (swnd->sbarHorz.fScrollVisible ? WS_HSCROLL : 0) | (swnd->sbarVert.fScrollVisible ? WS_VSCROLL : 0)); + LRESULT ret = (swnd->fWndUnicode) ? CallWindowProcW(swnd->oldproc, hwnd, message, wParam, lParam) : + CallWindowProcA(swnd->oldproc, hwnd, message, wParam, lParam); + SetWindowLongPtr(hwnd, GWL_STYLE, style); + swnd->bPreventStyleChange = FALSE; + return ret; + } + + case WM_USER + 0x3443: //manually sent by other windows (like columns header for ex.) + if (swnd) swnd->update(); + break; + + default: + break; + } + return (swnd->fWndUnicode) ? CallWindowProcW(swnd->oldproc, hwnd, message, wParam, lParam) : + CallWindowProcA(swnd->oldproc, hwnd, message, wParam, lParam); +} + +SCROLLBAR *GetScrollBarFromHwnd(HWND hwnd, UINT nBar) +{ + ScrollWnd *sw = GetScrollWndFromHwnd(hwnd); + + if (!sw) return 0; + + if (nBar == SB_HORZ) + return &sw->sbarHorz; + else if (nBar == SB_VERT) + return &sw->sbarVert; + else + return 0; +} + +// +// return the default minimum size of a scrollbar thumb +// +int WINAPI CoolSB_GetDefaultMinThumbSize(void) +{ + DWORD dwVersion = GetVersion(); + + // set the minimum thumb size for a scrollbar. This + // differs between NT4 and 2000, so need to check to see + // which platform we are running under + if (dwVersion < 0x80000000) // Windows NT/2000 + { + if (LOBYTE(LOWORD(dwVersion)) >= 5) + return MINTHUMBSIZE_2000; + else + return MINTHUMBSIZE_NT4; + } + else + { + return MINTHUMBSIZE_NT4; + } +} + +// +// Set the minimum size, in pixels, that the thumb box will shrink to. +// +BOOL WINAPI CoolSB_SetMinThumbSize(HWND hwnd, UINT wBar, UINT size) +{ + SCROLLBAR *sbar; + + if (!GetScrollWndFromHwnd(hwnd)) + return FALSE; + + if (size == -1) + size = CoolSB_GetDefaultMinThumbSize(); + + if ((wBar == SB_HORZ || wBar == SB_BOTH) && + (sbar = GetScrollBarFromHwnd(hwnd, SB_HORZ))) + { + sbar->nMinThumbSize = size; + } + + if ((wBar == SB_VERT || wBar == SB_BOTH) && + (sbar = GetScrollBarFromHwnd(hwnd, SB_VERT))) + { + sbar->nMinThumbSize = size; + } + + return TRUE; +} + +static void RedrawNonClient(HWND hwnd, BOOL fFrameChanged) +{ + if (fFrameChanged == FALSE) + { + SendMessage(hwnd, WM_NCPAINT, (WPARAM)1, 0); + } + else + { + SetWindowPos(hwnd, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE + | SWP_FRAMECHANGED | SWP_DRAWFRAME); + } +} + +ScrollWnd::ScrollWnd(HWND hwnd, int flags) +{ + SCROLLINFO *si; + RECT rect; + DWORD dwCurStyle; + + bars = 0; + oldproc = NULL; + memset(&sbarHorz, 0, sizeof(sbarHorz)); + memset(&sbarVert, 0, sizeof(sbarVert)); + sbarHorz.flags = flags; + sbarVert.flags = flags; + fThumbTracking = 0; + fLeftScrollbar = 0; + cxLeftEdge = cxRightEdge = cyTopEdge = cyBottomEdge = 0; + bPreventStyleChange = 0; + m_disable_hscroll = 0; + m_xp_theme_disabled = 0; + + m_hwnd = hwnd; + + GetClientRect(hwnd, &rect); + + si = &sbarHorz.scrollInfo; + si->cbSize = sizeof(SCROLLINFO); + si->fMask = SIF_ALL; + GetScrollInfo(hwnd, SB_HORZ, si); + + si = &sbarVert.scrollInfo; + si->cbSize = sizeof(SCROLLINFO); + si->fMask = SIF_ALL; + GetScrollInfo(hwnd, SB_VERT, si); + + //check to see if the window has left-aligned scrollbars + if (GetWindowLong(hwnd, GWL_EXSTYLE) & WS_EX_LEFTSCROLLBAR) + fLeftScrollbar = TRUE; + else + fLeftScrollbar = FALSE; + + dwCurStyle = GetWindowLong(hwnd, GWL_STYLE); + + SetProp(hwnd, szPropStr, (HANDLE)this); + + //scrollbars will automatically get enabled, even if + //they aren't to start with....sorry, but there isn't an + //easy alternative. + if (dwCurStyle & WS_HSCROLL) + sbarHorz.fScrollFlags = CSBS_VISIBLE; + + if (dwCurStyle & WS_VSCROLL) + sbarVert.fScrollFlags = CSBS_VISIBLE; + + //need to be able to distinguish between horizontal and vertical + //scrollbars in some instances + sbarHorz.nBarType = SB_HORZ; + sbarVert.nBarType = SB_VERT; + + sbarHorz.fFlatScrollbar = CSBS_NORMAL; + sbarVert.fFlatScrollbar = CSBS_NORMAL; + + //set the default arrow sizes for the scrollbars + sbarHorz.nArrowLength = SYSTEM_METRIC; + sbarHorz.nArrowWidth = SYSTEM_METRIC; + sbarVert.nArrowLength = SYSTEM_METRIC; + sbarVert.nArrowWidth = SYSTEM_METRIC; + + bPreventStyleChange = FALSE; + + fWndUnicode = IsWindowUnicode(hwnd); + oldproc = (WNDPROC)(LONG_PTR) ((fWndUnicode) ? SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (LONGX86)(LONG_PTR)CoolSBWndProc) : + SetWindowLongPtrA(hwnd, GWLP_WNDPROC, (LONGX86)(LONG_PTR)CoolSBWndProc)); + + CoolSB_SetMinThumbSize(hwnd, SB_BOTH, CoolSB_GetDefaultMinThumbSize()); + + //send the window a frame changed message to update the scrollbars + RedrawNonClient(hwnd, TRUE); + + //disable XP styles + if (SetWindowTheme && !m_xp_theme_disabled) + { + SetWindowTheme(m_hwnd, L" ", L" "); + m_xp_theme_disabled = 1; + + } + +} + +BOOL WINAPI CoolSB_ShowScrollBar(HWND hwnd, int wBar, BOOL fShow) +{ + SCROLLBAR *sbar; + BOOL bFailed = FALSE; + DWORD dwStyle = GetWindowLong(hwnd, GWL_STYLE); + + if ((wBar == SB_HORZ || wBar == SB_BOTH) && + (sbar = GetScrollBarFromHwnd(hwnd, SB_HORZ))) + { + sbar->fScrollFlags = sbar->fScrollFlags & ~CSBS_VISIBLE; + sbar->fScrollFlags |= (fShow == TRUE ? CSBS_VISIBLE : 0); + //bFailed = TRUE; + + if (fShow) SetWindowLong(hwnd, GWL_STYLE, dwStyle | WS_HSCROLL); + else SetWindowLong(hwnd, GWL_STYLE, dwStyle & ~WS_HSCROLL); + } + + if ((wBar == SB_VERT || wBar == SB_BOTH) && + (sbar = GetScrollBarFromHwnd(hwnd, SB_VERT))) + { + sbar->fScrollFlags = sbar->fScrollFlags & ~CSBS_VISIBLE; + sbar->fScrollFlags |= (fShow == TRUE ? CSBS_VISIBLE : 0); + //bFailed = TRUE; + + if (fShow) SetWindowLong(hwnd, GWL_STYLE, dwStyle | WS_VSCROLL); + else SetWindowLong(hwnd, GWL_STYLE, dwStyle & ~WS_VSCROLL); + } + + if (bFailed) + { + return FALSE; + } + else + { + SetWindowPos(hwnd, 0, 0, 0, 0, 0, + SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | + SWP_NOACTIVATE | SWP_FRAMECHANGED); + + return TRUE; + } +} + +ScrollWnd::~ScrollWnd() +{ + if (oldproc) + { + ((fWndUnicode) ? SetWindowLongPtrW(m_hwnd, GWLP_WNDPROC, (LONGX86)(LONG_PTR)oldproc) : + SetWindowLongPtrA(m_hwnd, GWLP_WNDPROC, (LONGX86)(LONG_PTR)oldproc)); + } + RemoveProp(m_hwnd, szPropStr); + RedrawNonClient(m_hwnd, TRUE); +} + +void ScrollWnd::update() +{ + int dohorz = 0, dovert = 0; + + SCROLLINFO tsi = {sizeof(SCROLLINFO), SIF_ALL, }; + + if (!m_disable_hscroll) + { + GetScrollInfo(m_hwnd, SB_HORZ, &tsi); + if (memcmp(&tsi, &sbarHorz.scrollInfo, sizeof(SCROLLINFO))) + { + memcpy(&sbarHorz.scrollInfo, &tsi, sizeof(SCROLLINFO)); + dohorz = 1; + } + } + GetScrollInfo(m_hwnd, SB_VERT, &tsi); + if (memcmp(&tsi, &sbarVert.scrollInfo, sizeof(SCROLLINFO))) + { + memcpy(&sbarVert.scrollInfo, &tsi, sizeof(SCROLLINFO)); + dovert = 1; + } + + BOOL fRecalcFrame = FALSE; + if (dohorz) updatesb(SB_HORZ, &fRecalcFrame); + if (dovert) updatesb(SB_VERT, &fRecalcFrame); + + if (dohorz || dovert) RedrawNonClient(m_hwnd, fRecalcFrame); +} + +void ScrollWnd::updatesb(int fnBar, BOOL *fRecalcFrame) +{ + SCROLLBAR *sbar = (fnBar == SB_HORZ ? &sbarHorz : &sbarVert); + SCROLLINFO *mysi = &sbar->scrollInfo; + + if (mysi->nPage > (UINT)mysi->nMax + || (mysi->nPage == (UINT)mysi->nMax && mysi->nMax == 0) + || mysi->nMax <= mysi->nMin) + { + if (sbar->fScrollVisible) + { + CoolSB_ShowScrollBar(m_hwnd, fnBar, FALSE); + *fRecalcFrame = TRUE; + } + } + else + { + if (!sbar->fScrollVisible && mysi->nPage > 0) + { + CoolSB_ShowScrollBar(m_hwnd, fnBar, TRUE); + *fRecalcFrame = TRUE; + } + else if (sbar->fScrollVisible && mysi->nPage == 0) + { + CoolSB_ShowScrollBar(m_hwnd, fnBar, FALSE); + *fRecalcFrame = TRUE; + } + } +} + +void ScrollWnd::disableHorzScroll() +{ + m_disable_hscroll = 1; + sbarHorz.fScrollFlags = 0; +} + +#if 0 // unused +int ScrollWnd::setScrollInfo(int fnBar, LPSCROLLINFO lpsi, BOOL fRedraw) +{ + SCROLLINFO *mysi; + SCROLLBAR *sbar; + BOOL fRecalcFrame = FALSE; + + if (!lpsi) + return FALSE; + + if (fnBar == SB_HORZ) mysi = &sbarHorz.scrollInfo; + else mysi = &sbarVert.scrollInfo; + + if (lpsi->fMask & SIF_RANGE) + { + mysi->nMin = lpsi->nMin; + mysi->nMax = lpsi->nMax; + } + + //The nPage member must specify a value from 0 to nMax - nMin +1. + if (lpsi->fMask & SIF_PAGE) + { + UINT t = (UINT)(mysi->nMax - mysi->nMin + 1); + mysi->nPage = min(max(0, lpsi->nPage), t); + } + + //The nPos member must specify a value between nMin and nMax - max(nPage - 1, 0). + if (lpsi->fMask & SIF_POS) + { + mysi->nPos = max(lpsi->nPos, mysi->nMin); + mysi->nPos = min((UINT)mysi->nPos, mysi->nMax - max(mysi->nPage - 1, 0)); + } + + sbar = GetScrollBarFromHwnd(m_hwnd, fnBar); + + if ((lpsi->fMask & SIF_DISABLENOSCROLL) || (sbar->fScrollFlags & CSBS_THUMBALWAYS)) + { + if (!sbar->fScrollVisible) + { + CoolSB_ShowScrollBar(m_hwnd, fnBar, TRUE); + fRecalcFrame = TRUE; + } + } + else + { + if (mysi->nPage > (UINT)mysi->nMax + || mysi->nPage == (UINT)mysi->nMax && mysi->nMax == 0 + || mysi->nMax <= mysi->nMin) + { + if (sbar->fScrollVisible) + { + CoolSB_ShowScrollBar(m_hwnd, fnBar, FALSE); + fRecalcFrame = TRUE; + } + } + else + { + if (!sbar->fScrollVisible) + { + CoolSB_ShowScrollBar(m_hwnd, fnBar, TRUE); + fRecalcFrame = TRUE; + } + } + } + + if (fRedraw && !fThumbTracking) + RedrawNonClient(m_hwnd, fRecalcFrame); + + return mysi->nPos; +} +#endif \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/scrollwnd.h b/Src/Plugins/General/gen_ml/scrollwnd.h new file mode 100644 index 00000000..91aa2ae6 --- /dev/null +++ b/Src/Plugins/General/gen_ml/scrollwnd.h @@ -0,0 +1,220 @@ +#ifndef _SCROLLWND_H +#define _SCROLLWND_H + +/* minimum size of scrollbar before inserted buttons are + hidden to make room when the window is sized too small */ +#define MIN_COOLSB_SIZE 24 + +/* min size of scrollbar when resizing a button, before the + resize is stopped because the scrollbar has gotten too small */ +#define MINSCROLLSIZE 50 + +/* a normal scrollbar "snaps" its scroll-thumb back into position if + you move the mouse too far away from the window, whilst you are + dragging the thumb, that is. #undeffing this results in the thumb + never snapping back into position, no matter how far away you move + the mouse */ +#define SNAP_THUMB_BACK + +/* distance (in pixels) the mouse must move away from the thumb + during tracking to cause the thumb bar to snap back to its + starting place. Has no effect unless SNAP_THUMB_BACK is defined */ +#define THUMBTRACK_SNAPDIST 128 + + +#include + +// To complement the exisiting SB_HORZ, SB_VERT, SB_BOTH +// scrollbar identifiers +#define COOLSB_NONE (-1) +#define SB_INSBUT (-2) + +// +// Arrow size defines +// +#define SYSTEM_METRIC (-1) + +// +// general scrollbar styles +// +// use the standard ESB_DISABLE_xxx flags to represent the +// enabled / disabled states. (defined in winuser.h) +// +#define CSBS_THUMBALWAYS 4 +#define CSBS_VISIBLE 8 + +//cool scrollbar styles for Flat scrollbars +#define CSBS_NORMAL 0 +#define CSBS_FLAT 1 +#define CSBS_HOTTRACKED 2 + +// +// Button mask flags for indicating which members of SCROLLBUT +// to use during a button insertion / modification +// +#define SBBF_TYPE 0x0001 +#define SBBF_ID 0x0002 +#define SBBF_PLACEMENT 0x0004 +#define SBBF_SIZE 0x0008 +#define SBBF_BITMAP 0x0010 +#define SBBF_ENHMETAFILE 0x0020 +//#define SBBF_OWNERDRAW 0x0040 //unused at present +#define SBBF_CURSOR 0x0080 +#define SBBF_BUTMINMAX 0x0100 +#define SBBF_STATE 0x0200 + +//button styles (states) +#define SBBS_NORMAL 0 +#define SBBS_PUSHED 1 +#define SBBS_CHECKED SBBS_PUSHED + +// +// scrollbar button types +// +#define SBBT_PUSHBUTTON 1 //standard push button +#define SBBT_TOGGLEBUTTON 2 //toggle button +#define SBBT_FIXED 3 //fixed button (non-clickable) +#define SBBT_FLAT 4 //blank area (flat, with border) +#define SBBT_BLANK 5 //blank area (flat, no border) +#define SBBT_DARK 6 //dark blank area (flat) +#define SBBT_OWNERDRAW 7 //user draws the button via a WM_NOTIFY + +#define SBBT_MASK 0x1f //mask off low 5 bits + +//button type modifiers +#define SBBM_RECESSED 0x0020 //recessed when clicked (like Word 97) +#define SBBM_LEFTARROW 0x0040 +#define SBBM_RIGHTARROW 0x0080 +#define SBBM_UPARROW 0x0100 +#define SBBM_DOWNARROW 0x0200 +#define SBBM_RESIZABLE 0x0400 +#define SBBM_TYPE2 0x0800 +#define SBBM_TYPE3 0x1000 +#define SBBM_TOOLTIPS 0x2000 //currently unused (define COOLSB_TOOLTIPS in userdefs.h) + +//button placement flags +#define SBBP_LEFT 1 +#define SBBP_RIGHT 2 +#define SBBP_TOP 1 //3 +#define SBBP_BOTTOM 2 //4 + + +// +// Button command notification codes +// for sending with a WM_COMMAND message +// +#define CSBN_BASE 0 +#define CSBN_CLICKED (1 + CSBN_BASE) +#define CSBN_HILIGHT (2 + CSBN_BASE) + +// +// Minimum size in pixels of a scrollbar thumb +// +#define MINTHUMBSIZE_NT4 9 +#define MINTHUMBSIZE_2000 7 + +//define some more hittest values for our cool-scrollbar +#define HTSCROLL_LEFT (SB_LINELEFT) +#define HTSCROLL_RIGHT (SB_LINERIGHT) +#define HTSCROLL_UP (SB_LINEUP) +#define HTSCROLL_DOWN (SB_LINEDOWN) +#define HTSCROLL_THUMB (SB_THUMBTRACK) +#define HTSCROLL_PAGEGUP (SB_PAGEUP) +#define HTSCROLL_PAGEGDOWN (SB_PAGEDOWN) +#define HTSCROLL_PAGELEFT (SB_PAGELEFT) +#define HTSCROLL_PAGERIGHT (SB_PAGERIGHT) + +#define HTSCROLL_NONE (-1) +#define HTSCROLL_NORMAL (-1) + +#define HTSCROLL_INSERTED (128) +#define HTSCROLL_PRE (32 | HTSCROLL_INSERTED) +#define HTSCROLL_POST (64 | HTSCROLL_INSERTED) + +// +// SCROLLBAR datatype. There are two of these structures per window +// +#define SCROLLBAR_LISTVIEW 1 // scrollbar is for a listview +typedef struct +{ + UINT fScrollFlags; //flags + BOOL fScrollVisible; //if this scrollbar visible? + SCROLLINFO scrollInfo; //positional data (range, position, page size etc) + + int nArrowLength; //perpendicular size (height of a horizontal, width of a vertical) + int nArrowWidth; //parallel size (width of horz, height of vert) + + //data for inserted buttons + int nButSizeBefore; //size to the left / above the bar + int nButSizeAfter; //size to the right / below the bar + + BOOL fButVisibleBefore; //if the buttons to the left are visible + BOOL fButVisibleAfter; //if the buttons to the right are visible + + int nBarType; //SB_HORZ / SB_VERT + + UINT fFlatScrollbar; //do we display flat scrollbars? + int nMinThumbSize; + + int flags; + +} SCROLLBAR; + +// +// PRIVATE INTERNAL FUNCTIONS +// +#define COOLSB_TIMERID1 65533 //initial timer +#define COOLSB_TIMERID2 65534 //scroll message timer +#define COOLSB_TIMERID3 -14 //mouse hover timer +#define COOLSB_TIMERINTERVAL1 300 +#define COOLSB_TIMERINTERVAL2 55 +#define COOLSB_TIMERINTERVAL3 20 //mouse hover time + +// +// direction: 0 - same axis as scrollbar (i.e. width of a horizontal bar) +// 1 - perpendicular dimesion (i.e. height of a horizontal bar) +// +#define SM_CXVERTSB 1 +#define SM_CYVERTSB 0 +#define SM_CXHORZSB 0 +#define SM_CYHORZSB 1 +#define SM_SCROLL_WIDTH 1 +#define SM_SCROLL_LENGTH 0 + +class ScrollWnd { +public: + ScrollWnd(HWND hwnd, int flags=0); + ~ScrollWnd(); + + void update(); + + HWND m_hwnd; + + UINT bars; //which of the scrollbars do we handle? SB_VERT / SB_HORZ / SB_BOTH + WNDPROC oldproc; //old window procedure to call for every message + BOOL fWndUnicode; + + SCROLLBAR sbarHorz; //one scrollbar structure each for + SCROLLBAR sbarVert; //the horizontal and vertical scrollbars + + BOOL fThumbTracking; // are we currently thumb-tracking?? + BOOL fLeftScrollbar; // support the WS_EX_LEFTSCROLLBAR style + + //size of the window borders + int cxLeftEdge, cxRightEdge; + int cyTopEdge, cyBottomEdge; + + // To prevent calling original WindowProc in response + // to our own temporary style change (fixes TreeView problem) + BOOL bPreventStyleChange; + + void updatesb(int fnBar, BOOL *fRecalcFrame); + void disableHorzScroll(); + + //int setScrollInfo(int fnBar, LPSCROLLINFO lpsi, BOOL fRedraw); + + int m_disable_hscroll; + int m_xp_theme_disabled; +}; + +#endif diff --git a/Src/Plugins/General/gen_ml/sendto.cpp b/Src/Plugins/General/gen_ml/sendto.cpp new file mode 100644 index 00000000..8efeb012 --- /dev/null +++ b/Src/Plugins/General/gen_ml/sendto.cpp @@ -0,0 +1,305 @@ +#include "main.h" +#include +#include "resource.h" +#include "config.h" +#include "sendto.h" +#include "api__gen_ml.h" +#include "../nu/AutoWideFn.h" +#include "../Winamp/strutil.h" + +SendToMenu::SendToMenu() +{ + activePlaylist=0; + m_addtolibrary=0; + _hm=0; + branch=0; + branch_pos=0; + _pos=0; + _len=0; + _start=0; + m_start=0; + m_len=0; + plugin_start=0; + plugin_len=0; +} + +SendToMenu::~SendToMenu() +{ +} + +void SendToMenu::onAddItem(mlAddToSendToStruct *ptr) +{ + if (--_len < 0) + return; + + MENUITEMINFOA mii= {sizeof(MENUITEMINFOA),}; + + if (ptr->desc && *ptr->desc == '-') + { + // cannot insert a seperator at the top + if (_pos <= 2) return; + mii.fMask = MIIM_TYPE; + mii.fType = MFT_SEPARATOR; + } + else + { + mii.fMask = MIIM_TYPE | MIIM_ID | MIIM_DATA | MIIM_STATE; + mii.fType = MFT_STRING; + if (ptr->desc && *ptr->desc == '#') + { + mii.fState = MFS_GRAYED; + mii.dwTypeData = ptr->desc+1; + } + else + { + mii.fState = MFS_ENABLED; + mii.dwTypeData = ptr->desc; + } + mii.wID = _start++; + mii.dwItemData = ptr->user32; + mii.cch = (UINT)strlen(mii.dwTypeData); + } + InsertMenuItemA(_hm,_pos++,TRUE,&mii); +} + +void SendToMenu::onAddItem(mlAddToSendToStructW *ptr) +{ + if (--_len < 0) + return; + + MENUITEMINFOW mii= {sizeof(MENUITEMINFOW),}; + + if (ptr->desc && *ptr->desc == L'-') + { + // cannot insert a seperator at the top + if (_pos <= 2) return; + mii.fMask = MIIM_TYPE; + mii.fType = MFT_SEPARATOR; + } + else + { + mii.fMask = MIIM_TYPE | MIIM_ID | MIIM_DATA | MIIM_STATE; + mii.fType = MFT_STRING; + if (ptr->desc && *ptr->desc == L'#') + { + mii.fState = MFS_GRAYED; + mii.dwTypeData = ptr->desc+1; + } + else + { + mii.fState = MFS_ENABLED; + mii.dwTypeData = ptr->desc; + } + mii.wID = _start++; + mii.dwItemData = ptr->user32; + mii.cch = (UINT)wcslen(mii.dwTypeData); + } + InsertMenuItemW(_hm,_pos++,TRUE,&mii); +} + +void SendToMenu::addItemToBranch(mlAddToSendToStructW *ptr) +{ + if (!branch) + return; + + if (--_len < 0) + return; + + MENUITEMINFOW mii= {sizeof(MENUITEMINFOW),}; + + if (ptr->desc && *ptr->desc == L'-') + { + // cannot insert a seperator at the top + if (_pos <= 2) return; + mii.fMask = MIIM_TYPE; + mii.fType = MFT_SEPARATOR; + } + else + { + mii.fMask = MIIM_TYPE | MIIM_ID | MIIM_DATA | MIIM_STATE; + mii.fType = MFT_STRING; + if (ptr->desc && *ptr->desc == L'#') + { + mii.fState = MFS_GRAYED; + mii.dwTypeData = ptr->desc+1; + } + else + { + mii.fState = MFS_ENABLED; + mii.dwTypeData = ptr->desc; + } + mii.wID = _start++; + mii.dwItemData = ptr->user32; + mii.cch = (UINT)wcslen(mii.dwTypeData); + } + InsertMenuItemW(branch,branch_pos++,TRUE,&mii); +} + +void SendToMenu::startBranch() +{ + branch=CreateMenu(); + branch_pos=0; +} + +void SendToMenu::endBranch(const wchar_t *name) +{ + MENUITEMINFOW mii= + { + sizeof(MENUITEMINFOW), + MIIM_TYPE|MIIM_DATA|MIIM_SUBMENU, + MFT_STRING, + MFS_ENABLED, + 0, + branch, + NULL, + NULL, + 0, + (LPWSTR)name, + }; + mii.cch= (UINT)wcslen(mii.dwTypeData); + InsertMenuItemW(_hm,_pos++,TRUE,&mii); + branch=0; +} + +void SendToMenu::buildmenu(HMENU hMenu, int type, int simple, int true_type, int start, int len) +{ + _start=start; + _len=len; + _hm=hMenu; + _pos=0; + m_start=_start; + plugin_start=0; + + while (DeleteMenu(hMenu,0,MF_BYPOSITION)); + + if (type == ML_TYPE_ITEMRECORDLIST || type == ML_TYPE_FILENAMES || + type == ML_TYPE_ITEMRECORDLISTW || type == ML_TYPE_FILENAMESW || + type == ML_TYPE_STREAMNAMES || type == ML_TYPE_CDTRACKS) + { + activePlaylist=_start++; _len--; + + // hardcode playlists :) + MENUITEMINFOW mii= + { + sizeof(MENUITEMINFO), + MIIM_TYPE|MIIM_ID, + MFT_STRING, + MFS_ENABLED, + (UINT)activePlaylist, + NULL, + NULL, + NULL, + 0, + }; + + if(simple == FALSE) + { + mii.dwTypeData = WASABI_API_LNGSTRINGW(IDS_ENQUEUE_IN_WINAMP); + mii.cch = (UINT)wcslen(mii.dwTypeData); + InsertMenuItemW(hMenu,_pos++,TRUE,&mii); + + mii.fType = MFT_SEPARATOR; + mii.wID=0; + InsertMenuItemW(hMenu,_pos++,TRUE,&mii); + } + } + + plugin_start=_start; + plugin_SendMessage(ML_MSG_ONSENDTOBUILD,type,reinterpret_cast(this),(true_type-1)); + plugin_len=_start - plugin_start; + m_len=_start-m_start; +} + +int SendToMenu::isourcmd(int id) +{ + return id >= m_start && id < m_start+m_len; +} + +void TAG_FMT_EXT(const wchar_t *filename, void *f, void *ff, void *p, wchar_t *out, int out_len, int extended); +wchar_t *itemrecordTagFunc(wchar_t *tag, void * p); +wchar_t *itemrecordWTagFunc(wchar_t *tag, void * p); +void fieldTagFuncFree(wchar_t * tag, void * p); + +int SendToMenu::handlecmd(HWND hwndParent, int id, int type, void *data) +{ + if (!isourcmd(id)) + return 0; + + if (plugin_start && id >= plugin_start && id < plugin_start+plugin_len) + { + MENUITEMINFO i={sizeof(i),MIIM_DATA,}; + GetMenuItemInfo(_hm,id,FALSE,&i); + return (INT)plugin_SendMessage(ML_MSG_ONSENDTOSELECT,type,reinterpret_cast(data),i.dwItemData); + } + else if (activePlaylist && id == activePlaylist) + { + if (type == ML_TYPE_FILENAMES || type == ML_TYPE_STREAMNAMES) + { + char *ptr=(char*)data; + + while (ptr && *ptr) + { + COPYDATASTRUCT cds; + cds.dwData = IPC_PLAYFILE; + cds.lpData = (void *) ptr; + cds.cbData = (DWORD)strlen((char *)cds.lpData)+1; // include space for null char + SendMessage(plugin.hwndParent,WM_COPYDATA,(WPARAM)NULL,(LPARAM)&cds); + ptr+=strlen(ptr)+1; + } + return 1; + } + else if (type == ML_TYPE_FILENAMESW || type == ML_TYPE_STREAMNAMESW) + { + wchar_t *ptr=(wchar_t*)data; + + while (ptr && *ptr) + { + COPYDATASTRUCT cds; + cds.dwData = IPC_PLAYFILEW; + cds.lpData = (void *) ptr; + cds.cbData = (DWORD)sizeof(wchar_t) * wcslen((wchar_t *)cds.lpData) + sizeof(wchar_t); // include space for null char + SendMessage(plugin.hwndParent,WM_COPYDATA,(WPARAM)NULL,(LPARAM)&cds); + ptr+=wcslen(ptr)+1; + } + return 1; + } + else if (type == ML_TYPE_ITEMRECORDLIST) + { + wchar_t title[FILETITLE_SIZE] = {0}; + + itemRecordList *obj = (itemRecordList *)data; + for (int i=0;i < obj->Size;i++) + { + AutoWideFn wfn( obj->Items[ i ].filename ); + + TAG_FMT_EXT(wfn, itemrecordTagFunc, fieldTagFuncFree, (void*)&obj->Items[i], title, FILETITLE_SIZE, 1); + + enqueueFileWithMetaStructW enqueueFile; + enqueueFile.filename = wfn; + enqueueFile.title = title; + enqueueFile.ext = NULL; + enqueueFile.length = obj->Items[i].length; + SendMessage(plugin.hwndParent, WM_WA_IPC, (WPARAM)&enqueueFile, IPC_PLAYFILEW); + } + return 1; + } + else if (type == ML_TYPE_ITEMRECORDLISTW) + { + wchar_t title[FILETITLE_SIZE] = {0}; + + itemRecordListW *obj = (itemRecordListW *)data; + for (int i=0;i < obj->Size;i++) + { + TAG_FMT_EXT(obj->Items[i].filename, itemrecordWTagFunc, fieldTagFuncFree, (void*)&obj->Items[i], title, FILETITLE_SIZE, 1); + enqueueFileWithMetaStructW enqueueFile; + enqueueFile.filename = obj->Items[i].filename; + enqueueFile.title = title; + enqueueFile.ext = NULL; + enqueueFile.length = obj->Items[i].length; + SendMessage(plugin.hwndParent, WM_WA_IPC, (WPARAM)&enqueueFile, IPC_PLAYFILEW); + } + return 1; + } + } + return 0; +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/sendto.h b/Src/Plugins/General/gen_ml/sendto.h new file mode 100644 index 00000000..e8097e4f --- /dev/null +++ b/Src/Plugins/General/gen_ml/sendto.h @@ -0,0 +1,31 @@ +#ifndef _SENDTO_H_ +#define _SENDTO_H_ + +class SendToMenu +{ +public: + SendToMenu(); + ~SendToMenu(); + void buildmenu(HMENU hMenu, int type, int simple, int true_type=0, int start=0x1500, int len=0x1500); + int isourcmd(int id); + int handlecmd(HWND hwndParent, int id, int type, void *data); // returns 1 if handled, 0 if not + + void onAddItem(mlAddToSendToStruct *ptr); + void onAddItem(mlAddToSendToStructW *ptr); + + void startBranch(); + void addItemToBranch(mlAddToSendToStructW *ptr); + void endBranch(const wchar_t *name); + +private: + HMENU _hm; + HMENU branch; + int branch_pos; + int _pos,_len,_start; + int m_start, m_len; + int m_addtolibrary; + int activePlaylist; + int plugin_start, plugin_len; +}; + +#endif//_SENDTO_H_ \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/service.cpp b/Src/Plugins/General/gen_ml/service.cpp new file mode 100644 index 00000000..8173b2e0 --- /dev/null +++ b/Src/Plugins/General/gen_ml/service.cpp @@ -0,0 +1,147 @@ +#include "main.h" +#include "service.h" +#include "api__gen_ml.h" +#include "../winamp/wa_ipc.h" +#include + +#define IS_INVALIDISPATCH(__disp) (((IDispatch *)1) == (__disp) || NULL == (__disp)) + +OmService::OmService(UINT nId) + : ref(1), id(nId), name(NULL), url(NULL) +{ +} + +OmService::~OmService() +{ + free(name); + free(url); +} + +HRESULT OmService::CreateInstance(UINT nId, LPCWSTR pszName, OmService **instance) +{ + if (NULL == instance) return E_POINTER; + *instance = NULL; + + OmService *service = new OmService(nId); + if (NULL == service) return E_OUTOFMEMORY; + + service->SetName(pszName); + wchar_t url[256] = {0}; + if (nId == SERVICE_LABS) + { + lstrcpynW(url, L"http://www.winamp.com/labs/pc", 256); + } + + service->SetUrl(url); + + *instance = service; + return S_OK; +} + +size_t OmService::AddRef() +{ + return InterlockedIncrement((LONG*)&ref); +} + +size_t OmService::Release() +{ + if (0 == ref) + return ref; + + LONG r = InterlockedDecrement((LONG*)&ref); + if (0 == r) + delete(this); + + return r; +} + +int OmService::QueryInterface(GUID interface_guid, void **object) +{ + if (NULL == object) return E_POINTER; + + if (IsEqualIID(interface_guid, IFC_OmService)) + *object = static_cast(this); + else + { + *object = NULL; + return E_NOINTERFACE; + } + + if (NULL == *object) + return E_UNEXPECTED; + + AddRef(); + return S_OK; +} + +unsigned int OmService::GetId() +{ + return id; +} + +HRESULT OmService::GetName(wchar_t *pszBuffer, int cchBufferMax) +{ + return StringCchCopyW(pszBuffer, cchBufferMax, name); +} + +HRESULT OmService::GetUrl(wchar_t *pszBuffer, int cchBufferMax) +{ + return StringCchCopyExW(pszBuffer, cchBufferMax, url, NULL, NULL, STRSAFE_IGNORE_NULLS); +} + +HRESULT OmService::GetIcon(wchar_t *pszBuffer, int cchBufferMax) +{ + return E_FAIL; +} + +HRESULT OmService::GetExternal(IDispatch **ppDispatch) +{ + if (NULL == ppDispatch) + return E_POINTER; + + *ppDispatch = NULL; + + // try JSAPI2 first + WCHAR szBuffer[64] = {0}; + if (SUCCEEDED(StringCchPrintfW(szBuffer, ARRAYSIZE(szBuffer), L"%u", id))) + { + *ppDispatch = (IDispatch*)SendMessage(plugin.hwndParent, WM_WA_IPC, (WPARAM)szBuffer, IPC_JSAPI2_GET_DISPATCH_OBJECT); + AGAVE_API_JSAPI2_SECURITY->SetBypass(szBuffer, true); + } + else + return E_FAIL; + + return S_OK; +} + +HRESULT OmService::SetName(LPCWSTR pszName) +{ + free(name); + name = wcsdup(pszName); + return S_OK; +} + +HRESULT OmService::SetUrl(LPCWSTR pszUrl) +{ + free(url); + url = wcsdup(pszUrl); + return S_OK; +} + +HRESULT OmService::SetIcon(LPCWSTR pszIcon) +{ + return S_OK; +} + +#define CBCLASS OmService +START_DISPATCH; +CB(ADDREF, AddRef) +CB(RELEASE, Release) +CB(QUERYINTERFACE, QueryInterface) +CB(API_GETID, GetId) +CB(API_GETNAME, GetName) +CB(API_GETURL, GetUrl) +CB(API_GETICON, GetIcon) +CB(API_GETEXTERNAL, GetExternal) +END_DISPATCH; +#undef CBCLASS \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/service.h b/Src/Plugins/General/gen_ml/service.h new file mode 100644 index 00000000..1070641b --- /dev/null +++ b/Src/Plugins/General/gen_ml/service.h @@ -0,0 +1,49 @@ +#pragma once + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include +#include "../ombrowser/ifc_omservice.h" + +#define SERVICE_SIGN_IN 750 +#define SERVICE_SOURCES 751 +#define SERVICE_LABS 752 + +class OmService : public ifc_omservice +{ +protected: + OmService(UINT nId); + ~OmService(); + +public: + static HRESULT CreateInstance(UINT nId, LPCWSTR pszName, OmService **instance); + +public: + /* Dispatchable */ + size_t AddRef(); + size_t Release(); + int QueryInterface(GUID interface_guid, void **object); + + /* ifc_omservice */ + unsigned int GetId(); + HRESULT GetName(wchar_t *pszBuffer, int cchBufferMax); + HRESULT GetUrl(wchar_t *pszBuffer, int cchBufferMax); + HRESULT GetExternal(IDispatch **ppDispatch); + HRESULT GetIcon(wchar_t *pszBuffer, int cchBufferMax); + +public: + HRESULT SetName(LPCWSTR pszName); + HRESULT SetUrl(LPCWSTR pszUrl); + HRESULT SetIcon(LPCWSTR pszIcon); + +protected: + RECVS_DISPATCH; + +protected: + ULONG ref; + UINT id; + LPWSTR name; + LPWSTR url; +}; \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/setup.cpp b/Src/Plugins/General/gen_ml/setup.cpp new file mode 100644 index 00000000..1bc0c50d --- /dev/null +++ b/Src/Plugins/General/gen_ml/setup.cpp @@ -0,0 +1,39 @@ +#include "./main.h" +#include "../winamp/setup/svc_setup.h" + +#include + +EXTERN_C _declspec(dllexport) BOOL RegisterSetup(HINSTANCE hInstance, api_service *waServices) +{ + WIN32_FIND_DATAW findData = {0}; + WCHAR szPath[MAX_PATH] = {0}, szBase[MAX_PATH] = {0}; + + if (0 == GetModuleFileNameW(hInstance, szBase, ARRAYSIZE(szBase))) + return 0; + + PathRemoveFileSpecW(szBase); + PathCombineW(szPath, szBase, L"ml_*.dll"); + + + HANDLE hFind = FindFirstFileW(szPath, &findData); + if (INVALID_HANDLE_VALUE == hFind) + return FALSE; + + do + { + PathCombineW(szPath, szBase, findData.cFileName); + HINSTANCE hLib = LoadLibraryW(szPath); + if (NULL != hLib) + { + Plugin_RegisterSetup fn = (Plugin_RegisterSetup)GetProcAddress(hLib, "RegisterSetup"); + if (NULL == fn || FALSE == fn(hLib, waServices)) + { + FreeModule(hLib); + } + } + } + while (FindNextFileW(hFind, &findData)); + FindClose(hFind); + + return FALSE; +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/skinexport.cpp b/Src/Plugins/General/gen_ml/skinexport.cpp new file mode 100644 index 00000000..5b1aba1a --- /dev/null +++ b/Src/Plugins/General/gen_ml/skinexport.cpp @@ -0,0 +1,72 @@ +#include "./skinExport.h" +#include "./skinning.h" +#include "./colors.h" +#include "./ml_rating.h" + +extern HMLIMGLST hmlilRating; + +EXTERN_C _declspec(dllexport) BOOL MlSkinWindow(HWND hwndToSkin, UINT style) +{ + return SkinWindow(hwndToSkin, style); +} +EXTERN_C _declspec(dllexport) BOOL MlSkinWindowEx(HWND hwndToSkin, INT type, UINT style) +{ + return SkinWindowEx(hwndToSkin, type, style); +} +EXTERN_C _declspec(dllexport) BOOL MlUnskinWindow(HWND hwndToUnskin) +{ + return UnskinWindow(hwndToUnskin); +} +EXTERN_C _declspec(dllexport) BOOL MlTrackSkinnedPopupMenuEx(HMENU hmenu, UINT fuFlags, INT x, INT y, HWND hwnd, LPTPMPARAMS lptpm, + HMLIMGLST hmlil, INT width, UINT skinStyle, MENUCUSTOMIZEPROC customProc, ULONG_PTR customParam) +{ + return TrackSkinnedPopupMenuEx(hmenu, fuFlags, x, y, hwnd, lptpm, hmlil, width, skinStyle, customProc, customParam); +} + +EXTERN_C _declspec(dllexport) BOOL MlIsSkinnedPopupEnabled(void) +{ + return IsSkinnedPopupEnabled(FALSE); +} + +EXTERN_C _declspec(dllexport) BOOL MlEnableSkinnedPopup(BOOL fEnable) +{ + return EnableSkinnedPopup(fEnable); +} + +EXTERN_C _declspec(dllexport) BOOL MlGetSkinColor(UINT uObject, UINT uPart, UINT uState, COLORREF *pColor) +{ + return SUCCEEDED(MLGetSkinColor(uObject, uPart, uState, pColor)); +} + +EXTERN_C _declspec(dllexport) void MlResetSkinColor(void) +{ + ResetColors(FALSE); +} + +EXTERN_C _declspec(dllexport) HANDLE MlInitSkinnedPopupHook(HWND hwndOwner, HMLIMGLST hmlil, INT width, UINT skinStyle, MENUCUSTOMIZEPROC customProc, ULONG_PTR customParam) +{ + return InitSkinnedPopupHook(hwndOwner, hmlil, width, skinStyle, customProc, customParam); +} + +EXTERN_C _declspec(dllexport) void MlRemoveSkinnedPopupHook(HANDLE hPopupHook) +{ + RemoveSkinnedPopupHook(hPopupHook); +} + +EXTERN_C _declspec(dllexport) BOOL MlRatingDraw(HDC hdc, INT maxValue, INT value, INT trackingVal, HMLIMGLST hmlil, INT index, RECT *prc, UINT fStyle) +{ + if (NULL == hmlil) hmlil = hmlilRating; + return MLRatingI_Draw(hdc, maxValue, value, trackingVal, hmlil, index, prc, fStyle); +} + +EXTERN_C _declspec(dllexport) LONG MlRatingHitTest(POINT pt, INT maxValue, HMLIMGLST hmlil, RECT *prc, UINT fStyle) +{ + if (NULL == hmlil) hmlil = hmlilRating; + return MLRatingI_HitTest(pt, maxValue, hmlil, prc, fStyle); +} + +EXTERN_C _declspec(dllexport) BOOL MlRatingCalcMinRect(INT maxValue, HMLIMGLST hmlil, RECT *prc) +{ + if (NULL == hmlil) hmlil = hmlilRating; + return MLRatingI_CalcMinRect(maxValue, hmlil, prc); +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/skinexport.h b/Src/Plugins/General/gen_ml/skinexport.h new file mode 100644 index 00000000..38200299 --- /dev/null +++ b/Src/Plugins/General/gen_ml/skinexport.h @@ -0,0 +1,31 @@ +#ifndef NULLOSFT_MEDIALIBRARY_SKINNING_EXPORT_HEADER +#define NULLOSFT_MEDIALIBRARY_SKINNING_EXPORT_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include + +typedef LPVOID HMLIMGLST; +typedef INT (CALLBACK *MENUCUSTOMIZEPROC)(INT /*action*/, HMENU /*hMenu*/, HDC /*hdc*/, LPARAM /*param*/, ULONG_PTR /*user*/); + +EXTERN_C _declspec(dllexport) BOOL MlSkinWindow(HWND hwndToSkin, UINT style); +EXTERN_C _declspec(dllexport) BOOL MlSkinWindowEx(HWND hwndToSkin, INT type, UINT style); +EXTERN_C _declspec(dllexport) BOOL MlUnskinWindow(HWND hwndToUnskin); +EXTERN_C _declspec(dllexport) BOOL MlTrackSkinnedPopupMenuEx(HMENU hmenu, UINT fuFlags, INT x, INT y, HWND hwnd, LPTPMPARAMS lptpm, + HMLIMGLST hmlil, INT width, UINT skinStyle, MENUCUSTOMIZEPROC customProc, ULONG_PTR customParam); +EXTERN_C _declspec(dllexport) BOOL MlIsSkinnedPopupEnabled(void); +EXTERN_C _declspec(dllexport) BOOL MlEnableSkinnedPopup(BOOL fEnable); +EXTERN_C _declspec(dllexport) HANDLE MlInitSkinnedPopupHook(HWND hwndOwner, HMLIMGLST hmlil, INT width, UINT skinStyle, MENUCUSTOMIZEPROC customProc, ULONG_PTR customParam); +EXTERN_C _declspec(dllexport) void MlRemoveSkinnedPopupHook(HANDLE hPopupHook); + +EXTERN_C _declspec(dllexport) BOOL MlGetSkinColor(UINT uObject, UINT uPart, UINT uState, COLORREF *pColor); +EXTERN_C _declspec(dllexport) void MlResetSkinColor(void); + +EXTERN_C _declspec(dllexport) BOOL MlRatingDraw(HDC hdc, INT maxValue, INT value, INT trackingVal, HMLIMGLST hmlil, INT index, RECT *prc, UINT fStyle); +EXTERN_C _declspec(dllexport) LONG MlRatingHitTest(POINT pt, INT maxValue, HMLIMGLST hmlil, RECT *prc, UINT fStyle); +EXTERN_C _declspec(dllexport) BOOL MlRatingCalcMinRect(INT maxValue, HMLIMGLST hmlil, RECT *prc); + + +#endif //NULLOSFT_MEDIALIBRARY_SKINNING_EXPORT_HEADER \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/skinnedbutton.cpp b/Src/Plugins/General/gen_ml/skinnedbutton.cpp new file mode 100644 index 00000000..db5f046b --- /dev/null +++ b/Src/Plugins/General/gen_ml/skinnedbutton.cpp @@ -0,0 +1,1343 @@ +#include "main.h" +#include "./skinnedbutton.h" +#include "../winamp/wa_dlg.h" +#include "./skinning.h" +#include "./stockobjects.h" +#include "./colors.h" +#include "./ml_imagefilter.h" +#include "./ml_imagelist.h" +#include "./resource.h" + + +#include +#include + +#define MARGIN_TOP 2 +#define MARGIN_BOTTOM 2 +#define MARGIN_LEFT 6 +#define MARGIN_RIGHT 6 +#define IMAGE_SPACE 3 +#define SPLITAREA_WIDTH 19 +#define SPLITAREA_SPACE -5 +#define SPLITAREA_SPACE_ALT -3 + +#define IMAGEFILTER_BLEND 0x00 +#define IMAGEFILTER_BLENDPLUSCOLOR 0x01 + +// internaly used button styles +#define SWBS_SPLITSETMANUAL 0x10000000 +#define SWBS_SPLITHOVER 0x20000000 +#define SWBS_BUTTONHOVER 0x40000000 + +// additional colors +typedef struct _EXTENDEDCOLORTABLE +{ + BOOL updateRequired; + COLORREF rgbTextBk; + BOOL bTextBkUnified; + COLORREF rgbTextBkDown; + BOOL bTextBkDownUnified; + COLORREF rgbTextDisabled; + COLORREF rgbTextDisabled2; // disabled based on WNDBG + COLORREF rgbText15; + COLORREF rgbText15Down; + COLORREF rgbText75; + COLORREF rgbText75Down; + COLORREF rgbToolBkHigh; + COLORREF rgbToolBkPressed; + +} EXTENDEDCOLORTABLE; + +typedef struct _IMAGEFILTERPARAM +{ + BUTTONPAINTSTRUCT *pbps; + RECT *prcImage; +} IMAGEFILTERPARAM; + +extern HMLIMGFLTRMNGR hmlifMngr; // default gen_ml fitler manager + +static EXTENDEDCOLORTABLE extendedColors = { TRUE, 0, }; +static HWND hoverHWND = NULL; +static BOOL hoverSplit = FALSE; +static HMLIMGLST hmlilSplitter = NULL; +static IMAGEFILTERPARAM imagefilterParam; +static HWND lButtonDownHWND = NULL; + +static HMLIMGLST CreateSplitterImageList() +{ + HMLIMGLST hmlil = MLImageListI_Create(6, 4, MLILC_COLOR32, 2, 1, 3, hmlifMngr); + if (hmlil) + { + MLIMAGESOURCE_I mlis = {0}; + mlis.type = SRC_TYPE_PNG_I; + mlis.hInst = plugin.hDllInstance; + mlis.bpp = 32; + mlis.flags = ISF_FORCE_BPP_I; + mlis.lpszName = MAKEINTRESOURCEW(IDB_SPLITARROW); + MLImageListI_Add(hmlil, &mlis, MLIF_BUTTONBLENDPLUSCOLOR_UID, 0); + mlis.lpszName = MAKEINTRESOURCEW(IDB_SPLITARROW_PRESSED); + MLImageListI_Add(hmlil, &mlis, MLIF_BUTTONBLENDPLUSCOLOR_UID, 0); + } + return hmlil; +} + +static void DrawButtonImage(HMLIMGLST hmlil, INT hmlilIndex, RECT *prcImage, BUTTONPAINTSTRUCT *pbps) +{ + INT offset; + + HIMAGELIST himl = MLImageListI_GetRealList(hmlil); + imagefilterParam.pbps = pbps; + imagefilterParam.prcImage = prcImage; + + if (BST_PUSHED & pbps->buttonState) + offset = 10000; + else if (WS_DISABLED & pbps->windowStyle) + offset = 20000; + else if (SWBS_BUTTONHOVER & pbps->skinnedStyle) + offset = 30000; + else + offset = 0; + + BOOL bUN; + if (SWBS_TOOLBAR & pbps->skinnedStyle) + { + bUN = TRUE; + offset += 40000; + if (offset == 40000 && (WS_BORDER & pbps->windowStyle)) + { + offset = 40000 + 30000; + } + } + else + { + bUN = ((BST_PUSHED & pbps->buttonState) ? + extendedColors.bTextBkDownUnified : + extendedColors.bTextBkUnified); + } + + COLORREF colorBk, colorFg; + colorFg = (COLORREF)(INT_PTR)WADlg_getBitmap(); + colorBk = (COLORREF)MAKELONG(prcImage->bottom - prcImage->top, ((bUN) ? prcImage->top : 0) + offset); + + INT index = MLImageListI_GetRealIndex(hmlil, hmlilIndex, colorBk, colorFg); + + if (-1 != index) + ImageList_Draw(himl, index, pbps->hdc, prcImage->left, prcImage->top, ILD_NORMAL); +} + +typedef struct _DIBDATA +{ + BYTE *pData; + INT bpp; + INT cx; + INT cy; + INT step; + LONG pitch; + BOOL bFree; +} DIBDATA; + +static BYTE* GetButtonDib(DIBDATA *pdd, BOOL bPressed) +{ + BITMAPINFOHEADER bi; + HBITMAP hbmp; + + if (!pdd) return NULL; + ZeroMemory(pdd, sizeof(DIBDATA)); + + hbmp = WADlg_getBitmap(); + if (!hbmp) return NULL; + + HDC hdc = (HDC)MlStockObjects_Get(CACHED_DC); + + ZeroMemory(&bi, sizeof(BITMAPINFOHEADER)); + bi.biSize = sizeof(BITMAPINFOHEADER); + if (GetDIBits(hdc, hbmp, 0, 0, NULL, (BITMAPINFO*)&bi, DIB_RGB_COLORS)) + { + bi.biCompression = BI_RGB; + + pdd->bFree = TRUE; + pdd->step = (bi.biBitCount>>3); + pdd->pitch = bi.biWidth * pdd->step; + while (pdd->pitch%4) pdd->pitch++; + + pdd->pData = (BYTE*)malloc(7 * pdd->pitch); + pdd->cx = bi.biWidth; + + INT o = (bPressed) ? 15 : 0; + pdd->cy = GetDIBits(hdc, hbmp, + ((bi.biHeight > 0) ? (bi.biHeight - (11 + o)) : (4 + o)), 7, + pdd->pData, (BITMAPINFO*)&bi, DIB_RGB_COLORS); + if (!pdd->cy) + { + free(pdd->pData); + pdd->pData = NULL; + } + } + return pdd->pData; +} + +COLORREF SkinnedButton::GetButtonBkColor(BOOL bPressed, BOOL *pbUnified) +{ + DIBDATA dib; + BOOL bUnified = TRUE; + ULONG r(0), g(0), b(0); + int cy; + + if (GetButtonDib(&dib, bPressed)) + { + cy = dib.cy; + BYTE *line = dib.pData + (dib.step * 4); + DWORD p = *(DWORD*)line; + + for (; cy-- != 0; line += dib.pitch) + { + r += line[2]; + g += line[1]; + b += line[0]; + if (bUnified && p != *(DWORD*)line) bUnified = FALSE; + } + if (dib.bFree) free(dib.pData); + } + + if (pbUnified) *pbUnified = bUnified; + cy = (dib.cy ? dib.cy : 1); + return RGB(r/cy, g/cy, b/cy); +} + +static void UpdateExtendedColorTable() +{ + COLORREF fg, bk; + float r; + + extendedColors.updateRequired = FALSE; + extendedColors.rgbTextBk = SkinnedButton::GetButtonBkColor(FALSE, &extendedColors.bTextBkUnified); + extendedColors.rgbTextBkDown = SkinnedButton::GetButtonBkColor(TRUE, &extendedColors.bTextBkDownUnified); + extendedColors.rgbTextDisabled = WADlg_getColor(WADLG_BUTTONFG); + + r = 77; + do + { + r -= 1; + fg = BlendColors(extendedColors.rgbTextDisabled, extendedColors.rgbTextBk, (COLORREF)r); + } while (GetColorDistance(fg, extendedColors.rgbTextBk) > 70); + extendedColors.rgbTextDisabled = fg; + + extendedColors.rgbTextDisabled2 = WADlg_getColor(WADLG_WNDFG); + bk = WADlg_getColor(WADLG_WNDBG); + + r = 77; + do + { + r -= 1; + fg = BlendColors(extendedColors.rgbTextDisabled2, bk, (COLORREF)r); + } while (GetColorDistance(fg, bk) > 70); + extendedColors.rgbTextDisabled2 = fg; + + fg = WADlg_getColor(WADLG_BUTTONFG); + extendedColors.rgbText15 = BlendColors(fg, extendedColors.rgbTextBk, 40); + extendedColors.rgbText75 = BlendColors(fg, extendedColors.rgbTextBk, 191); + extendedColors.rgbText15Down = BlendColors(fg, extendedColors.rgbTextBkDown, 40); + extendedColors.rgbText75Down = BlendColors(fg, extendedColors.rgbTextBkDown, 191); + + fg = WADlg_getColor(WADLG_HILITE); + extendedColors.rgbToolBkHigh = BlendColors(fg, bk, 64); + extendedColors.rgbToolBkPressed = BlendColors(fg, bk, 127); +} + +SkinnedButton::SkinnedButton(void) : SkinnedWnd(FALSE) +{ + ZeroMemory(&imagelist, sizeof(MLBUTTONIMAGELIST)); +} + +SkinnedButton::~SkinnedButton(void) +{ +} + +BOOL SkinnedButton::Attach(HWND hwndButton) +{ + if(!SkinnedWnd::Attach(hwndButton)) return FALSE; + SetType(SKINNEDWND_TYPE_BUTTON); + SetWindowPos(hwnd, NULL, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED); + return TRUE; +} + +void SkinnedButton::OnSkinChanged(BOOL bNotifyChildren, BOOL bRedraw) +{ + extendedColors.updateRequired = TRUE; + __super::OnSkinChanged(bNotifyChildren, bRedraw); +} + +static void PushButton_DrawBackground(BUTTONPAINTSTRUCT *pbps) +{ + if (SWBS_TOOLBAR & pbps->skinnedStyle) + { + SetBkColor(pbps->hdc, pbps->rgbTextBk); + ExtTextOutW(pbps->hdc, 0, 0, ETO_OPAQUE, &pbps->rcPaint, NULL, 0, NULL); + if (WS_BORDER & pbps->windowStyle) FrameRect(pbps->hdc, &pbps->rcClient, (HBRUSH)MlStockObjects_Get(HILITE_BRUSH)); + } + else + { + HBITMAP hbmp = WADlg_getBitmap(); + if (hbmp) + { + INT stretchModeOld; + INT yoffs = (BST_PUSHED & pbps->buttonState) ? 15 : 0; + HDC hdcSrc = (HDC)MlStockObjects_Get(CACHED_DC); + HBITMAP hbmpOld = (HBITMAP)SelectObject(hdcSrc, hbmp); + SetBrushOrgEx(pbps->hdc, pbps->rcClient.left, pbps->rcClient.top, NULL); + stretchModeOld = SetStretchBltMode(pbps->hdc, COLORONCOLOR); + + if (pbps->rcPaint.top <=4) + { + if (pbps->rcPaint.left <= 4) + BitBlt(pbps->hdc, pbps->rcPaint.left, pbps->rcPaint.top, 4 - pbps->rcPaint.left, 4 - pbps->rcPaint.top, + hdcSrc, pbps->rcPaint.left, pbps->rcPaint.top + yoffs, SRCCOPY); // top left + if (pbps->rcPaint.right > 4 && pbps->rcPaint.left < pbps->rcClient.right - 4) + StretchBlt(pbps->hdc, 4, pbps->rcClient.top, pbps->rcClient.right -4 -4, 4, + hdcSrc, 4, 0 + yoffs, 47-4-4, 4, SRCCOPY); // top center + + if (pbps->rcPaint.right >= pbps->rcClient.right - 4) + BitBlt(pbps->hdc, pbps->rcClient.right - 4, pbps->rcPaint.top, 4, 4 - pbps->rcPaint.top, + hdcSrc, 47 - 4, pbps->rcPaint.top + yoffs, SRCCOPY); // top right + } + if (pbps->rcPaint.left <= 4) + StretchBlt(pbps->hdc, 0, 4, 4, pbps->rcClient.bottom - 4 -4, hdcSrc, 0, 4 + yoffs, 4, 15-4-4, SRCCOPY); // left edge + if (pbps->rcPaint.right >= pbps->rcClient.right - 4) + StretchBlt(pbps->hdc, pbps->rcClient.right - 4, pbps->rcClient.top + 4, 4, pbps->rcClient.bottom - 4 - 4, hdcSrc, 47-4,4+yoffs, 4, 15-4-4,SRCCOPY); // right edge + + // center + if ((pbps->rcPaint.right > 4 && pbps->rcPaint.left < pbps->rcClient.right - 4) && + (pbps->rcPaint.bottom > 4 && pbps->rcPaint.top < pbps->rcClient.bottom- 4)) + StretchBlt(pbps->hdc, 4, 4, pbps->rcClient.right-4-4, pbps->rcClient.bottom -4 -4, + hdcSrc, 4,4+yoffs,47-4-4,15-4-4,SRCCOPY); + + if (pbps->rcPaint.bottom >= pbps->rcClient.bottom - 4) + { + if (pbps->rcPaint.left <= 4) + BitBlt(pbps->hdc, 0, pbps->rcClient.bottom-4,4,4, hdcSrc, 0, 15-4+yoffs, SRCCOPY); // bottom left + if (pbps->rcPaint.right > 4 && pbps->rcPaint.left < pbps->rcClient.right - 4) + StretchBlt(pbps->hdc, 4, pbps->rcClient.bottom-4, pbps->rcClient.right-4-4,4, hdcSrc, 4,15-4+yoffs,47-4-4,4,SRCCOPY); // bottom center + if (pbps->rcPaint.right >= pbps->rcClient.right - 4) + BitBlt(pbps->hdc, pbps->rcClient.right - 4, pbps->rcClient.bottom - 4, 4, 4, hdcSrc, 47-4, 15-4+yoffs, SRCCOPY); // bottom right + } + + SelectObject(hdcSrc, hbmpOld); + if (COLORONCOLOR != stretchModeOld) SetStretchBltMode(pbps->hdc, stretchModeOld); + } + } +} + +static void PushButton_CalulateRects(BUTTONPAINTSTRUCT *pbps, RECT *prcText, RECT *prcImage, INT imageWidth, INT imageHeight) +{ + SetRect(prcText, 0, 0, 0, 0); + SetRect(prcImage, 0, 0, 0, 0); + + if (!pbps->hImage && !pbps->cchText) return; + + if (0 != (0x0F & pbps->textFormat)) + { + INT l, t; + + if (pbps->cchText) + { + if (FALSE == DrawTextW(pbps->hdc, pbps->szText, pbps->cchText, prcText, + (~0x0F & pbps->textFormat) | DT_CALCRECT)) + { + SetRectEmpty(prcText); + } + + if (imageWidth) prcText->right += (imageWidth + IMAGE_SPACE); + } + else SetRect(prcText, 0, 0, imageWidth, imageHeight); + + if (DT_BOTTOM & pbps->textFormat) t = pbps->rcClient.bottom - prcText->bottom - MARGIN_BOTTOM; + else if (DT_VCENTER & pbps->textFormat) t = pbps->rcClient.top + ((pbps->rcClient.bottom - pbps->rcClient.top) - prcText->bottom)/2; + else t = pbps->rcClient.top + MARGIN_TOP; + + if (DT_RIGHT & pbps->textFormat) + { + l = pbps->rcClient.right - prcText->right; + if (SWBS_DROPDOWNBUTTON & pbps->skinnedStyle) + l -= (SPLITAREA_WIDTH + SPLITAREA_SPACE); + if (SWBS_SPLITBUTTON & pbps->skinnedStyle) + l -= (SPLITAREA_WIDTH + SPLITAREA_SPACE_ALT); + else + l -= MARGIN_RIGHT; + } + + else if (DT_CENTER & pbps->textFormat) + { + int w = pbps->rcClient.right - pbps->rcClient.left; + if (SWBS_DROPDOWNBUTTON & pbps->skinnedStyle) + { + w -= (SPLITAREA_WIDTH + SPLITAREA_SPACE); + } + else if (SWBS_SPLITBUTTON & pbps->skinnedStyle) + { + w -= (SPLITAREA_WIDTH + SPLITAREA_SPACE_ALT); + } + l = pbps->rcClient.left + (w - prcText->right)/2; + } + else + l = pbps->rcClient.left + MARGIN_LEFT; + + if (BST_PUSHED & pbps->buttonState && (0 == (BS_PUSHLIKE & pbps->windowStyle))) + { + if (SWBS_DROPDOWNBUTTON & pbps->skinnedStyle) t++; + else + { + l++; + if (SWBS_TOOLBAR & pbps->skinnedStyle) t++; + } + } + OffsetRect(prcText, l, t); + } + else + { + SetRect(prcText, MARGIN_LEFT , MARGIN_TOP, + pbps->rcClient.right - MARGIN_RIGHT, pbps->rcClient.bottom - MARGIN_BOTTOM); + + if (0 != (BST_PUSHED & pbps->buttonState)) + prcText +=1; + } + + // Fit Text Rect + if (prcText->top < (pbps->rcClient.top + MARGIN_TOP)) + { + LONG h = prcText->bottom - prcText->top; + prcText->top = pbps->rcClient.top + MARGIN_TOP; + if ((BST_PUSHED & pbps->buttonState) && (SWBS_DROPDOWNBUTTON & pbps->skinnedStyle)) prcText->top++; + prcText->bottom = prcText->top + h; + } + if (prcText->bottom > (pbps->rcClient.bottom - MARGIN_BOTTOM)) prcText->bottom = pbps->rcClient.bottom - MARGIN_BOTTOM; + + if (prcText->left < (pbps->rcClient.left + MARGIN_LEFT)) + { + LONG w = prcText->right - prcText->left; + prcText->left = pbps->rcClient.left + MARGIN_LEFT; + if ((BST_PUSHED & pbps->buttonState) && 0 == (SWBS_DROPDOWNBUTTON & pbps->skinnedStyle)) prcText->left++; + prcText->right = prcText->left + w - ((BST_PUSHED & pbps->buttonState) ? 1 : 0); + } + + int lim = pbps->rcClient.right; + if (SWBS_SPLITBUTTON & pbps->skinnedStyle) + { + lim -= (SPLITAREA_WIDTH + SPLITAREA_SPACE_ALT); + if (BST_PUSHED & pbps->buttonState) lim++; + } + else if (SWBS_DROPDOWNBUTTON & pbps->skinnedStyle) + lim -= (SPLITAREA_WIDTH + SPLITAREA_SPACE); + else + lim -= MARGIN_RIGHT; + + if (prcText->right > lim) prcText->right = lim; + + if (imageHeight && imageWidth) + { + INT top; + if (prcText->left + imageWidth > prcText->right) imageWidth = prcText->right - prcText->left; + + top = prcText->top; + if (prcText->top + imageHeight > prcText->bottom) + { + if ((DT_BOTTOM | DT_VCENTER) & pbps->textFormat) + { + if (DT_BOTTOM & pbps->textFormat) top = prcText->bottom -imageHeight; + else top += ((prcText->bottom - prcText->top) - imageHeight) / 2; + if (top < pbps->rcClient.top + MARGIN_TOP) top = pbps->rcClient.top + MARGIN_TOP; + } + if (top + imageHeight > (pbps->rcClient.bottom - MARGIN_BOTTOM)) imageHeight = pbps->rcClient.bottom - top - MARGIN_BOTTOM; + } + + if ((DT_VCENTER & pbps->textFormat) && (prcText->bottom - top) > imageHeight) + top += ((prcText->bottom - top) - imageHeight)/2; + + SetRect(prcImage, prcText->left, top, prcText->left + imageWidth, top + imageHeight); + prcText->left += (imageWidth + IMAGE_SPACE); + } +} + +void SkinnedButton::DrawPushButton(BUTTONPAINTSTRUCT *pbps) +{ + RECT rcText, rcImage; + SetRect(&rcText, 0, 0, 0, 0); + SetRect(&rcImage, 0, 0, 0, 0); + if (pbps->hImage) + { + if (-1 == pbps->imageIndex) + { + BITMAP bi; + if (GetObjectW(pbps->hImage, sizeof(BITMAP), &bi)) { rcImage.right = bi.bmWidth; rcImage.bottom = bi.bmHeight; } + } + else + { + MLImageListI_GetImageSize((HMLIMGLST)pbps->hImage, (INT*)&rcImage.right, (INT*)&rcImage.bottom); + } + } + + PushButton_CalulateRects(pbps, &rcText, &rcImage, rcImage.right, rcImage.bottom); + PushButton_DrawBackground(pbps); + IntersectClipRect(pbps->hdc, pbps->rcClient.left + MARGIN_LEFT, pbps->rcClient.top + MARGIN_TOP, + pbps->rcClient.right - MARGIN_RIGHT, pbps->rcClient.bottom - MARGIN_BOTTOM); + + if (rcImage.left != rcImage.right) + { + if (-1 == pbps->imageIndex) + { + HDC hdcSrc = (HDC)MlStockObjects_Get(CACHED_DC); + HBITMAP hbmpOld = (HBITMAP)SelectObject(hdcSrc, pbps->hImage); + BitBlt(pbps->hdc, rcImage.left, rcImage.top, rcImage.right - rcImage.left, rcImage.bottom - rcImage.top, hdcSrc, 0, 0, SRCCOPY); + SelectObject(hdcSrc, hbmpOld); + } + else DrawButtonImage((HMLIMGLST)pbps->hImage, pbps->imageIndex, &rcImage, pbps); + } + + if (0 != pbps->cchText) // draw text + { + INT bkModeOld = SetBkMode(pbps->hdc, TRANSPARENT); + DrawTextW(pbps->hdc, pbps->szText, pbps->cchText, &rcText, (~0x0F & pbps->textFormat) | DT_WORD_ELLIPSIS | DT_NOCLIP); + if (TRANSPARENT != bkModeOld) SetBkMode(pbps->hdc, bkModeOld); + } + + if ((BST_FOCUS & pbps->buttonState) ||(SWBS_SPLITBUTTON & pbps->skinnedStyle)) + { + HRGN rgn; + rgn = CreateRectRgnIndirect(&pbps->rcClient); + SelectClipRgn(pbps->hdc, rgn); + DeleteObject(rgn); + } + + if ((SWBS_SPLITBUTTON | SWBS_DROPDOWNBUTTON) & pbps->skinnedStyle) + { + if (NULL == hmlilSplitter) hmlilSplitter = CreateSplitterImageList(); + if (hmlilSplitter) + { + RECT rs; + INT left = pbps->rcClient.right - SPLITAREA_WIDTH; + if ((BST_PUSHED & pbps->buttonState) && 0 == (SWBS_DROPDOWNBUTTON & pbps->skinnedStyle)) left++; + + SetRect(&rs, 0, 0, 0, 0); + MLImageListI_GetImageSize(hmlilSplitter, (INT*)&rs.right, (INT*)&rs.bottom); + + OffsetRect(&rs, left + (SPLITAREA_WIDTH - rs.right)/2 - (SPLITAREA_WIDTH - rs.right)%2, + pbps->rcClient.top + (pbps->rcClient.bottom - pbps->rcClient.top - rs.bottom)/2 + 1 + + ((SWBS_SPLITPRESSED & pbps->skinnedStyle) ? 1 : 0)); + if (rs.left > (pbps->rcClient.left + 4) && rs.top > (pbps->rcClient.top + 2)) + DrawButtonImage(hmlilSplitter, ((SWBS_SPLITPRESSED | SWBS_SPLITHOVER) & pbps->skinnedStyle) ? 1 : 0, + &rs, pbps); + + if (SWBS_SPLITBUTTON & pbps->skinnedStyle) + { + HPEN pen = (HPEN)GetStockObject(DC_PEN); + HPEN penOld = (HPEN)SelectObject(pbps->hdc, pen); + + SetDCPenColor(pbps->hdc, (BST_PUSHED & pbps->buttonState) ? extendedColors.rgbText75Down : + ((WS_DISABLED & pbps->windowStyle)? extendedColors.rgbText15 : extendedColors.rgbText75)); + MoveToEx(pbps->hdc, left, pbps->rcClient.top + (MARGIN_TOP + 1), NULL); + LineTo(pbps->hdc, left, pbps->rcClient.bottom - (MARGIN_BOTTOM + 1)); + + left++; + SetDCPenColor(pbps->hdc, (BST_PUSHED & pbps->buttonState) ? extendedColors.rgbText15Down : extendedColors.rgbText15); + MoveToEx(pbps->hdc, left, pbps->rcClient.top + (MARGIN_TOP + 1), NULL); + LineTo(pbps->hdc, left, pbps->rcClient.bottom - (MARGIN_BOTTOM + 1)); + + if (SWBS_SPLITPRESSED & pbps->skinnedStyle) + { + MoveToEx(pbps->hdc, left, pbps->rcClient.top + (MARGIN_TOP + 1), NULL); + LineTo(pbps->hdc, pbps->rcClient.right - 2, pbps->rcClient.top + (MARGIN_TOP + 1)); + } + SelectObject(pbps->hdc, penOld); + } + } + } + + if (BST_FOCUS & pbps->buttonState) + { + if (SWBS_TOOLBAR & pbps->skinnedStyle) + { + FrameRect(pbps->hdc, &pbps->rcClient, (HBRUSH)MlStockObjects_Get(HILITE_BRUSH)); + } + else + { + COLORREF fg, bk; + RECT rf; + + SetRect(&rf, pbps->rcClient.left + 2, pbps->rcClient.top + 2, pbps->rcClient.right - 2, pbps->rcClient.bottom - 2); + if (SWBS_SPLITBUTTON & pbps->skinnedStyle) rf.right = pbps->rcClient.right - SPLITAREA_WIDTH - 1; + fg = SetTextColor(pbps->hdc, GetSysColor(COLOR_WINDOWTEXT)); + bk = SetBkColor(pbps->hdc, GetSysColor(COLOR_WINDOW)); + DrawFocusRect(pbps->hdc, &rf); + SetTextColor(pbps->hdc, fg); + SetBkColor(pbps->hdc, bk); + } + } +} + +void SkinnedButton::DrawGroupBox(BUTTONPAINTSTRUCT *pbps) +{ +} + +void SkinnedButton::DrawCheckBox(BUTTONPAINTSTRUCT *pbps) +{ + ExtTextOutW(pbps->hdc, 0, 0, ETO_OPAQUE, &pbps->rcPaint, L"", 0, NULL); + if (pbps->cchText) DrawTextW(pbps->hdc, pbps->szText, pbps->cchText, &pbps->rcClient, pbps->textFormat); +} + +void SkinnedButton::DrawRadioButton(BUTTONPAINTSTRUCT *pbps) +{ + ExtTextOutW(pbps->hdc, 0, 0, ETO_OPAQUE, &pbps->rcPaint, L"", 0, NULL); + if (pbps->cchText) DrawTextW(pbps->hdc, pbps->szText, pbps->cchText, &pbps->rcClient, pbps->textFormat); +} + +void SkinnedButton::DrawButton(BUTTONPAINTSTRUCT *pbps) +{ + HFONT hfo(NULL); + COLORREF rgbTextOld, rgbTextBkOld; + + if (pbps->hFont) hfo = (HFONT)SelectObject(pbps->hdc, pbps->hFont); + rgbTextOld = SetTextColor(pbps->hdc, pbps->rgbText); + rgbTextBkOld = SetBkColor(pbps->hdc, pbps->rgbTextBk); + + INT type; + if (BS_PUSHLIKE & pbps->windowStyle) type = BS_PUSHBUTTON; + else type = (BS_TYPEMASK & pbps->windowStyle); + + switch(type) + { + case BS_3STATE: + case BS_AUTO3STATE: + case BS_AUTOCHECKBOX: + case BS_CHECKBOX: + DrawCheckBox(pbps); + break; + case BS_RADIOBUTTON: + case BS_AUTORADIOBUTTON: + DrawRadioButton(pbps); + break; + case BS_GROUPBOX: + DrawGroupBox(pbps); + break; + case BS_PUSHBUTTON: + case BS_DEFPUSHBUTTON: + DrawPushButton(pbps); + break; + } + + SetTextColor(pbps->hdc, rgbTextOld); + SetBkColor(pbps->hdc, rgbTextBkOld); + if (NULL != hfo) SelectObject(pbps->hdc, hfo); +} + +void SkinnedButton::PrePaint(HWND hwnd, BUTTONPAINTSTRUCT *pbps, UINT skinStyle, UINT uiState) +{ + GetClientRect(hwnd, &pbps->rcClient); + pbps->windowStyle = (UINT)GetWindowLongPtrW(hwnd, GWL_STYLE); + pbps->buttonState = (UINT)SendMessageW(hwnd, BM_GETSTATE, 0, 0L); + pbps->skinnedStyle = skinStyle; + + if (hoverHWND == hwnd) + { + if (hoverSplit) pbps->skinnedStyle |= SWBS_SPLITHOVER; + else pbps->skinnedStyle |= SWBS_BUTTONHOVER; + } + + if ((BST_FOCUS & pbps->buttonState) && (0x01/*UISF_HIDEFOCUS*/ & uiState)) + { + pbps->buttonState &= ~BST_FOCUS; + } + + if (SWBS_DROPDOWNBUTTON & pbps->skinnedStyle) + { + if (hoverHWND == hwnd) pbps->skinnedStyle |= SWBS_BUTTONHOVER; + if (SWBS_SPLITPRESSED & pbps->skinnedStyle) pbps->buttonState |= BST_PUSHED; + } + + pbps->textFormat = 0; + pbps->cchText = (INT)SendMessageW(hwnd, WM_GETTEXTLENGTH, 0, 0L); + + if (!IsWindowEnabled(hwnd)) pbps->windowStyle |= WS_DISABLED; + if (extendedColors.updateRequired) UpdateExtendedColorTable(); + + if (BS_OWNERDRAW == (BS_TYPEMASK & pbps->windowStyle)) pbps->windowStyle = ((pbps->windowStyle & ~BS_OWNERDRAW) | BS_PUSHBUTTON); + + INT type; + if (BS_PUSHLIKE & pbps->windowStyle) type = BS_PUSHBUTTON; + else type = (BS_TYPEMASK & pbps->windowStyle); + + switch(type) + { + case BS_PUSHBUTTON: + case BS_DEFPUSHBUTTON: + if (SWBS_TOOLBAR & pbps->skinnedStyle) + { + BOOL bChecked = (BS_PUSHLIKE & pbps->windowStyle) ? (BST_CHECKED == SendMessageW(hwnd, BM_GETCHECK, 0, 0L)) : FALSE; + if ((BST_PUSHED & pbps->buttonState) || hoverHWND == hwnd || bChecked) pbps->windowStyle |= WS_BORDER; + + if (BST_PUSHED & pbps->buttonState) + pbps->rgbTextBk = extendedColors.rgbToolBkPressed; + else if (hoverHWND == hwnd || bChecked) pbps->rgbTextBk = extendedColors.rgbToolBkHigh; + else pbps->rgbTextBk = WADlg_getColor(WADLG_WNDBG); + + pbps->rgbText = WADlg_getColor(WADLG_WNDFG); + } + else + { + pbps->rgbTextBk = (BST_PUSHED & pbps->buttonState) ? extendedColors.rgbTextBkDown : extendedColors.rgbTextBk; + pbps->rgbText = (WS_DISABLED & pbps->windowStyle) ? extendedColors.rgbTextDisabled : WADlg_getColor(WADLG_BUTTONFG); + } + break; + default: + pbps->rgbTextBk = WADlg_getColor(WADLG_WNDBG); + pbps->rgbText = (WS_DISABLED & pbps->windowStyle) ? extendedColors.rgbTextDisabled2 : WADlg_getColor(WADLG_WNDFG); + break; + } + + if (pbps->cchText) + { + if (pbps->cchText > BUTTON_TEXT_MAX) pbps->cchText = BUTTON_TEXT_MAX; + SendMessageW(hwnd, WM_GETTEXT, (WPARAM)BUTTON_TEXT_MAX, (LPARAM)pbps->szText); + + pbps->hFont = (HFONT)SendMessageW(hwnd, WM_GETFONT, 0, 0L); + if (NULL == pbps->hFont) pbps->hFont = (HFONT)MlStockObjects_Get(DEFAULT_FONT); + + if ( 0 == (BS_MULTILINE & pbps->windowStyle)) pbps->textFormat |= DT_SINGLELINE; + if (0x02/*UISF_HIDEACCEL*/ & uiState) pbps->textFormat |= 0x00100000 /*DT_HIDEPREFIX*/; + } + + MLBUTTONIMAGELIST buttonIL; + if(MLSkinnedButton_GetImageList(hwnd, &buttonIL) && buttonIL.hmlil) + { + pbps->imageIndex = -1; + if (WS_DISABLED & pbps->windowStyle) pbps->imageIndex = buttonIL.disabledIndex; + else if (BST_PUSHED & pbps->buttonState) pbps->imageIndex = buttonIL.pressedIndex; + + if (-1 == pbps->imageIndex) + pbps->imageIndex = ((SWBS_BUTTONHOVER & pbps->skinnedStyle) && -1 != buttonIL.hoverIndex) ? buttonIL.hoverIndex : buttonIL.normalIndex; + + if (-1 != pbps->imageIndex) pbps->hImage = (HBITMAP)buttonIL.hmlil; + else pbps->hImage = NULL; + } + else + { + pbps->hImage = (HBITMAP)SendMessageW(hwnd, BM_GETIMAGE, IMAGE_BITMAP, 0L); + pbps->imageIndex = -1; + } + + if (pbps->cchText || pbps->hImage) + { + switch((BS_CENTER & pbps->windowStyle)) + { + case BS_LEFT: pbps->textFormat |= DT_LEFT; break; + case BS_RIGHT: pbps->textFormat |= DT_RIGHT; break; + case BS_CENTER: pbps->textFormat |= DT_CENTER; break; + case 0: + switch(BS_TYPEMASK & pbps->windowStyle) + { + case BS_PUSHBUTTON: + case BS_DEFPUSHBUTTON: pbps->textFormat |= DT_CENTER; break; + default: pbps->textFormat |= DT_LEFT; break; + } + break; + } + + switch((BS_VCENTER & pbps->windowStyle)) + { + case BS_TOP: pbps->textFormat |= DT_TOP; break; + case BS_BOTTOM: pbps->textFormat |= DT_BOTTOM; break; + case 0: + case BS_VCENTER: pbps->textFormat |= DT_VCENTER; break; + } + } +} + +void SkinnedButton::OnPaint() +{ + PAINTSTRUCT ps; + BUTTONPAINTSTRUCT bps; + + PrePaint(hwnd, &bps, style, uiState); + bps.hdc = BeginPaint(hwnd, &ps); + if (NULL == bps.hdc) return; + CopyRect(&bps.rcPaint, &ps.rcPaint); + DrawButton(&bps); + EndPaint(hwnd, &ps); +} + +void SkinnedButton::OnPrintClient(HDC hdc, UINT options) +{ + if ((PRF_CLIENT & options) && (0 == (PRF_CHECKVISIBLE & options) || IsWindowVisible(hwnd))) + { + BUTTONPAINTSTRUCT bps; + + PrePaint(hwnd, &bps, style, uiState); + bps.hdc = hdc; + CopyRect(&bps.rcPaint, &bps.rcClient); + DrawButton(&bps); + } +} + +void SkinnedButton::Emulate_LeftButtonDown(UINT flags, POINTS pts, BOOL forwardMessage) +{ + if(SWBS_SPLITBUTTON & style) + { + POINT pt; + RECT rc; + UINT os = (style & ~SWBS_SPLITSETMANUAL); + POINTSTOPOINT(pt, pts); + GetClientRect(hwnd, &rc); + rc.left = rc.right - SPLITAREA_WIDTH; + + style &= ~(SWBS_SPLITPRESSED | SWBS_SPLITSETMANUAL); + if (PtInRect(&rc, pt)) + { + style |= SWBS_SPLITPRESSED; + if (0 == (SWBS_TOOLBAR & style) && hwnd != GetFocus()) SetFocus(hwnd); + } + if (style != os) InvalidateRect(hwnd, NULL, FALSE); + if (SWBS_SPLITPRESSED & style) + { + return; + } + } + else if (SWBS_DROPDOWNBUTTON & style) + { + UINT os = (style & ~SWBS_SPLITSETMANUAL); + + style &= ~SWBS_SPLITSETMANUAL; + style |= SWBS_SPLITPRESSED; + + if ( 0 == (SWBS_TOOLBAR & style) && hwnd != GetFocus()) SetFocus(hwnd); + + if (style != os) InvalidateRect(hwnd, NULL, FALSE); + return; + } + + if (SWBS_TOOLBAR & style) + { + if (lButtonDownHWND != hwnd) + { + lButtonDownHWND = hwnd; + SendMessageW(hwnd, BM_SETSTATE, TRUE, 0L); + InvalidateRect(hwnd, NULL, FALSE); + UpdateWindow(hwnd); + } + return; + } + + UINT s = (UINT)SendMessageW(hwnd, BM_GETSTATE, 0, 0L); + if (BST_PUSHED & s) return; + + if (FALSE != forwardMessage) + { + DisableRedraw(); + CallPrevWndProc(WM_LBUTTONDOWN, (WPARAM)flags, *((LONG*)&pts)); + EnableRedraw(SWR_INVALIDATE | SWR_UPDATE); + } +} + +void SkinnedButton::Emulate_LeftButtonUp(UINT flags, POINTS pts, BOOL forwardMessage) +{ + if ((SWBS_DROPDOWNBUTTON | SWBS_SPLITBUTTON) & style) + { + if (SWBS_SPLITPRESSED & style) + { + SendMessageW(GetParent(hwnd), WM_COMMAND, MAKELONG(GetDlgCtrlID(hwnd), MLBN_DROPDOWN), (LPARAM)hwnd); + if (0 == (SWBS_SPLITSETMANUAL &style)) + { + style &= ~SWBS_SPLITPRESSED; + InvalidateRect(hwnd, NULL, FALSE); + return; + } + } + } + + if (FALSE != forwardMessage) + { + __super::CallPrevWndProc(WM_LBUTTONUP, (WPARAM)flags, *((LONG*)&pts)); + } + + if (lButtonDownHWND) + { + lButtonDownHWND = FALSE; + HWND hParent = GetParent(hwnd); + SendMessageW(hwnd, BM_SETSTATE, FALSE, 0L); + + DWORD ws = GetWindowLongPtrW(hwnd, GWL_STYLE); + INT type = (BS_TYPEMASK & ws); + switch(type) + { + case BS_AUTO3STATE: + case BS_AUTORADIOBUTTON: + case BS_AUTOCHECKBOX: + { + INT state = (INT)SendMessageW(hwnd, BM_GETCHECK, 0, 0L); + if (BS_AUTORADIOBUTTON == type && BST_CHECKED == state) break; + switch(state) + { + case BST_CHECKED: + state = (BS_AUTO3STATE == type) ? BST_INDETERMINATE : BST_UNCHECKED; + break; + case BST_INDETERMINATE: + case BST_UNCHECKED: + state = BST_CHECKED; + break; + } + if (BS_AUTORADIOBUTTON == type) + { + HWND h = hwnd; + wchar_t szClass[32] = {0}; + DWORD lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT); + while (NULL != (h = GetWindow(h, GW_HWNDPREV))) + { + if (!GetClassNameW(h, szClass, sizeof(szClass)/sizeof(szClass[0])) || + CSTR_EQUAL != CompareStringW(lcid, NORM_IGNORECASE, WC_BUTTONW, -1, szClass, -1)) break; + SendMessageW(h, BM_SETCHECK, BST_UNCHECKED, 0L); + if (WS_GROUP & GetWindowLongPtrW(h, GWL_STYLE)) break; + } + SendMessageW(hwnd, BM_SETCHECK, state, 0L); + + h = hwnd; + while (NULL != (h = GetWindow(h, GW_HWNDNEXT))) + { + if (!GetClassNameW(h, szClass, sizeof(szClass)/sizeof(szClass[0])) || + CSTR_EQUAL != CompareStringW(lcid, NORM_IGNORECASE, WC_BUTTONW, -1, szClass, -1)) break; + SendMessageW(h, BM_SETCHECK, BST_UNCHECKED, 0L); + if (WS_GROUP & GetWindowLongPtrW(h, GWL_STYLE)) break; + } + } + else SendMessageW(hwnd, BM_SETCHECK, state, 0L); + } + break; + } + + if (hParent) + SendMessageW(hParent, WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(hwnd), BN_CLICKED), (LPARAM)hwnd); + } + UpdateWindow(hwnd); +} + +void SkinnedButton::OnLButtonDown(UINT flags, POINTS pts) +{ + Emulate_LeftButtonDown(flags, pts, TRUE); +} + +void SkinnedButton::OnLButtonUp(UINT flags, POINTS pts) +{ + Emulate_LeftButtonUp(flags, pts, TRUE); +} + +void SkinnedButton::OnMouseMove(UINT flags, POINTS pts) +{ + BOOL bInvalidate(FALSE); + if (hwnd != hoverHWND && (!lButtonDownHWND || lButtonDownHWND == hwnd) && (0 == (SWBS_TOOLBAR & style) || IsChild(GetActiveWindow(), hwnd))) + { + if (NULL != hoverHWND && hoverHWND != hwnd) SendMessageW(hoverHWND, WM_MOUSELEAVE, 0, 0L); + + hoverHWND = hwnd; + if (lButtonDownHWND == hwnd) SendMessageW(hwnd, BM_SETSTATE, TRUE, 0L); + bInvalidate = TRUE; + hoverSplit = (SWBS_DROPDOWNBUTTON & style); + TRACKMOUSEEVENT track; + track.cbSize = sizeof(TRACKMOUSEEVENT); + track.dwFlags = TME_LEAVE; + track.hwndTrack = hwnd; + track.dwHoverTime = HOVER_DEFAULT; + _TrackMouseEvent(&track); + } + + if (SWBS_SPLITBUTTON == ((SWBS_SPLITPRESSED | SWBS_SPLITBUTTON) & style)) + { + BOOL bSplit; + POINT pt; + RECT rc; + POINTSTOPOINT(pt, pts); + GetClientRect(hwnd, &rc); + rc.left = rc.right - SPLITAREA_WIDTH; + + bSplit = PtInRect(&rc, pt); + if (hoverSplit != bSplit) + { + hoverSplit = bSplit; + bInvalidate = TRUE; + } + } + + __super::CallPrevWndProc(WM_MOUSEMOVE, (WPARAM)flags, *((LONG*)&pts)); + + if (bInvalidate) + { + InvalidateRect(hwnd, NULL, FALSE); + UpdateWindow(hwnd); + } +} + +LRESULT SkinnedButton::GetIdealSize(LPCWSTR pszText) +{ + INT cchText; + SIZE szButton; + szButton.cx = 0; + szButton.cy = 0; + + cchText = (pszText) ? lstrlenW(pszText) : (INT)SendMessageW(hwnd, WM_GETTEXTLENGTH, 0, 0L); + + { + HDC hdc = GetDCEx(hwnd, NULL, DCX_CACHE); + if (hdc) + { + wchar_t szText[BUTTON_TEXT_MAX] = {0}; + if (NULL == pszText) + { + SendMessageW(hwnd, WM_GETTEXT, (WPARAM)BUTTON_TEXT_MAX, (LPARAM)szText); + pszText = szText; + } + + HFONT hFont = (HFONT)SendMessageW(hwnd, WM_GETFONT, 0, 0L); + if (NULL == hFont) hFont = (HFONT)MlStockObjects_Get(DEFAULT_FONT); + HFONT hfo = (NULL != hFont) ? (HFONT)SelectObject(hdc, hFont) : NULL; + + if (0 != cchText) + { + RECT rt; + SetRect(&rt, 0, 0, 0, 0); + if (FALSE == DrawTextW(hdc, pszText, cchText, &rt, DT_CALCRECT | DT_SINGLELINE)) + { + szButton.cx = 0; + szButton.cy = 0; + } + else + { + szButton.cx = rt.right - rt.left; + szButton.cy = rt.bottom - rt.top; + } + } + else + { + TEXTMETRIC metrics; + + szButton.cx = 0; + if (FALSE == GetTextMetrics(hdc, &metrics)) + szButton.cy = 0; + else + szButton.cy = metrics.tmHeight; + } + + if (0 != szButton.cy) + szButton.cy += (MARGIN_TOP + MARGIN_BOTTOM); + + if (0 != szButton.cx) + szButton.cx += (MARGIN_LEFT + MARGIN_RIGHT) + 2; + + if (NULL != hfo) + SelectObject(hdc, hfo); + + ReleaseDC(hwnd, hdc); + } + } + + if (SWBS_DROPDOWNBUTTON & style) szButton.cx += (SPLITAREA_WIDTH + SPLITAREA_SPACE); + if (SWBS_SPLITBUTTON & style) szButton.cx += (SPLITAREA_WIDTH + SPLITAREA_SPACE_ALT); + + MLBUTTONIMAGELIST buttonIL; + if(MLSkinnedButton_GetImageList(hwnd, &buttonIL) && buttonIL.hmlil) + { + INT imageCX, imageCY; + if (MLImageListI_GetImageSize(buttonIL.hmlil, &imageCX, &imageCY)) + { + imageCY += (MARGIN_TOP + MARGIN_BOTTOM); + if (szButton.cy < imageCY) szButton.cy = imageCY; + szButton.cx += imageCX; + if (szButton.cx != imageCX) szButton.cx += IMAGE_SPACE; + else szButton.cx += (MARGIN_LEFT + MARGIN_RIGHT); + } + } + + return MAKELPARAM(szButton.cx, szButton.cy); +} + +BOOL SkinnedButton::OnMediaLibraryIPC(INT msg, INT_PTR param, LRESULT *pResult) +{ + switch(msg) + { + case ML_IPC_SKINNEDBUTTON_SETIMAGELIST: + ZeroMemory(&imagelist, sizeof(MLBUTTONIMAGELIST)); + if (param) CopyMemory(&imagelist, (void*)param, sizeof(MLBUTTONIMAGELIST)); + *pResult = TRUE; + InvalidateRect(hwnd, NULL, FALSE); + return TRUE; + case ML_IPC_SKINNEDBUTTON_GETIMAGELIST: + if (!param) *pResult = FALSE; + else + { + CopyMemory((void*)param, &imagelist, sizeof(MLBUTTONIMAGELIST)); + *pResult = TRUE; + } + return TRUE; + + case ML_IPC_SKINNEDBUTTON_SETDROPDOWNSTATE: + { + UINT os = style; + style &= ~SWBS_SPLITPRESSED; + if (param) style |= SWBS_SPLITPRESSED; + style |= SWBS_SPLITSETMANUAL; + if (style != os) InvalidateRect(hwnd, NULL, FALSE); + } + return TRUE; + case ML_IPC_SKINNEDBUTTON_GETIDEALSIZE: + *pResult = GetIdealSize((LPCWSTR)param); + return TRUE; + } + return __super::OnMediaLibraryIPC(msg, param, pResult); +} + +LRESULT SkinnedButton::WindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + if (SWS_USESKINCOLORS & style) + { + switch(uMsg) + { + case WM_PAINT: OnPaint(); return 0; + case WM_ERASEBKGND: return 0; + case WM_PRINTCLIENT: OnPrintClient((HDC)wParam, (UINT)lParam); return 0; + + case WM_SETFOCUS: + case WM_KILLFOCUS: + case 0x0128/*WM_UPDATEUISTATE*/: + case WM_SETTEXT: + case WM_ENABLE: + { + DisableRedraw(); + LRESULT r = __super::WindowProc(uMsg, wParam, lParam); + EnableRedraw(SWR_INVALIDATE | SWR_UPDATE); + return r; + } + case BM_SETSTATE: + if (GetUpdateRect(hwnd, NULL, FALSE)) UpdateWindow(hwnd); + { + LRESULT os = SendMessageW(hwnd, BM_GETSTATE, 0, 0L); + DisableRedraw(); + __super::WindowProc(uMsg, wParam, lParam); + EnableRedraw(SWR_NONE); + if (os != SendMessageW(hwnd, BM_GETSTATE, 0, 0L)) + { + InvalidateRect(hwnd, NULL, FALSE); + UpdateWindow(hwnd); + } + } + break; + + case BM_SETCHECK: + if (GetUpdateRect(hwnd, NULL, FALSE)) UpdateWindow(hwnd); + { + LRESULT os = SendMessageW(hwnd, BM_GETCHECK, 0, 0L); + DisableRedraw(); + __super::WindowProc(uMsg, wParam, lParam); + EnableRedraw(SWR_NONE); + if (os != SendMessageW(hwnd, BM_GETCHECK, 0, 0L)) + { + InvalidateRect(hwnd, NULL, FALSE); + UpdateWindow(hwnd); + } + } + break; + case 0x0127/*WM_CHANGEUISTATE*/: + { + if (GetUpdateRect(hwnd, NULL, FALSE)) UpdateWindow(hwnd); + DisableRedraw(); + LRESULT r = __super::WindowProc(uMsg, wParam, lParam); + EnableRedraw(SWR_NONE); + return r; + } + case WM_GETDLGCODE: + { + LRESULT r = __super::WindowProc(uMsg, wParam, lParam); + if ((SWBS_SPLITBUTTON | SWBS_DROPDOWNBUTTON) & style) + r |= DLGC_WANTARROWS; + return r; + } + case WM_KEYDOWN: + switch(wParam) + { + case VK_LEFT: + case VK_RIGHT: + if ((SWBS_SPLITBUTTON | SWBS_DROPDOWNBUTTON) & style) + { + HWND hwndParent = GetParent(hwnd); + if (hwndParent) SendMessageW(hwndParent, WM_NEXTDLGCTL, (VK_LEFT == wParam), 0L); + SendMessageW(hwnd, 0x127/*WM_CHANGEUISTATE*/, MAKELONG(2/*UIS_CLEAR*/, 1/*UISF_HIDEFOCUS*/), 0L); + return 0; + } + break; + case VK_DOWN: + case VK_UP: + case VK_SPACE: + if (((SWBS_SPLITBUTTON | SWBS_DROPDOWNBUTTON) & style) && + 1 == LOWORD(lParam) && 0 == (0x40000000 & lParam)) + { + RECT rc; + GetClientRect(hwnd, &rc); + if (VK_SPACE != wParam) rc.left = rc.right - SPLITAREA_WIDTH; + OffsetRect(&rc, 1, 1); + LONG pts = POINTTOPOINTS(*(POINT*)&rc); + Emulate_LeftButtonDown(0, MAKEPOINTS(pts), FALSE); + SendMessageW(hwnd, 0x127/*WM_CHANGEUISTATE*/, MAKELONG(2/*UIS_CLEAR*/, 1/*UISF_HIDEFOCUS*/), 0L); + return 0; + } + break; + } + break; + case WM_KEYUP: + switch(wParam) + { + case VK_DOWN: + case VK_UP: + case VK_SPACE: + if (((SWBS_SPLITBUTTON | SWBS_DROPDOWNBUTTON) & style) && + 1 == LOWORD(lParam)) + { + RECT rc; + GetClientRect(hwnd, &rc); + if (VK_SPACE != wParam) rc.left = rc.right - SPLITAREA_WIDTH; + OffsetRect(&rc, 1, 1); + LONG pts = POINTTOPOINTS(*(POINT*)&rc); + Emulate_LeftButtonUp(0, MAKEPOINTS(pts), FALSE); + return 0; + } + break; + } + break; + case WM_LBUTTONDBLCLK: + case WM_LBUTTONDOWN: OnLButtonDown((UINT)wParam, MAKEPOINTS(lParam)); return 0; + case WM_MOUSEMOVE: OnMouseMove((UINT)wParam, MAKEPOINTS(lParam)); return 0; + case WM_LBUTTONUP: OnLButtonUp((UINT)wParam, MAKEPOINTS(lParam)); return 0; + case WM_MOUSELEAVE: + if (hwnd == hoverHWND || + (SWBS_SPLITPRESSED | SWBS_SPLITBUTTON) == ((SWBS_SPLITPRESSED | SWBS_SPLITBUTTON | SWBS_SPLITSETMANUAL) & style)) + { + hoverHWND = NULL; + hoverSplit = FALSE; + if (0 == (SWBS_SPLITSETMANUAL &style)) style &= ~SWBS_SPLITPRESSED; + InvalidateRect(hwnd, NULL, FALSE); + UpdateWindow(hwnd); + } + if (lButtonDownHWND == hwnd) SendMessageW(hwnd, BM_SETSTATE, FALSE, 0L); + + break; + } + } + + return __super::WindowProc(uMsg, wParam, lParam); +} + +static BOOL CALLBACK ImageFilter(LPBYTE pData, LONG cx, LONG cy, INT bpp, COLORREF rgbBk, COLORREF rgbFg, INT_PTR imageTag, LPARAM lParam) +{ + LONG pitch, x, dibPitch; + LPBYTE dib, cursor, line; + HDC hdc, hdcSrc; + HBITMAP hbmp, hbmpOld; + IMAGEFILTERPARAM *param; + + param = &imagefilterParam; + if (32 != bpp || !param) + return FALSE; + pitch = cx*4; + + hdcSrc = param->pbps->hdc; + rgbFg = param->pbps->rgbText; + rgbBk = param->pbps->rgbTextBk; + + BITMAPINFOHEADER bi; + ZeroMemory(&bi, sizeof(bi)); + bi.biSize = sizeof(bi); + bi.biWidth = param->pbps->rcClient.right - param->pbps->rcClient.left; + bi.biHeight = -(param->pbps->rcClient.bottom - param->pbps->rcClient.top); + bi.biPlanes = 1; + bi.biBitCount = 32; + + hdc = CreateCompatibleDC(NULL); + if (NULL == hdc) + return FALSE; + + IntersectClipRect(hdc, param->prcImage->left, param->prcImage->top, + param->prcImage->right, param->prcImage->bottom); + + hbmp = CreateDIBSection(hdc, (BITMAPINFO*)&bi, DIB_RGB_COLORS, (VOID**)&dib, NULL, NULL); + if (NULL == hbmp) + { + DeleteDC(hdc); + return FALSE; + } + + hbmpOld = (HBITMAP)SelectObject(hdc, hbmp); + param->pbps->hdc = hdc; + + RECT rcPaint; + CopyRect(&rcPaint, ¶m->pbps->rcPaint); + CopyRect(¶m->pbps->rcPaint, param->prcImage); + + PushButton_DrawBackground(param->pbps); + + param->pbps->hdc = hdcSrc; + CopyRect(¶m->pbps->rcPaint, &rcPaint); + + SelectObject(hdc, hbmpOld); + DeleteDC(hdc); + + dibPitch = 4*(bi.biWidth); + dib += 4*(param->prcImage->left) + param->prcImage->top*dibPitch; + cy = param->prcImage->bottom - param->prcImage->top; + for (line = pData; cy-- != 0; line += pitch, dib += dibPitch ) + { + for (x = cx, cursor = line; x-- != 0; cursor += 4) + { + if (IMAGEFILTER_BLENDPLUSCOLOR == lParam) + { + BYTE luma; + luma = 255 - (BYTE)((cursor[2]*30 + cursor[1]*59 + cursor[0]*11)/100); + cursor[0] = GetBValue(rgbFg) - ((GetBValue(rgbFg) - GetBValue(rgbBk))*luma>>8); + cursor[1] = GetGValue(rgbFg) - ((GetGValue(rgbFg) - GetGValue(rgbBk))*luma>>8); + cursor[2] = GetRValue(rgbFg) - ((GetRValue(rgbFg) - GetRValue(rgbBk))*luma>>8); + } + if (0x00 == cursor[3]) + { + cursor[0] = dib[0]; + cursor[1] = dib[1]; + cursor[2] = dib[2]; + cursor[3] = 0xFF; + } + else if (cursor[3] != 0xFF) + { + cursor[0] = (cursor[0]*cursor[3] + (((255 - cursor[3])*255 + 127)/255)*dib[0] + 127)/255; + cursor[1] = (cursor[1]*cursor[3] + (((255 - cursor[3])*255 + 127)/255)*dib[1] + 127)/255; + cursor[2] = (cursor[2]*cursor[3] + (((255 - cursor[3])*255 + 127)/255)*dib[2] + 127)/255; + cursor[3] = 0xFF; + } + } + } + + DeleteObject(hbmp); ///Last call + return TRUE; +} + +BOOL SkinnedButton::RegisterImageFilter(HANDLE filterMananger) +{ + BOOL result; + MLIMAGEFILTERINFO_I mlif; + ZeroMemory(&mlif, sizeof(MLIMAGEFILTERINFO_I)); + + mlif.mask = MLIFF_TITLE_I | MLIFF_FLAGS_I | MLIFF_PROC_I | MLIFF_PARAM_I; + mlif.uid = MLIF_BUTTONBLEND_UID; + mlif.fnProc = ImageFilter; + mlif.pszTitle = L"Button image filter (blend)"; + mlif.lParam = IMAGEFILTER_BLEND; + mlif.fFlags = 0; + result = MLImageFilterI_Register((HMLIMGFLTRMNGR)filterMananger, &mlif); + + mlif.mask = MLIFF_TITLE_I | MLIFF_FLAGS_I | MLIFF_PROC_I | MLIFF_PARAM_I; + mlif.uid = MLIF_BUTTONBLENDPLUSCOLOR_UID; + mlif.fnProc = ImageFilter; + mlif.pszTitle = L"Button image filter (blend plus color)"; + mlif.lParam = IMAGEFILTER_BLENDPLUSCOLOR; + mlif.fFlags = 0; + if (!MLImageFilterI_Register((HMLIMGFLTRMNGR)filterMananger, &mlif)) result = FALSE; + return result; +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/skinnedbutton.h b/Src/Plugins/General/gen_ml/skinnedbutton.h new file mode 100644 index 00000000..6d82bbfa --- /dev/null +++ b/Src/Plugins/General/gen_ml/skinnedbutton.h @@ -0,0 +1,71 @@ +#ifndef NULLOSFT_MEDIALIBRARY_SKINNED_BUTTON_HEADER +#define NULLOSFT_MEDIALIBRARY_SKINNED_BUTTON_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include "./skinnedwnd.h" + +#define BUTTON_TEXT_MAX 512 + +typedef struct _BUTTONPAINTSTRUCT +{ + HDC hdc; + RECT rcClient; + RECT rcPaint; + UINT buttonState; + UINT windowStyle; + UINT skinnedStyle; + HBITMAP hImage; + INT imageIndex; + UINT textFormat; + HFONT hFont; + COLORREF rgbText; + COLORREF rgbTextBk; + INT cchText; + wchar_t szText[BUTTON_TEXT_MAX]; +} BUTTONPAINTSTRUCT; + +class SkinnedButton : public SkinnedWnd +{ +protected: + SkinnedButton(void); + virtual ~SkinnedButton(void); + +protected: + virtual BOOL Attach(HWND hwndButton); + virtual LRESULT WindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam); // treat this as dialog proc + virtual void OnPaint(void); + virtual void OnPrintClient(HDC hdc, UINT options); + + virtual void OnSkinChanged(BOOL bNotifyChildren, BOOL bRedraw); + virtual BOOL OnMediaLibraryIPC(INT msg, INT_PTR param, LRESULT *pResult); + virtual void OnLButtonDown(UINT flags, POINTS pts); + virtual void OnLButtonUp(UINT flags, POINTS pts); + virtual void OnMouseMove(UINT flags, POINTS pts); + virtual LRESULT GetIdealSize(LPCWSTR pszText); + +protected: + void Emulate_LeftButtonDown(UINT flags, POINTS pts, BOOL forwardMessage); + void Emulate_LeftButtonUp(UINT flags, POINTS pts, BOOL forwardMessage); + +public: + static COLORREF GetButtonBkColor(BOOL bPressed, BOOL *pbUnified); + static void PrePaint(HWND hwnd, BUTTONPAINTSTRUCT *pbps, UINT skinStyle, UINT uiStat); + static void DrawButton(BUTTONPAINTSTRUCT *pbps); + static void DrawPushButton(BUTTONPAINTSTRUCT *pbps); + static void DrawGroupBox(BUTTONPAINTSTRUCT *pbps); + static void DrawCheckBox(BUTTONPAINTSTRUCT *pbps); + static void DrawRadioButton(BUTTONPAINTSTRUCT *pbps); + static BOOL RegisterImageFilter(HANDLE filterMananger); + +protected: + MLBUTTONIMAGELIST imagelist; + +private: + friend BOOL SkinWindowEx(HWND hwndToSkin, INT type, UINT style); + +}; + +#endif // NULLOSFT_MEDIALIBRARY_SKINNED_BUTTON_HEADER \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/skinnedcombo.cpp b/Src/Plugins/General/gen_ml/skinnedcombo.cpp new file mode 100644 index 00000000..1473384d --- /dev/null +++ b/Src/Plugins/General/gen_ml/skinnedcombo.cpp @@ -0,0 +1,444 @@ +#include "./skinnedcombo.h" +#include "./skinnedheader.h" +#include "../winamp/wa_dlg.h" +#include "./skinning.h" +#include "../nu/trace.h" +#include +#include + +#define COMBO_TEXT_MAX 512 + +SkinnedCombobox::SkinnedCombobox(void) : SkinnedWnd(FALSE), activeBorder(TRUE) +{ +} + +SkinnedCombobox::~SkinnedCombobox(void) +{ +} + +BOOL SkinnedCombobox::Attach(HWND hwndCombo) +{ + if(!SkinnedWnd::Attach(hwndCombo)) return FALSE; + SetType(SKINNEDWND_TYPE_COMBOBOX); + activeBorder = TRUE; + COMBOBOXINFO cbi; + ZeroMemory(&cbi, sizeof(COMBOBOXINFO)); + cbi.cbSize = sizeof(COMBOBOXINFO); + if (GetComboBoxInfo(hwnd, &cbi)) + { + if (NULL != cbi.hwndItem) SkinWindowEx(cbi.hwndItem, SKINNEDWND_TYPE_EDIT, style); + if (NULL != cbi.hwndList) + { + SkinWindowEx(cbi.hwndList, SKINNEDWND_TYPE_LISTBOX, style); + } + } + SetWindowPos(hwnd, NULL, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED); + return TRUE; +} + +BOOL SkinnedCombobox::SetStyle(UINT newStyle, BOOL bRedraw) +{ + BOOL result = __super::SetStyle(newStyle, bRedraw); + if (hwnd) + { + COMBOBOXINFO cbi; + ZeroMemory(&cbi, sizeof(COMBOBOXINFO)); + cbi.cbSize = sizeof(COMBOBOXINFO); + activeBorder = (0 == (SWCBS_TOOLBAR & style)); + if (GetComboBoxInfo(hwnd, &cbi)) + { + if (NULL != cbi.hwndItem) MLSkinnedWnd_SetStyle(cbi.hwndItem, style); + if (NULL != cbi.hwndList) MLSkinnedWnd_SetStyle(cbi.hwndList, style); + } + } + return result; +} + +BOOL SkinnedCombobox::IsButtonDown(DWORD windowStyle) +{ + if(GetAsyncKeyState((GetSystemMetrics(SM_SWAPBUTTON)) ? VK_RBUTTON : VK_LBUTTON) & 0x8000) + { + if (CBS_DROPDOWNLIST == (0x0F & windowStyle)) + { + POINT pt; + RECT rc; + + if (hwnd == GetFocus() && GetClientRect(hwnd, &rc)) + { + GetCursorPos(&pt); + MapWindowPoints(HWND_DESKTOP, hwnd, &pt, 1); + return PtInRect(&rc, pt); + } + return FALSE; + } + + COMBOBOXINFO cbi; + ZeroMemory(&cbi, sizeof(COMBOBOXINFO)); + cbi.cbSize = sizeof(COMBOBOXINFO); + + if (GetComboBoxInfo(hwnd, &cbi)) + { + //check if in arrow down area + POINT pt; + GetCursorPos(&pt); + MapWindowPoints(HWND_DESKTOP, hwnd, &pt, 1); + return PtInRect(&cbi.rcButton, pt); + } + } + return FALSE; +} + +void SkinnedCombobox::DrawButton(HDC hdc, RECT *prcButton, BOOL bPressed, BOOL bActive) +{ + COLORREF rgbBkOld, rgbBk; + + rgbBk = WADlg_getColor(WADLG_LISTHEADER_BGCOLOR); + rgbBkOld = SetBkColor(hdc, rgbBk); + ExtTextOutW(hdc, 0, 0, ETO_OPAQUE, prcButton, NULL, 0, 0); + + if (bActive) + { + HPEN pen, penOld; + pen = (HPEN)MlStockObjects_Get((bPressed) ? HEADERBOTTOM_PEN : HEADERTOP_PEN); + penOld = (HPEN)SelectObject(hdc, pen); + + MoveToEx(hdc, prcButton->left, prcButton->top, NULL); + LineTo(hdc, prcButton->right, prcButton->top); + MoveToEx(hdc, prcButton->left, prcButton->top, NULL); + LineTo(hdc, prcButton->left, prcButton->bottom); + + if (!bPressed) + { + SelectObject(hdc, penOld); + pen = (HPEN)MlStockObjects_Get(HEADERBOTTOM_PEN); + penOld = (HPEN)SelectObject(hdc, pen); + } + + MoveToEx(hdc, prcButton->right - 1, prcButton->top, NULL); + LineTo(hdc, prcButton->right - 1, prcButton->bottom); + MoveToEx(hdc, prcButton->right - 1, prcButton->bottom - 1, NULL); + LineTo(hdc, prcButton->left - 1, prcButton->bottom - 1); + + if (!bPressed) + { + SelectObject(hdc, penOld); + + pen = (HPEN)MlStockObjects_Get(HEADERMIDDLE_PEN); + penOld = (HPEN)SelectObject(hdc, pen); + + MoveToEx(hdc, prcButton->right - 2, prcButton->top + 1, NULL); + LineTo(hdc, prcButton->right - 2, prcButton->bottom - 2); + MoveToEx(hdc, prcButton->right - 2, prcButton->bottom - 2, NULL); + LineTo(hdc, prcButton->left, prcButton->bottom - 2); + } + SelectObject(hdc, penOld); + } + + RECT r; + DWORD arrowSize = SkinnedHeader::GetSortArrowSize(); + + SetRect(&r, 0, 0, GET_X_LPARAM(arrowSize), GET_Y_LPARAM(arrowSize)); + + OffsetRect(&r, + prcButton->left + (prcButton->right - prcButton->left - r.right)/2 + (prcButton->right - prcButton->left - r.right)%2, + prcButton->top + (prcButton->bottom - prcButton->top - r.bottom)/2); + if (bPressed) OffsetRect(&r, 1, 1); + if (r.left > (prcButton->left + 1) && r.top > (prcButton->top + 1)) + { + SkinnedHeader::DrawSortArrow(hdc, &r, rgbBk, WADlg_getColor(WADLG_LISTHEADER_FONTCOLOR), FALSE); + } + + SetBkColor(hdc, rgbBkOld); +} + +void SkinnedCombobox::OnPaint() +{ + HDC hdc; + PAINTSTRUCT ps; + RECT rc; + COMBOBOXINFO cbi; + BOOL ctrlActive; + + if (!GetClientRect(hwnd, &rc) || rc.bottom <= rc.top || rc.right <= rc.left) return; + + hdc= BeginPaint(hwnd, &ps); + if (NULL == hdc) return; + + ctrlActive = (activeBorder /*|| hwnd == GetFocus()*/); + + if (SWCBS_TOOLBAR & style) + { + DrawBorder(hdc, &rc, BORDER_FLAT, (HPEN)MlStockObjects_Get(WNDBCK_PEN)); + if (ctrlActive) + { + InflateRect(&rc, -1, -1); + DrawBorder(hdc, &rc, BORDER_FLAT, __super::GetBorderPen()); + } + } + else DrawBorder(hdc, &rc, BORDER_FLAT, GetBorderPen()); + + InflateRect(&rc, -1, -1); + + ZeroMemory(&cbi, sizeof(COMBOBOXINFO)); + cbi.cbSize = sizeof(COMBOBOXINFO); + if (GetComboBoxInfo(hwnd, &cbi)) + { + RECT r; + + DWORD ws = GetWindowLongPtrW(hwnd, GWL_STYLE); + + SetBkMode(hdc, OPAQUE); + SetBkColor(hdc, WADlg_getColor(WADLG_ITEMBG)); + + SetRect(&r, rc.left, rc.top, cbi.rcItem.left, rc.bottom); + if (r.left < r.right) ExtTextOutW(hdc, 0, 0, ETO_OPAQUE, &r, L"", 0, NULL); + SetRect(&r, cbi.rcItem.left, rc.top, cbi.rcItem.right, cbi.rcItem.top); + if (r.top < r.bottom) ExtTextOutW(hdc, 0, 0, ETO_OPAQUE, &r, L"", 0, NULL); + SetRect(&r, cbi.rcItem.left, cbi.rcItem.bottom, cbi.rcItem.right, rc.bottom); + if (r.top < r.bottom) ExtTextOutW(hdc, 0, 0, ETO_OPAQUE, &r, L"", 0, NULL); + + if (cbi.rcButton.left != cbi.rcButton.right) + { + SetRect(&r, cbi.rcItem.right, rc.top, cbi.rcButton.left, rc.bottom); + if (r.left < r.right) ExtTextOutW(hdc, 0, 0, ETO_OPAQUE, &r, L"", 0, NULL); + SetRect(&r, cbi.rcButton.right, rc.top, rc.right, rc.bottom); + + if (r.left < r.right) ExtTextOutW(hdc, 0, 0, ETO_OPAQUE, &r, L"", 0, NULL); + SetRect(&r, cbi.rcButton.left, rc.top, cbi.rcButton.right, cbi.rcButton.top); + if (r.top < r.bottom) ExtTextOutW(hdc, 0, 0, ETO_OPAQUE, &r, L"", 0, NULL); + SetRect(&r, cbi.rcButton.left, cbi.rcButton.bottom, cbi.rcButton.right, rc.bottom); + if (r.top < r.bottom) ExtTextOutW(hdc, 0, 0, ETO_OPAQUE, &r, L"", 0, NULL); + + DrawButton(hdc, &cbi.rcButton, IsButtonDown(ws), ctrlActive); + } + else + { + SetRect(&r, cbi.rcItem.right, rc.top, rc.right, rc.bottom); + if (r.left < r.right) ExtTextOutW(hdc, 0, 0, ETO_OPAQUE, &r, L"", 0, NULL); + } + + if (CBS_DROPDOWNLIST == (0x0F & ws)) + { + INT cchText = (INT)SendMessageW(hwnd, WM_GETTEXTLENGTH, 0, 0); + if (cchText) + { + HFONT hFont, hFontOld; + wchar_t szText[COMBO_TEXT_MAX] = {0}; + + SendMessageW(hwnd, WM_GETTEXT, (WPARAM)COMBO_TEXT_MAX, (LPARAM)szText); + hFont = (HFONT)SendMessage(hwnd, WM_GETFONT, 0, 0L); + if (!hFont) hFont = (HFONT)MlStockObjects_Get(DEFAULT_FONT); + hFontOld = (hFont) ? (HFONT)SelectObject(hdc, hFont) : NULL; + + COLORREF rgbText; + BOOL bFocused = (hwnd == GetFocus() && !SendMessageW(hwnd, CB_GETDROPPEDSTATE, 0, 0L)); + if (bFocused && 0 == (SWCBS_TOOLBAR & style)) + { + rgbText = WADlg_getColor(WADLG_SELBAR_FGCOLOR); + SetBkColor(hdc, WADlg_getColor(WADLG_SELBAR_BGCOLOR)); + } + else rgbText = WADlg_getColor(WADLG_ITEMFG); + if(!IsWindowEnabled(hwnd)) + { + COLORREF rgbBack = GetBkColor(hdc); + rgbText = RGB((GetRValue(rgbText)+GetRValue(rgbBack))/2, + (GetGValue(rgbText)+GetGValue(rgbBack))/2, + (GetBValue(rgbText)+GetBValue(rgbBack))/2); + } + SetTextColor(hdc, rgbText); + ExtTextOutW(hdc, 0, 0, ETO_OPAQUE, &cbi.rcItem, L"", 0, NULL); + if (bFocused && + 0 == (0x01/*UISF_HIDEFOCUS*/ & uiState) && + 0 == (SWCBS_TOOLBAR & style)) + { + COLORREF fg, bk; + fg = SetTextColor(hdc, GetSysColor(COLOR_WINDOWTEXT)); + bk = SetBkColor(hdc, GetSysColor(COLOR_WINDOW)); + DrawFocusRect(hdc, &cbi.rcItem); + SetTextColor(hdc, fg); + SetBkColor(hdc, bk); + } + + InflateRect(&cbi.rcItem, -1, -1); + DrawTextW(hdc, szText, min(cchText,COMBO_TEXT_MAX), &cbi.rcItem, DT_NOPREFIX | DT_SINGLELINE | DT_VCENTER); + if (hFontOld) SelectObject(hdc, hFontOld); + } + else + { + ExtTextOutW(hdc, 0, 0, ETO_OPAQUE, &cbi.rcItem, L"", 0, NULL); + } + } + } + + EndPaint(hwnd, &ps); +} + +void SkinnedCombobox::OnSkinUpdated(BOOL bNotifyChildren, BOOL bRedraw) +{ + __super::OnSkinUpdated(bNotifyChildren, bRedraw); + + if (SWS_USESKINFONT & style) + { + if (0 == (CBS_OWNERDRAWVARIABLE & GetWindowLongPtrW(hwnd, GWL_STYLE))) + { + HDC hdc = GetDCEx(hwnd, NULL, DCX_CACHE | DCX_NORESETATTRS); + HFONT hf, hfo; + TEXTMETRIC tm; + hf = (HFONT)SendMessageW(hwnd, WM_GETFONT, 0, 0L); + if (NULL == hf) hf = (HFONT)MlStockObjects_Get(DEFAULT_FONT); + hfo = (HFONT)SelectObject(hdc, hf); + GetTextMetrics(hdc, &tm); + SendMessageW(hwnd, CB_SETITEMHEIGHT, -1, tm.tmHeight + 2); + SendMessageW(hwnd, CB_SETITEMHEIGHT, 0, tm.tmHeight + 2); + SelectObject(hdc, hfo); + ReleaseDC(hwnd, hdc); + + } + } +} +INT SkinnedCombobox::OnNcHitTest(POINTS pts) +{ + INT ht = __super::OnNcHitTest(pts); + + if (ht > 0 && !activeBorder && (SWCBS_TOOLBAR & style) && IsChild(GetActiveWindow(), hwnd)) + { + TRACKMOUSEEVENT track; + track.cbSize = sizeof(TRACKMOUSEEVENT); + track.dwFlags = TME_LEAVE; + track.hwndTrack = hwnd; + TrackMouseEvent(&track); + activeBorder = TRUE; + InvalidateRect(hwnd, NULL, TRUE); + UpdateWindow(hwnd); + } + return ht; +} + + +void SkinnedCombobox::OnMouseLeave(void) +{ + if (!activeBorder) return; + + COMBOBOXINFO cbi; + ZeroMemory(&cbi, sizeof(COMBOBOXINFO)); + cbi.cbSize = sizeof(COMBOBOXINFO); + if (GetComboBoxInfo(hwnd, &cbi) && IsWindowVisible(cbi.hwndList)) + return; + + activeBorder = FALSE; + if (SWCBS_TOOLBAR & style) + { + InvalidateRect(hwnd, NULL, TRUE); + UpdateWindow(hwnd); + } +} +LRESULT SkinnedCombobox::SilenceMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + LRESULT result; + UpdateWindow(hwnd); + CallDefWndProc(WM_SETREDRAW, FALSE, 0L); + result = __super::WindowProc(uMsg, wParam, lParam); + CallDefWndProc(WM_SETREDRAW, TRUE, 0L); + InvalidateRect(hwnd, NULL, TRUE); + return result; +} + +static HWND hwndPreviousFocus = NULL; + +LRESULT SkinnedCombobox::WindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + if (SWCBS_TOOLBAR & style) + { + switch(uMsg) + { + case WM_SETFOCUS: + hwndPreviousFocus = (HWND)wParam; + break; + case REFLECTED_COMMAND: + switch(HIWORD(wParam)) + { + case CBN_CLOSEUP: + { + if (NULL != hwndPreviousFocus && hwnd == GetFocus()) + { + do + { + if (IsWindowVisible(hwndPreviousFocus) && IsWindowEnabled(hwndPreviousFocus)) + { + SetFocus(hwndPreviousFocus); + break; + } + } while (NULL != (hwndPreviousFocus = GetAncestor(hwndPreviousFocus, GA_PARENT))); + + } + hwndPreviousFocus = NULL; + } + + break; + + } + break; + } + } + if (SWS_USESKINCOLORS & style) + { + switch(uMsg) + { + case WM_PAINT: OnPaint(); return 0; + case WM_ERASEBKGND: return 0; + case WM_MOUSELEAVE: OnMouseLeave(); break; + + case WM_KILLFOCUS: + case WM_SETFOCUS: + case WM_COMMAND: + case WM_CAPTURECHANGED: + case 0x0128/*WM_UPDATEUISTATE*/: + SilenceMessage(uMsg, wParam, lParam); + return 0; + case WM_LBUTTONUP: + if (activeBorder && (SWCBS_TOOLBAR & style)) + { + TRACKMOUSEEVENT track; + track.cbSize = sizeof(TRACKMOUSEEVENT); + track.dwFlags = TME_LEAVE; + track.hwndTrack = hwnd; + TrackMouseEvent(&track); + + POINT pt; + pt.x = GET_X_LPARAM(lParam); + pt.y = GET_Y_LPARAM(lParam); + RECT rw; + if (GetWindowRect(hwnd, &rw)) + { + MapWindowPoints(hwnd, HWND_DESKTOP, &pt, 1); + if (!PtInRect(&rw, pt)) + { + INPUT pi[2]; + ZeroMemory(&pi[0], sizeof(INPUT)); + pi[0].type = INPUT_MOUSE; + pi[0].mi.dx = pt.x; + pi[0].mi.dy = pt.y; + pi[0].mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_LEFTDOWN; + CopyMemory(&pi[1], &pi[0], sizeof(INPUT)); + pi[1].mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_LEFTUP; + SendInput(2, pi, sizeof(INPUT)); + } + } + + } + break; + case WM_LBUTTONDOWN: + if (!activeBorder && (SWCBS_TOOLBAR & style)) + { + TRACKMOUSEEVENT track; + track.cbSize = sizeof(TRACKMOUSEEVENT); + track.dwFlags = TME_LEAVE; + track.hwndTrack = hwnd; + TrackMouseEvent(&track); + activeBorder = TRUE; + } + break; + } + } + return __super::WindowProc(uMsg, wParam, lParam); +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/skinnedcombo.h b/Src/Plugins/General/gen_ml/skinnedcombo.h new file mode 100644 index 00000000..37dfcda0 --- /dev/null +++ b/Src/Plugins/General/gen_ml/skinnedcombo.h @@ -0,0 +1,42 @@ +#ifndef NULLOSFT_MEDIALIBRARY_SKINNED_COMBOBOX_HEADER +#define NULLOSFT_MEDIALIBRARY_SKINNED_COMBOBOX_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include "./skinnedwnd.h" + + +class SkinnedCombobox : public SkinnedWnd +{ + +protected: + SkinnedCombobox(void); + virtual ~SkinnedCombobox(void); +public: + virtual BOOL SetStyle(UINT newStyle, BOOL bRedraw); + +protected: + virtual BOOL Attach(HWND hwndCombo); + virtual LRESULT WindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam); // treat this as dialog proc + virtual void OnPaint(void); + BOOL IsButtonDown(DWORD windowStyle); + virtual void OnSkinUpdated(BOOL bNotifyChildren, BOOL bRedraw); + virtual INT OnNcHitTest(POINTS pts); + virtual void OnMouseLeave(void); + +public: + static void DrawButton(HDC hdc, RECT *prcButton, BOOL bPressed, BOOL bActive); + +private: + LRESULT SilenceMessage(UINT uMsg, WPARAM wParam, LPARAM lParam); + friend BOOL SkinWindowEx(HWND hwndToSkin, INT type, UINT style); + + +protected: + BOOL activeBorder; + +}; + +#endif // NULLOSFT_MEDIALIBRARY_SKINNED_COMBOBOX_HEADER \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/skinneddivider.cpp b/Src/Plugins/General/gen_ml/skinneddivider.cpp new file mode 100644 index 00000000..4e28f51f --- /dev/null +++ b/Src/Plugins/General/gen_ml/skinneddivider.cpp @@ -0,0 +1,128 @@ +#include "./skinneddivider.h" +#include "../winamp/wa_dlg.h" +#include "./skinning.h" + +SkinnedDivider::SkinnedDivider(void) : SkinnedWnd(FALSE) +{ + callback = NULL; + userParam = 0L; + clickoffs = 0; +} + +SkinnedDivider::~SkinnedDivider(void) +{ +} + +BOOL SkinnedDivider::Attach(HWND hwndDivider) +{ + if(!SkinnedWnd::Attach(hwndDivider)) return FALSE; + SetType(SKINNEDWND_TYPE_DIVIDER); + SetWindowPos(hwnd, NULL, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED); + return TRUE; +} + +BOOL SkinnedDivider::OnMediaLibraryIPC(INT msg, INT_PTR param, LRESULT *pResult) +{ + switch(msg) + { + case ML_IPC_SKINNEDDIVIDER_SETCALLBACK: + if ( 0 != param) + { + callback = ((MLDIVIDERCALLBACK*)param)->fnCallback; + userParam = ((MLDIVIDERCALLBACK*)param)->userParam; + } + else { callback = NULL; userParam= 0L;} + return TRUE; + } + return __super::OnMediaLibraryIPC(msg, param, pResult); +} + +void SkinnedDivider::OnPaint(void) +{ + PAINTSTRUCT ps; + RECT rc, rh; + HDC hdc = BeginPaint(hwnd, &ps); + + GetClientRect(hwnd, &rc); + + SetBkColor(hdc, WADlg_getColor(WADLG_WNDBG)); + if (SWDIV_NOHILITE & style) + { + ExtTextOutW(hdc, 0, 0, ETO_OPAQUE, &rc, L"", 0, NULL); + } + else + { + int l; + HPEN pen = (HPEN)MlStockObjects_Get(HILITE_PEN), + penOld = (HPEN)SelectObject(hdc, pen); + + if (SWDIV_VERT & style) + { + l = (rc.right - rc.left)/2 - 1; + SetRect(&rh, ps.rcPaint.left, ps.rcPaint.top, l, ps.rcPaint.bottom); + if (rh.left < rh.right) ExtTextOutW(hdc, 0, 0, ETO_OPAQUE, &rh, L"", 0, 0); + SetRect(&rh, l + 1, ps.rcPaint.top, ps.rcPaint.right, ps.rcPaint.bottom); + if (rh.left < rh.right) ExtTextOutW(hdc, 0, 0, ETO_OPAQUE, &rh, L"", 0, 0); + + MoveToEx(hdc, l, rc.top, NULL); + LineTo(hdc, l, rc.bottom); + } + else + { + l = (rc.bottom - rc.top)/2 - 1; + + SetRect(&rh, ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right, l); + if (rh.top < rh.bottom) ExtTextOutW(hdc, 0, 0, ETO_OPAQUE, &rh, L"", 0, 0); + SetRect(&rh, ps.rcPaint.left, l + 1, ps.rcPaint.right, ps.rcPaint.bottom); + if (rh.left < rh.right) ExtTextOutW(hdc, 0, 0, ETO_OPAQUE, &rh, L"", 0, 0); + + MoveToEx(hdc, rc.left, l, NULL); + LineTo(hdc, rc.right, l); + } + + SelectObject(hdc, penOld); + } + EndPaint(hwnd, &ps); +} + +LRESULT SkinnedDivider::WindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch(uMsg) + { + case WM_NCHITTEST: return HTCLIENT; + case WM_ERASEBKGND: return 0; + case WM_PAINT: OnPaint(); return 0; + + case WM_LBUTTONDOWN: + clickoffs = (SWDIV_VERT & style) ? LOWORD(lParam) : HIWORD(lParam); + SetCapture(hwnd); + break; + case WM_LBUTTONUP: + ReleaseCapture(); + clickoffs = 0; + break; + + case WM_SETCURSOR: + SetCursor(LoadCursor(NULL, (SWDIV_VERT & style) ? IDC_SIZEWE : IDC_SIZENS)); + return TRUE; + + case WM_MOUSEMOVE: + if (GetCapture() == hwnd) + { + RECT rw; + BOOL vert = (SWDIV_VERT & style); + GetWindowRect(hwnd, &rw); + GetCursorPos(((LPPOINT)&rw) + 1); + (SWDIV_VERT & style) ? rw.right -= clickoffs : rw.bottom -= clickoffs; + + if ((vert && rw.left != rw.right) || (!vert && rw.top != rw.bottom)) + { + MapWindowPoints(HWND_DESKTOP, GetParent(hwnd), ((LPPOINT)&rw) + 1, 1); + if (callback) callback(hwnd, (vert) ? rw.right : rw.bottom, userParam); + } + } + break; + + } + return __super::WindowProc(uMsg, wParam, lParam); +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/skinneddivider.h b/Src/Plugins/General/gen_ml/skinneddivider.h new file mode 100644 index 00000000..7b71d503 --- /dev/null +++ b/Src/Plugins/General/gen_ml/skinneddivider.h @@ -0,0 +1,34 @@ +#ifndef NULLOSFT_MEDIALIBRARY_SKINNED_DIVIDER_HEADER +#define NULLOSFT_MEDIALIBRARY_SKINNED_DIVIDER_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include "./skinnedwnd.h" + + +class SkinnedDivider : public SkinnedWnd +{ + +protected: + SkinnedDivider(void); + virtual ~SkinnedDivider(void); + +protected: + virtual BOOL Attach(HWND hwndButton); + virtual LRESULT WindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam); // treat this as dialog proc + virtual BOOL OnMediaLibraryIPC(INT msg, INT_PTR param, LRESULT *pResult); + void OnPaint(void); + +private: + friend BOOL SkinWindowEx(HWND hwndToSkin, INT type, UINT style); + +protected: + MLDIVIDERMOVED callback; + LPARAM userParam; + int clickoffs; + +}; + +#endif // NULLOSFT_MEDIALIBRARY_SKINNED_BUTTON_HEADER \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/skinneddlg.cpp b/Src/Plugins/General/gen_ml/skinneddlg.cpp new file mode 100644 index 00000000..fbf3968e --- /dev/null +++ b/Src/Plugins/General/gen_ml/skinneddlg.cpp @@ -0,0 +1,45 @@ +#include "../winamp/wa_dlg.h" +#include "./skinneddlg.h" + + +SkinnedDialog::SkinnedDialog(void) : SkinnedWnd(TRUE) +{ +} + +SkinnedDialog::~SkinnedDialog(void) +{ +} + +BOOL SkinnedDialog::Attach(HWND hwndDialog) +{ + if(!__super::Attach(hwndDialog)) return FALSE; + SetType(SKINNEDWND_TYPE_DIALOG); + return TRUE; +} + +HBRUSH SkinnedDialog::OnColorDialog(HDC hdc) +{ + if (hdc) + { + SetTextColor(hdc, WADlg_getColor(WADLG_WNDFG)); + SetBkColor(hdc, WADlg_getColor(WADLG_WNDBG)); + } + return (HBRUSH)MlStockObjects_Get(WNDBCK_BRUSH); +} + + +LRESULT SkinnedDialog::WindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch(uMsg) + { + case WM_NCPAINT: + case WM_NCCALCSIZE: + __super::WindowProc(uMsg, wParam, lParam); + return TRUE; + + case WM_CTLCOLORDLG: + if (SWS_USESKINCOLORS & style) return (LRESULT)OnColorDialog((HDC)wParam); + break; + } + return __super::WindowProc(uMsg, wParam, lParam); +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/skinneddlg.h b/Src/Plugins/General/gen_ml/skinneddlg.h new file mode 100644 index 00000000..86668d52 --- /dev/null +++ b/Src/Plugins/General/gen_ml/skinneddlg.h @@ -0,0 +1,29 @@ +#ifndef NULLOSFT_MEDIALIBRARY_SKINNED_DIALOG_HEADER +#define NULLOSFT_MEDIALIBRARY_SKINNED_DIALOG_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include "./skinnedscrollwnd.h" +#include + +class SkinnedDialog : public SkinnedWnd +{ + +protected: + SkinnedDialog(void); + virtual ~SkinnedDialog(void); + +protected: + virtual BOOL Attach(HWND hwndDialog); + virtual LRESULT WindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam); // treat this as dialog proc + virtual HBRUSH OnColorDialog(HDC hdc); +private: + friend BOOL SkinWindowEx(HWND hwndToSkin, INT type, UINT style); + +protected: + +}; + +#endif // NULLOSFT_MEDIALIBRARY_SKINNED_DIALOG_HEADER \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/skinnededit.cpp b/Src/Plugins/General/gen_ml/skinnededit.cpp new file mode 100644 index 00000000..955718bb --- /dev/null +++ b/Src/Plugins/General/gen_ml/skinnededit.cpp @@ -0,0 +1,787 @@ +#include "./skinnededit.h" +#include "../winamp/wa_dlg.h" +#include "./skinning.h" +#include "./stockobjects.h" +#include "./skinnedmenu.h" +#include "../nu/trace.h" +#include +#include + + +#define EDIT_TEXT_MAX 65536 +static WCHAR *pszTextGlobal = NULL; + +static int caretPos = -1; +static BOOL updateCaret = FALSE; + +#define GET_CHAR_X(__hwnd, __index) (GET_X_LPARAM(CallPrevWndProc(EM_POSFROMCHAR, (WPARAM)(__index), 0L))) + +#define MLSEM_FIRST (WM_APP + 0x2FFE) +#define MLSEM_ENABLEREDRAW (MLSEM_FIRST - 0) + +SkinnedEdit::SkinnedEdit(void) : SkinnedWnd(FALSE), firstVisible(0), lastVisible(0), + firstSelected(0), lastSelected(0), maxCharWidth(0), + mouseWParam(0), mouseLParam(0), cx(0), cy(0) +{ + if (NULL == pszTextGlobal) + pszTextGlobal = (LPWSTR)calloc(EDIT_TEXT_MAX, sizeof(WCHAR)); +} + +SkinnedEdit::~SkinnedEdit(void) +{ +} + +BOOL SkinnedEdit::Attach(HWND hwndEdit) +{ + if(!SkinnedWnd::Attach(hwndEdit)) return FALSE; + SetType(SKINNEDWND_TYPE_EDIT); + FontChanged(); + SetWindowPos(hwnd, NULL, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED); + return TRUE; +} + +void SkinnedEdit::EraseBckGnd(HDC hdc, RECT *prc, RECT *prcText, BOOL fEraseAll, HBRUSH hBrush) +{ + HRGN rgn = CreateRectRgnIndirect(prc); + if (!IsRectEmpty(prcText)) + { + HRGN rgn2 = CreateRectRgnIndirect(prcText); + if (NULL != rgn2) + { + CombineRgn(rgn, rgn, rgn2, RGN_DIFF); + + /* if (FALSE != fEraseAll) + { + SetRectRgn(rgn2, prc->left, prc->top, prc->right, prcText->top); + CombineRgn(rgn, rgn, rgn2, RGN_DIFF); + + SetRectRgn(rgn2, prc->left, prcText->bottom, prc->right, prc->bottom); + CombineRgn(rgn, rgn, rgn2, RGN_DIFF); + }*/ + DeleteObject(rgn2); + } + } + + FillRgn(hdc, rgn, hBrush); + DeleteObject(rgn); +} + + +void SkinnedEdit::DrawText(HDC hdc, RECT *prc, RECT *prcText, LPCWSTR pszText, INT cchText) +{ + + if ((lastSelected != firstSelected) && + (lastSelected > firstVisible) && + (firstSelected < lastVisible) && + ((hwnd == GetFocus()) || (ES_NOHIDESEL & GetWindowLongPtrW(hwnd, GWL_STYLE)))) + { + RECT rt; + int lim, limSel; + LPCWSTR pszTextOut; + pszTextOut = pszText + firstVisible; + lim = firstSelected - firstVisible; + CopyRect(&rt, prcText); + if (lim > 0) + { + // DrawTextW(hdc, pszTextOut, lim, prcText,DT_TOP | DT_EDITCONTROL | DT_NOPREFIX | DT_NOCLIP); + ExtTextOutW(hdc, rt.left, rt.top, 0, &rt, pszTextOut, lim, NULL); + pszTextOut += lim; + } + + limSel = min(lastSelected, lastVisible) - (int)(pszTextOut - pszText); + lim = lastVisible - lastSelected; + if(lim > 0) + { + rt.left = GET_CHAR_X(hwnd, lastSelected); + // DrawTextW(hdc, pszTextOut + limSel, lim, &rt, DT_TOP | DT_EDITCONTROL | DT_NOPREFIX | DT_NOCLIP); + ExtTextOutW(hdc, rt.left, rt.top, 0, &rt, pszTextOut + limSel, lim, NULL); + } + + SetTextColor(hdc, WADlg_getColor(WADLG_SELBAR_FGCOLOR)); + SetBkColor(hdc, WADlg_getColor(WADLG_SELBAR_BGCOLOR)); + + if (lim > 0) rt.right = rt.left - 1; + rt.left = GET_CHAR_X(hwnd, max(firstSelected, firstVisible)); + // DrawTextW(hdc, pszTextOut, limSel, &rt, DT_TOP | DT_EDITCONTROL | DT_NOPREFIX | DT_NOCLIP ); + if (rt.right > rt.left) ExtTextOutW(hdc, rt.left, rt.top, ETO_CLIPPED, &rt, pszTextOut, limSel, NULL); + } + else + { + // DrawTextW(hdc, pszText + firstVisible, cchText - firstVisible - (cchText - lastVisible), prcText, DT_TOP | DT_NOPREFIX | DT_NOCLIP); + ExtTextOutW(hdc, prcText->left, prcText->top, 0, prcText, + pszText+ firstVisible, cchText - firstVisible - (cchText - lastVisible), NULL); + } +} + +static void GetEditColors(DWORD windowStyle, BOOL bEnabled, COLORREF *prgbText, COLORREF *prgbTextBk) +{ + COLORREF fg, bg; + bg = WADlg_getColor((ES_READONLY & windowStyle) ? WADLG_WNDBG : WADLG_ITEMBG); + fg = WADlg_getColor((ES_READONLY & windowStyle) ? WADLG_WNDFG : WADLG_ITEMFG); + if(!bEnabled) + { + fg = RGB((GetRValue(fg)+GetRValue(bg))/2, + (GetGValue(fg)+GetGValue(bg))/2, + (GetBValue(fg)+GetBValue(bg))/2); + } + if (prgbText) *prgbText = fg; + if (prgbTextBk) *prgbTextBk = bg; +} + + +void SkinnedEdit::OnPaint() +{ + HDC hdc; + int cchText; + + PAINTSTRUCT ps; + RECT rc, rt; + HFONT hFont, hFontOld; + DWORD margins, ws; + TEXTMETRICW tm; + + + cchText = (INT)CallPrevWndProc(WM_GETTEXTLENGTH, 0, 0); + if (cchText) CallPrevWndProc(WM_GETTEXT, (WPARAM)EDIT_TEXT_MAX, (LPARAM)pszTextGlobal); + + hFont = (HFONT)CallPrevWndProc(WM_GETFONT, 0, 0L); + if (!hFont) hFont = (HFONT)MlStockObjects_Get(DEFAULT_FONT); + + hdc = GetDCEx(hwnd, NULL, DCX_PARENTCLIP | DCX_CACHE | DCX_CLIPSIBLINGS | + DCX_INTERSECTUPDATE | DCX_VALIDATE); + if (NULL == hdc) return; + + hFontOld = (hFont) ? (HFONT)SelectObject(hdc, hFont) : NULL; + GetTextMetricsW(hdc, &tm); + + + GetClientRect(hwnd, &rc); + CopyRect(&rt, &rc); + + if (SWES_BOTTOM & style) + { + if ((rt.bottom - tm.tmHeight) > rt.top) rt.top = rt.bottom - tm.tmHeight; + } + else if (SWES_VCENTER & style) + { + INT t = rc.top + ((rc.bottom - rc.top) - tm.tmHeight)/2; + if (t > rc.top) rt.top = t; + } + + ws = GetWindowLongPtrW(hwnd, GWL_STYLE); + + + margins = (DWORD)CallPrevWndProc(EM_GETMARGINS, 0, 0L); + + + if (cchText) + { + int x; + + CallPrevWndProc(EM_GETSEL, (WPARAM)&firstSelected, (LPARAM)&lastSelected); + lastVisible = (int)(INT_PTR)CallPrevWndProc(EM_CHARFROMPOS, 0, MAKELPARAM(rc.right - (GET_Y_LPARAM(margins)), 0)); + if ( -1 == lastVisible) lastVisible = cchText; + firstVisible =(INT)(INT_PTR)CallPrevWndProc(EM_CHARFROMPOS, 0, MAKELPARAM(rc.left + GET_X_LPARAM(margins), 0)); + if (cchText == firstVisible) firstVisible = 0; + else if (firstVisible > 0) firstVisible++; + while (12000 < (x = GET_CHAR_X(hwnd, firstVisible))) firstVisible++; + rt.left = x; + if (firstVisible > 0 && rt.left > rc.left + GET_X_LPARAM(margins) + 1) + { // we can try to display one more + int t = GET_CHAR_X(hwnd, firstVisible -1); + if (t != -1 && t < rc.right) { rt.left = t; firstVisible--; } + } + + if (lastVisible) + { + if (lastVisible < cchText -1) + { + rt.right = GET_CHAR_X(hwnd, lastVisible); + } + else + { + INT i = lastVisible - ((cchText == lastVisible && cchText) ? 1 : 0); + ABC abc; + if (GetCharABCWidthsW(hdc, pszTextGlobal[i], pszTextGlobal[i], &abc)) + rt.right = abc.abcA + abc.abcB + abc.abcC; + else + GetCharWidth32W(hdc, pszTextGlobal[i], pszTextGlobal[i], (INT*)&rt.right); + rt.right += GET_CHAR_X(hwnd, i); + } + } + else rt.right = rt.left; + + if (rt.top + tm.tmHeight < rt.bottom) rt.bottom = rt.top + tm.tmHeight; + if (rt.right > rc.right - GET_Y_LPARAM(margins)) rt.right = rc.right - GET_Y_LPARAM(margins); + } + else + { + firstVisible = 0; + lastVisible = 0; + rt.left += GET_X_LPARAM(margins); + rt.right -= GET_Y_LPARAM(margins); + if (ES_CENTER & ws) + { + rt.left += (rt.right - rt.left)/2; + rt.right = rt.left; + } + else if (ES_RIGHT & ws) rt.left = rt.right; + else rt.right = rt.left; + } + + + if (FALSE != updateCaret) + { + updateCaret = FALSE; + // ShowCaret(hwnd); + + if (hwnd == GetFocus()) + { + INT x; + if (caretPos >= lastVisible) x = rt.right; + else if (caretPos <= firstVisible) x = rt.left; + else x = GET_CHAR_X(hwnd, caretPos); + + if (x < rc.left) + x = rc.left; + else + { + INT caretWidth = GetSystemMetrics(SM_CXBORDER); + if (x + caretWidth > rc.right) + x = rc.right - caretWidth; + } + SetCaretPos(x, rt.top); + } + } + + if (hFontOld) SelectObject(hdc, hFontOld); + ReleaseDC(hwnd, hdc); + + hdc = BeginPaint(hwnd, &ps); + if (NULL == hdc) return; + + hFontOld = (hFont) ? (HFONT)SelectObject(hdc, hFont) : NULL; + + HBRUSH brushBk = NULL; + BOOL overrideColors = FALSE; + + HWND hParent = GetParent(hwnd); + if (NULL != hParent) + { + UINT uMsg = (0 == ((ES_READONLY | WS_DISABLED) & ws)) ? WM_CTLCOLOREDIT : WM_CTLCOLORSTATIC; + brushBk = (HBRUSH)SendMessage(hParent, uMsg, (WPARAM)hdc, (LPARAM)hwnd); + + HBRUSH stockBursh = GetSysColorBrush( (0 == ((ES_READONLY | WS_DISABLED) & ws)) ? COLOR_WINDOW : COLOR_3DFACE); + if (NULL == brushBk || stockBursh == brushBk) + overrideColors = TRUE; + } + + if (FALSE != overrideColors) + { + COLORREF rgbText, rgbTextBk; + GetEditColors(ws, IsWindowEnabled(hwnd), &rgbText, &rgbTextBk); + + SetBkColor(hdc, rgbTextBk); + SetTextColor(hdc, rgbText); + brushBk = (HBRUSH)MlStockObjects_Get((ES_READONLY & ws) ? WNDBCK_BRUSH : ITEMBCK_BRUSH); + } + + + + + IntersectClipRect(hdc, rc.left, rc.top, rc.right, rc.bottom); + EraseBckGnd(hdc, &rc, NULL, ps.fErase, brushBk); + if (cchText) + { + IntersectClipRect(hdc, rc.left + GET_X_LPARAM(margins), rt.top, rc.right - GET_Y_LPARAM(margins), rt.bottom); + DrawText(hdc, &rc, &rt, pszTextGlobal, cchText); + } + + if (hFontOld) SelectObject(hdc, hFontOld); + EndPaint(hwnd, &ps); +} + +void SkinnedEdit::OnSkinUpdated(BOOL bNotifyChildren, BOOL bRedraw) +{ + __super::OnSkinUpdated(bNotifyChildren, bRedraw); + + if (SWS_USESKINFONT & style) + { + CallPrevWndProc(EM_SETMARGINS, (WPARAM)(EC_LEFTMARGIN | EC_RIGHTMARGIN), MAKELPARAM(EC_USEFONTINFO, EC_USEFONTINFO)); + } +} + + + + +BOOL SkinnedEdit::GetSelection(SELECTION *selection, INT cchText, const RECT *clientRect) +{ + if (NULL == selection) return FALSE; + if (0 == cchText) + { + selection->first = 0; + selection->last = 0; + selection->leftX = clientRect->left; + selection->rightX = clientRect->left; + return TRUE; + } + + CallPrevWndProc(EM_GETSEL, (WPARAM)&selection->first, (LPARAM)&selection->last); + + selection->leftX = GET_CHAR_X(hwnd, selection->first); + + if (-1 == selection->last || cchText == selection->last) + { + selection->last = cchText; + selection->rightX = GET_CHAR_X(hwnd, cchText - 1) + maxCharWidth; + } + else + { + selection->rightX = GET_CHAR_X(hwnd, selection->last); + } + + return TRUE; +} + +LRESULT SkinnedEdit::OverrideDefault(UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + LRESULT result; + + UINT windowStyle = GetWindowStyle(hwnd); + if (0 == (WS_VISIBLE & windowStyle)) + { + result = __super::WindowProc(uMsg, wParam, lParam); + return result; + } + + SELECTION selectionOrig; + + RECT ri; + GetClientRect(hwnd, &ri); + + INT lengthOrig = (INT)CallPrevWndProc(WM_GETTEXTLENGTH, 0, 0L); + INT startXOrig = (lengthOrig > 0) ? GET_CHAR_X(hwnd, 0) : 0; + GetSelection(&selectionOrig, lengthOrig, &ri); + + SetWindowLongPtr(hwnd, GWL_STYLE, windowStyle & ~ WS_VISIBLE); + + result = __super::WindowProc(uMsg, wParam, lParam); + + windowStyle = GetWindowStyle(hwnd); + if (0 == (WS_VISIBLE & windowStyle)) + { + windowStyle |= WS_VISIBLE; + SetWindowLongPtr(hwnd, GWL_STYLE, windowStyle); + } + + if (hwnd == GetFocus()) + { + + INT length = (INT)CallPrevWndProc(WM_GETTEXTLENGTH, 0, 0L); + + SELECTION selection; + GetSelection(&selection, length, &ri); + + if (selectionOrig.first != selection.first || selectionOrig.last != selection.last || lengthOrig != length ) + { + caretPos = (selectionOrig.last != selection.last) ? selection.last : selection.first; + if( FALSE == updateCaret) + updateCaret = TRUE; + + if (0 != (WS_VISIBLE & windowStyle)) + { + INT startX = (lengthOrig > 0) ? GET_CHAR_X(hwnd, 0) : 0; + if (lengthOrig != length || startXOrig != startX) + { + RedrawWindow(hwnd, NULL, NULL, RDW_INVALIDATE | RDW_VALIDATE | RDW_INTERNALPAINT | RDW_NOERASE | RDW_ERASENOW | RDW_UPDATENOW | RDW_NOCHILDREN); + } + else + { + //RECT ri; + GetClientRect(hwnd, &ri); + + if (selectionOrig.first != selectionOrig.last) + { + if (selectionOrig.rightX > selection.leftX && selectionOrig.rightX < selection.rightX) + selectionOrig.rightX--; + + if (selectionOrig.leftX > selection.leftX && selectionOrig.leftX < selection.rightX) + selectionOrig.leftX++; + } + + // aTRACE_FMT("redraw: (%d, %d) - (%d, %d)\r\n",selectionOrig.leftX, selectionOrig.rightX, selection.leftX, selection.rightX); + HRGN rgn1 = CreateRectRgn(selectionOrig.leftX, ri.top, selectionOrig.rightX, ri.bottom); + HRGN rgn2 = CreateRectRgn(selection.leftX, ri.top, selection.rightX, ri.bottom); + CombineRgn(rgn1, rgn1, rgn2, (selectionOrig.first != selectionOrig.last) ? RGN_XOR : RGN_OR); + + POINT caretPt; + if (FALSE != GetCaretPos(&caretPt)) + { + INT caretWidth = GetSystemMetrics(SM_CXBORDER); + if (0 == caretWidth) caretWidth = 1; + SetRectRgn(rgn2, caretPt.x, ri.top, caretPt.x + caretWidth, ri.bottom); + CombineRgn(rgn1, rgn1, rgn2, RGN_OR); + } + + RedrawWindow(hwnd, NULL, rgn1, RDW_INVALIDATE | RDW_NOERASE | RDW_ERASENOW | RDW_UPDATENOW); + + INT scrollCX = 0; + if (selectionOrig.first == selection.first && selectionOrig.leftX != selection.leftX) + { + scrollCX = selectionOrig.leftX - selection.leftX; + } + + if (selectionOrig.last == selection.last && selectionOrig.rightX != selection.rightX) + { + scrollCX = selectionOrig.rightX - selection.rightX; + } + if (0 != scrollCX) + { + if (scrollCX > 0) + aTRACE_LINE("move on left side"); + else + aTRACE_LINE("move on right side"); + } + DeleteObject(rgn1); + DeleteObject(rgn2); + } + } + } + } + + return result; +} + +void SkinnedEdit::OnWindowPosChanged(WINDOWPOS *pwp) +{ + + HRGN updateRgn = CreateRectRgn(0, 0, 0, 0); + + UINT windowStyle = GetWindowStyle(hwnd); + if (0 != (WS_VISIBLE & windowStyle)) + { + if (NULL != updateRgn) + { + GetUpdateRgn(hwnd, updateRgn, FALSE); + } + + SetWindowLongPtr(hwnd, GWL_STYLE, windowStyle & ~ WS_VISIBLE); + } + + __super::WindowProc(WM_WINDOWPOSCHANGED, 0, (LPARAM)pwp); + + if (0 != (WS_VISIBLE & windowStyle)) + { + windowStyle = GetWindowStyle(hwnd); + if (0 == (WS_VISIBLE & windowStyle)) + SetWindowLongPtr(hwnd, GWL_STYLE, windowStyle | WS_VISIBLE); + } + + RECT rc; + GetClientRect(hwnd, &rc); + + if( FALSE == updateCaret) + { + updateCaret = TRUE; +// HideCaret(hwnd); + } + + if (0 == ((SWP_NOSIZE | SWP_NOREDRAW) & pwp->flags)) + { + HRGN rgn = NULL; + if (rc.right - rc.left != cx) + { + cx -= GET_Y_LPARAM((DWORD)CallPrevWndProc(EM_GETMARGINS, 0, 0L)); + cx -= maxCharWidth; + + LONG l = rc.left; + rc.left = cx; + + if (NULL != rgn) SetRectRgn(rgn, rc.left, rc.top, rc.right, rc.bottom); + else rgn = CreateRectRgnIndirect(&rc); + + rc.left = l; + + } + if (rc.bottom - rc.top != cy) + { + LONG t = rc.top; + rc.top = cy; + + if (NULL != rgn) SetRectRgn(rgn, rc.left, rc.top, rc.right, rc.bottom); + else rgn = CreateRectRgnIndirect(&rc); + CombineRgn(updateRgn, updateRgn, rgn, RGN_OR); + + rc.top = t; + } + + if (0 == (SWP_NOREDRAW & pwp->flags)) + { + if (NULL != updateRgn) + InvalidateRgn(hwnd, updateRgn, FALSE); + } + + if (NULL != rgn) + DeleteObject(rgn); + + } + + cx = rc.right - rc.left; + cy = rc.bottom - rc.top; + + if (NULL != updateRgn) + DeleteObject(updateRgn); + +} +void SkinnedEdit::FontChanged() +{ + HDC hdc = GetDCEx(hwnd, NULL, DCX_CACHE | DCX_NORESETATTRS); + if (NULL != hdc) + { + HFONT font, fontOrig; + font = (HFONT)CallPrevWndProc(WM_GETFONT, 0, 0L); + if (NULL != font) + fontOrig = (HFONT)SelectObject(hdc, font); + + TEXTMETRICW tm; + maxCharWidth = (FALSE != GetTextMetricsW(hdc, &tm)) ? tm.tmMaxCharWidth : 0; + + if (NULL != font) + SelectObject(hdc, fontOrig); + + ReleaseDC(hwnd, hdc); + } +} + +void SkinnedEdit::OnSetFont(HFONT hFont, BOOL fRedraw) +{ + __super::WindowProc(WM_SETFONT, (WPARAM)hFont, (LPARAM)fRedraw); + FontChanged(); +} + +LRESULT SkinnedEdit::WindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + if ( 0 != (SWES_SELECTONCLICK & style)) + { + switch(uMsg) + { + case WM_LBUTTONDOWN: + case WM_RBUTTONDOWN: + if (hwnd != GetFocus()) + { + CallPrevWndProc(EM_SETSEL, 0, -1); + SetFocus(hwnd); + if (WM_LBUTTONDOWN == uMsg) return 0; + } + break; + } + } + + if (SWS_USESKINCOLORS & style) + { + UINT windowStyle = GetWindowStyle(hwnd); + switch(uMsg) + { + case WM_CONTEXTMENU: + if (IsSkinnedPopupEnabled(FALSE)) + { + SkinnedMenu sm; + if (sm.InitializeHook(hwnd, SMS_USESKINFONT, NULL, 0, NULL, 0L)) + { + __super::WindowProc(uMsg, wParam, lParam); + InvalidateRect(hwnd, NULL, TRUE); + UpdateWindow(hwnd); + return 0; + } + } + break; + case MLSEM_ENABLEREDRAW: + windowStyle = GetWindowStyle(hwnd); + if (0 == (WS_VISIBLE & windowStyle)) + { + SetWindowLongPtr(hwnd, GWL_STYLE, windowStyle | WS_VISIBLE); + } + return 0; + } + + if (0 == (ES_MULTILINE & windowStyle)) + { + switch(uMsg) + { + case WM_PAINT: OnPaint(); return 0; + case WM_ERASEBKGND: return 0; + case WM_SETFONT: OnSetFont((HFONT)wParam, (BOOL)lParam); return 0; + + case WM_MOUSEMOVE: + if (wParam == mouseWParam && lParam == mouseLParam) return 0; + mouseWParam = wParam; + mouseLParam = lParam; + return (MK_LBUTTON & mouseWParam) ? OverrideDefault(uMsg, wParam, lParam) : 0; + + case WM_SETFOCUS: + caretPos = -1; + + case WM_CAPTURECHANGED: + + if (0 != (WS_VISIBLE & windowStyle)) + SetWindowLongPtr(hwnd, GWL_STYLE, windowStyle & ~WS_VISIBLE); + + __super::WindowProc(uMsg, wParam, lParam); + + if (0 != (WS_VISIBLE & windowStyle)) + { + windowStyle = GetWindowStyle(hwnd); + if (0 == (WS_VISIBLE & windowStyle)) + { + SetWindowLongPtr(hwnd, GWL_STYLE, windowStyle | WS_VISIBLE); + InvalidateRect(hwnd, NULL, FALSE); + } + } + + if (FALSE == updateCaret) + { + // HideCaret(hwnd); + updateCaret = TRUE; + } + + return 0; + + + + case WM_UPDATEUISTATE: + case WM_KILLFOCUS: + + if (0 != (WS_VISIBLE & windowStyle)) + SetWindowLongPtr(hwnd, GWL_STYLE, windowStyle & ~WS_VISIBLE); + + __super::WindowProc(uMsg, wParam, lParam); + + if (0 != (WS_VISIBLE & windowStyle)) + { + windowStyle = GetWindowStyle(hwnd); + if (0 == (WS_VISIBLE & windowStyle)) + { + SetWindowLongPtr(hwnd, GWL_STYLE, windowStyle | WS_VISIBLE); + RedrawWindow(hwnd, NULL, NULL, RDW_INVALIDATE); + } + } + return 0; + + + case WM_WINDOWPOSCHANGED: + OnWindowPosChanged((WINDOWPOS*)lParam); + return 0; + case WM_SETTEXT: + if (OverrideDefault(uMsg, wParam, lParam)) + { + InvalidateRect(hwnd, NULL, TRUE); + return TRUE; + } + return FALSE; + + case EM_SETSEL: + { + BOOL recoverRegion = FALSE; + HRGN windowRegion = CreateRectRgn(0, 0, 0, 0); + INT result = GetWindowRgn(hwnd, windowRegion); + if (SIMPLEREGION != result && COMPLEXREGION != result) + { + DeleteObject(windowRegion); + windowRegion = NULL; + } + + if (0 != (WS_VISIBLE & windowStyle)) + SetWindowLongPtr(hwnd, GWL_STYLE, windowStyle & ~WS_VISIBLE); + + HRGN fakeRegion = CreateRectRgn(0, 0, 0, 0); + if (0 != SetWindowRgn(hwnd, fakeRegion, FALSE)) + recoverRegion = TRUE; + + INT prevFirst, prevLast, currFirst, currLast; + CallPrevWndProc(EM_GETSEL, (WPARAM)&prevFirst, (LPARAM)&prevLast); + + __super::WindowProc(uMsg, wParam, lParam); + + CallPrevWndProc(EM_GETSEL, (WPARAM)&currFirst, (LPARAM)&currLast); + + if (currFirst != prevFirst || currLast != prevLast) + { + if (currLast != prevLast) caretPos = currLast; + else caretPos = currFirst; + + if (FALSE == updateCaret) + { +// HideCaret(hwnd); + updateCaret = TRUE; + } + } + + if (FALSE != recoverRegion) + SetWindowRgn(hwnd, windowRegion, FALSE); + + if (NULL != windowRegion) + { + DeleteObject(windowRegion); + windowRegion = NULL; + } + + if (0 != (WS_VISIBLE & windowStyle)) + { + windowStyle = GetWindowStyle(hwnd); + if (0 == (WS_VISIBLE & windowStyle)) + { + SetWindowLongPtr(hwnd, GWL_STYLE, windowStyle | WS_VISIBLE); + InvalidateRect(hwnd, NULL, FALSE); + } + } + } + return 0; + case WM_LBUTTONUP: + { + if (0 != (WS_VISIBLE & windowStyle)) + SetWindowLongPtr(hwnd, GWL_STYLE, windowStyle & ~WS_VISIBLE); + + __super::WindowProc(uMsg, wParam, lParam); + + if (0 != (WS_VISIBLE & windowStyle)) + { + windowStyle = GetWindowStyle(hwnd); + if (0 == (WS_VISIBLE & windowStyle)) + SetWindowLongPtr(hwnd, GWL_STYLE, windowStyle | WS_VISIBLE); + + } + } + return 0; + case WM_CHAR: + case WM_KEYDOWN: + case WM_CUT: + //case WM_PASTE: + case WM_CLEAR: + case WM_UNDO: + case EM_UNDO: + case WM_LBUTTONDBLCLK: + case WM_LBUTTONDOWN: + return OverrideDefault(uMsg, wParam, lParam); + } + } + } + + switch(uMsg) + { + case WM_SETCURSOR: + if ((HWND)wParam == hwnd && HTCLIENT == LOWORD(lParam)) + { + HCURSOR cursor; + cursor = LoadCursor(NULL, IDC_IBEAM); + if (NULL != cursor) + { + SetCursor(cursor); + return TRUE; + } + } + break; + } + + return __super::WindowProc(uMsg, wParam, lParam); +} + diff --git a/Src/Plugins/General/gen_ml/skinnededit.h b/Src/Plugins/General/gen_ml/skinnededit.h new file mode 100644 index 00000000..d56eaed6 --- /dev/null +++ b/Src/Plugins/General/gen_ml/skinnededit.h @@ -0,0 +1,54 @@ +#ifndef NULLOSFT_MEDIALIBRARY_SKINNED_EDIT_HEADER +#define NULLOSFT_MEDIALIBRARY_SKINNED_EDIT_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include "./skinnedwnd.h" + + +class SkinnedEdit : public SkinnedWnd +{ +protected: + SkinnedEdit(void); + virtual ~SkinnedEdit(void); + +protected: + virtual BOOL Attach(HWND hwndEdit); + virtual LRESULT WindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam); // treat this as dialog proc + virtual void OnPaint(void); + virtual void OnSkinUpdated(BOOL bNotifyChildren, BOOL bRedraw); + virtual void OnWindowPosChanged(WINDOWPOS *pwp); + virtual void OnSetFont(HFONT hFont, BOOL fRedraw); + void FontChanged(); + +private: + friend BOOL SkinWindowEx(HWND hwndToSkin, INT type, UINT style); + void EraseBckGnd(HDC hdc, RECT *prc, RECT *prcText, BOOL fEraseAll, HBRUSH hBrush); + void DrawText(HDC hdc, RECT *prc, RECT *prcText, LPCWSTR pszText, INT cchText); + LRESULT OverrideDefault(UINT uMsg, WPARAM wParam, LPARAM lParam); + + typedef struct __SELECTION + { + INT first; + INT last; + LONG leftX; + LONG rightX; + } SELECTION; + + BOOL GetSelection(SELECTION *selection, INT cchText, const RECT *clientRect); + +protected: + int firstVisible; + int lastVisible; + int firstSelected; + int lastSelected; + INT maxCharWidth; + WPARAM mouseWParam; + LPARAM mouseLParam; + int cx; + int cy; +}; + +#endif // NULLOSFT_MEDIALIBRARY_SKINNED_EDIT_HEADER \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/skinnedfolder.cpp b/Src/Plugins/General/gen_ml/skinnedfolder.cpp new file mode 100644 index 00000000..cd649e46 --- /dev/null +++ b/Src/Plugins/General/gen_ml/skinnedfolder.cpp @@ -0,0 +1,61 @@ +#include "./skinnedfolder.h" +#include "../winamp/wa_dlg.h" +#include "./skinning.h" + + +SkinnedFolderBrowser::SkinnedFolderBrowser(void) : SkinnedScrollWnd(FALSE) +{ +} + +SkinnedFolderBrowser::~SkinnedFolderBrowser(void) +{ + +} + +BOOL SkinnedFolderBrowser::Attach(HWND hwndFB) +{ + if(!SkinnedScrollWnd::Attach(hwndFB)) return FALSE; + + SetType(SKINNEDWND_TYPE_FOLDERBROWSER); + SetMode(SCROLLMODE_STANDARD); + DisableNoScroll(TRUE); + + FOLDERBROWSERINFO fbi; + fbi.cbSize = sizeof(FOLDERBROWSERINFO); + if (FolderBrowser_GetInfo(hwnd, &fbi)) + { + if (NULL != fbi.hwndActive) SkinWindowEx(fbi.hwndActive, SKINNEDWND_TYPE_LISTBOX, style); + if (NULL != fbi.hwndDraw) SkinWindowEx(fbi.hwndDraw, SKINNEDWND_TYPE_LISTBOX, style); + } + + SetWindowPos(hwnd, NULL, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED); + return TRUE; +} + + +BOOL SkinnedFolderBrowser::SetStyle(UINT newStyle, BOOL bRedraw) +{ + BOOL result = __super::SetStyle(newStyle, bRedraw); + if (hwnd) + { + FOLDERBROWSERINFO fbi; + fbi.cbSize = sizeof(FOLDERBROWSERINFO); + if (FolderBrowser_GetInfo(hwnd, &fbi)) + { + if (NULL != fbi.hwndActive) MLSkinnedWnd_SetStyle(fbi.hwndActive, style); + if (NULL != fbi.hwndDraw) MLSkinnedWnd_SetStyle(fbi.hwndDraw, style); + } + } + return result; +} + +void SkinnedFolderBrowser::OnSkinChanged(BOOL bNotifyChildren, BOOL bRedraw) +{ + if (SWS_USESKINCOLORS & style) + { + FolderBrowser_SetBkColor(hwnd, WADlg_getColor(WADLG_ITEMBG)); + FolderBrowser_SetTextColor(hwnd, WADlg_getColor(WADLG_ITEMFG)); + } + __super::OnSkinChanged(bNotifyChildren, bRedraw); +} + diff --git a/Src/Plugins/General/gen_ml/skinnedfolder.h b/Src/Plugins/General/gen_ml/skinnedfolder.h new file mode 100644 index 00000000..140fe33e --- /dev/null +++ b/Src/Plugins/General/gen_ml/skinnedfolder.h @@ -0,0 +1,32 @@ +#ifndef NULLOSFT_MEDIALIBRARY_SKINNED_FOLDERBROWSER_HEADER +#define NULLOSFT_MEDIALIBRARY_SKINNED_FOLDERBROWSER_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include "./skinnedscrollwnd.h" + + +class SkinnedFolderBrowser : public SkinnedScrollWnd +{ + +protected: + SkinnedFolderBrowser(void); + virtual ~SkinnedFolderBrowser(void); +public: + virtual BOOL SetStyle(UINT newStyle, BOOL bRedraw); + +protected: + virtual BOOL Attach(HWND hwndFB); + virtual void OnSkinChanged(BOOL bNotifyChildren, BOOL bRedraw); + + +private: + friend BOOL SkinWindowEx(HWND hwndToSkin, INT type, UINT style); + +protected: + +}; + +#endif // NULLOSFT_MEDIALIBRARY_SKINNED_FOLDERBROWSER_HEADER \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/skinnedheader.cpp b/Src/Plugins/General/gen_ml/skinnedheader.cpp new file mode 100644 index 00000000..ac0d7ea9 --- /dev/null +++ b/Src/Plugins/General/gen_ml/skinnedheader.cpp @@ -0,0 +1,832 @@ +#include "main.h" +#include "config.h" +#include "../winamp/wa_dlg.h" +#include "./skinnedheader.h" +#include "./skinning.h" +#include "./ml_imagelist.h" +#include "./imagefilters.h" +#include "./ml_cloudcolumn.h" +#include "./resource.h" + +// Timers +#define TIMER_UNBLOCKREDRAW_ID 1978 +#define TIMER_ENDTRACK_ID 1979 +#define TIMER_ENDTRACK_DELAY 5 + +// Header internal flags (LOWORD reserved to sort index) +#define HIF_ASCENDING 0x00010000 +#define HIF_OWNPARENTSW 0x00020000 // parent was subclassed by this control +#define HIF_REALCXY 0x00040000 +#define HIF_DEFSIZERULE 0x00100000 // item will use default size rule. +#define HIF_BLOCKCHANGING 0x10000000 +#define HIF_BLOCKCHANGED 0x20000000 +#define HIF_BLOCKWNDPOS 0x40000000 +#define HIF_BLOCKREDRAW 0x80000000 + +extern HMLIMGFLTRMNGR hmlifMngr; // default gen_ml fitler manager + +typedef struct _HDSIZE +{ + INT instanceRef; + INT *pCXY; // stored by index + INT *pOrder; // current left-to-right order of items (index values for items in the header). + INT count; + INT allocated; + INT headerCXY; + INT lastCXY; + HWND hwndParent; + HDITEMW hdi; // safe to use duaring size +} HDSIZE; + +typedef struct _HDCURSOR +{ + HWND hwndParent; + NMMOUSE nm; + HDHITTESTINFO hitTest; + DWORD pts; +} HDCURSOR; + +static HDSIZE hdrSize = {0,0,0, } ; + +static INT GetDefaultSizeRule(void) +{ + INT rule; + rule = g_config->ReadInt(L"column_resize_mode", 0); + switch(rule) + { + case 1: return SHS_SIZERULE_ADJUSTONE; + case 2: return SHS_SIZERULE_PROPORTIONAL; + case 3: return SHS_SIZERULE_ADJUSTALL; + } + return SHS_SIZERULE_WINDOWS; +} + +SkinnedHeader::SkinnedHeader(void) : SkinnedWnd(FALSE), hcurNormal(NULL), cloudColumn(-1) +{ + if (!hdrSize.instanceRef) + ZeroMemory(&hdrSize, sizeof(HDSIZE)); + + hdrSize.instanceRef++; + + hdrFlags = LOWORD(-1) | HIF_ASCENDING | HIF_DEFSIZERULE; + hdrSizeRule = (HIF_DEFSIZERULE & hdrFlags) ? GetDefaultSizeRule() : SHS_SIZERULE_WINDOWS; +} + +SkinnedHeader::~SkinnedHeader(void) +{ + if (hdrSize.instanceRef && !--hdrSize.instanceRef) + { + if (hdrSize.pCXY) + free(hdrSize.pCXY); // pOrder - use same buffer + ZeroMemory(&hdrSize, sizeof(HDSIZE)); + } +} + +extern HMLIMGLST hmlilCloud; +BOOL SkinnedHeader::Attach(HWND hwndHeader) +{ + HWND hwndParent; + + if(!__super::Attach(hwndHeader)) return FALSE; + + SetType(SKINNEDWND_TYPE_HEADER); + + hwndParent = GetParent(hwndHeader); + if (hwndParent) SkinWindow(hwndParent, SWS_NORMAL); + + hdrFlags = LOWORD(-1) | HIF_ASCENDING | HIF_DEFSIZERULE; + hdrSizeRule = (HIF_DEFSIZERULE & hdrFlags) ? GetDefaultSizeRule() : SHS_SIZERULE_WINDOWS; + + // set default height + SetHeight(-1); + + return TRUE; +} + +void SkinnedHeader::OnSkinChanged(BOOL bNotifyChildren, BOOL bRedraw) +{ + hcurNormal = (HCURSOR)SendMessageW(plugin.hwndParent, WM_WA_IPC, WACURSOR_NORMAL, IPC_GETSKINCURSORS); + __super::OnSkinChanged(bNotifyChildren, bRedraw); +} + +void SkinnedHeader::BlockRedraw(BOOL bBlock, UINT unblockDelay) +{ + KillTimer(hwnd, TIMER_UNBLOCKREDRAW_ID); + + if (FALSE != bBlock) + hdrFlags |= HIF_BLOCKREDRAW; + else + hdrFlags &= ~HIF_BLOCKREDRAW; + + CallDefWndProc(WM_SETREDRAW, (WPARAM)!bBlock, 0L); + + if (FALSE != bBlock && -1 != unblockDelay) + SetTimer(hwnd, TIMER_UNBLOCKREDRAW_ID, unblockDelay, NULL); +} + +DWORD SkinnedHeader::GetSortArrowSize(void) +{ + return MAKELPARAM(7, 8); +} + +BOOL SkinnedHeader::DrawSortArrow(HDC hdc, RECT *prc, COLORREF rgbBk, COLORREF rgbFg, BOOL bAscending) +{ + static HMLIMGLST hmlilSort = NULL; + IMAGELISTDRAWPARAMS ildp; + + if (!hmlilSort) + { + DWORD arrowSize = GetSortArrowSize(); + hmlilSort = MLImageListI_Create(GET_X_LPARAM(arrowSize)*2, GET_Y_LPARAM(arrowSize), MLILC_COLOR24_I, 1, 1, 1, hmlifMngr); + if (hmlilSort) + { + MLIMAGESOURCE_I mlis; + ZeroMemory(&mlis, sizeof(MLIMAGESOURCE_I)); + mlis.type = SRC_TYPE_PNG_I; + mlis.hInst = plugin.hDllInstance; + mlis.lpszName = MAKEINTRESOURCEW(IDB_SORTARROW); + MLImageListI_Add(hmlilSort, &mlis, MLIF_FILTER1_UID, 0); + } + } + + ZeroMemory(&ildp, sizeof(IMAGELISTDRAWPARAMS)); + ildp.cbSize = 56; // keep it hardcoded for now - otherwise brakes win2000. sizeof(IMAGELISTDRAWPARAMS); + + ildp.i = MLImageListI_GetRealIndex(hmlilSort, 0, rgbBk, rgbFg); + if (-1 != ildp.i) + { + ildp.hdcDst = hdc; + ildp.himl = MLImageListI_GetRealList(hmlilSort); + + ildp.cx = prc->right - prc->left; + ildp.cy = prc->bottom - prc->top; + ildp.x = prc->left; + ildp.y = prc->top; + ildp.fStyle = ILD_NORMAL; + ildp.dwRop = SRCCOPY; + ildp.xBitmap = (bAscending) ? ildp.cx : 0; + return ImageList_DrawIndirect(&ildp); + } + + return FALSE; +} + +BOOL SkinnedHeader::DrawCloudIcon(HDC hdc, RECT *prc, COLORREF rgbBk, COLORREF rgbFg) +{ + IMAGELISTDRAWPARAMS ildp; + + ZeroMemory(&ildp, sizeof(IMAGELISTDRAWPARAMS)); + ildp.cbSize = 56; // keep it hardcoded for now - otherwise brakes win2000. sizeof(IMAGELISTDRAWPARAMS); + + ildp.i = MLImageListI_GetRealIndex(hmlilCloud, 0, rgbBk, rgbFg); + if (-1 != ildp.i) + { + ildp.hdcDst = hdc; + ildp.himl = MLImageListI_GetRealList(hmlilCloud); + + ildp.cx = 16; + ildp.cy = 16; + ildp.x = prc->left - 7; + ildp.y = prc->top - 4; + ildp.fStyle = ILD_NORMAL; + ildp.dwRop = SRCCOPY; + ildp.xBitmap = 0; + return ImageList_DrawIndirect(&ildp); + } + return FALSE; +} + +void SkinnedHeader::DrawHeaderItem(LPNMCUSTOMDRAW pnmcd) +{ + HPEN pen, penOld; + HDC hdc; + RECT *prc, rcText; + INT textRight; + INT ox, oy; + + hdc = pnmcd->hdc; + prc = &pnmcd->rc; + + ox = (CDIS_SELECTED & pnmcd->uItemState) ? 1 : 0; + oy = 0; + + SetBkColor(hdc, WADlg_getColor(WADLG_LISTHEADER_BGCOLOR)); + ExtTextOutW(hdc, 0, 0, ETO_OPAQUE, prc, L"", 0, 0); + + pen = (HPEN)MlStockObjects_Get((CDIS_SELECTED & pnmcd->uItemState) ? HEADERBOTTOM_PEN : HEADERTOP_PEN); + penOld = (HPEN)SelectObject(hdc, pen); + + MoveToEx(hdc, prc->left, prc->top, NULL); + LineTo(hdc, prc->right, prc->top); + MoveToEx(hdc, prc->left, prc->top, NULL); + LineTo(hdc, prc->left, prc->bottom); + + if (0 == (CDIS_SELECTED & pnmcd->uItemState)) + { + SelectObject(hdc, penOld); + + pen = (HPEN)MlStockObjects_Get(HEADERBOTTOM_PEN); + penOld = (HPEN)SelectObject(hdc, pen); + } + + MoveToEx(hdc, prc->right - 1, prc->top, NULL); + LineTo(hdc, prc->right - 1, prc->bottom); + MoveToEx(hdc, prc->right - 1, prc->bottom - 1, NULL); + LineTo(hdc, prc->left - 1, prc->bottom - 1); + + if (0 == (CDIS_SELECTED & pnmcd->uItemState)) + { + SelectObject(hdc, penOld); + + pen = (HPEN)MlStockObjects_Get(HEADERMIDDLE_PEN); + penOld = (HPEN)SelectObject(hdc, pen); + + MoveToEx(hdc, prc->right - 2, prc->top + 1, NULL); + LineTo(hdc, prc->right - 2, prc->bottom - 2); + MoveToEx(hdc, prc->right - 2, prc->bottom - 2, NULL); + LineTo(hdc, prc->left, prc->bottom - 2); + } + + textRight = prc->right - 5 + ox; + if (LOWORD(hdrFlags) == pnmcd->dwItemSpec || pnmcd->dwItemSpec == cloudColumn) + { + RECT r; + DWORD arrowSize; + int dividerArea; + + arrowSize = GetSortArrowSize(); + SetRect(&r, 0, 0, GET_X_LPARAM(arrowSize), GET_Y_LPARAM(arrowSize)); + + dividerArea = 4*GetSystemMetrics(SM_CXEDGE); + + OffsetRect(&r, prc->right - r.right - dividerArea, (((prc->bottom - prc->top) - r.bottom) + 1)/2); + + if (pnmcd->dwItemSpec != cloudColumn && (r.left > (prc->left + 16) && r.top > (prc->top + 2))) + { + if (DrawSortArrow(hdc, &r, + WADlg_getColor(WADLG_LISTHEADER_BGCOLOR), + WADlg_getColor(WADLG_LISTHEADER_FONTCOLOR), + (HIF_ASCENDING & hdrFlags))) textRight = r.left - 3; + } + else if (pnmcd->dwItemSpec == cloudColumn) + { + if ((prc->right - prc->left) >= MLCloudColumnI_GetMinWidth()) + { + DrawCloudIcon(hdc, &r, + WADlg_getColor(WADLG_LISTHEADER_BGCOLOR), + WADlg_getColor(WADLG_LISTHEADER_FONTCOLOR)); + } + } + } + + if (pnmcd->dwItemSpec != cloudColumn && (textRight > (prc->left + 4 + ox))) + { + WCHAR buffer[128] = {0}; + HDITEMW item; + + item.mask = HDI_TEXT | HDI_FORMAT; + item.pszText = buffer; + item.cchTextMax = sizeof(buffer)/sizeof(wchar_t) -1; + + if (CallPrevWndProc(HDM_GETITEMW, (WPARAM)pnmcd->dwItemSpec, (LPARAM)&item)) + { + DWORD format; + SetBkMode(hdc, TRANSPARENT); + SetTextColor(hdc, WADlg_getColor(WADLG_LISTHEADER_FONTCOLOR)); + + SetRect(&rcText, prc->left + 5 + ox, prc->top + 2 + oy, textRight, prc->bottom - 2); + format = DT_VCENTER | DT_SINGLELINE | DT_LEFT | DT_NOCLIP; + + switch(HDF_JUSTIFYMASK & item.fmt) + { + case HDF_RIGHT: format |= DT_RIGHT; break; + case HDF_CENTER: format |= DT_CENTER; break; + } + // because we draw with DT_NOCLIP, make sure we don't overhang to the right + RECT testRect = rcText; + DrawTextW(hdc, item.pszText, -1, &testRect, format | DT_CALCRECT); + if (testRect.right > textRight) + { + format &= ~(HDF_JUSTIFYMASK | DT_NOCLIP); + format |= DT_LEFT; + + if ((testRect.right - textRight) > 6) + format |= DT_END_ELLIPSIS; + } + + DrawTextW(hdc, item.pszText, -1, &rcText, format); + } + } + + SelectObject(hdc, penOld); +} + +BOOL SkinnedHeader::OnCustomDraw(HWND hwndFrom, NMCUSTOMDRAW *pnmcd, LRESULT *pResult) +{ + switch(pnmcd->dwDrawStage) + { + case CDDS_PREPAINT: + *pResult = (SWS_USESKINCOLORS & style) ? + CDRF_NOTIFYITEMDRAW : + CDRF_DODEFAULT; + return TRUE; + case CDDS_ITEMPREPAINT: + DrawHeaderItem(pnmcd); + *pResult = CDRF_SKIPDEFAULT; + return TRUE; + } + return FALSE; +} + +UINT SkinnedHeader::SizeRuleAdjustOne(HDITEMW *phdi, INT index, UINT uMsg) +{ + UINT result, flags; + + flags = hdrFlags; + hdrFlags |= HIF_REALCXY; + + result = 0; + if (index < hdrSize.count) + { + INT delta; + delta = phdi->cxy - hdrSize.pCXY[index]; + + hdrSize.hdi.mask = HDI_WIDTH; + phdi = &hdrSize.hdi; + + if (delta) + { + if(CallPrevWndProc(HDM_GETITEMW, index + 1, (LPARAM)phdi)) + { + INT temp = phdi->cxy; + phdi->cxy -= delta; + if (CallPrevWndProc(HDM_SETITEMW, index + 1, (LPARAM)phdi)) delta = temp - phdi->cxy; + else delta = 0; + } + hdrSize.pCXY[index] += delta; + result = 1; + } + } + + hdrFlags = flags; + + return result; +} + +UINT SkinnedHeader::SizeRuleProportional(HDITEMW *phdi, INT index, UINT uMsg) +{ + UINT flags; + INT o, delta; + + if (index >= hdrSize.count || + hdrSize.lastCXY == phdi->cxy || + 0 == (phdi->cxy - hdrSize.pCXY[index])) + { + return 0; + } + + flags = hdrFlags; + hdrFlags |= HIF_REALCXY; + + delta = phdi->cxy - hdrSize.pCXY[index]; + + hdrSize.lastCXY = phdi->cxy; + + for (o = 0; o < hdrSize.count; o++) + { + if (hdrSize.pOrder[o] == index) + break; + } + + phdi = &hdrSize.hdi; + phdi->mask = HDI_WIDTH; + + for(++o; o < hdrSize.count && 0 != delta; o++) + { + INT i = hdrSize.pOrder[o]; + + INT r = delta / (hdrSize.count - o) + ((delta % (hdrSize.count - o)) ? ((delta >0) ? 1 : -1) : 0); + if (0 == r) + r = delta; + + phdi->cxy = hdrSize.pCXY[i] - r; + INT temp = phdi->cxy; + + CallPrevWndProc(HDM_SETITEMW, i, (LPARAM)phdi); + delta -= r; + + // hdrSize.pCXY[i] = phdi->cxy; + + if (phdi->cxy != temp) + delta += (phdi->cxy- temp); + } + + hdrFlags = flags; + + return 1; +} + +BOOL SkinnedHeader::OnBeginTrack(HWND hwndFrom, NMHEADERW *phdn, LRESULT *pResult) +{ + if (HIF_DEFSIZERULE & hdrFlags) hdrSizeRule = GetDefaultSizeRule(); + + KillTimer(hwnd, TIMER_ENDTRACK_ID); + + hdrSize.headerCXY = 0; + hdrSize.lastCXY = -1; + hdrSize.hwndParent = hwndFrom; + + hdrSize.count = (INT)CallPrevWndProc(HDM_GETITEMCOUNT, 0, 0L); + if (hdrSize.count < 0) hdrSize.count = 0; + + if (hdrSize.count > 0) + { + HDITEMW item; + + if (hdrSize.count > hdrSize.allocated) + { + VOID *data; + data = realloc(hdrSize.pCXY, sizeof(INT)*hdrSize.count*2); + if (data) + { + hdrSize.pCXY = (INT*)data; + hdrSize.pOrder = hdrSize.pCXY + hdrSize.count; + hdrSize.allocated = hdrSize.count; + } + if (!data) hdrSize.count = 0; + } + + if (hdrSize.count) + { + INT i; + if (!CallPrevWndProc(HDM_GETORDERARRAY, hdrSize.count, (LPARAM)hdrSize.pOrder)) + { + for (i = 0; i < hdrSize.count; i++) hdrSize.pOrder[i] = i; + } + + item.mask = HDI_WIDTH; + for (INT o = 0; o < hdrSize.count; o++) + { + i = hdrSize.pOrder[o]; + if (SendMessageW(hwnd, HDM_GETITEMW, i, (LPARAM)&item) && item.lParam) + { + hdrSize.pCXY[i] = item.cxy; + hdrSize.headerCXY += item.cxy; + } + } + } + } + + return FALSE; +} + +BOOL SkinnedHeader::OnEndTrack(HWND hwndFrom, NMHEADERW *phdn) +{ + if (hdrSize.count) + { + BlockRedraw(FALSE, 0); + hdrFlags &= ~(HIF_BLOCKCHANGING | HIF_BLOCKCHANGED); + SetTimer(hwnd, TIMER_ENDTRACK_ID, TIMER_ENDTRACK_DELAY, NULL); + } + return FALSE; +} + +BOOL SkinnedHeader::OnItemChanging(HWND hwndFrom, NMHEADERW *phdn, LRESULT *pResult) +{ + if (HIF_BLOCKCHANGING & hdrFlags) + { + *pResult = FALSE; + return TRUE; + } + + if (0 != hdrSize.count && + 0 == (HIF_REALCXY & hdrFlags)) + { + switch(hdrSizeRule) + { + case SHS_SIZERULE_ADJUSTONE: + case SHS_SIZERULE_PROPORTIONAL: + case SHS_SIZERULE_ADJUSTALL: + BlockRedraw(TRUE, 10); + break; + } + } + return FALSE; +} + +BOOL SkinnedHeader::OnItemChanged(HWND hwndFrom, NMHEADERW *phdn) +{ + UINT result; + + if (HIF_BLOCKCHANGED & hdrFlags) + return TRUE; + + if((HIF_REALCXY & hdrFlags) || !phdn->pitem || 0== (HDI_WIDTH & phdn->pitem->mask)) + return FALSE; + + hdrFlags |= HIF_BLOCKCHANGED; + + switch(hdrSizeRule) + { + case SHS_SIZERULE_ADJUSTONE: result = SizeRuleAdjustOne(phdn->pitem, phdn->iItem, phdn->hdr.code); break; + case SHS_SIZERULE_PROPORTIONAL: result = SizeRuleProportional(phdn->pitem, phdn->iItem, phdn->hdr.code); break; + case SHS_SIZERULE_ADJUSTALL: result = 0; break; + default: result = 0; break; + } + + hdrFlags &= ~HIF_BLOCKCHANGED; + + if (result) + { + if(hdrSize.count) + { + INT i; + HDITEMW item; + item.mask = HDI_WIDTH; + i = hdrSize.pOrder[hdrSize.count - 1]; + if (CallPrevWndProc(HDM_GETITEMW, i, (LPARAM)&item)) + { + long cxy = item.cxy; + hdrFlags |= (HIF_BLOCKCHANGING | HIF_BLOCKCHANGED | HIF_REALCXY); + item.cxy = cxy + 1; + CallPrevWndProc(HDM_SETITEMW, i, (LPARAM)&item); + hdrFlags &= ~(HIF_BLOCKCHANGING | HIF_BLOCKCHANGED); + item.cxy = cxy; + CallPrevWndProc(HDM_SETITEMW, i, (LPARAM)&item); + hdrFlags &= ~HIF_REALCXY; + } + } + + BlockRedraw(FALSE, 0); + + if (NULL != hdrSize.hwndParent) + { + RedrawWindow(hdrSize.hwndParent, NULL, NULL, + RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASENOW | RDW_ALLCHILDREN/*| RDW_VALIDATE*/); + } + else + RedrawWindow(hwnd, NULL, NULL, + RDW_INVALIDATE | RDW_UPDATENOW | RDW_ERASENOW | RDW_ALLCHILDREN/*| RDW_VALIDATE*/); + + return TRUE; + } + + return FALSE; +} + +BOOL SkinnedHeader::OnCursorNotify(HWND hwndFrom, NMMOUSE *pnm, LRESULT *pResult) +{ + if (0 == ((HHT_ONDIVIDER | HHT_ONDIVOPEN)& pnm->dwHitInfo) && (SWS_USESKINCURSORS & style) && hcurNormal) + { + SetCursor(hcurNormal); + *pResult = TRUE; + return TRUE; + } + return FALSE; +} + +BOOL SkinnedHeader::OnReflectedNotify(INT idCtrl, REFLECTPARAM *rParam) +{ + if (rParam->hwndFrom != GetParent(hwnd)) return FALSE; // controls such as listview forward headers notifications to they parent. + + switch(((NMHDR*)(rParam->lParam))->code) + { + case NM_CUSTOMDRAW: return OnCustomDraw(rParam->hwndFrom, (NMCUSTOMDRAW*)rParam->lParam, &rParam->result); + case HDN_ITEMCHANGINGA: + case HDN_ITEMCHANGINGW: return OnItemChanging(rParam->hwndFrom, (NMHEADERW*)rParam->lParam, &rParam->result); + case HDN_ITEMCHANGEDA: + case HDN_ITEMCHANGEDW: return OnItemChanged(rParam->hwndFrom, (NMHEADERW*)rParam->lParam); + case HDN_BEGINTRACKA: + case HDN_BEGINTRACKW: return OnBeginTrack(rParam->hwndFrom, (NMHEADERW*)rParam->lParam, &rParam->result); + case HDN_ENDTRACKA: + case HDN_ENDTRACKW: return OnEndTrack(rParam->hwndFrom, (NMHEADERW*)rParam->lParam); + case NM_SETCURSOR: return OnCursorNotify(rParam->hwndFrom, (NMMOUSE*)rParam->lParam, &rParam->result); + } + + return FALSE; +} + +void SkinnedHeader::OnPaint(void) +{ + if (HIF_BLOCKREDRAW & hdrFlags) + { + ValidateRgn(hwnd, NULL); + } + else if (SWS_USESKINCOLORS & style) + { + //process the grey area with our color + RECT rc, ri; + int i; + GetClientRect(hwnd, &rc); + i = (INT)CallPrevWndProc(HDM_GETITEMCOUNT, 0, 0L); + if(i > 0) + { + Header_GetItemRect(hwnd, (INT)CallPrevWndProc(HDM_ORDERTOINDEX, (i-1), 0L), &ri); + rc.left = ri.right; + } + + if(rc.left < rc.right && GetUpdateRect(hwnd, &ri, FALSE) && IntersectRect(&rc, &rc, &ri)) + { + UINT flags = DCX_PARENTCLIP | DCX_CACHE | DCX_CLIPSIBLINGS | DCX_CLIPCHILDREN | + DCX_INTERSECTUPDATE | DCX_VALIDATE; + + HDC hdc = GetDCEx(hwnd, NULL, flags); + if(hdc) + { + COLORREF rgbBkOld; + rgbBkOld = SetBkColor(hdc, WADlg_getColor(WADLG_LISTHEADER_EMPTY_BGCOLOR)); + ExtTextOutW(hdc, 0, 0, ETO_OPAQUE, &rc, L"", 0, 0); + SetBkColor(hdc, rgbBkOld); + ReleaseDC(hwnd, hdc); + ValidateRect(hwnd, &rc); + } + } + } +} + +void SkinnedHeader::OnTimer(UINT_PTR nIDEvent, TIMERPROC lpTimerFunc) +{ + switch (nIDEvent) + { + case TIMER_UNBLOCKREDRAW_ID: + BlockRedraw(FALSE, 0); + return; + case TIMER_ENDTRACK_ID: + KillTimer(hwnd, TIMER_ENDTRACK_ID); + hdrSize.count = 0; + hdrSize.headerCXY = 0; + hdrSize.lastCXY = -1; + return; + } + __super::WindowProc(WM_TIMER, (WPARAM)nIDEvent, (LPARAM)lpTimerFunc); +} + +void SkinnedHeader::SetHeight(INT nHeight) +{ + RECT rw; + GetWindowRect(hwnd, &rw); + + if (nHeight < 0) + { + HDC hdc; + hdc = GetWindowDC(hwnd); + if (hdc) + { + HFONT hfnt; + hfnt = (HFONT)CallPrevWndProc(WM_GETFONT, 0, 0L); + if (!hfnt) hfnt = (HFONT)MlStockObjects_Get(DEFAULT_FONT); + if (hfnt) + { + TEXTMETRICW tm = {0}; + HFONT hfntOld = (HFONT)SelectObject(hdc, hfnt); + + if (GetTextMetricsW(hdc, &tm)) nHeight = tm.tmHeight + 6/*Borders*/; + SelectObject(hdc, hfntOld); + } + ReleaseDC(hwnd, hdc); + } + } + + if (nHeight != (rw.bottom - rw.top)) + { + INT i; + + SetWindowPos(hwnd, NULL, 0, 0, rw.right - rw.left, nHeight, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOZORDER); + if (HDS_HIDDEN & GetWindowLongPtrW(hwnd, GWL_STYLE)) return; + + i = (INT)CallPrevWndProc(HDM_GETITEMCOUNT, 0, 0L); + if(i > 0) + { + HDITEMW item; + i = (INT)CallPrevWndProc(HDM_ORDERTOINDEX, (i-1), 0L); + item.mask = HDI_WIDTH; + if (CallPrevWndProc(HDM_GETITEMW, i, (LPARAM)&item)) + { + hdrFlags |= (HIF_BLOCKCHANGING | HIF_BLOCKCHANGED | HIF_REALCXY); + item.cxy++; CallPrevWndProc(HDM_SETITEMW, i, (LPARAM)&item); + hdrFlags &= ~(HIF_BLOCKCHANGING | HIF_BLOCKCHANGED); + item.cxy--; CallPrevWndProc(HDM_SETITEMW, i, (LPARAM)&item); + hdrFlags &= ~HIF_REALCXY; + } + } + } +} + +BOOL SkinnedHeader::OnLayout(HDLAYOUT *pLayout) +{ + BOOL result; + + result = (BOOL)__super::WindowProc(HDM_LAYOUT, 0, (LPARAM)pLayout); + + if (0 == (HDS_HIDDEN & GetWindowLongPtrW(hwnd, GWL_STYLE))) + { + RECT rw; + + GetWindowRect(hwnd, &rw); + OffsetRect(&rw, -rw.left, -rw.top); + + pLayout->pwpos->cy= rw.bottom; + pLayout->prc->top = rw.bottom; + + if (GetClientRect(GetParent(hwnd), &rw)) + pLayout->pwpos->cx = (rw.right - rw.left) - pLayout->pwpos->x; + } + return result; +} + +BOOL SkinnedHeader::OnSetCursor(HWND hwdCursor, UINT hitTest, UINT message) +{ + static HDCURSOR hdrCursor; + + hdrCursor.hwndParent = GetParent(hwnd); + if (hdrCursor.hwndParent) + { + hdrCursor.nm.hdr.code = NM_SETCURSOR; + hdrCursor.nm.hdr.hwndFrom = hwnd; + hdrCursor.nm.hdr.idFrom = GetDlgCtrlID(hwnd); + + hdrCursor.pts = GetMessagePos(); + POINTSTOPOINT(hdrCursor.nm.pt, hdrCursor.pts); + + hdrCursor.hitTest.pt = hdrCursor.nm.pt; + MapWindowPoints(HWND_DESKTOP, hwnd, &hdrCursor.hitTest.pt, 1); + CallPrevWndProc(HDM_HITTEST, 0, (LPARAM)&hdrCursor.hitTest); + + hdrCursor.nm.dwItemSpec = hdrCursor.hitTest.iItem; + hdrCursor.nm.dwHitInfo = hdrCursor.hitTest.flags; + if (SendMessage(hdrCursor.hwndParent, WM_NOTIFY, (WPARAM)hdrCursor.nm.hdr.code, (LPARAM)&hdrCursor.nm)) + return TRUE; + } + return (BOOL)__super::WindowProc(WM_SETCURSOR, (WPARAM)hwdCursor, MAKELPARAM(hitTest, message)); +} + +LRESULT SkinnedHeader::OnSetItem(INT iIndex, HDITEMW *phdItem, BOOL bUnicode) +{ + if (NULL != phdItem && + 0 != (HDI_FORMAT & phdItem->mask)) + { + DWORD newFlags = 0x0000FFFF | (hdrFlags & (0xFFFF0000 & ~HIF_ASCENDING)); + if (0 != ((HDF_SORTDOWN | HDF_SORTUP) & phdItem->fmt) && + iIndex >= 0 && iIndex < 0xFFFF) + { + newFlags = (newFlags & 0xFFFF0000) | (0xFFFF & iIndex); + if (0 != (HDF_SORTUP & phdItem->fmt)) + newFlags |= HIF_ASCENDING; + } + + if ((LOWORD(newFlags) != LOWORD(hdrFlags)) || + ((HIF_ASCENDING & newFlags) != (HIF_ASCENDING & hdrFlags))) + { + hdrFlags = newFlags; + RECT rcItem; + if (NULL != CallPrevWndProc(HDM_GETITEMRECT, (WPARAM)iIndex, (LPARAM)&rcItem)) + { + InvalidateRect(hwnd, &rcItem, FALSE); + } + } + } + + return __super::WindowProc(((bUnicode) ? HDM_SETITEMW : HDM_SETITEMA), (WPARAM)iIndex, (LPARAM)phdItem); +} + +BOOL SkinnedHeader::OnMediaLibraryIPC(INT msg, INT_PTR param, LRESULT *pResult) +{ + switch(msg) + { + case ML_IPC_SKINNEDHEADER_DISPLAYSORT: + hdrFlags = (hdrFlags & 0xFFFF0000) | LOWORD(param); + hdrFlags = (hdrFlags & ~HIF_ASCENDING) | ((HIWORD(param)) ? HIF_ASCENDING : 0); + *pResult = 1; + InvalidateRect(hwnd, NULL, FALSE); + UpdateWindow(hwnd); + return TRUE; + case ML_IPC_SKINNEDHEADER_SETHEIGHT: SetHeight((INT)param); *pResult = TRUE; return TRUE; + case ML_IPC_SKINNEDHEADER_GETSORT: *pResult = MAKELONG(LOWORD(hdrFlags), 0 != (HIF_ASCENDING & hdrFlags)); return TRUE; + case ML_IPC_SKINNEDHEADER_SETCLOUDCOLUMN: + cloudColumn = (INT)param; + InvalidateRect(hwnd, NULL, FALSE); + UpdateWindow(hwnd); + return TRUE; + } + return __super::OnMediaLibraryIPC(msg, param, pResult); +} + +LRESULT SkinnedHeader::WindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch(uMsg) + { + case WM_ERASEBKGND: return 1; + case WM_PAINT: OnPaint(); break; + case WM_TIMER: OnTimer((UINT_PTR)wParam, (TIMERPROC)lParam); return 0; + case WM_SETCURSOR: return OnSetCursor((HWND)wParam, LOWORD(lParam), HIWORD(lParam)); + case WM_CAPTURECHANGED: InvalidateRect(hwnd, NULL, TRUE); break; + case WM_WINDOWPOSCHANGING: if (HIF_BLOCKWNDPOS & hdrFlags) return TRUE; + break; + case REFLECTED_NOTIFY: return OnReflectedNotify((INT)wParam, (REFLECTPARAM*)lParam); + case HDM_LAYOUT: return OnLayout((LPHDLAYOUT)lParam); + case HDM_SETITEMA: + case HDM_SETITEMW: + return OnSetItem((INT)wParam, (HDITEMW*)lParam, (HDM_SETITEMW == uMsg)); + } + return __super::WindowProc(uMsg, wParam, lParam); +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/skinnedheader.h b/Src/Plugins/General/gen_ml/skinnedheader.h new file mode 100644 index 00000000..71368f3d --- /dev/null +++ b/Src/Plugins/General/gen_ml/skinnedheader.h @@ -0,0 +1,71 @@ +#ifndef NULLOSFT_MEDIALIBRARY_SKINNED_HEADER_CONTROL_HEADER +#define NULLOSFT_MEDIALIBRARY_SKINNED_HEADER_CONTROL_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include "./skinnedwnd.h" +#include + + +#ifndef HDF_SORTUP +#define HDF_SORTUP 0x0400 +#define HDF_SORTDOWN 0x0200 +#endif // !HDF_SORTUP + + +// size rules +#define SHS_SIZERULE_WINDOWS 0x00 // Use windows default size rule. +#define SHS_SIZERULE_ADJUSTONE 0x01 // Resize column and adjust adjacent column. +#define SHS_SIZERULE_ADJUSTALL 0x03 // Resize column and adjust adjacent column when column reach minimum adjust next one. +#define SHS_SIZERULE_PROPORTIONAL 0x02 // Resize column and adjust all columns proportionately. + + +class SkinnedHeader : public SkinnedWnd +{ + +protected: + SkinnedHeader(void); + virtual ~SkinnedHeader(void); + + void SetHeight(INT nHeight); // if nHeight == -1 control will calculate height required to fit current font + +protected: + virtual BOOL Attach(HWND hwndHeader); + virtual LRESULT WindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam); + virtual BOOL OnReflectedNotify(INT idCtrl, REFLECTPARAM *rParam); + virtual BOOL OnCustomDraw(HWND hwndFrom, NMCUSTOMDRAW *pnmcd, LRESULT *pResult); + virtual void OnSkinChanged(BOOL bNotifyChildren, BOOL bRedraw); + virtual void OnPaint(void); + virtual BOOL OnLayout(HDLAYOUT *pLayout); + virtual BOOL OnSetCursor(HWND hwdCursor, UINT hitTest, UINT message); + virtual BOOL OnBeginTrack(HWND hwndFrom, NMHEADERW *phdn, LRESULT *pResult); + virtual BOOL OnEndTrack(HWND hwndFrom, NMHEADERW *phdn); + virtual BOOL OnItemChanging(HWND hwndFrom, NMHEADERW *phdn, LRESULT *pResult); + virtual BOOL OnItemChanged(HWND hwndFrom, NMHEADERW *phdn); + virtual void OnTimer(UINT_PTR nIDEvent, TIMERPROC lpTimerFunc); + virtual BOOL OnCursorNotify(HWND hwndFrom, NMMOUSE *pnm, LRESULT *pResult); + virtual BOOL OnMediaLibraryIPC(INT msg, INT_PTR param, LRESULT *pResult); + virtual LRESULT OnSetItem(INT iIndex, HDITEMW *phdItem, BOOL bUnicode); + +private: + void DrawHeaderItem(LPNMCUSTOMDRAW pnmcd); + UINT SizeRuleAdjustOne(HDITEMW *phdi, INT index, UINT uMsg); + UINT SizeRuleProportional(HDITEMW *phdi, INT index, UINT uMsg); + void BlockRedraw(BOOL bBlock, UINT unblockDelay); + friend BOOL SkinWindowEx(HWND hwndToSkin, INT type, UINT style); + +public: + static DWORD GetSortArrowSize(void); + static BOOL DrawSortArrow(HDC hdc, RECT *prc, COLORREF rgbBk, COLORREF rgbFg, BOOL bAscending); + static BOOL DrawCloudIcon(HDC hdc, RECT *prc, COLORREF rgbBk, COLORREF rgbFg); + +protected: + DWORD hdrFlags; + DWORD hdrSizeRule; + HCURSOR hcurNormal; + INT cloudColumn; +}; + +#endif // NULLOSFT_MEDIAIBRARY_SKINNED_HEADER_CONTROL_HEADER \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/skinnedlistbox.cpp b/Src/Plugins/General/gen_ml/skinnedlistbox.cpp new file mode 100644 index 00000000..5984c2c4 --- /dev/null +++ b/Src/Plugins/General/gen_ml/skinnedlistbox.cpp @@ -0,0 +1,250 @@ +#include "./skinnedlistbox.h" +#include "../winamp/wa_dlg.h" +#include "./skinning.h" +#include "../nu/trace.h" +#include + +SkinnedListbox::SkinnedListbox(void) : SkinnedScrollWnd(FALSE) +{ +} + +SkinnedListbox::~SkinnedListbox(void) +{ +} + +BOOL SkinnedListbox::Attach(HWND hwndListbox) +{ + DWORD ws; + + if(!SkinnedScrollWnd::Attach(hwndListbox)) return FALSE; + + ws = GetWindowLongPtrW(hwnd, GWL_STYLE); + + SetType(SKINNEDWND_TYPE_LISTBOX); + SetMode((LBS_COMBOBOX & ws) ? SCROLLMODE_COMBOLBOX : SCROLLMODE_STANDARD); + + if (0 == (LBS_COMBOBOX & ws)) + { + HWND hwndParent = GetParent(hwndListbox); + if (hwndParent) SkinWindow(hwndParent, SWS_NORMAL); + } + + if (LBS_DISABLENOSCROLL & ws) DisableNoScroll(TRUE); + SetWindowPos(hwnd, NULL, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED); + return TRUE; +} + +HPEN SkinnedListbox::GetBorderPen(void) +{ + if (LBS_COMBOBOX & GetWindowLongPtrW(hwnd, GWL_STYLE)) + { + return (HPEN)MlStockObjects_Get(MENUBORDER_PEN); + } + else return __super::GetBorderPen(); +} + +void SkinnedListbox::OnDrawItem(DRAWITEMSTRUCT *pdis) +{ + static wchar_t szText[512]; + if (pdis->itemID == -1) return; + INT cchLen = (INT)SendMessageW(pdis->hwndItem, LB_GETTEXT, pdis->itemID, (LPARAM)szText); + DrawItem(pdis, szText, cchLen); +} + +void SkinnedListbox::DrawItem(DRAWITEMSTRUCT *pdis, LPCWSTR pszText, INT cchText) +{ + RECT ri; + COLORREF rgbTextOld, rgbBkOld; + + if (pdis->itemID == -1) return; + + CopyRect(&ri, &pdis->rcItem); + + if (ODA_FOCUS == pdis->itemAction) + { + if (0 == (0x0200/*ODS_NOFOCUSRECT*/ & pdis->itemState)) + { + rgbTextOld = SetTextColor(pdis->hDC, 0x000000); + rgbBkOld = SetBkColor(pdis->hDC, 0xFFFFFF); + DrawFocusRect(pdis->hDC, &ri); + SetTextColor(pdis->hDC, rgbTextOld); + SetBkColor(pdis->hDC, rgbBkOld); + } + return; + } + + if (ODS_SELECTED & pdis->itemState) + { + BOOL bActive = (pdis->hwndItem == GetFocus()); + if (!bActive && (LBS_COMBOBOX & GetWindowLongPtrW(pdis->hwndItem, GWL_STYLE))) bActive = TRUE; + + rgbTextOld = SetTextColor(pdis->hDC, WADlg_getColor((bActive) ? WADLG_SELBAR_FGCOLOR : WADLG_INACT_SELBAR_FGCOLOR)); + rgbBkOld = SetBkColor(pdis->hDC, WADlg_getColor((bActive) ? WADLG_SELBAR_BGCOLOR : WADLG_INACT_SELBAR_BGCOLOR)); + } + else + { + rgbTextOld = GetTextColor(pdis->hDC); + rgbBkOld = GetBkColor(pdis->hDC); + } + + if (cchText> 0) + { + //InflateRect(&ri, -4, -1); + //int mode = SetBkMode(pdis->hDC, TRANSPARENT); + + //DrawTextW(pdis->hDC, pszText, cchText, &ri, DT_NOPREFIX | DT_VCENTER | DT_NOCLIP); + //if (mode != TRANSPARENT) SetBkMode(pdis->hDC, mode); + ExtTextOutW(pdis->hDC, ri.left + 4, ri.top + 1, ETO_OPAQUE, &ri, pszText, cchText, NULL); + } + else if ((ODA_SELECT | ODA_DRAWENTIRE) & pdis->itemAction || (ODS_SELECTED & pdis->itemState)) + { + ExtTextOutW(pdis->hDC, 0, 0, ETO_OPAQUE, &ri, L"", 0, NULL); + } + + if (ODS_SELECTED & pdis->itemState) + { + SetTextColor(pdis->hDC, rgbTextOld); + SetBkColor(pdis->hDC, rgbBkOld); + } +} + +void SkinnedListbox::MeasureItem(HWND hwnd, LPCWSTR pszText, INT cchText, UINT *pWidth, UINT *pHeight) +{ + HDC hdc = GetDCEx(hwnd, NULL, DCX_CACHE | DCX_NORESETATTRS); + HFONT hf, hfo; + + hf = (HFONT)SendMessageW(hwnd, WM_GETFONT, 0, 0L); + if (NULL == hf) hf = (HFONT) MlStockObjects_Get(DEFAULT_FONT); + hfo = (HFONT)SelectObject(hdc, hf); + + SIZE sz; + if (!GetTextExtentPoint32W(hdc, pszText, cchText, &sz)) + ZeroMemory(&sz, sizeof(SIZE)); + + if (pWidth) *pWidth = sz.cx; + if (pHeight) *pHeight= sz.cy; + + SelectObject(hdc, hfo); + ReleaseDC(hwnd, hdc); +} + +void SkinnedListbox::OnSkinUpdated(BOOL bNotifyChildren, BOOL bRedraw) +{ + __super::OnSkinUpdated(bNotifyChildren, bRedraw); + + if (SWS_USESKINFONT & style) + { + if (0 == (LBS_OWNERDRAWVARIABLE & GetWindowLongPtrW(hwnd, GWL_STYLE))) + { + HDC hdc = GetDCEx(hwnd, NULL, DCX_CACHE | DCX_NORESETATTRS); + HFONT hf, hfo; + TEXTMETRIC tm; + hf = (HFONT)SendMessageW(hwnd, WM_GETFONT, 0, 0L); + if (NULL == hf) hf = (HFONT)MlStockObjects_Get(DEFAULT_FONT); + hfo = (HFONT)SelectObject(hdc, hf); + GetTextMetrics(hdc, &tm); + SendMessageW(hwnd, LB_SETITEMHEIGHT, 0, tm.tmHeight + 2); + SelectObject(hdc, hfo); + ReleaseDC(hwnd, hdc); + } + } +} + +void SkinnedListbox::OnPrint(HDC hdc, UINT options) +{ + if ((PRF_CHECKVISIBLE & options) && !IsWindowVisible(hwnd)) return; + + if (LBS_COMBOBOX & GetWindowLongPtrW(hwnd, GWL_STYLE)) + { + __super::OnPrint(hdc, options & ~PRF_CLIENT); + if (((PRF_CLIENT | PRF_ERASEBKGND) & options)) + { + OffsetViewportOrgEx(hdc, 1, 1, NULL); + SendMessageW(hwnd, WM_PRINTCLIENT, (WPARAM)hdc, (LPARAM)((PRF_CLIENT | PRF_ERASEBKGND) & options)); + OffsetViewportOrgEx(hdc, -1, -1, NULL); + } + return; + } + __super::OnPrint(hdc, options); +} + +LRESULT SkinnedListbox::WindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + if (SWS_USESKINCOLORS & style) + { + switch(uMsg) + { + case WM_ERASEBKGND: + if (LBS_OWNERDRAWFIXED & GetWindowLongPtrW(hwnd, GWL_STYLE)) + { + RECT rc; + GetClientRect(hwnd, &rc); + HBRUSH hb = (HBRUSH)MlStockObjects_Get(ITEMBCK_BRUSH); + INT count = GetListBoxInfo(hwnd); + + if (count > 0) + { + RECT ri; + if (LB_ERR != SendMessageW(hwnd, LB_GETITEMRECT, count -1, (LPARAM)&ri)) + { + __super::WindowProc(uMsg, 0, lParam); + if (ri.bottom < rc.bottom) + { + rc.top = ri.bottom; + FillRect((HDC)wParam, &rc, hb); + } + if (ri.right < rc.right) + { + rc.top = 0; + rc.left = ri.right; + FillRect((HDC)wParam, &rc, hb); + } + return 1; + } + } + else + { + __super::WindowProc(uMsg, 0, lParam); + FillRect((HDC)wParam, &rc, hb); + return 1; + } + } + break; + case WM_SETFOCUS: + case WM_KILLFOCUS: + InvalidateRect(hwnd, NULL, TRUE); + UpdateWindow(hwnd); + break; + + case REFLECTED_DRAWITEM: + if ((LBS_OWNERDRAWFIXED | LBS_HASSTRINGS) == + ((LBS_OWNERDRAWFIXED | LBS_NODATA | LBS_HASSTRINGS) & GetWindowLongPtrW(hwnd, GWL_STYLE))) + { + OnDrawItem((DRAWITEMSTRUCT*)((REFLECTPARAM*)lParam)->lParam); + ((REFLECTPARAM*)lParam)->result = TRUE; + return TRUE; + } + return FALSE; + + case REFLECTED_CTLCOLORLISTBOX: + { + COLORREF rgbText, rgbTextBk; + rgbText = WADlg_getColor(WADLG_ITEMFG); + rgbTextBk = WADlg_getColor(WADLG_ITEMBG); + + if(!IsWindowEnabled(hwnd)) + { + rgbText = RGB((GetRValue(rgbText)+GetRValue(rgbTextBk))/2, + (GetGValue(rgbText)+GetGValue(rgbTextBk))/2, + (GetBValue(rgbText)+GetBValue(rgbTextBk))/2); + } + + SetBkColor((HDC)wParam, rgbTextBk); + SetTextColor((HDC)wParam, rgbText); + } + ((REFLECTPARAM*)lParam)->result = (LRESULT)MlStockObjects_Get(ITEMBCK_BRUSH); + return TRUE; + } + } + return __super::WindowProc(uMsg, wParam, lParam); +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/skinnedlistbox.h b/Src/Plugins/General/gen_ml/skinnedlistbox.h new file mode 100644 index 00000000..2dc6a8ce --- /dev/null +++ b/Src/Plugins/General/gen_ml/skinnedlistbox.h @@ -0,0 +1,33 @@ +#ifndef NULLOSFT_MEDIALIBRARY_SKINNED_LISTBOX_HEADER +#define NULLOSFT_MEDIALIBRARY_SKINNED_LISTBOX_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include "./skinnedscrollwnd.h" + + +class SkinnedListbox : public SkinnedScrollWnd +{ + +protected: + SkinnedListbox(void); + virtual ~SkinnedListbox(void); + +protected: + virtual BOOL Attach(HWND hwndListbox); + virtual void OnDrawItem(DRAWITEMSTRUCT *pdis); + virtual LRESULT WindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam); + virtual void OnSkinUpdated(BOOL bNotifyChildren, BOOL bRedraw); + virtual HPEN GetBorderPen(void); + virtual void OnPrint(HDC hdc, UINT options); +public: + static void DrawItem(DRAWITEMSTRUCT *pdis, LPCWSTR pszText, INT cchText); + static void MeasureItem(HWND hwnd, LPCWSTR pszText, INT cchText, UINT *pWidth, UINT *pHeight); + +private: + friend BOOL SkinWindowEx(HWND hwndToSkin, INT type, UINT style); +}; + +#endif // NULLOSFT_MEDIALIBRARY_SKINNED_LISTBOX_HEADER \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/skinnedlistview.cpp b/Src/Plugins/General/gen_ml/skinnedlistview.cpp new file mode 100644 index 00000000..2f602aae --- /dev/null +++ b/Src/Plugins/General/gen_ml/skinnedlistview.cpp @@ -0,0 +1,526 @@ +#include "./skinnedlistview.h" +#include "../winamp/gen.h" +#include "../winamp/wa_dlg.h" +#include "./skinning.h" +#include "../nu/trace.h" +#include "config.h" + +#ifndef LVS_EX_DOUBLEBUFFER //this will work XP only +#define LVS_EX_DOUBLEBUFFER 0x00010000 +#endif + +// internal flags +#define LVIF_ENABLED 0x0001 +#define LVIF_FOCUSED 0x0002 +#define LVIF_HEADERATTACHED 0x0100 +#define LVIF_REMOVEREFLECTOR 0x0200 + +extern "C" winampGeneralPurposePlugin plugin; + +SkinnedListView::SkinnedListView(void) : SkinnedScrollWnd(FALSE), listFlags(0), currentItem(-1) +{ + currentColor = SendMessageW(plugin.hwndParent, WM_WA_IPC, 4, IPC_GET_GENSKINBITMAP); +} + +SkinnedListView::~SkinnedListView(void) +{ + if (NULL != hwnd && + 0 != (LVIF_REMOVEREFLECTOR & listFlags)) + { + HWND hParent = GetParent(hwnd); + RemoveReflector(hParent); + } +} + +BOOL SkinnedListView::Attach(HWND hwndListView) +{ + HWND hwndParent; + listFlags = 0x000; + if(!SkinnedScrollWnd::Attach(hwndListView)) return FALSE; + + SetType(SKINNEDWND_TYPE_LISTVIEW); + SetMode(SCROLLMODE_LISTVIEW); + + hwndParent = GetParent(hwndListView); + if (NULL != hwndParent && S_OK == InstallReflector(hwndParent)) + listFlags |= LVIF_REMOVEREFLECTOR; + + TryAttachHeader(); + + SendMessageW(hwnd, CCM_SETVERSION, 5, 80); + + return TRUE; +} + +void SkinnedListView::OnSkinChanged(BOOL bNotifyChildren, BOOL bRedraw) +{ + if (SWS_USESKINCOLORS & style) + { + DisableRedraw(); + + currentColor = SendMessageW(plugin.hwndParent, WM_WA_IPC, 4, IPC_GET_GENSKINBITMAP); + SendMessageW(hwnd, LVM_SETTEXTCOLOR, 0, (LPARAM)WADlg_getColor(WADLG_ITEMFG)); + SendMessageW(hwnd, LVM_SETTEXTBKCOLOR, 0, (LPARAM)WADlg_getColor(WADLG_ITEMBG)); + SendMessageW(hwnd, LVM_SETBKCOLOR, 0, (LPARAM)WADlg_getColor(WADLG_ITEMBG)); + + EnableRedraw(SWR_NONE); + } + + __super::OnSkinChanged(bNotifyChildren, bRedraw); +} + +BOOL SkinnedListView::SetStyle(UINT newStyle, BOOL bRedraw) +{ + BOOL succeeded; + + succeeded = __super::SetStyle(newStyle, bRedraw); + + if (hwnd) + { + UINT lvStyle(0); + if(SWLVS_FULLROWSELECT & newStyle) lvStyle |= LVS_EX_FULLROWSELECT | LVS_EX_INFOTIP; + if(SWLVS_DOUBLEBUFFER & newStyle) lvStyle |= LVS_EX_DOUBLEBUFFER; + if (0 != lvStyle) + ListView_SetExtendedListViewStyleEx(hwnd, lvStyle, lvStyle); + + if (0 != (SWS_USESKINCOLORS & style)) + { + HWND tooltipWindow; + tooltipWindow = (HWND)SendMessageW(hwnd, LVM_GETTOOLTIPS, 0, 0L); + if (NULL != tooltipWindow) + { + unsigned int skinStyle; + + skinStyle = SWS_USESKINCOLORS; + skinStyle |= ((SWS_USESKINFONT | SWS_USESKINCURSORS) & style); + SkinWindowEx(tooltipWindow, SKINNEDWND_TYPE_TOOLTIP, skinStyle); + } + } + } + return succeeded; +} + +BOOL SkinnedListView::OnCustomDraw(HWND hwndFrom, NMLVCUSTOMDRAW *plvcd, LRESULT *pResult) +{ + static COLORREF rgbBk, rgbFg, rgbBkSel, rgbFgSel; + static BOOL restoreSelect(FALSE), bFullRowSelect(FALSE); + BOOL bSelected; + + switch(plvcd->nmcd.dwDrawStage) + { + case CDDS_PREPAINT: + + listFlags &= ~0x00FF; + if (IsWindowEnabled(hwnd)) listFlags |= LVIF_ENABLED; + if (GetFocus() == hwnd || GetForegroundWindow() == hwnd) + listFlags |= LVIF_FOCUSED; + + if (MLSkinnedWnd_EnableReflection(hwndFrom, FALSE)) + { + *pResult = SendMessageW(hwndFrom, WM_NOTIFY, (WPARAM)plvcd->nmcd.hdr.idFrom, (LPARAM)plvcd); + MLSkinnedWnd_EnableReflection(hwndFrom, TRUE); + } + else *pResult = CDRF_DODEFAULT; + + if (plvcd->nmcd.rc.bottom != 0 && plvcd->nmcd.rc.right != 0) *pResult |= CDRF_NOTIFYITEMDRAW; + bFullRowSelect = ( LVS_REPORT != (LVS_TYPEMASK & GetWindowLongPtrW(hwnd, GWL_STYLE)) || + 0 != (LVS_EX_FULLROWSELECT & CallPrevWndProc(LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0L))); + return TRUE; + + // Modify item text and or background + case CDDS_ITEMPREPAINT: + bSelected = FALSE; + if(LVIF_ENABLED & listFlags) + { + bSelected = (LVIS_SELECTED == CallPrevWndProc(LVM_GETITEMSTATE, plvcd->nmcd.dwItemSpec, LVIS_SELECTED)); + if (bSelected) + { + if(LVIF_FOCUSED & listFlags) + { + rgbFgSel = WADlg_getColor(WADLG_SELBAR_FGCOLOR); + rgbBkSel = WADlg_getColor(WADLG_SELBAR_BGCOLOR); + } + else + { + rgbFgSel = WADlg_getColor(WADLG_INACT_SELBAR_FGCOLOR); + rgbBkSel = WADlg_getColor(WADLG_INACT_SELBAR_BGCOLOR); + } + if (bFullRowSelect) + { + rgbBk = rgbBkSel; + rgbFg = rgbFgSel; + } + } + else + { + if (plvcd->nmcd.dwItemSpec%2 && (style&SWLVS_ALTERNATEITEMS) && config_use_alternate_colors) + { + rgbFg = WADlg_getColor(WADLG_ITEMFG2); + rgbBk = WADlg_getColor(WADLG_ITEMBG2); + } + else + { + rgbBk = plvcd->clrTextBk; + rgbFg = plvcd->clrText; + } + } + } + else + { + #define BLENDER(fg,bg) (RGB((GetRValue(fg)+GetRValue(bg))/2,(GetGValue(fg)+GetGValue(bg))/2,(GetBValue(fg)+GetBValue(bg))/2)) + rgbFg = BLENDER(WADlg_getColor(WADLG_INACT_SELBAR_FGCOLOR),WADlg_getColor(WADLG_WNDBG)); + rgbBk = WADlg_getColor(WADLG_ITEMBG); + } + + plvcd->clrTextBk = rgbBk; + + if (plvcd->nmcd.dwItemSpec == currentItem) + plvcd->clrText = currentColor; + else + plvcd->clrText = rgbFg; + + if (MLSkinnedWnd_EnableReflection(hwndFrom, FALSE)) + { + *pResult = SendMessageW(hwndFrom, WM_NOTIFY, (WPARAM)plvcd->nmcd.hdr.idFrom, (LPARAM)plvcd); + MLSkinnedWnd_EnableReflection(hwndFrom, TRUE); + } + else *pResult = CDRF_DODEFAULT; + + if (!bFullRowSelect) *pResult |= CDRF_NOTIFYSUBITEMDRAW; + + if (bSelected && 0 == (CDRF_SKIPDEFAULT & *pResult)) + { + plvcd->nmcd.uItemState &= ~CDIS_SELECTED; + restoreSelect = TRUE; + *pResult |= CDRF_NOTIFYPOSTPAINT; + } + return TRUE; + + case CDDS_ITEMPOSTPAINT: + if(restoreSelect) + { + plvcd->nmcd.uItemState |= CDIS_SELECTED; + restoreSelect = FALSE; + } + break; + case (CDDS_SUBITEM | CDDS_ITEMPREPAINT): + if (restoreSelect && (bFullRowSelect || 0 == plvcd->iSubItem)) + { + plvcd->clrTextBk = rgbBkSel; + plvcd->clrText = rgbFgSel; + } + else + { + plvcd->clrTextBk = rgbBk; + plvcd->clrText = rgbFg; + } + + if (plvcd->nmcd.dwItemSpec == currentItem) + plvcd->clrText = currentColor; + + break; + } + return FALSE; +} + +BOOL SkinnedListView::OnReflectedNotify(HWND hwndFrom, INT idCtrl, NMHDR *pnmh, LRESULT *pResult) +{ + switch(pnmh->code) + { + case NM_CUSTOMDRAW: + return OnCustomDraw(hwndFrom, (NMLVCUSTOMDRAW*)pnmh, pResult); + case LVN_ITEMCHANGED: + { + NMLISTVIEW *plv = (NMLISTVIEW*)pnmh; + if (((LVIS_SELECTED | LVIS_FOCUSED) & plv->uNewState) != ((LVIS_SELECTED | LVIS_FOCUSED) & plv->uOldState) && + plv->iSubItem == 0 && plv->iItem >= 0 && + LVS_REPORT == (LVS_TYPEMASK & GetWindowLongPtrW(hwnd, GWL_STYLE)) && + 0 == (LVS_EX_FULLROWSELECT & CallPrevWndProc(LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0L))) + { + RECT rc; + ListView_GetItemRect(hwnd, plv->iItem, &rc, LVIR_ICON | ((LVIS_FOCUSED & plv->uNewState) ? LVIR_LABEL : 0)); + if (rc.left != rc.right) InvalidateRect(hwnd, &rc, FALSE); + } + } + break; + } + return FALSE; +} + +LRESULT SkinnedListView::OnEraseBackground(HDC hdc) +{ + HWND hwndHeader; + + hwndHeader = ListView_GetHeader(hwnd); + if (hdc && hwndHeader && IsWindowVisible(hwndHeader)) + { + RECT rc; + if(GetClientRect(hwndHeader, &rc)) + { + MapWindowPoints(hwndHeader, hwnd, (POINT*)&rc, 2); + ExcludeClipRect(hdc, rc.left, rc.top, rc.right, rc.bottom); + } + } + return __super::OnEraseBackground(hdc); +} + +BOOL SkinnedListView::OnMediaLibraryIPC(INT msg, INT_PTR param, LRESULT *pResult) +{ + HWND hwndHeader; + switch(msg) + { + case ML_IPC_SKINNEDLISTVIEW_DISPLAYSORT: + hwndHeader = (HWND)CallPrevWndProc(LVM_GETHEADER, 0, 0L); + *pResult = (hwndHeader) ? SENDMLIPC(hwndHeader, ML_IPC_SKINNEDHEADER_DISPLAYSORT, (WPARAM)param) : 0L; + return TRUE; + case ML_IPC_SKINNEDLISTVIEW_GETSORT: + hwndHeader = (HWND)CallPrevWndProc(LVM_GETHEADER, 0, 0L); + *pResult = (hwndHeader) ? SENDMLIPC(hwndHeader, ML_IPC_SKINNEDHEADER_GETSORT, 0) : 0L; + return TRUE; + case ML_IPC_SKINNEDLISTVIEW_SETCURRENT: + currentItem = (INT)param; + InvalidateRect(hwnd, NULL, FALSE); + UpdateWindow(hwnd); + return TRUE; + } + return __super::OnMediaLibraryIPC(msg, param, pResult); +} + +static BOOL +SkinnedListView_OverrideEdgeItemNavigation(HWND hwnd, unsigned int vkCode) +{ + int iItem, iNextItem, iTest; + + iItem = (int)SendMessageW(hwnd, LVM_GETNEXTITEM, (WPARAM)-1, (LPARAM)(LVNI_ALL | LVNI_FOCUSED)); + if (-1 == iItem) + return FALSE; + + if (VK_RIGHT == vkCode) + { + iNextItem = (int)SendMessageW(hwnd, LVM_GETNEXTITEM, (WPARAM)iItem, (LPARAM)(LVNI_TORIGHT)); + if (iNextItem != iItem) + return FALSE; + + iNextItem = iItem; + for(;;) + { + iTest = (int)SendMessageW(hwnd, LVM_GETNEXTITEM, (WPARAM)iNextItem, (LPARAM)(LVNI_TOLEFT)); + if (-1 == iTest || iTest == iNextItem) + break; + iNextItem = iTest; + } + + iNextItem = (int)SendMessageW(hwnd, LVM_GETNEXTITEM, (WPARAM)iNextItem, (LPARAM)(LVNI_BELOW)); + if (iNextItem == iTest) + return FALSE; + } + else if (VK_LEFT == vkCode) + { + iNextItem = (int)SendMessageW(hwnd, LVM_GETNEXTITEM, (WPARAM)iItem, (LPARAM)(LVNI_TOLEFT)); + if (iNextItem != iItem) + return FALSE; + + iNextItem = (int)SendMessageW(hwnd, LVM_GETNEXTITEM, (WPARAM)iItem, (LPARAM)(LVNI_ABOVE)); + if (-1 != iNextItem && iItem != iNextItem) + { + for(;;) + { + iTest = (int)SendMessageW(hwnd, LVM_GETNEXTITEM, (WPARAM)iNextItem, (LPARAM)(LVNI_TORIGHT)); + if (-1 == iTest || iTest == iNextItem) + break; + iNextItem = iTest; + } + } + } + else + return FALSE; + + if (-1 == iNextItem || iItem == iNextItem) + return FALSE; + else + { + LVITEM item; + BOOL ctrlPressed, shiftPressed; + + ctrlPressed = (0 != (0x8000 & GetAsyncKeyState(VK_CONTROL))); + shiftPressed = (0 != (0x8000 & GetAsyncKeyState(VK_SHIFT))); + + if (FALSE == shiftPressed && FALSE == ctrlPressed) + { + item.state = 0; + item.stateMask = LVIS_SELECTED; + SendMessageW(hwnd, LVM_SETITEMSTATE, (WPARAM)-1, (LPARAM)&item); + } + + item.stateMask = LVIS_FOCUSED; + item.state = 0; + SendMessageW(hwnd, LVM_SETITEMSTATE, (WPARAM)iItem, (LPARAM)&item); + + item.state = LVIS_FOCUSED; + if (FALSE == ctrlPressed) + { + item.state |= LVIS_SELECTED; + item.stateMask |= LVIS_SELECTED; + } + + SendMessageW(hwnd, LVM_SETITEMSTATE, (WPARAM)iNextItem, (LPARAM)&item); + SendMessageW(hwnd, LVM_ENSUREVISIBLE, (WPARAM)iNextItem, (LPARAM)FALSE); + } + return TRUE; +} + +void SkinnedListView::OnKeyDown(UINT vkCode, UINT flags) +{ + switch(vkCode) + { + case VK_LEFT: + case VK_RIGHT: + { + unsigned long windowStyle; + windowStyle = GetWindowLongPtrW(hwnd, GWL_STYLE); + + if (LVS_REPORT == (LVS_TYPEMASK & windowStyle)) + { + if (0 != IsHorzBarHidden()) + { + HWND hParent = GetParent(hwnd); + if (NULL != hParent) + { + NMLVKEYDOWN lvkd; + lvkd.hdr.code = LVN_KEYDOWN; + lvkd.hdr.hwndFrom = hwnd; + lvkd.hdr.idFrom = (UINT)(UINT_PTR)GetWindowLongPtr(hwnd, GWLP_ID); + lvkd.wVKey = vkCode; + SendMessage(hParent, WM_NOTIFY, (WPARAM)lvkd.hdr.idFrom, (LPARAM)&lvkd); + } + vkCode = 0; + } + } + else if (LVS_ICON == (LVS_TYPEMASK & windowStyle)) + { + if (FALSE != SkinnedListView_OverrideEdgeItemNavigation(hwnd, vkCode)) + { + vkCode = 0; + } + } + } + break; + case VK_SPACE: + if (0 != (SWLVS_SELALWAYS & style)) + { + UINT windowStyle = GetWindowLongPtr(hwnd, GWL_STYLE); + if (0 != (LVS_SINGLESEL & windowStyle) && + 0 != (0x8000 & GetAsyncKeyState(VK_CONTROL))) + { + vkCode = 0; + } + } + break; + } + __super::WindowProc(WM_KEYDOWN, (WPARAM)vkCode, (LPARAM)flags); +} + +void SkinnedListView::TryAttachHeader(void) +{ + if (0 != (LVIF_HEADERATTACHED & listFlags)) return; + DWORD ws = GetWindowLongPtrW(hwnd, GWL_STYLE); + if (LVS_REPORT == (LVS_TYPEMASK & ws) && 0 == (LVS_NOCOLUMNHEADER & ws)) + { + HWND hHeader = (HWND)CallPrevWndProc(LVM_GETHEADER, 0, 0L); + if (hHeader) + { + SkinWindow(hHeader, SWS_USESKINFONT | SWS_USESKINCOLORS | SWS_USESKINCURSORS); + listFlags |= LVIF_HEADERATTACHED; + } + } +} + +void SkinnedListView::OnLButtonDown(UINT uFlags, POINTS pts) +{ + if (0 != (SWLVS_SELALWAYS & style)) + { + LVHITTESTINFO ht; + POINTSTOPOINT(ht.pt, pts); + INT index = (INT)CallPrevWndProc(LVM_HITTEST, 0, (LPARAM)&ht); + if (-1 == index) + { + if (hwnd != GetFocus() && + 0 != (LVS_EX_FULLROWSELECT & CallPrevWndProc(LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0L))) + { + SetFocus(hwnd); + } + return; + } + } + + __super::WindowProc(WM_LBUTTONDOWN, (WPARAM)uFlags, *((LPARAM*)&pts)); +} + +void SkinnedListView::OnRButtonDown(UINT uFlags, POINTS pts) +{ + if (0 != (SWLVS_SELALWAYS & style)) + { + LVHITTESTINFO ht; + POINTSTOPOINT(ht.pt, pts); + INT index = (INT)CallPrevWndProc(LVM_HITTEST, 0, (LPARAM)&ht); + if (-1 == index) + { + return; + } + } + + __super::WindowProc(WM_RBUTTONDOWN, (WPARAM)uFlags, *((LPARAM*)&pts)); +} + +LRESULT SkinnedListView::OnGetDlgCode(UINT vkCode, MSG* pMsg) +{ + if (NULL != pMsg) + { + switch(vkCode) + { + case VK_RETURN: + SendMessage(hwnd, pMsg->message, pMsg->wParam, pMsg->lParam); + return 0; + } + } + + LRESULT result = __super::WindowProc(WM_GETDLGCODE, (WPARAM)vkCode, (LPARAM)pMsg); + if (NULL == pMsg) + { + result |= DLGC_WANTMESSAGE; + } + return result; +} + +LRESULT SkinnedListView::WindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch(uMsg) + { + case REFLECTED_NOTIFY: return OnReflectedNotify(((REFLECTPARAM*)lParam)->hwndFrom, (INT)wParam, (NMHDR*)((REFLECTPARAM*)lParam)->lParam, &((REFLECTPARAM*)lParam)->result); + // custom handling of this allows us to handle disabled listview controls correctly + // so we don't have the part skinned / part OS colouring which happened before + case WM_ENABLE: InvalidateRect(hwnd, NULL, TRUE); return 1; + case WM_SETFONT: + HWND hwndHeader; + hwndHeader = ListView_GetHeader(hwnd); + if (hwndHeader) + { + SendMessageW(hwndHeader, WM_SETFONT, wParam, lParam); + MLSkinnedHeader_SetHeight(hwndHeader, -1); + } + break; + case WM_LBUTTONDOWN: OnLButtonDown((UINT)wParam, MAKEPOINTS(lParam)); return 0; + case WM_RBUTTONDOWN: OnRButtonDown((UINT)wParam, MAKEPOINTS(lParam)); return 0; + case WM_KEYDOWN: OnKeyDown((UINT)wParam, (UINT)lParam); return 0; + case LVM_INSERTCOLUMNA: + case LVM_INSERTCOLUMNW: + { + LRESULT r = __super::WindowProc(uMsg, wParam, lParam); + TryAttachHeader(); + return r; + } + break; + case WM_GETDLGCODE: return OnGetDlgCode((UINT)wParam, (MSG*)lParam); + } + return __super::WindowProc(uMsg, wParam, lParam); +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/skinnedlistview.h b/Src/Plugins/General/gen_ml/skinnedlistview.h new file mode 100644 index 00000000..9d81b6de --- /dev/null +++ b/Src/Plugins/General/gen_ml/skinnedlistview.h @@ -0,0 +1,46 @@ +#ifndef NULLOSFT_MEDIALIBRARY_SKINNED_LISTVIEW_CONTROL_HEADER +#define NULLOSFT_MEDIALIBRARY_SKINNED_LISTVIEW_CONTROL_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include "./skinnedscrollwnd.h" +#include + +extern int config_use_alternate_colors; + +class SkinnedListView : public SkinnedScrollWnd +{ + +protected: + SkinnedListView(void); + virtual ~SkinnedListView(void); + + +public: + virtual BOOL SetStyle(UINT newStyle, BOOL bRedraw); +protected: + virtual BOOL Attach(HWND hwndListView); + virtual void TryAttachHeader(void); + virtual LRESULT WindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam); + virtual BOOL OnReflectedNotify(HWND hwndFrom, INT idCtrl, NMHDR *pnmh, LRESULT *pResult); + virtual BOOL OnCustomDraw(HWND hwndFrom, NMLVCUSTOMDRAW *plvcd, LRESULT *pResult); + virtual void OnSkinChanged(BOOL bNotifyChildren, BOOL bRedraw); + virtual LRESULT OnEraseBackground(HDC hdc); + virtual BOOL OnMediaLibraryIPC(INT msg, INT_PTR param, LRESULT *pResult); + virtual void OnLButtonDown(UINT uFlags, POINTS pts); + virtual void OnRButtonDown(UINT uFlags, POINTS pts); + virtual void OnKeyDown(UINT vkCode, UINT flags); + virtual LRESULT OnGetDlgCode(UINT vkCode, MSG* pMsg); + +private: + friend BOOL SkinWindowEx(HWND hwndToSkin, INT type, UINT style); + +private: + UINT listFlags; + COLORREF currentColor; + DWORD currentItem; +}; + +#endif // NULLOSFT_MEDIALIBRARY_SKINNED_LISTVIEW_CONTROL_HEADER \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/skinnedmenu.cpp b/Src/Plugins/General/gen_ml/skinnedmenu.cpp new file mode 100644 index 00000000..f583dd49 --- /dev/null +++ b/Src/Plugins/General/gen_ml/skinnedmenu.cpp @@ -0,0 +1,81 @@ +#include "main.h" +#include "./skinnedmenu.h" +#include "./skinnedmenuwnd.h" + + +SkinnedMenu::SkinnedMenu() +{ + hwndOwner = NULL; + skinStyle = SMS_NORMAL; + hmlil = NULL; + width = 0; + + if (FAILED(SkinnedMenuThreadInfo::GetInstance(TRUE, &threadInfo))) + threadInfo = NULL; +} + +SkinnedMenu::~SkinnedMenu(void) +{ + if (NULL != threadInfo) + { + threadInfo->RemoveAttachHook(this); + threadInfo->Release(); + } +} + +HWND SkinnedMenu::WindowFromHandle(HMENU menu) +{ + HWND hwnd; + SkinnedMenuThreadInfo *threadInfo; + + if (S_OK != SkinnedMenuThreadInfo::GetInstance(FALSE, &threadInfo)) + return NULL; + + hwnd = threadInfo->FindMenuWindow(menu); + + threadInfo->Release(); + + return hwnd; +} + +BOOL SkinnedMenu::InitializeHook(HWND hwndOwner, UINT skinStyle, HMLIMGLST hmlil, INT width, MENUCUSTOMIZEPROC _customProc, ULONG_PTR customParam) +{ + if (NULL == threadInfo) + return FALSE; + + if (FALSE != threadInfo->IsAttachHookActive()) + return FALSE; + + this->hwndOwner = hwndOwner; + this->hmlil = hmlil; + this->width = width; + this->skinStyle = skinStyle; + this->customProc = _customProc; + this->customParam = customParam; + + return threadInfo->SetAttachHook(this); +} + +BOOL SkinnedMenu::TrackMenuPopupEx(HMENU hmenu, UINT fuFlags, INT x, INT y, HWND hwnd, LPTPMPARAMS lptpm, UINT skinStyle, + HMLIMGLST hmlil, INT width, MENUCUSTOMIZEPROC customProc, ULONG_PTR customParam) +{ + if (NULL == hwnd || + !InitializeHook(hwnd, skinStyle, hmlil, width, customProc, customParam)) + { + return FALSE; + } + + return TrackPopupMenuEx(hmenu, fuFlags, x, y, hwnd, lptpm); +} + +BOOL SkinnedMenu::AttachToHwnd(HWND hwndMenu) +{ + SkinnedMenuWnd *psw = new SkinnedMenuWnd(skinStyle, hmlil, width, customProc, customParam); + if (!psw || !psw->Attach(hwndMenu, hwndOwner)) + { + delete(psw); + return FALSE; + } + + return TRUE; +} diff --git a/Src/Plugins/General/gen_ml/skinnedmenu.h b/Src/Plugins/General/gen_ml/skinnedmenu.h new file mode 100644 index 00000000..cc2cac78 --- /dev/null +++ b/Src/Plugins/General/gen_ml/skinnedmenu.h @@ -0,0 +1,42 @@ +#ifndef NULLOSFT_MEDIALIBRARY_SKINNED_MENU_HEADER +#define NULLOSFT_MEDIALIBRARY_SKINNED_MENU_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include +#include "./skinnedmenuthreadinfo.h" + +class SkinnedMenu +{ +public: + SkinnedMenu(); + virtual ~SkinnedMenu(void); + +public: + static HWND WindowFromHandle(HMENU menu); + +public: + virtual BOOL InitializeHook(HWND hwndOwner, UINT skinStyle, HMLIMGLST hmlil, INT width, MENUCUSTOMIZEPROC customProc, ULONG_PTR customParam); + virtual BOOL TrackMenuPopupEx(HMENU hmenu, UINT fuFlags, INT x, INT y, HWND hwnd, LPTPMPARAMS lptpm, UINT skinStyle, + HMLIMGLST hmlil, INT width, MENUCUSTOMIZEPROC customProc, ULONG_PTR customParam); + +protected: + virtual BOOL AttachToHwnd(HWND hwndMenu); + +protected: + friend class SkinnedMenuThreadInfo; + +protected: + HWND hwndOwner; + UINT skinStyle; + HMLIMGLST hmlil; + INT width; + MENUCUSTOMIZEPROC customProc; + ULONG_PTR customParam; + SkinnedMenuThreadInfo *threadInfo; +}; + + +#endif // NULLOSFT_MEDIALIBRARY_SKINNED_MENU_HEADER \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/skinnedmenuthreadinfo.cpp b/Src/Plugins/General/gen_ml/skinnedmenuthreadinfo.cpp new file mode 100644 index 00000000..d0b19a7e --- /dev/null +++ b/Src/Plugins/General/gen_ml/skinnedmenuthreadinfo.cpp @@ -0,0 +1,468 @@ +#include "main.h" +#include "./skinnedMenuThreadInfo.h" +#include "./skinnedMenu.h" +#include "./skinnedMenuWnd.h" +#ifndef _DEBUG + #include +#endif + +static DWORD tlsIndex = TLS_OUT_OF_INDEXES; + + size_t ref; + HHOOK attachHook; + SkinnedMenu *attachMenu; + HHOOK validationHook; + SkinnedMenuWnd *validationWindow; + khash_t(intptr_map) *windowMap; + khash_t(int_set) *claimedIdSet; + unsigned int lastAssignedId; + HMENU activeMeasureMenu; + +SkinnedMenuThreadInfo::SkinnedMenuThreadInfo() + : ref(1), attachHook(NULL), attachMenu(NULL), validationHook(NULL), + validationWindow(NULL), lastAssignedId((unsigned int)-100), + activeMeasureMenu(NULL) +{ + windowMap = kh_init(intptr_map); + claimedIdSet = kh_init(int_set); +} + +SkinnedMenuThreadInfo::~SkinnedMenuThreadInfo() +{ + if (TLS_OUT_OF_INDEXES != tlsIndex) + TlsSetValue(tlsIndex, NULL); + + if (NULL != attachHook) + { + UnhookWindowsHookEx(attachHook); + attachHook = NULL; + } + + if (NULL != validationHook) + { + UnhookWindowsHookEx(validationHook); + validationHook = NULL; + } + + if (NULL != windowMap) + kh_destroy(intptr_map, windowMap); + + if (NULL != claimedIdSet) + kh_destroy(int_set, claimedIdSet); +} + +HRESULT SkinnedMenuThreadInfo::GetInstance(BOOL allowCreate, SkinnedMenuThreadInfo **instance) +{ + HRESULT hr; + SkinnedMenuThreadInfo *self; + + if (NULL == instance) + return E_POINTER; + + *instance = NULL; + + if (TLS_OUT_OF_INDEXES == tlsIndex) + { + tlsIndex = TlsAlloc(); + if (TLS_OUT_OF_INDEXES == tlsIndex) + return E_OUTOFMEMORY; + + self = NULL; + + if (FALSE != TlsSetValue(tlsIndex, NULL)) + SetLastError(ERROR_SUCCESS); + } + else + { + self = (SkinnedMenuThreadInfo*)TlsGetValue(tlsIndex); + } + + if (NULL == self) + { + unsigned long errorCode; + errorCode = GetLastError(); + if (ERROR_SUCCESS != errorCode) + { + hr = HRESULT_FROM_WIN32(errorCode); + } + else + { + if (FALSE == allowCreate) + { + self = NULL; + hr = S_FALSE; + } + else + { + #ifndef _DEBUG + self = new (std::nothrow) SkinnedMenuThreadInfo(); + #else + self = new SkinnedMenuThreadInfo(); + #endif + if (NULL == self) + hr = E_OUTOFMEMORY; + else + { + if (FALSE == TlsSetValue(tlsIndex, self)) + { + errorCode = GetLastError(); + hr = HRESULT_FROM_WIN32(errorCode); + self->Release(); + self = NULL; + } + else + { + hr = S_OK; + } + } + } + } + } + else + { + self->AddRef(); + hr = S_OK; + } + + *instance = self; + return hr; +} + +size_t SkinnedMenuThreadInfo::AddRef() +{ + return InterlockedIncrement((LONG*)&ref); +} + +size_t SkinnedMenuThreadInfo::Release() +{ + if (0 == ref) + return ref; + + LONG r = InterlockedDecrement((LONG*)&ref); + if (0 == r) + delete(this); + + return r; +} + +BOOL SkinnedMenuThreadInfo::SetAttachHook(SkinnedMenu *menu) +{ + if (NULL == menu) + return FALSE; + + if (NULL != attachHook) + return FALSE; + + attachMenu = menu; + attachHook = SetWindowsHookEx(WH_CALLWNDPROC, SkinnedMenuThreadInfo_AttachHookCb, NULL, GetCurrentThreadId()); + if (NULL == attachHook) + { + attachMenu = FALSE; + return FALSE; + } + + return TRUE; +} + +BOOL SkinnedMenuThreadInfo::RemoveAttachHook(SkinnedMenu *menu) +{ + if (NULL == attachHook) + return FALSE; + + if (menu != attachMenu) + return FALSE; + + UnhookWindowsHookEx(attachHook); + + attachHook = NULL; + attachMenu = NULL; + + return TRUE; +} + +BOOL SkinnedMenuThreadInfo::IsAttachHookActive() +{ + return (NULL != attachHook); +} + +LRESULT SkinnedMenuThreadInfo::AttachHook(int nCode, WPARAM wParam, LPARAM lParam) +{ + if (HC_ACTION == nCode) + { + CWPSTRUCT *pcwp = (CWPSTRUCT*)lParam; + if (WM_NCCREATE == pcwp->message) + { + wchar_t szName[128] = {0}; + if (GetClassNameW(pcwp->hwnd, szName, ARRAYSIZE(szName)) && + CSTR_EQUAL == CompareStringW(CSTR_INVARIANT, NORM_IGNORECASE, szName, -1, L"#32768", -1)) + { + LRESULT result; + HHOOK hookCopy; + SkinnedMenu *menuCopy; + + menuCopy = attachMenu; + hookCopy = attachHook; + + attachHook = NULL; + attachMenu = NULL; + + result = CallNextHookEx(hookCopy, nCode, wParam, lParam); + UnhookWindowsHookEx(hookCopy); + + if (NULL != menuCopy) + menuCopy->AttachToHwnd(pcwp->hwnd); + + return result; + } + } + } + + return CallNextHookEx(attachHook, nCode, wParam, lParam); +} + +BOOL SkinnedMenuThreadInfo::SetValidationHook(SkinnedMenuWnd *window) +{ + HMENU prevMenu; + + if (NULL == window) + return FALSE; + + if (NULL != validationHook) + return FALSE; + + validationWindow = window; + prevMenu = SetActiveMeasureMenu(window->GetMenuHandle()); + + validationHook = SetWindowsHookEx(WH_CALLWNDPROC, SkinnedMenuThreadInfo_ValidationHookCb, NULL, GetCurrentThreadId()); + if (NULL == validationHook) + { + validationWindow = FALSE; + SetActiveMeasureMenu(prevMenu); + return FALSE; + } + + return TRUE; +} + +BOOL SkinnedMenuThreadInfo::RemoveValidationHook(SkinnedMenuWnd *window) +{ + if (NULL == validationHook) + return FALSE; + + if (window != validationWindow) + return FALSE; + + + UnhookWindowsHookEx(validationHook); + + validationWindow = NULL; + validationHook = NULL; + + return TRUE; +} + +BOOL SkinnedMenuThreadInfo::IsValidationHookActive() +{ + return (NULL != validationHook); +} + +LRESULT SkinnedMenuThreadInfo::ValidationHook(int nCode, WPARAM wParam, LPARAM lParam) +{ + if (HC_ACTION == nCode) + { + CWPSTRUCT *pcwp = (CWPSTRUCT*)lParam; + + if (WM_MEASUREITEM == pcwp->message || + WM_DRAWITEM == pcwp->message) + { + if (NULL != validationWindow && NULL != pcwp->lParam) + { + BOOL validationCompleted(FALSE); + + if (WM_MEASUREITEM == pcwp->message) + { + MEASUREITEMSTRUCT *measureItem; + measureItem = (MEASUREITEMSTRUCT*)pcwp->lParam; + if (ODT_MENU == measureItem->CtlType && + validationWindow->GetMenuHandle() == GetActiveMeasureMenu()) + { + validationCompleted = TRUE; + } + } + else + { + DRAWITEMSTRUCT *drawItem; + drawItem = (DRAWITEMSTRUCT*)pcwp->lParam; + + if (ODT_MENU == drawItem->CtlType && + validationWindow->GetMenuHandle() == (HMENU)drawItem->hwndItem) + { + validationCompleted = TRUE; + } + } + + if (FALSE != validationCompleted) + { + LRESULT result; + HHOOK hookCopy; + SkinnedMenuWnd *windowCopy; + + windowCopy = validationWindow; + hookCopy = validationHook; + + validationHook = NULL; + validationWindow = NULL; + + if (NULL != windowCopy && windowCopy->GetOwnerWindow() != pcwp->hwnd) + { + windowCopy->SetOwnerWindow(pcwp->hwnd); + } + + result = CallNextHookEx(hookCopy, nCode, wParam, lParam); + UnhookWindowsHookEx(hookCopy); + + + return result; + } + } + } + } + + return CallNextHookEx(attachHook, nCode, wParam, lParam); +} + +BOOL SkinnedMenuThreadInfo::RegisterMenu(HMENU menu, HWND window) +{ + int code; + khint_t key; + + if (NULL == menu) + return FALSE; + + if (NULL == windowMap) + return FALSE; + + key = kh_put(intptr_map, windowMap, (intptr_t)menu, &code); + kh_val(windowMap, key) = window; + + return TRUE; +} + +BOOL SkinnedMenuThreadInfo::UnregisterMenu(HMENU menu) +{ + khint_t key; + + if (NULL == menu) + return FALSE; + + if (NULL == windowMap) + return FALSE; + + key = kh_get(intptr_map, windowMap, (intptr_t)menu); + if (kh_end(windowMap) == key) + return FALSE; + + kh_del(intptr_map, windowMap, key); + return TRUE; +} + +HWND SkinnedMenuThreadInfo::FindMenuWindow(HMENU menu) +{ + khint_t key; + + if (NULL == menu) + return NULL; + + if (NULL == windowMap) + return NULL; + + key = kh_get(intptr_map, windowMap, (intptr_t)menu); + if (kh_end(windowMap) == key) + return NULL; + + return kh_val(windowMap, key); +} + +void SkinnedMenuThreadInfo::ClaimId(unsigned int id) +{ + int code; + kh_put(int_set, claimedIdSet, id, &code); +} + +void SkinnedMenuThreadInfo::ReleaseId(unsigned int id) +{ + khint_t key; + key = kh_get(int_set, claimedIdSet, id); + if (kh_end(claimedIdSet) != key) + kh_del(int_set, claimedIdSet, key); +} + +unsigned int SkinnedMenuThreadInfo::GetAvailableId() +{ + khint_t key; + unsigned int originalId; + + lastAssignedId--; + if ((unsigned int)-1 == lastAssignedId) + lastAssignedId--; + + originalId = lastAssignedId; + + for(;;) + { + key = kh_get(int_set, claimedIdSet, lastAssignedId); + if (kh_end(claimedIdSet) == key) + return lastAssignedId; + + lastAssignedId--; + if ((unsigned int)-1 == lastAssignedId) + lastAssignedId--; + + if (lastAssignedId == originalId) + break; + } + + return (unsigned int)-1; +} + +HMENU SkinnedMenuThreadInfo::SetActiveMeasureMenu(HMENU menu) +{ + HMENU prevMenu; + prevMenu = activeMeasureMenu; + activeMeasureMenu = menu; + return prevMenu; + +} + +HMENU SkinnedMenuThreadInfo::GetActiveMeasureMenu() +{ + return activeMeasureMenu; +} + +static LRESULT CALLBACK SkinnedMenuThreadInfo_AttachHookCb(int nCode, WPARAM wParam, LPARAM lParam) +{ + LRESULT result; + SkinnedMenuThreadInfo *self; + + if (S_OK != SkinnedMenuThreadInfo::GetInstance(FALSE, &self)) + return 0; + + result = self->AttachHook(nCode, wParam, lParam); + + self->Release(); + return result; +} + +static LRESULT CALLBACK SkinnedMenuThreadInfo_ValidationHookCb(int nCode, WPARAM wParam, LPARAM lParam) +{ + LRESULT result; + SkinnedMenuThreadInfo *self; + + if (S_OK != SkinnedMenuThreadInfo::GetInstance(FALSE, &self)) + return 0; + + result = self->ValidationHook(nCode, wParam, lParam); + + self->Release(); + return result; +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/skinnedmenuthreadinfo.h b/Src/Plugins/General/gen_ml/skinnedmenuthreadinfo.h new file mode 100644 index 00000000..347f37b1 --- /dev/null +++ b/Src/Plugins/General/gen_ml/skinnedmenuthreadinfo.h @@ -0,0 +1,74 @@ +#ifndef NULLOSFT_MEDIALIBRARY_SKINNED_MENU_THREAD_INFO_HEADER +#define NULLOSFT_MEDIALIBRARY_SKINNED_MENU_THREAD_INFO_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include +#include "./klib/khash.h" + +#ifdef _WIN64 + KHASH_MAP_INIT_INT64(intptr_map, HWND); +#else + KHASH_MAP_INIT_INT(intptr_map, HWND); +#endif + +KHASH_SET_INIT_INT(int_set) + +class SkinnedMenu; +class SkinnedMenuWnd; + +class SkinnedMenuThreadInfo +{ +protected: + SkinnedMenuThreadInfo(); + ~SkinnedMenuThreadInfo(); + +public: + static HRESULT GetInstance(BOOL allowCreate, SkinnedMenuThreadInfo **instance); + +public: + size_t AddRef(); + size_t Release(); + + BOOL SetAttachHook(SkinnedMenu *menu); + BOOL RemoveAttachHook(SkinnedMenu *menu); + BOOL IsAttachHookActive(); + + BOOL SetValidationHook(SkinnedMenuWnd *window); + BOOL RemoveValidationHook(SkinnedMenuWnd *window); + BOOL IsValidationHookActive(); + + BOOL RegisterMenu(HMENU menu, HWND window); + BOOL UnregisterMenu(HMENU menu); + HWND FindMenuWindow(HMENU menu); + + void ClaimId(unsigned int id); + void ReleaseId(unsigned int id); + unsigned int GetAvailableId(); + + HMENU SetActiveMeasureMenu(HMENU menu); + HMENU GetActiveMeasureMenu(); + +protected: + LRESULT AttachHook(int nCode, WPARAM wParam, LPARAM lParam); + LRESULT ValidationHook(int nCode, WPARAM wParam, LPARAM lParam); + +protected: + friend static LRESULT CALLBACK SkinnedMenuThreadInfo_AttachHookCb(int nCode, WPARAM wParam, LPARAM lParam); + friend static LRESULT CALLBACK SkinnedMenuThreadInfo_ValidationHookCb(int nCode, WPARAM wParam, LPARAM lParam); + +protected: + size_t ref; + HHOOK attachHook; + SkinnedMenu *attachMenu; + HHOOK validationHook; + SkinnedMenuWnd *validationWindow; + khash_t(intptr_map) *windowMap; + khash_t(int_set) *claimedIdSet; + unsigned int lastAssignedId; + HMENU activeMeasureMenu; +}; + +#endif //NULLOSFT_MEDIALIBRARY_SKINNED_MENU_THREAD_INFO_HEADER \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/skinnedmenuwnd.cpp b/Src/Plugins/General/gen_ml/skinnedmenuwnd.cpp new file mode 100644 index 00000000..f1591737 --- /dev/null +++ b/Src/Plugins/General/gen_ml/skinnedmenuwnd.cpp @@ -0,0 +1,1538 @@ +#include "api__gen_ml.h" +#include "main.h" +#include "./skinnedmenuwnd.h" +#include "./skinnedmenu.h" +#include "./skinning.h" + +#include "./ml_imagelist.h" +#include "./colors.h" +#include "./resource.h" + +#include "../winamp/wa_dlg.h" + + +#define MENU_BORDER_WIDTH 3 + +static HMLIMGLST hmlilCheck = NULL; +static INT imageCheckMark = -1; +static INT imageRadioMark = -1; +static INT imageExpandArrow = -1; + +// menu hit test codes +#define MHF_NOWHERE 0xFFFFFFFF +#define MHF_SCROLLUP 0xFFFFFFFD +#define MHF_SCROLLDOWN 0xFFFFFFFC + +#define MN_SIZEWINDOW 0x01E2 +#define MN_SELECTITEM 0x01E5 // wParam - item position or MHF_XXX +#define MN_LBUTTONDOWN 0x01ED // wParam - item position or MHF_XXX +#define MN_LBUTTONUP 0x01EF // wParam - item position or MHF_XXX +#define MN_LBUTTONDBLCLK 0x01F1 // ? + +// menu timer id +#define MTID_OPENSUBMENU 0x0000FFFE +#define MTID_SCROLLUP 0xFFFFFFFD +#define MTID_SCROLLDOWN 0xFFFFFFFC + + +#define MTID_EX_UNBLOCKDRAW 0x0001980 + +extern HMLIMGFLTRMNGR hmlifMngr; // default gen_ml fitler manager + +#define SMIF_BLOCKDRAW 0x00000001 +#define SMIF_REMOVEREFLECTOR 0x00000002 + +static HBRUSH SkinnedMenuWnd_GetBackBrush(HMENU hMenu) +{ + MENUINFO mi = {0}; + mi.cbSize = sizeof(MENUINFO); + mi.fMask = MIM_BACKGROUND; + if (NULL == hMenu || !GetMenuInfo(hMenu, &mi)) + mi.hbrBack = NULL; + return (NULL != mi.hbrBack) ? mi.hbrBack : GetSysColorBrush(COLOR_MENU); +} + +static INT SkinnedMenuWnd_AddPngResource(HMLIMGLST imageList, INT resourceId) +{ + MLIMAGESOURCE_I src; + ZeroMemory(&src, sizeof(MLIMAGESOURCE_I)); + src.type = SRC_TYPE_PNG_I; + src.hInst = plugin.hDllInstance; + src.lpszName = MAKEINTRESOURCEW(resourceId); + return MLImageListI_Add(hmlilCheck, &src, MLIF_FILTER1_UID, 0); +} + +static HBITMAP SkinnedMenuWnd_LoadPngResource(INT resourceId, COLORREF rgbBk, COLORREF rgbFg) +{ + MLIMAGESOURCE_I imageSource; + ZeroMemory(&imageSource, sizeof(MLIMAGESOURCE_I)); + imageSource.type = SRC_TYPE_PNG_I; + imageSource.hInst = plugin.hDllInstance; + imageSource.lpszName = MAKEINTRESOURCEW(resourceId); + HBITMAP hbmp = MLImageLoaderI_LoadDib(&imageSource); + if (NULL != hbmp) + MLImageFilterI_Apply(hmlifMngr, &MLIF_FILTER1_UID, hbmp, rgbBk, rgbFg, NULL); + return hbmp; +} + +SkinnedMenuWnd::SkinnedMenuWnd(UINT menuExStyle, HMLIMGLST hmlil, INT forcedWidth, MENUCUSTOMIZEPROC _customProc, ULONG_PTR customParam) : + SkinnedWnd(FALSE) +{ + if (FAILED(SkinnedMenuThreadInfo::GetInstance(TRUE, &threadInfo))) + threadInfo = NULL; + + hMenu = NULL; + hOwner = NULL; + this->menuExStyle = menuExStyle; + this->hmlil = hmlil; + this->lineWidth = forcedWidth; + bRestoreShadow = FALSE; + hBoldFont = NULL; + + menuFlags = 0; + + backBrush = NULL; + borderPen = NULL; + + menuOrigBrush = NULL; + skinnedItems = NULL; + skinnedItemCount = 0; + skinnedItemCursor = 0; + prevSelectedItem = 0; + + shortcutCX = 0; + textCX = 0; + + scrollBitmap = NULL; + disabledScrollBitmap = NULL; + + this->customProc = _customProc; + this->customParam = customParam; +} + +SkinnedMenuWnd::~SkinnedMenuWnd(void) +{ + SetOwnerWindow(NULL); + + if (hMenu) + { + MENUINFO mi = {0}; + mi.cbSize = sizeof(MENUINFO); + mi.fMask = MIM_BACKGROUND; + if (GetMenuInfo(hMenu, &mi)) + { + mi.fMask = 0; + if (menuOrigBrush != mi.hbrBack) + { + mi.hbrBack = menuOrigBrush; + mi.fMask |= MIM_BACKGROUND; + } + + if (0 != mi.fMask) + SetMenuInfo(hMenu, &mi); + } + + if (NULL != skinnedItems && skinnedItemCount > 0) + { + MENUITEMINFOW mii = {0}; + mii.cbSize = sizeof(MENUITEMINFOW); + + for (INT i = 0; i < skinnedItemCount; i++) + { + SkinnedItemRecord *record = &skinnedItems[i]; + if(FALSE == record->failed) + { + mii.fMask = MIIM_FTYPE | MIIM_ID | MIIM_BITMAP; + if (FALSE != GetMenuItemInfoW(hMenu, i, TRUE, &mii)) + { + mii.fMask = 0; + if (FALSE != record->skinned) + { + mii.fMask |= (MIIM_FTYPE | MIIM_BITMAP); + mii.fType &= ~MFT_OWNERDRAW; + record->skinned = FALSE; + } + + if (record->itemId != record->originalId && + record->itemId == mii.wID) + { + mii.fMask |= MIIM_ID; + mii.wID = record->originalId; + } + + if (NULL != threadInfo) + { + threadInfo->ReleaseId(record->itemId); + if (record->itemId != record->originalId) + threadInfo->ReleaseId(record->originalId); + } + + if (0 != mii.fMask) + { + if (FALSE == SetMenuItemInfoW(hMenu, i, TRUE, &mii)) + { + } + } + } + } + } + } + + if (NULL != threadInfo) + threadInfo->UnregisterMenu(hMenu); + } + + if (NULL != skinnedItems) + free(skinnedItems); + + if (hwnd && bRestoreShadow) + { + SetClassLongPtrW(hwnd, GCL_STYLE, GetClassLongPtrW(hwnd, GCL_STYLE) | 0x00020000/*CS_DROPSHADOW*/); + } + + if (NULL != hBoldFont) + DeleteObject(hBoldFont); + + if (NULL != backBrush) + DeleteObject(backBrush); + + if (NULL != borderPen) + DeleteObject(borderPen); + + if (NULL != scrollBitmap) + DeleteObject(scrollBitmap); + + if (NULL != disabledScrollBitmap) + DeleteObject(disabledScrollBitmap); + + if (NULL != threadInfo) + { + threadInfo->RemoveValidationHook(this); + threadInfo->Release(); + } +} + +HMENU SkinnedMenuWnd::GetMenuHandle() +{ + return hMenu; +} + +HWND SkinnedMenuWnd::GetOwnerWindow() +{ + return hOwner; +} + +HWND SkinnedMenuWnd::SetOwnerWindow(HWND hwndOwner) +{ + if (hOwner == hwndOwner) + return hOwner; + + HWND prevOwner = hOwner; + + if (NULL != hOwner && + 0 != (SMIF_REMOVEREFLECTOR & menuFlags)) + { + RemoveReflector(hOwner); + } + + menuFlags &= ~SMIF_REMOVEREFLECTOR; + + hOwner = hwndOwner; + + if (NULL != hOwner && + S_OK == InstallReflector(hOwner)) + { + menuFlags |= SMIF_REMOVEREFLECTOR; + } + + return prevOwner; +} + +BOOL SkinnedMenuWnd::AttachMenu(HMENU hMenuToAttach) +{ + MENUINFO mi = {0}; + MENUITEMINFOW mii = {0}; + + if (NULL != hMenu || + NULL == hMenuToAttach) + { + return FALSE; + } + + hMenu = hMenuToAttach; + + mi.cbSize = sizeof(MENUINFO); + mi.fMask = MIM_BACKGROUND; + + if (GetMenuInfo(hMenu, &mi)) + { + menuOrigBrush = mi.hbrBack; + mi.fMask = 0; + + if (NULL == mi.hbrBack) + { + COLORREF rgb; + + if (0 != (SMS_SYSCOLORS & menuExStyle) || + FAILED(MLGetSkinColor(MLSO_MENU, MP_BACKGROUND, MBS_NORMAL, &rgb))) + { + rgb = GetSysColor(COLOR_MENU); + } + + backBrush = CreateSolidBrush(rgb); + + mi.hbrBack = backBrush; + mi.fMask |= MIM_BACKGROUND; + } + + if (0 != mi.fMask) + SetMenuInfo(hMenu, &mi); + } + + if (NULL != threadInfo) + threadInfo->RegisterMenu(hMenu, hwnd); + + mii.cbSize = sizeof(MENUITEMINFOW); + + INT count = GetMenuItemCount(hMenu); + + if (count > 0) + { + skinnedItems = (SkinnedItemRecord*)calloc(count, sizeof(SkinnedItemRecord)); + if (NULL != skinnedItems) + { + skinnedItemCount = count; + } + } + + if (NULL == skinnedItems) + return FALSE; + + skinnedItemCursor = 0; + + for (int i = 0; i < count; i++) + { + SkinnedItemRecord *record = &skinnedItems[i]; + mii.fMask = MIIM_FTYPE | MIIM_ID | MIIM_BITMAP; // MIIM_BITMAP - keep it... this forces menu to call WM_MEASUREITEM + if (FALSE != GetMenuItemInfoW(hMenu, i, TRUE, &mii)) + { + record->originalId = mii.wID; + record->itemId = record->originalId; + + if (NULL != threadInfo) + threadInfo->ClaimId(record->originalId); + + if (0 == (MFT_OWNERDRAW & mii.fType)) + { + mii.fType |= MFT_OWNERDRAW; + mii.fMask &= ~MIIM_ID; + + // copes with separators and popup menus (menu and menuex types) + if (0 == mii.wID || (UINT)-1 == mii.wID || 65535 == mii.wID) + { + if (NULL != threadInfo) + { + mii.wID = threadInfo->GetAvailableId(); + if ((unsigned int)-1 != mii.wID) + { + record->itemId = mii.wID; + mii.fMask |= MIIM_ID; + } + else + mii.wID = record->itemId; + } + } + + record->skinned = TRUE; + if (FALSE == SetMenuItemInfoW(hMenu, i, TRUE, &mii)) + { + record->skinned = FALSE; + record->itemId = record->originalId; + } + else + { + if (record->itemId != record->originalId && + NULL != threadInfo) + { + threadInfo->ClaimId(record->itemId); + } + } + } + } + else + { + record->failed = TRUE; + } + } + + return TRUE; +} + +BOOL SkinnedMenuWnd::Attach(HWND hwndMenu, HWND hwndOwner) +{ + menuFlags &= ~SMIF_BLOCKDRAW; + + if(!__super::Attach(hwndMenu)) + return FALSE; + + SetOwnerWindow(hwndOwner); + + DWORD windowStyle = GetWindowLongPtrW(hwnd, GWL_STYLE); + DWORD windowStyleEx = GetWindowLongPtrW(hwnd, GWL_EXSTYLE); + DWORD newStyle = windowStyle & ~(WS_BORDER | WS_THICKFRAME | WS_DLGFRAME); + if (newStyle != windowStyle) + SetWindowLongPtr(hwnd, GWL_STYLE, newStyle); + + newStyle = windowStyleEx & ~(WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE); + if (newStyle != windowStyleEx) + SetWindowLongPtr(hwnd, GWL_EXSTYLE, newStyle); + + if (0 == (SMS_SYSCOLORS & menuExStyle)) + { + SetStyle(SWS_USESKINCOLORS, FALSE); + } + + SetType(SKINNEDWND_TYPE_POPUPMENU); + + if (!hmlilCheck) + hmlilCheck = MLImageListI_Create(16, 16, MLILC_COLOR24_I, 2, 1, 2, hmlifMngr); + + if ((SMS_FORCEWIDTH & menuExStyle) && lineWidth > 0) + { + lineWidth -= ((GetSystemMetrics(SM_CXMENUCHECK) - 1) + WASABI_API_APP->getScaleX(MENU_BORDER_WIDTH*2)); + if (lineWidth < 0) lineWidth = 0; + } + else lineWidth = 0; + + lineHeight = GetLineHeight(); + if (!hmlil || !MLImageListI_GetImageSize(hmlil, &imageWidth, &imageHeight)) { imageWidth = WASABI_API_APP->getScaleX(24); imageHeight = 0; } + if (hmlilCheck) + { + INT imageCX, imageCY; + if(MLImageListI_GetImageSize(hmlilCheck, &imageCX, &imageCY)) + { + if (imageWidth < imageCX) imageWidth = imageCX; + if (imageWidth < WASABI_API_APP->getScaleX(25)) imageWidth = WASABI_API_APP->getScaleX(25); // clamp to a min width to better match the OS + if (imageHeight < imageCY) imageHeight = imageCY; + } + } + if (lineHeight < (imageHeight + WASABI_API_APP->getScaleY(4))) lineHeight = (imageHeight + WASABI_API_APP->getScaleY(4)); + + if ((SMS_DISABLESHADOW & menuExStyle)) + { + UINT cs = GetClassLongPtrW(hwnd, GCL_STYLE); + if (0x00020000/*CS_DROPSHADOW*/ & cs) + { + bRestoreShadow = TRUE; + SetClassLongPtrW(hwnd, GCL_STYLE, cs & ~0x00020000/*CS_DROPSHADOW*/); + } + } + return TRUE; +} + +HPEN SkinnedMenuWnd::GetBorderPen(void) +{ + if (NULL == borderPen) + { + COLORREF rgb; + if (0 != (SMS_SYSCOLORS & menuExStyle)) + rgb = GetSysColor(COLOR_GRAYTEXT); + else + MLGetSkinColor(MLSO_MENU, MP_FRAME, 0, &rgb); + borderPen = CreatePen(PS_SOLID, 0, rgb); + } + + return borderPen; +} + +INT SkinnedMenuWnd::GetLineHeight() +{ + INT h = 0; + HDC hdc = GetDCEx(hwnd, NULL, DCX_CACHE); + if (hdc) + { + HFONT hf = GetMenuFont(TRUE); + if (NULL != hf) + { + TEXTMETRICW tm = {0}; + HFONT hfo = (HFONT)SelectObject(hdc, hf); + if (GetTextMetricsW(hdc, &tm)) h = tm.tmHeight + WASABI_API_APP->getScaleY(4); + SelectObject(hdc, hfo); + } + ReleaseDC(hwnd, hdc); + } + return h; +} + +HFONT SkinnedMenuWnd::GetMenuFont(BOOL fBold) +{ + HFONT hFont = NULL; + if (SMS_USESKINFONT & menuExStyle) + { + hFont = (HFONT)MlStockObjects_Get(SKIN_FONT); + } + + if (NULL == hFont) + hFont = (HFONT)MlStockObjects_Get(DEFAULT_FONT); + + if (FALSE != fBold) + { + if (NULL == hBoldFont) + { + LOGFONTW lf = {0}; + if (sizeof(LOGFONTW) == GetObjectW(hFont, sizeof(LOGFONTW), &lf)) + { + if (lf.lfWeight < FW_BOLD) + lf.lfWeight = FW_BOLD; + hBoldFont = CreateFontIndirectW(&lf); + } + } + + if (NULL != hBoldFont) + { + hFont = hBoldFont; + } + } + + return hFont; +} + +INT SkinnedMenuWnd::OnNcCalcSize(BOOL bCalcValidRects, NCCALCSIZE_PARAMS *pncsp) +{ + DWORD windowStyle = GetWindowLongPtr(hwnd, GWL_STYLE); + DWORD windowStyleEx = GetWindowLongPtr(hwnd, GWL_EXSTYLE); + DWORD newStyle = windowStyle & ~(WS_BORDER | WS_THICKFRAME | WS_DLGFRAME); + if (newStyle != windowStyle) + SetWindowLongPtr(hwnd, GWL_STYLE, newStyle); + + newStyle = windowStyleEx & ~(WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE | WS_EX_CLIENTEDGE | WS_EX_STATICEDGE); + if (newStyle != windowStyleEx) + SetWindowLongPtr(hwnd, GWL_EXSTYLE, newStyle); + + LRESULT result = CallPrevWndProc(WM_NCCALCSIZE, (WPARAM)bCalcValidRects, (LPARAM)pncsp); + + InflateRect(&pncsp->rgrc[0], WASABI_API_APP->getScaleX(-MENU_BORDER_WIDTH), WASABI_API_APP->getScaleY(-MENU_BORDER_WIDTH)); + if (bCalcValidRects) + { + InflateRect(&pncsp->rgrc[1], WASABI_API_APP->getScaleX(-MENU_BORDER_WIDTH), WASABI_API_APP->getScaleY(-MENU_BORDER_WIDTH)); + InflateRect(&pncsp->rgrc[2], WASABI_API_APP->getScaleX(-MENU_BORDER_WIDTH), WASABI_API_APP->getScaleY(-MENU_BORDER_WIDTH)); + } + + return (INT)result; +} + +void SkinnedMenuWnd::PaintScrollButton(HDC hdc, const RECT *prc, UINT scrollButton, BOOL buttonState) +{ + COLORREF rgbBk, rgbFg; + + MLGetSkinColor(MLSO_MENU, MP_BACKGROUND, MBS_NORMAL, &rgbBk); + MLGetSkinColor(MLSO_MENU, MP_TEXT, MTS_NORMAL, &rgbFg); + + HBITMAP hbmp; + + if (0 == (MENU_BUTTON_STATE_DISABLED & buttonState)) + { + if (NULL == scrollBitmap) + scrollBitmap = SkinnedMenuWnd_LoadPngResource(IDB_MENU_SCROLLARROW, rgbBk, rgbFg); + hbmp = scrollBitmap; + } + else + { + if (NULL == disabledScrollBitmap) + disabledScrollBitmap = SkinnedMenuWnd_LoadPngResource(IDB_MENU_SCROLLARROW_DISABLED, rgbBk, rgbFg); + hbmp = disabledScrollBitmap; + } + + BOOL imageFailed = TRUE; + if (NULL != hbmp) + { + DIBSECTION bitmapInfo; + if (!GetObjectW(hbmp, sizeof(bitmapInfo), &bitmapInfo)) + ZeroMemory(&bitmapInfo, sizeof(bitmapInfo)); + + INT h = abs(bitmapInfo.dsBm.bmHeight); + INT w = bitmapInfo.dsBm.bmWidth; + if (h > 0 && w > 0) + { + INT x, y, nWidth, nHeight; + x = prc->left + ((prc->right - prc->left) - w) / 2; + y = prc->top + ((prc->bottom - prc->top) - h) / 2; + if (MENU_BUTTON_SCROLLDOWN == scrollButton) + { + nWidth = -w; + nHeight = h; + x += w; + } + else + { + nWidth = w; + nHeight = -h; + y += h; + } + + SetBkColor(hdc, rgbBk); + ExtTextOut(hdc, 0, 0, ETO_OPAQUE, prc, NULL, 0, NULL); + StretchDIBits(hdc, x, y, nWidth, nHeight, 0, 0, w, h, bitmapInfo.dsBm.bmBits, + (BITMAPINFO*)&bitmapInfo.dsBmih, DIB_RGB_COLORS, SRCCOPY); + imageFailed = FALSE; + } + } + + if (imageFailed) + ExtTextOut(hdc, 0, 0, ETO_OPAQUE, prc, NULL, 0, NULL); +} + +BOOL SkinnedMenuWnd::DrawScrollButton(HDC hdc, UINT scrollButton) +{ + RECT rc, rcWindow, rcPaint; + + if (0 == scrollButton || + 0 == GetWindowRect(hwnd, &rcWindow) || + 0 == GetClientRect(hwnd, &rc)) + { + return FALSE; + } + + MapWindowPoints(hwnd, HWND_DESKTOP, (POINT*)&rc, 2); + + HDC hdcOwned = NULL; + if (NULL == hdc) + { + hdcOwned = GetDCEx(hwnd, NULL, DCX_CACHE | DCX_WINDOW | DCX_CLIPSIBLINGS); + hdc = hdcOwned; + + if (NULL == hdcOwned) + return FALSE; + } + + rcPaint.left = rc.left - rcWindow.left; + rcPaint.right = rc.right - rcWindow.left; + + BOOL scrollEnabled; + + POINT ptTest; + ptTest.x = rc.left + (rc.right - rc.left) / 2; + + if (0 != (MENU_BUTTON_SCROLLUP & scrollButton)) + { + rcPaint.top = MENU_BORDER_WIDTH; + rcPaint.bottom = rc.top - rcWindow.top; + + if (rcPaint.bottom > rcPaint.top && rcPaint.right > rcPaint.left) + { + ptTest.y = rc.top; + scrollEnabled = (MenuItemFromPoint(hwnd, hMenu, ptTest) > 0); + PaintScrollButton(hdc, &rcPaint, MENU_BUTTON_SCROLLUP, (scrollEnabled) ? 0 : MENU_BUTTON_STATE_DISABLED); + } + } + + if (0 != (MENU_BUTTON_SCROLLDOWN & scrollButton)) + { + rcPaint.top = rc.bottom - rcWindow.top; + rcPaint.bottom = (rcWindow.bottom - rcWindow.top) - MENU_BORDER_WIDTH; + + if (rcPaint.bottom > rcPaint.top && rcPaint.right > rcPaint.left) + { + ptTest.y = rc.bottom - WASABI_API_APP->getScaleY(1); + INT last = MenuItemFromPoint(hwnd, hMenu, ptTest); + scrollEnabled = FALSE; + if (last >= 0) + { + INT count = GetMenuItemCount(hMenu); + if (last != (count - 1)) + scrollEnabled = TRUE; + } + PaintScrollButton(hdc, &rcPaint, MENU_BUTTON_SCROLLDOWN, (scrollEnabled) ? 0 : MENU_BUTTON_STATE_DISABLED); + } + } + if (NULL != hdcOwned) + ReleaseDC(hwnd, hdcOwned); + + return TRUE; +} + +void SkinnedMenuWnd::DrawBorder(HDC hdc) +{ + RECT rc, rcWindow, rp; + GetClientRect(hwnd, &rc); + GetWindowRect(hwnd, &rcWindow); + + MapWindowPoints(hwnd, HWND_DESKTOP, (POINT*)&rc, 2); + + OffsetRect(&rc, -rcWindow.left, -rcWindow.top); + OffsetRect(&rcWindow, -rcWindow.left, -rcWindow.top); + + SkinnedWnd::DrawBorder(hdc, &rcWindow, BORDER_FLAT, GetBorderPen()); + + HBRUSH brushBk = SkinnedMenuWnd_GetBackBrush(hMenu); + + SetRect(&rp, rcWindow.left + 1, rcWindow.top + 1, rc.left, rcWindow.bottom - 1); + FillRect(hdc, &rp, brushBk); + SetRect(&rp, rc.left, rcWindow.top + 1, rc.right, rc.top); + FillRect(hdc, &rp, brushBk); + SetRect(&rp, rc.right, rcWindow.top + 1, rcWindow.right - 1, rcWindow.bottom - 1); + FillRect(hdc, &rp, brushBk); + SetRect(&rp, rc.left, rc.bottom, rc.right, rcWindow.bottom - 1); + FillRect(hdc, &rp, brushBk); + + if ((rc.top - rcWindow.top) > MENU_BORDER_WIDTH || (rcWindow.bottom - rc.bottom) > MENU_BORDER_WIDTH) + DrawScrollButton(hdc, MENU_BUTTON_SCROLLUP | MENU_BUTTON_SCROLLDOWN); +} + +BOOL SkinnedMenuWnd::IsSkinnedItem(UINT itemId) +{ + if (NULL == skinnedItems) + return TRUE; + + if (skinnedItemCursor >= skinnedItemCount) + skinnedItemCursor = 0; + + INT start = skinnedItemCursor; + + while(itemId != skinnedItems[skinnedItemCursor].itemId) + { + skinnedItemCursor++; + if (skinnedItemCursor == skinnedItemCount) + skinnedItemCursor = 0; + if (skinnedItemCursor == start) + { + skinnedItemCursor = 0; + return FALSE; + } + } + + return skinnedItems[skinnedItemCursor].skinned; +} + +static void SkinnedMenuWnd_DrawFrame(HDC hdc, const RECT *prc, INT width, COLORREF rgbFrame) +{ + if (width > 0) + { + COLORREF rgbOld = SetBkColor(hdc, rgbFrame); + + RECT rcPart; + SetRect(&rcPart, prc->left, prc->top, prc->right, prc->top + width); + ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rcPart, NULL, 0, NULL); + SetRect(&rcPart, prc->left, prc->bottom - width, prc->right, prc->bottom); + ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rcPart, NULL, 0, NULL); + SetRect(&rcPart, prc->left, prc->top + width, prc->left + width, prc->bottom - width); + ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rcPart, NULL, 0, NULL); + SetRect(&rcPart, prc->right - width, prc->top + width, prc->right, prc->bottom - width); + ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rcPart, NULL, 0, NULL); + + if (rgbOld != rgbFrame) + SetBkColor(hdc, rgbOld); + } +} + +BOOL SkinnedMenuWnd::OnReflectedDrawItem(DRAWITEMSTRUCT *pdis) +{ + if (0 != (SMIF_BLOCKDRAW & menuFlags)) + { + ExcludeClipRect(pdis->hDC, pdis->rcItem.left, pdis->rcItem.top, pdis->rcItem.right, pdis->rcItem.bottom); + } + + if (!IsSkinnedItem(pdis->itemID)) + return FALSE; + + MENUITEMINFOW mii = {0}; + wchar_t szText[256] = {0}; + INT imageCX, imageCY, realIndex; + LONG imageTop; + + mii.cbSize = sizeof(MENUITEMINFO); + mii.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_STATE | MIIM_SUBMENU; + mii.cch = ARRAYSIZE(szText); + mii.dwTypeData = szText; + + if (!GetMenuItemInfoW((HMENU)pdis->hwndItem, pdis->itemID, FALSE, &mii)) + mii.cch = 0; + + COLORREF rgbText, rgbTextBk, rgbTextFrame; + if (0 != (SMS_SYSCOLORS & menuExStyle)) + { + INT foreIndex = (0 != (MFS_GRAYED & mii.fState)) ? COLOR_GRAYTEXT : COLOR_MENUTEXT; + INT backIndex = (0 != (ODS_SELECTED & pdis->itemState)) ? COLOR_HIGHLIGHT : COLOR_MENU; + + rgbText = GetSysColor(foreIndex); + rgbTextBk = GetSysColor(backIndex); + rgbTextFrame = rgbTextBk; + } + else + { + MLGetSkinColor(MLSO_MENU, MP_BACKGROUND, (0 != (ODS_SELECTED & pdis->itemState)) ? MBS_SELECTED : MBS_NORMAL, &rgbTextBk); + MLGetSkinColor(MLSO_MENU, MP_TEXT, (0 != (MFS_GRAYED & mii.fState)) ? MTS_DISABLED : ((0 != (ODS_SELECTED & pdis->itemState)) ? MBS_SELECTED : MBS_NORMAL), &rgbText); + rgbTextFrame = rgbTextBk; + if (0 != (ODS_SELECTED & pdis->itemState)) + { + MLGetSkinColor(MLSO_MENU, MP_BACKGROUND, MBS_SELECTEDFRAME, &rgbTextFrame); + } + } + + COLORREF origText = SetTextColor(pdis->hDC, rgbText); + COLORREF origTextBk = SetBkColor(pdis->hDC, rgbTextBk); + + if (0 != (MFT_MENUBARBREAK & mii.fType)) + { + RECT rect; + if (FALSE != GetClientRect(hwnd, &rect)) + { + COLORREF lineColor, prevColor; + HBRUSH brush = SkinnedMenuWnd_GetBackBrush(hMenu); + if(NULL == brush) + brush = GetSysColorBrush(COLOR_WINDOW); + + rect.right = pdis->rcItem.left - WASABI_API_APP->getScaleX(1); + rect.left = pdis->rcItem.left - WASABI_API_APP->getScaleX(2); + FillRect(pdis->hDC, &rect, brush); + + if (0 != (SMS_SYSCOLORS & menuExStyle) || + FAILED(MLGetSkinColor(MLSO_MENU, MP_SEPARATOR, 0, &lineColor))) + { + lineColor = GetSysColor(COLOR_3DSHADOW); + } + + prevColor = SetBkColor(pdis->hDC, lineColor); + OffsetRect(&rect, -1, 0); + ExtTextOut(pdis->hDC, 0, 0, ETO_OPAQUE, &rect, NULL, 0, NULL); + SetBkColor(pdis->hDC, prevColor); + } + } + + if (NULL != customProc) + { + INT customResult = customProc(MLMENU_ACTION_DRAWITEM, (HMENU)pdis->hwndItem, pdis->hDC, (LPARAM)pdis, customParam); + if (MLMENU_WANT_DRAWPART != customResult && FALSE != customResult) + return TRUE; + } + + INT type = ((MFT_STRING | MFT_SEPARATOR) & mii.fType); + switch(type) + { + case MFT_STRING: + if (NULL == customProc || + FALSE == customProc(MLMENU_ACTION_DRAWBACK, (HMENU)pdis->hwndItem, pdis->hDC, (LPARAM)pdis, customParam)) + { + ExtTextOutW(pdis->hDC, 0, 0, ETO_OPAQUE, &pdis->rcItem, NULL, 0, NULL); + if (rgbTextFrame != rgbTextBk) + { + SkinnedMenuWnd_DrawFrame(pdis->hDC, &pdis->rcItem, 1, rgbTextFrame); + } + } + + if (NULL == customProc || + FALSE == customProc(MLMENU_ACTION_DRAWICON, (HMENU)pdis->hwndItem, pdis->hDC, (LPARAM)pdis, customParam)) + { + if (hmlil) + { + if (MLImageListI_GetImageSize(hmlil, &imageCX, &imageCY)) + { + imageTop = pdis->rcItem.top + (pdis->rcItem.bottom - pdis->rcItem.top - imageCY) / 2; + if (imageTop < pdis->rcItem.top) imageTop = pdis->rcItem.top; + + INT index = MLImageListI_GetIndexFromTag(hmlil, pdis->itemID); + if (-1 != index) + { + HIMAGELIST himl = MLImageListI_GetRealList(hmlil); + realIndex = MLImageListI_GetRealIndex(hmlil, index, rgbTextBk, rgbText); + if (-1 != realIndex) + { + ImageList_Draw(himl, realIndex, pdis->hDC, pdis->rcItem.left + WASABI_API_APP->getScaleX(1) + (imageWidth - imageCX) / 2, imageTop, ILD_NORMAL); + } + } + } + } + else if ((MFS_CHECKED & mii.fState) && hmlilCheck) + { + if (0 != (MFT_RADIOCHECK & mii.fType)) + { + if (-1 == imageRadioMark) + imageRadioMark = SkinnedMenuWnd_AddPngResource(hmlilCheck, IDB_MENU_RADIOMARK); + } + else + { + if (-1 == imageCheckMark) + imageCheckMark = SkinnedMenuWnd_AddPngResource(hmlilCheck, IDB_MENU_CHECKMARK); + } + + if (MLImageListI_GetImageSize(hmlilCheck, &imageCX, &imageCY)) + { + imageTop = pdis->rcItem.top + (pdis->rcItem.bottom - pdis->rcItem.top - imageCY) / 2; + if (imageTop < pdis->rcItem.top) imageTop = pdis->rcItem.top; + HIMAGELIST himl = MLImageListI_GetRealList(hmlilCheck); + realIndex = MLImageListI_GetRealIndex(hmlilCheck, (MFT_RADIOCHECK & mii.fType) ? imageRadioMark : imageCheckMark, rgbTextBk, rgbText); + if (-1 != realIndex) + { + ImageList_Draw(himl, realIndex, pdis->hDC, pdis->rcItem.left + WASABI_API_APP->getScaleX(1) + (imageWidth - imageCX) / 2, imageTop, ILD_NORMAL); + } + } + } + } + + if (NULL != mii.hSubMenu && hmlilCheck) + { + if (-1 == imageExpandArrow) + imageExpandArrow = SkinnedMenuWnd_AddPngResource(hmlilCheck, IDB_MENU_EXPANDARROW); + + if (MLImageListI_GetImageSize(hmlilCheck, &imageCX, &imageCY)) + { + imageTop = pdis->rcItem.top + (pdis->rcItem.bottom - pdis->rcItem.top - imageCY) / 2; + if (imageTop < pdis->rcItem.top) imageTop = pdis->rcItem.top; + HIMAGELIST himl = MLImageListI_GetRealList(hmlilCheck); + realIndex = MLImageListI_GetRealIndex(hmlilCheck, imageExpandArrow, rgbTextBk, rgbText); + if (-1 != realIndex) + ImageList_Draw(himl, realIndex, pdis->hDC, pdis->rcItem.right - imageCX - WASABI_API_APP->getScaleX(1), imageTop, ILD_NORMAL); + } + } + + if (NULL == customProc || + FALSE == customProc(MLMENU_ACTION_DRAWTEXT, (HMENU)pdis->hwndItem, pdis->hDC, (LPARAM)pdis, customParam)) + { + if (mii.cch) + { + RECT rt; + CopyRect(&rt, &pdis->rcItem); + if (imageWidth && imageHeight) rt.left += imageWidth + WASABI_API_APP->getScaleX(6); + rt.right -= imageWidth; + + HFONT hFont = GetMenuFont(FALSE); + HFONT originalFont = (NULL != hFont) ? (HFONT)SelectObject(pdis->hDC, hFont) : NULL; + INT originalBkMode = SetBkMode(pdis->hDC, TRANSPARENT); + + UINT len = mii.cch; + if (len > 0) + { + while(--len > 0 && L'\t' != mii.dwTypeData[len]); + if (0 == len) + len = mii.cch; + } + + BOOL showPrefix = FALSE; + if (!SystemParametersInfo(SPI_GETKEYBOARDCUES, 0, &showPrefix, 0)) + showPrefix = FALSE; + + if (!showPrefix && 0 == (ODS_NOACCEL & pdis->itemState)) + showPrefix = TRUE; + + UINT drawtextFlags = DT_LEFT | DT_VCENTER | DT_SINGLELINE | DT_END_ELLIPSIS | DT_NOCLIP; + if (!showPrefix) + drawtextFlags |= 0x000100000; /*DT_HIDEPREFIX*/ + + if (0 != (MFS_DEFAULT & mii.fState)) + { + SetTextColor(pdis->hDC, BlendColors(rgbText, rgbTextBk, 110)); + OffsetRect(&rt, 1,0); + DrawTextW(pdis->hDC, mii.dwTypeData, len, &rt, drawtextFlags); + OffsetRect(&rt, -1,0); + SetTextColor(pdis->hDC, rgbText); + } + + DrawTextW(pdis->hDC, mii.dwTypeData, len, &rt, drawtextFlags); + + if (mii.cch > (len + 1)) + { + len++; + rt.left = rt.right - shortcutCX; + SetTextColor(pdis->hDC, BlendColors(rgbText, rgbTextBk, 192)); + DrawTextW(pdis->hDC, mii.dwTypeData + len, mii.cch - len, &rt, DT_LEFT | DT_VCENTER | 0x000100000/*DT_HIDEPREFIX*/ | DT_SINGLELINE | DT_NOCLIP); + } + + SelectObject(pdis->hDC, originalFont); + + if (TRANSPARENT != originalBkMode) + SetBkMode(pdis->hDC, originalBkMode); + } + } + + ExcludeClipRect(pdis->hDC, pdis->rcItem.left + WASABI_API_APP->getScaleX(23), + pdis->rcItem.top, pdis->rcItem.right, pdis->rcItem.bottom); + break; + + case MFT_SEPARATOR: + { + COLORREF rgbSeparator; + HPEN hPen, originalPen; + INT y = (pdis->rcItem.top + (pdis->rcItem.bottom - pdis->rcItem.top) / 2); + + if (0 != (SMS_SYSCOLORS & menuExStyle) || + FAILED(MLGetSkinColor(MLSO_MENU, MP_SEPARATOR, 0, &rgbSeparator))) + { + rgbSeparator = GetSysColor(COLOR_3DSHADOW/*COLOR_GRAYTEXT*/); + } + + hPen = CreatePen(PS_SOLID, 0, rgbSeparator); + originalPen = (HPEN)SelectObject(pdis->hDC, hPen); + + ExtTextOutW(pdis->hDC, 0, 0, ETO_OPAQUE, &pdis->rcItem, NULL, 0, NULL); + MoveToEx(pdis->hDC, pdis->rcItem.left + WASABI_API_APP->getScaleX(23), y, NULL); + LineTo(pdis->hDC, pdis->rcItem.right, y); + + SelectObject(pdis->hDC, originalPen); + DeleteObject(hPen); + + // draws a edge on the separator so it looks more like the OS when trying to 'fake it' + if (0 != (SMS_SYSCOLORS & menuExStyle)) + { + RECT bottomRect = pdis->rcItem; + HBRUSH brush = GetSysColorBrush(COLOR_3DHIGHLIGHT); + + y += WASABI_API_APP->getScaleY(1); + bottomRect.top = y; + bottomRect.bottom = y + WASABI_API_APP->getScaleY(1); + FillRect(pdis->hDC,&bottomRect, brush); + } + } + ExcludeClipRect(pdis->hDC, pdis->rcItem.left + WASABI_API_APP->getScaleX(23), + pdis->rcItem.top, pdis->rcItem.right, pdis->rcItem.bottom); + break; + } + + { + COLORREF rgbSeparator; + if (0 != (SMS_SYSCOLORS & menuExStyle) || + FAILED(MLGetSkinColor(MLSO_MENU, MP_SEPARATOR, 0, &rgbSeparator))) + { + rgbSeparator = GetSysColor(COLOR_3DSHADOW/*COLOR_GRAYTEXT*/); + } + + HPEN hPen = CreatePen(PS_SOLID, 0, rgbSeparator); + HPEN originalPen = (HPEN)SelectObject(pdis->hDC, hPen); + + MoveToEx(pdis->hDC, pdis->rcItem.left + WASABI_API_APP->getScaleX(22), pdis->rcItem.top, NULL); + LineTo(pdis->hDC, pdis->rcItem.left + WASABI_API_APP->getScaleX(22), pdis->rcItem.top + (pdis->rcItem.bottom - pdis->rcItem.top)); + + SelectObject(pdis->hDC, originalPen); + DeleteObject(hPen); + } + + if (origText != rgbText) + SetTextColor(pdis->hDC, origText); + if (origTextBk != rgbTextBk) + SetBkColor(pdis->hDC, origTextBk); + + return TRUE; +} + +BOOL SkinnedMenuWnd::OnReflectedMeasureItem(MEASUREITEMSTRUCT *pmis) +{ + pmis->itemHeight = lineHeight; + pmis->itemWidth = lineWidth; + + if (!IsSkinnedItem(pmis->itemID)) + return FALSE; + + HDC hdc = GetDCEx(hwnd, NULL, DCX_CACHE); + if (NULL == hdc) return FALSE; + + MENUITEMINFOW mii = {0}; + wchar_t szText[128] = {0}; + + mii.cbSize = sizeof(MENUITEMINFO); + mii.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_STATE; + mii.cch = ARRAYSIZE(szText); + mii.dwTypeData = szText; + + if (!GetMenuItemInfoW(hMenu, pmis->itemID, FALSE, &mii)) + mii.cch = 0; + + HFONT originalFont = (HFONT)SelectObject(hdc, GetMenuFont(0 != (MFS_DEFAULT & mii.fState))); + + if (NULL == customProc || + FALSE == customProc(MLMENU_ACTION_MEASUREITEM, hMenu, hdc, (LPARAM)pmis, customParam)) + { + if (0 == lineWidth || 0 == lineHeight) + { + INT type = ((MFT_STRING | MFT_SEPARATOR) & mii.fType); + switch(type) + { + case MFT_STRING: + if (mii.cch != 0) + { + SIZE sz; + UINT len = mii.cch; + while(--len > 0 && L'\t' != mii.dwTypeData[len]); + if (0 == len) len = mii.cch; + + if (len != mii.cch) + { + szText[len] = L' '; + if (GetTextExtentPoint32W(hdc, szText + len, mii.cch - len, &sz) && + shortcutCX < sz.cx) + { + shortcutCX = sz.cx; + } + } + + if (GetTextExtentPoint32W(hdc, szText, len, &sz)) + { + if (textCX <= sz.cx) + textCX = sz.cx; + + if (lineHeight < sz.cy) + pmis->itemHeight = sz.cy + WASABI_API_APP->getScaleY(2); + + if (0 == lineWidth) + pmis->itemWidth = textCX + shortcutCX + 8; + } + } + if(imageHeight > (INT)(pmis->itemHeight + WASABI_API_APP->getScaleY(2))) pmis->itemHeight = imageHeight + WASABI_API_APP->getScaleY(2); + if (0 == lineWidth && imageWidth) pmis->itemWidth += imageWidth; + pmis->itemWidth -= (GetSystemMetrics(SM_CXMENUCHECK) - imageWidth - WASABI_API_APP->getScaleX(36)); + break; + case MFT_SEPARATOR: + pmis->itemHeight = WASABI_API_APP->getScaleY(7); + break; + } + } + } + + SelectObject(hdc, originalFont); + ReleaseDC(hwnd, hdc); + return TRUE; +} + +void SkinnedMenuWnd::OnNcPaint(HRGN rgnUpdate) +{ + if (0 != (SMIF_BLOCKDRAW & menuFlags)) + return; + + UINT flags = DCX_PARENTCLIP | DCX_WINDOW | DCX_CLIPSIBLINGS | + DCX_INTERSECTUPDATE | DCX_VALIDATE; + + HDC hdc = GetDCEx(hwnd, ((HRGN)NULLREGION != rgnUpdate) ? rgnUpdate : NULL, flags); + if (NULL == hdc) + return; + + DrawBorder(hdc); + ReleaseDC(hwnd, hdc); +} + +LRESULT SkinnedMenuWnd::OnEraseBackground(HDC hdc) +{ + HBRUSH brush; + + brush = SkinnedMenuWnd_GetBackBrush(hMenu); + if (NULL != brush) + { + RECT rect; + if (FALSE != GetClientRect(hwnd, &rect) && + FALSE != FillRect(hdc, &rect, brush)) + { + return 1; + } + } + + return __super::WindowProc(WM_ERASEBKGND, (WPARAM)hdc, 0L); +} + +void SkinnedMenuWnd::OnPrint(HDC hdc, UINT options) +{ + if (0 != (PRF_CHECKVISIBLE & options)) + { + DWORD windowStyle = GetWindowLongPtr(hwnd, GWL_STYLE); + if (0 == (WS_VISIBLE & windowStyle)) + { + return; + } + } + + if (0 != (PRF_NONCLIENT & options)) + { + DrawBorder(hdc); + } + + if (0 == ((PRF_ERASEBKGND | PRF_CLIENT) & options)) + { + return; + } + + POINT ptOrig; + RECT rc, rcWindow; + + if (GetClientRect(hwnd, &rc) && + GetWindowRect(hwnd, &rcWindow)) + { + MapWindowPoints(hwnd, HWND_DESKTOP, (POINT*)&rc, 2); + } + else + { + SetRectEmpty(&rc); + SetRectEmpty(&rcWindow); + } + + INT clipRegionCode; + HRGN clipRegion = CreateRectRgn(0, 0, 0, 0); + clipRegionCode = (NULL != clipRegion) ? GetClipRgn(hdc, clipRegion) : -1; + + OffsetViewportOrgEx(hdc, rc.left - rcWindow.left, rc.top - rcWindow.top, &ptOrig); + if (-1 != clipRegionCode) + { + IntersectClipRect(hdc, 0, 0, rc.right - rc.left, rc.bottom - rc.top); + } + OffsetRect(&rc, -rc.left, -rc.top); + + if (0 != (PRF_ERASEBKGND & options)) + { + MENUINFO mi = {0}; + mi.cbSize = sizeof(MENUINFO); + mi.fMask = MIM_BACKGROUND; + HBRUSH brushBk = NULL; + if (GetMenuInfo(hMenu, &mi) && mi.hbrBack) + brushBk = mi.hbrBack; + + if (NULL == brushBk) + brushBk = GetSysColorBrush(COLOR_WINDOW); + + FillRect(hdc, &rc, brushBk); + } + + if (0 != (PRF_CLIENT & options)) + { + menuFlags &= ~SMIF_BLOCKDRAW; + SendMessage(hwnd, WM_PRINTCLIENT, (WPARAM)hdc, (LPARAM)(~(PRF_NONCLIENT | PRF_ERASEBKGND) & options)); + menuFlags |= SMIF_BLOCKDRAW; + } + + if (-1 != clipRegionCode) + { + SelectClipRgn(hdc, (0 != clipRegionCode) ? clipRegion : NULL); + } + if (NULL != clipRegion) + DeleteObject(clipRegion); + + SetViewportOrgEx(hdc, ptOrig.x, ptOrig.y, NULL); + SetTimer(hwnd, MTID_EX_UNBLOCKDRAW, 250, NULL); +} + +LRESULT SkinnedMenuWnd::OnMenuSelect(UINT selectedItem) +{ + if (((UINT)-1) != selectedItem) + menuFlags &= ~SMIF_BLOCKDRAW; + + UINT updateScroll = 0; + + if (MHF_SCROLLUP == prevSelectedItem) + updateScroll |= MENU_BUTTON_SCROLLUP; + else if (MHF_SCROLLDOWN == prevSelectedItem) + updateScroll |= MENU_BUTTON_SCROLLDOWN; + + switch(selectedItem) + { + case MHF_SCROLLUP: + updateScroll |= MENU_BUTTON_SCROLLUP; + break; + case MHF_SCROLLDOWN: + updateScroll |= MENU_BUTTON_SCROLLDOWN; + break; + } + + RECT rc; + GetClientRect(hwnd, &rc); + MapWindowPoints(hwnd, HWND_DESKTOP, (POINT*)&rc, 2); + rc.top += WASABI_API_APP->getScaleY(1); + INT item = MenuItemFromPoint(hwnd, hMenu, *((POINT*)&rc)); + + LRESULT result; + BOOL fInvalidate = FALSE; + + if (0 != updateScroll) + { + DWORD windowStyle = GetWindowLongPtr(hwnd, GWL_STYLE); + SetWindowLongPtr(hwnd, GWL_STYLE, windowStyle & ~WS_VISIBLE); + + result = __super::WindowProc(MN_SELECTITEM, selectedItem, 0L); + + SetWindowLongPtr(hwnd, GWL_STYLE, windowStyle); + if (MenuItemFromPoint(hwnd, hMenu, *((POINT*)&rc)) != item) + { + updateScroll = MENU_BUTTON_SCROLLUP | MENU_BUTTON_SCROLLDOWN; + fInvalidate = TRUE; + } + } + else + { + result = __super::WindowProc(MN_SELECTITEM, selectedItem, 0L); + } + + prevSelectedItem = selectedItem; + + if (0 != updateScroll) + { + DrawScrollButton(NULL, updateScroll); + if (FALSE != fInvalidate) + { + InvalidateRect(hwnd, NULL, FALSE); + } + } + + return result; +} + +LRESULT SkinnedMenuWnd::CallHookedWindowProc(UINT uItem, BOOL fByPosition, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + MENUITEMINFOW mii = {0}; + mii.cbSize = sizeof(mii); + mii.fMask = MIIM_SUBMENU; + + if (FALSE != GetMenuItemInfoW(hMenu, uItem, fByPosition, &mii) && + NULL != mii.hSubMenu) + { + SkinnedMenu sm; + sm.InitializeHook(NULL, (menuExStyle & ~SMS_FORCEWIDTH), hmlil, 0, customProc, customParam); + return __super::WindowProc(uMsg, wParam, lParam); + } + + return __super::WindowProc(uMsg, wParam, lParam); +} + +INT SkinnedMenuWnd::FindHiliteItem(HMENU hMenu) +{ + MENUITEMINFOW mii = {0}; + mii.cbSize = sizeof(MENUITEMINFOW); + mii.fMask = MIIM_STATE; + + INT count = GetMenuItemCount(hMenu); + for (INT i = 0; i < count; i++) + { + if (0 != GetMenuItemInfoW(hMenu, i, TRUE, &mii)) + { + if (MFS_HILITE == ((MFS_HILITE | MFS_DISABLED | MFS_GRAYED) & mii.fState)) + return i; + } + } + return -1; +} + +LRESULT SkinnedMenuWnd::WindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch(uMsg) + { + case WM_ERASEBKGND: + return OnEraseBackground((HDC)wParam); + + case REFLECTED_DRAWITEM: + if (OnReflectedDrawItem((DRAWITEMSTRUCT*)((REFLECTPARAM*)lParam)->lParam)) + { + ((REFLECTPARAM*)lParam)->result = TRUE; + return TRUE; + } + break; + + case REFLECTED_MEASUREITEM: + if (OnReflectedMeasureItem((MEASUREITEMSTRUCT*)((REFLECTPARAM*)lParam)->lParam)) + { + ((REFLECTPARAM*)lParam)->result = TRUE; + return TRUE; + } + break; + + case MN_SIZEWINDOW: + { + BOOL validateOwner = FALSE; + + if (NULL == hMenu) + { + textCX = 0; + shortcutCX = 0; + HMENU menuToAttach = (HMENU)SendMessageW(hwnd, MN_GETHMENU, 0, 0L); + + if (FALSE != AttachMenu(menuToAttach)) + validateOwner = TRUE; + } + + if (NULL != threadInfo) + { + threadInfo->SetActiveMeasureMenu(hMenu); + if (FALSE != validateOwner && + FALSE == threadInfo->SetValidationHook(this)) + { + validateOwner = FALSE; + } + } + + LRESULT result = __super::WindowProc(uMsg, wParam, lParam); + + if (NULL != threadInfo) + { + threadInfo->SetActiveMeasureMenu(NULL); + if (FALSE != validateOwner) + threadInfo->RemoveValidationHook(this); + } + return result; + } + break; + + case MN_SELECTITEM: + return OnMenuSelect((UINT)wParam); + + case MN_LBUTTONDBLCLK: + case MN_LBUTTONDOWN: + case MN_LBUTTONUP: + menuFlags &= ~SMIF_BLOCKDRAW; + switch(wParam) + { + case MHF_SCROLLUP: + case MHF_SCROLLDOWN: + { + LRESULT result = __super::WindowProc(uMsg, wParam, lParam); + DrawScrollButton(NULL, MENU_BUTTON_SCROLLDOWN | MENU_BUTTON_SCROLLUP); + return result; + } + break; + default: + if (wParam >= 0) + { + return CallHookedWindowProc((UINT)wParam, TRUE, uMsg, wParam, lParam); + } + break; + } + break; + + case WM_TIMER: + switch(wParam) + { + case MTID_OPENSUBMENU: + { + POINT pt; + INT iItem; + if (GetCursorPos(&pt) && + -1 != (iItem = MenuItemFromPoint(NULL, hMenu, pt))) + { + CallHookedWindowProc(iItem, TRUE, uMsg, wParam, lParam); + return 0; + } + } + break; + case MHF_SCROLLUP: + case MHF_SCROLLDOWN: + __super::WindowProc(uMsg, wParam, lParam); + DrawScrollButton(NULL, MENU_BUTTON_SCROLLDOWN | MENU_BUTTON_SCROLLUP); + return 0; + case MTID_EX_UNBLOCKDRAW: + KillTimer(hwnd, wParam); + menuFlags &= ~SMIF_BLOCKDRAW; + return 0; + } + break; + + case WM_KEYDOWN: + menuFlags &= ~SMIF_BLOCKDRAW; + switch(wParam) + { + case VK_RETURN: + case VK_RIGHT: + { + INT iItem = FindHiliteItem(hMenu); + if (-1 != iItem) + return CallHookedWindowProc(iItem, TRUE, uMsg, wParam, lParam); + } + break; + + case VK_UP: + { + RECT rc; + GetClientRect(hwnd, &rc); + MapWindowPoints(hwnd, HWND_DESKTOP, (POINT*)&rc, 2); + rc.top += 1; + + INT iItem = MenuItemFromPoint(hwnd, hMenu, *((POINT*)&rc)); + if (iItem >= 0) + { + MENUITEMINFOW mii = {0}; + mii.cbSize = sizeof(MENUITEMINFOW); + mii.fMask = MIIM_STATE; + + if (GetMenuItemInfoW(hMenu, iItem, TRUE, &mii) && + 0 != (MFS_HILITE & mii.fState)) + { + DWORD windowStyle = GetWindowLongPtr(hwnd, GWL_STYLE); + if (0 != (WS_VISIBLE & windowStyle)) + SetWindowLongPtr(hwnd, GWL_STYLE, windowStyle & ~WS_VISIBLE); + + __super::WindowProc(uMsg, wParam, lParam); + + if (0 != (WS_VISIBLE & windowStyle)) + { + windowStyle = GetWindowLongPtr(hwnd, GWL_STYLE); + if (0 == (WS_VISIBLE & windowStyle)) + { + windowStyle |= WS_VISIBLE; + SetWindowLongPtr(hwnd, GWL_STYLE, windowStyle); + } + + INT iNew = MenuItemFromPoint(hwnd, hMenu, *((POINT*)&rc)); + if (iNew != iItem) + { + DrawScrollButton(NULL, MENU_BUTTON_SCROLLUP | MENU_BUTTON_SCROLLDOWN); + InvalidateRect(hwnd, NULL, FALSE); + } + else + { + int iHilite = GetMenuItemCount(hMenu); + while(0 < iHilite--) + { + if (FALSE != GetMenuItemInfoW(hMenu, iHilite, TRUE, &mii) && + 0 != (MFS_HILITE & mii.fState)) + { + break; + } + } + + if (FALSE != GetMenuItemRect(hwnd, hMenu, iItem, &rc)) + { + MapWindowPoints(HWND_DESKTOP, hwnd, (POINT*)&rc, 2); + InvalidateRect(hwnd, &rc, FALSE); + } + + if (iHilite != iItem && + FALSE != GetMenuItemRect(hwnd, hMenu, iHilite, &rc)) + { + MapWindowPoints(HWND_DESKTOP, hwnd, (POINT*)&rc, 2); + InvalidateRect(hwnd, &rc, FALSE); + } + } + } + return 0; + } + } + } + break; + case VK_DOWN: + { + RECT rc; + GetClientRect(hwnd, &rc); + MapWindowPoints(hwnd, HWND_DESKTOP, (POINT*)&rc, 2); + rc.top = rc.bottom - 1; + INT item = MenuItemFromPoint(hwnd, hMenu, *((POINT*)&rc)); + if (item >= 0) + { + MENUITEMINFOW mii = {0}; + mii.cbSize = sizeof(MENUITEMINFOW); + mii.fMask = MIIM_STATE; + + if (GetMenuItemInfoW(hMenu, item, TRUE, &mii) && + MFS_HILITE == ((MFS_HILITE | MFS_DISABLED | MFS_GRAYED) & mii.fState)) + { + DWORD windowStyle = GetWindowLongPtr(hwnd, GWL_STYLE); + SetWindowLongPtr(hwnd, GWL_STYLE, windowStyle & ~WS_VISIBLE); + + __super::WindowProc(uMsg, wParam, lParam); + + SetWindowLongPtr(hwnd, GWL_STYLE, windowStyle); + INT iNew = MenuItemFromPoint(hwnd, hMenu, *((POINT*)&rc)); + if (iNew != item) + { + DrawScrollButton(NULL, MENU_BUTTON_SCROLLUP | MENU_BUTTON_SCROLLDOWN); + InvalidateRect(hwnd, NULL, FALSE); + } + return 0; + } + } + } + break; + } + break; + } + + return __super::WindowProc(uMsg, wParam, lParam); +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/skinnedmenuwnd.h b/Src/Plugins/General/gen_ml/skinnedmenuwnd.h new file mode 100644 index 00000000..10476de6 --- /dev/null +++ b/Src/Plugins/General/gen_ml/skinnedmenuwnd.h @@ -0,0 +1,97 @@ +#ifndef NULLOSFT_MEDIALIBRARY_SKINNED_MENU_WINDOW_HEADER +#define NULLOSFT_MEDIALIBRARY_SKINNED_MENU_WINDOW_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include "./skinnedwnd.h" +#include "./skinnedmenuthreadinfo.h" + +#define MENU_BUTTON_SCROLLUP 0x0001 +#define MENU_BUTTON_SCROLLDOWN 0x0002 + +#define MENU_BUTTON_STATE_DISABLED 0x0001 +#define MENU_BUTTON_STATE_PRESSED 0x0002 + +class SkinnedMenuWnd : public SkinnedWnd +{ +protected: + SkinnedMenuWnd(UINT menuExStyle, HMLIMGLST hmlil, INT forcedWidth, MENUCUSTOMIZEPROC customProc, ULONG_PTR customParam); + virtual ~SkinnedMenuWnd(void); + +public: + HMENU GetMenuHandle(); + HWND GetOwnerWindow(); + HWND SetOwnerWindow(HWND hwndOwner); + +protected: + virtual BOOL Attach(HWND hwndMenu, HWND hwndOwner); + virtual BOOL AttachMenu(HMENU hMenuToAttach); + virtual LRESULT WindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam); // treat this as dialog proc + virtual HPEN GetBorderPen(void); + BOOL OnReflectedDrawItem(DRAWITEMSTRUCT *pdis); + BOOL OnReflectedMeasureItem(MEASUREITEMSTRUCT *pmis); + HFONT GetMenuFont(BOOL fBold); + INT GetLineHeight(); + + virtual LRESULT OnEraseBackground(HDC hdc); + virtual void OnPrint(HDC hdc, UINT options); + virtual void OnNcPaint(HRGN rgnUpdate); + + virtual INT OnNcCalcSize(BOOL bCalcValidRects, NCCALCSIZE_PARAMS *pncsp); + virtual void DrawBorder(HDC hdc); + + BOOL IsSkinnedItem(UINT itemId); + + BOOL DrawScrollButton(HDC hdc, UINT scrollButton); + void PaintScrollButton(HDC hdc, const RECT *prc, UINT scrollButton, BOOL buttonState); + LRESULT OnMenuSelect(UINT selectedItem); + LRESULT CallHookedWindowProc(UINT uItem, BOOL fByPosition, UINT uMsg, WPARAM wParam, LPARAM lParam); + INT FindHiliteItem(HMENU hMenu); + +private: + friend BOOL SkinWindowEx(HWND hwndToSkin, INT type, UINT style); + friend class SkinnedMenu; + +protected: + typedef struct SkinnedItemRecord + { + unsigned int itemId; + unsigned int originalId; + BOOL skinned; + BOOL failed; + }SkinnedItemRecord; + +protected: + SkinnedMenuThreadInfo *threadInfo; + HWND hOwner; + HMENU hMenu; + UINT menuExStyle; + HMLIMGLST hmlil; + INT lineWidth = 0; + INT lineHeight = 0; + INT imageWidth = 0; + INT imageHeight = 0; + INT shortcutCX = 0; + INT textCX = 0; + BOOL bRestoreShadow; + HFONT hBoldFont; + HBRUSH backBrush; + HPEN borderPen; + HBRUSH menuOrigBrush; + + SkinnedItemRecord *skinnedItems; + INT skinnedItemCount; + INT skinnedItemCursor; + INT prevSelectedItem; + HBITMAP scrollBitmap; + HBITMAP disabledScrollBitmap; + + UINT menuFlags; + + MENUCUSTOMIZEPROC customProc; + ULONG_PTR customParam; +}; + +#endif // NULLOSFT_MEDIALIBRARY_SKINNED_MENU_WINDOW_HEADER \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/skinnedprogressbar.cpp b/Src/Plugins/General/gen_ml/skinnedprogressbar.cpp new file mode 100644 index 00000000..45097b83 --- /dev/null +++ b/Src/Plugins/General/gen_ml/skinnedprogressbar.cpp @@ -0,0 +1,131 @@ +#include "api__gen_ml.h" +#include "main.h" +#include "./skinnedprogressbar.h" +#include "./skinning.h" +#include "../winamp/wa_dlg.h" +#include "./colors.h" +#include + +SkinnedProgressBar::SkinnedProgressBar(void) + : SkinnedWnd(FALSE), skinCursor(NULL) +{ +} + +SkinnedProgressBar::~SkinnedProgressBar(void) +{ +} + +BOOL SkinnedProgressBar::Attach(HWND hProgressBar) +{ + if(!__super::Attach(hProgressBar)) return FALSE; + + SetType(SKINNEDWND_TYPE_PROGRESSBAR); + return TRUE; +} + +void SkinnedProgressBar::OnSkinChanged(BOOL bNotifyChildren, BOOL bRedraw) +{ + skinCursor = (0 != (SWS_USESKINCURSORS & style)) ? + (HCURSOR)SENDWAIPC(plugin.hwndParent, IPC_GETSKINCURSORS, WACURSOR_NORMAL) : NULL; + + HFONT hfOld = (HFONT)CallPrevWndProc(WM_GETFONT, 0, 0L); + + __super::OnSkinChanged(bNotifyChildren, bRedraw); + + if (hfOld != (HFONT)CallPrevWndProc(WM_GETFONT, 0, 0L)) + CallPrevWndProc(TTM_UPDATE, 0, 0L); +} + +void SkinnedProgressBar::OnPaint() +{ + PAINTSTRUCT ps; + HDC hdc = BeginPaint(hwnd, &ps); + if (NULL == hdc) return; + + int radius = (ps.rcPaint.bottom - ps.rcPaint.top - WASABI_API_APP->getScaleY(1)) / WASABI_API_APP->getScaleY(2); + int radius2 = WASABI_API_APP->getScaleY(5); + + RECT r; + CopyRect(&r, &ps.rcPaint); + // fill the background accordingly + FillRect(hdc, &r, (HBRUSH)MlStockObjects_Get(WNDBCK_BRUSH)); + + r.right -= WASABI_API_APP->getScaleX(1); + r.bottom -= WASABI_API_APP->getScaleY(1); + OffsetRect(&r, 1, 1); + HPEN oldPen = SelectPen(hdc, MlStockObjects_Get(HILITE_PEN)); + HBRUSH oldBrush = SelectBrush(hdc, MlStockObjects_Get(WNDBCK_BRUSH)); + // draw the border edge offset to add a 'shadow' + RoundRect(hdc, r.left, r.top, r.right, r.bottom, radius2, radius); + + ps.rcPaint.bottom -= WASABI_API_APP->getScaleY(1); + + SelectObject(hdc, MlStockObjects_Get(ITEMBCK_PEN)); + SelectObject(hdc, MlStockObjects_Get(ITEMBCK_BRUSH)); + // draw the 'empty' part of the bar with a slight overlap of the 'shadow' + RoundRect(hdc, ps.rcPaint.left, ps.rcPaint.top, ps.rcPaint.right, ps.rcPaint.bottom, radius2, radius); + + int pos = (DWORD)CallPrevWndProc(PBM_GETPOS, 0, 0L); + if (pos > 0) + { + int range = (DWORD)CallPrevWndProc(PBM_GETRANGE, 0, 0L); + int val = (((ps.rcPaint.right) * pos) / range); + + if (val > 0) + { + if ((ps.rcPaint.left + val + radius) > ps.rcPaint.right) + { + SelectObject(hdc, MlStockObjects_Get(ITEMTEXT_PEN)); + SelectObject(hdc, MlStockObjects_Get(ITEMTEXT_BRUSH)); + // draws the 'filled' part of the bar with a slight overlap of the 'shadow' + RoundRect(hdc, ps.rcPaint.left - WASABI_API_APP->getScaleX(10), ps.rcPaint.top, + val + (radius * WASABI_API_APP->getScaleY(2)) - WASABI_API_APP->getScaleY(10), ps.rcPaint.bottom, radius2, radius); + } + else + { + ps.rcPaint.right = ps.rcPaint.left + val; + FillRect(hdc, &ps.rcPaint, (HBRUSH)MlStockObjects_Get(ITEMTEXT_BRUSH)); + } + } + + // not keen on this at all but it sorts out the progress overlap + SelectObject(hdc, MlStockObjects_Get(WNDBCK_PEN)); + MoveToEx(hdc, ps.rcPaint.left, ps.rcPaint.bottom, NULL); + LineTo(hdc, ps.rcPaint.left + ((radius)/ 2), ps.rcPaint.bottom); + LineTo(hdc, ps.rcPaint.left, ps.rcPaint.bottom - ((radius)/ WASABI_API_APP->getScaleY(2))); + LineTo(hdc, ps.rcPaint.left, ps.rcPaint.bottom); + + MoveToEx(hdc, ps.rcPaint.left, ps.rcPaint.top, NULL); + LineTo(hdc, ps.rcPaint.left, ps.rcPaint.top + ((radius)/ WASABI_API_APP->getScaleY(2))); + + MoveToEx(hdc, ps.rcPaint.left, ps.rcPaint.top, NULL); + LineTo(hdc, ps.rcPaint.left + ((radius)/ WASABI_API_APP->getScaleY(2)), ps.rcPaint.top); + } + + SelectPen(hdc, oldPen); + SelectBrush(hdc, oldBrush); +} + +UINT SkinnedProgressBar::GetBorderType(void) +{ + return BORDER_NONE; +} + +LRESULT SkinnedProgressBar::WindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch(uMsg) + { + case WM_PAINT: + OnPaint(); + return 0; + case WM_SETCURSOR: + if (NULL != skinCursor) + { + if (skinCursor != GetCursor()) + SetCursor(skinCursor); + return TRUE; + } + break; + } + return __super::WindowProc(uMsg, wParam, lParam); +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/skinnedprogressbar.h b/Src/Plugins/General/gen_ml/skinnedprogressbar.h new file mode 100644 index 00000000..405e75a4 --- /dev/null +++ b/Src/Plugins/General/gen_ml/skinnedprogressbar.h @@ -0,0 +1,33 @@ +#ifndef NULLOSFT_MEDIALIBRARY_SKINNED_PROGRESSBAR_HEADER +#define NULLOSFT_MEDIALIBRARY_SKINNED_PROGRESSBAR_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include "./skinnedwnd.h" + + +class SkinnedProgressBar : public SkinnedWnd +{ + +protected: + SkinnedProgressBar(void); + virtual ~SkinnedProgressBar(void); + +protected: + virtual BOOL Attach(HWND hProgressBar); + virtual LRESULT WindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam); // treat this as dialog proc + void OnSkinChanged(BOOL bNotifyChildren, BOOL bRedraw); + + void OnPaint(void); + UINT GetBorderType(void); + +private: + friend BOOL SkinWindowEx(HWND hwndToSkin, INT type, UINT style); + +protected: + HCURSOR skinCursor; +}; + +#endif // NULLOSFT_MEDIALIBRARY_SKINNED_PROGRESSBAR_HEADER \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/skinnedscrollwnd.cpp b/Src/Plugins/General/gen_ml/skinnedscrollwnd.cpp new file mode 100644 index 00000000..64a409b9 --- /dev/null +++ b/Src/Plugins/General/gen_ml/skinnedscrollwnd.cpp @@ -0,0 +1,3285 @@ +// some code taken from (freeware) Cool ScrollBar library by J Brown +#include "./skinnedscrollwnd.h" +#include "main.h" +#include +#include +#include "../winamp/wa_dlg.h" +#include "api__gen_ml.h" +#include "./colors.h" +#include +#include +#include +#include +#include "./stockobjects.h" + + +/* minimum size of scrollbar before inserted buttons are hidden to make room when the window is sized too small */ +#define MIN_COOLSB_SIZE 24 +/* min size of scrollbar when resizing a button, before the resize is stopped because the scrollbar has gotten too small */ +#define MINSCROLLSIZE 50 +/* a normal scrollbar "snaps" its scroll-thumb back into position if + you move the mouse too far away from the window, whilst you are + dragging the thumb, that is. #undeffing this results in the thumb + never snapping back into position, no matter how far away you move + the mouse */ +#define SNAP_THUMB_BACK +/* distance (in pixels) the mouse must move away from the thumb + during tracking to cause the thumb bar to snap back to its + starting place. Has no effect unless SNAP_THUMB_BACK is defined */ +#define THUMBTRACK_SNAPDIST 128 +// To complement the exisiting SB_HORZ, SB_VERT, SB_BOTH +// scrollbar identifiers +#define COOLSB_NONE (-1) +#define SB_INSBUT (-2) + +// Arrow size defines +#define SYSTEM_METRIC (-1) + + +// general scrollbar styles +// +// use the standard ESB_DISABLE_xxx flags to represent the +// enabled / disabled states. (defined in winuser.h) +// +#define CSBS_THUMBALWAYS 0x0004 +#define CSBS_VISIBLE 0x0008 +#define CSBS_TRACKING 0x0010 +#define CSBS_FLATSB 0x0020 +#define CSBS_BTNVISBEFORE 0x0040 //if the buttons to the left are visible +#define CSBS_BTNVISAFTER 0x0080 //if the buttons to the right are visible +#define CSBS_HOVERING 0x0100 //if the buttons to the right are visible + +//cool scrollbar styles for Flat scrollbars +#define CSBS_NORMAL 0 +#define CSBS_FLAT 1 +#define CSBS_HOTTRACKED 2 + + +// Button mask flags for indicating which members of SCROLLBUT +// to use during a button insertion / modification +#define SBBF_TYPE 0x0001 +#define SBBF_ID 0x0002 +#define SBBF_PLACEMENT 0x0004 +#define SBBF_SIZE 0x0008 +#define SBBF_BITMAP 0x0010 +#define SBBF_ENHMETAFILE 0x0020 +//#define SBBF_OWNERDRAW 0x0040 //unused at present +#define SBBF_CURSOR 0x0080 +#define SBBF_BUTMINMAX 0x0100 +#define SBBF_STATE 0x0200 + +//button styles (states) +#define SBBS_NORMAL 0 +#define SBBS_PUSHED 1 +#define SBBS_CHECKED SBBS_PUSHED + +// scrollbar button types +#define SBBT_PUSHBUTTON 1 //standard push button +#define SBBT_TOGGLEBUTTON 2 //toggle button +#define SBBT_FIXED 3 //fixed button (non-clickable) +#define SBBT_FLAT 4 //blank area (flat, with border) +#define SBBT_BLANK 5 //blank area (flat, no border) +#define SBBT_DARK 6 //dark blank area (flat) +#define SBBT_OWNERDRAW 7 //user draws the button via a WM_NOTIFY + +#define SBBT_MASK 0x1f //mask off low 5 bits + +//button type modifiers +#define SBBM_RECESSED 0x0020 //recessed when clicked (like Word 97) +#define SBBM_LEFTARROW 0x0040 +#define SBBM_RIGHTARROW 0x0080 +#define SBBM_UPARROW 0x0100 +#define SBBM_DOWNARROW 0x0200 +#define SBBM_RESIZABLE 0x0400 +#define SBBM_TYPE2 0x0800 +#define SBBM_TYPE3 0x1000 +#define SBBM_TOOLTIPS 0x2000 //currently unused (define COOLSB_TOOLTIPS in userdefs.h) + +//button placement flags +#define SBBP_LEFT 1 +#define SBBP_RIGHT 2 +#define SBBP_TOP 1 //3 +#define SBBP_BOTTOM 2 //4 + +#define DFCS_HOVER 0x800 +// +// Button command notification codes +// for sending with a WM_COMMAND message +// +#define CSBN_BASE 0 +#define CSBN_CLICKED (1 + CSBN_BASE) +#define CSBN_HILIGHT (2 + CSBN_BASE) + +// Minimum size in pixels of a scrollbar thumb +#define MINTHUMBSIZE_NT4 9 +#define MINTHUMBSIZE_2000 7 + +//define some more hittest values for our cool-scrollbar +#define HTSCROLL_LEFT (SB_LINELEFT) +#define HTSCROLL_RIGHT (SB_LINERIGHT) +#define HTSCROLL_UP (SB_LINEUP) +#define HTSCROLL_DOWN (SB_LINEDOWN) +#define HTSCROLL_THUMB (SB_THUMBTRACK) +#define HTSCROLL_PAGEGUP (SB_PAGEUP) +#define HTSCROLL_PAGEGDOWN (SB_PAGEDOWN) +#define HTSCROLL_PAGELEFT (SB_PAGELEFT) +#define HTSCROLL_PAGERIGHT (SB_PAGERIGHT) + +#define HTSCROLL_NONE (-1) +#define HTSCROLL_NORMAL (-1) + +#define HTSCROLL_INSERTED (128) +#define HTSCROLL_PRE (32 | HTSCROLL_INSERTED) +#define HTSCROLL_POST (64 | HTSCROLL_INSERTED) + +// SCROLLBAR datatype. There are two of these structures per window +typedef struct _SCROLLBAR +{ + UINT fScrollFlags; //flags + BOOL fScrollVisible; //if this scrollbar visible? + SCROLLINFO scrollInfo; //positional data (range, position, page size etc) + + //data for inserted buttons + int nButSizeBefore; //size to the left / above the bar + int nButSizeAfter; //size to the right / below the bar + + int nMinThumbSize; + int nBarType; //SB_HORZ / SB_VERT + +} SCROLLBAR; + +static WORD wCheckPat[8] = +{ + 0xaaaa, 0x5555, 0xaaaa, 0x5555, 0xaaaa, 0x5555, 0xaaaa, 0x5555 +}; + + +// scrollwnd styles +#define SWS_UPDATEFRAME 0x0001 +#define SWS_LEFT 0x0002 +#define SWS_DISABLENOSCROLL 0x0004 +#define SWS_HIDEHSCROLL 0x0008 +#define SWS_LISTVIEW 0x0010 +#define SWS_TREEVIEW 0x0020 +#define SWS_HIDEVSCROLL 0x0040 +#define SWS_COMBOLBOX 0x0080 +#define SWS_USEFREEFORM 0x0100 + +// +// PRIVATE INTERNAL FUNCTIONS +// +#define COOLSB_TIMERID1 65533 //initial timer +#define COOLSB_TIMERID2 65534 //scroll message timer +#define COOLSB_TIMERID3 -14 //mouse hover timer +#define COOLSB_TIMERINTERVAL1 300 +#define COOLSB_TIMERINTERVAL2 55 +#define COOLSB_TIMERINTERVAL3 20 //mouse hover time + +// +// direction: 0 - same axis as scrollbar (i.e. width of a horizontal bar) +// 1 - perpendicular dimesion (i.e. height of a horizontal bar) +// +#define SM_CXVERTSB 1 +#define SM_CYVERTSB 0 +#define SM_CXHORZSB 0 +#define SM_CYHORZSB 1 +#define SM_SCROLL_WIDTH 1 +#define SM_SCROLL_LENGTH 0 + +#ifndef WM_MOUSEWHEEL +#define WM_MOUSEWHEEL 0x020A +#endif + + +#define INACTIVEBAR_ALPHA 127 + +// +// Special thumb-tracking variables +// +// +static UINT uCurrentScrollbar = COOLSB_NONE; //SB_HORZ / SB_VERT +static UINT uCurrentScrollPortion = HTSCROLL_NONE; +static UINT uCurrentButton = 0; + +static RECT rcThumbBounds; //area that the scroll thumb can travel in +static int nThumbSize; //(pixels) +static int nThumbPos; //(pixels) +static int nThumbMouseOffset; //(pixels) +static int nLastPos = -1; //(scrollbar units) +static int nThumbPos0; //(pixels) initial thumb position +static int trackThumbPos; +// +// Temporary state used to auto-generate timer messages +// +static UINT uScrollTimerMsg = 0; +static UINT uScrollTimerPortion = HTSCROLL_NONE; +static UINT_PTR uScrollTimerId = 0; +static HWND hwndCurCoolSB = 0; + +static INT bUseUpdateRgn = -1; +static BOOL bDoHover=FALSE; +static BOOL ignoreCaptureChange = FALSE; +static BOOL captureSet = FALSE; +static HBRUSH hbrChecked = NULL; + +#define GetSBForeColor() WADlg_getColor(WADLG_SCROLLBAR_FGCOLOR) +#define GetSBBackColor() WADlg_getColor(WADLG_SCROLLBAR_BGCOLOR) + +// Send a WM_VSCROLL or WM_HSCROLL message +#define SendScrollMessage(__hwnd, __srcMsg, __srcId, __pos) ::SendMessageW(__hwnd, __srcMsg, (MAKEWPARAM(__srcId, __pos)), 0) + +static UINT GetPortion(SCROLLBAR *sb, HWND hwnd, RECT *rect, int x, int y, DWORD scrollFlags); + +static void RenderBaseTexture(Canvas *canvas, const RECT *r, HWND hwnd) +{ + // TODO: find the ifc_window * object for the media library container, and call renderBaseTexture on it + if (WASABI_API_WND) + { + HWND checkWnd = GetParent(hwnd); + while (checkWnd) + { + ifc_window *window = WASABI_API_WND->rootWndFromOSHandle(checkWnd); + if (window && window->getRenderBaseTexture()) + { + window->renderBaseTexture(canvas, r); + return; + } + checkWnd = GetParent(checkWnd); + } + } + + // fallback code + COLORREF bgcolor = WADlg_getColor(WADLG_WNDBG/*WADLG_SCROLLBAR_BGCOLOR*/); + canvas->fillRect(r, bgcolor); +} + +// swap the rectangle's x coords with its y coords +static void __stdcall RotateRect(RECT *rect) +{ + LONG temp; + temp = rect->left; + rect->left = rect->top; + rect->top = temp; + + temp = rect->right; + rect->right = rect->bottom; + rect->bottom = temp; +} + +// swap the coords if the scrollbar is a SB_VERT +#define RotateRect0(__psb, __prc) ((__psb && __psb->nBarType == SB_VERT) ? RotateRect(__prc) : 0) + +static bool UseFreeformScrollbars() +{ + if (config_use_ff_scrollbars && WASABI_API_SKIN && WASABI_API_SKIN->skin_isLoaded()) + { + return WASABI_API_SKIN->skin_getVersion() >= 1.3; + } + else + { + return false; + } +} + +// Calculate if the SCROLLINFO members produce an enabled or disabled scrollbar +static BOOL IsScrollInfoActive(SCROLLINFO *si) +{ + return (si->nPage <= (UINT)si->nMax && si->nMax > si->nMin && si->nMax != 0); +} + +// Return if the specified scrollbar is enabled or not +static BOOL IsScrollbarActive(SCROLLBAR *sb) +{ + return (((sb->fScrollFlags & ESB_DISABLE_BOTH) == ESB_DISABLE_BOTH) || + !(sb->fScrollFlags & CSBS_THUMBALWAYS) && !IsScrollInfoActive(&sb->scrollInfo)) ? FALSE : TRUE; +} + +enum +{ + HORIZ_LEFT, + HORIZ_LEFT_PRESSED, + HORIZ_LEFT_HOVER, + HORIZ_LEFT_INACTIVE, + HORIZ_RIGHT, + HORIZ_RIGHT_PRESSED, + HORIZ_RIGHT_HOVER, + HORIZ_RIGHT_INACTIVE, + VERT_UP, + VERT_UP_PRESSED, + VERT_UP_HOVER, + VERT_UP_INACTIVE, + VERT_DOWN, + VERT_DOWN_PRESSED, + VERT_DOWN_HOVER, + VERT_DOWN_INACTIVE, +}; + +static int GetBitmapEnum(UINT state, BOOL hover) +{ + int offset=0; + if (state&DFCS_PUSHED) + offset=1; + if (state&DFCS_INACTIVE) + offset=3; + else if (hover) + offset=2; + + switch (state&3) + { + case DFCS_SCROLLRIGHT: + return HORIZ_RIGHT+offset; + case DFCS_SCROLLLEFT: + return HORIZ_LEFT+offset; + case DFCS_SCROLLDOWN: + return VERT_DOWN+offset; + default://case DFCS_SCROLLUP: + return VERT_UP+offset; + } +} + +class ScrollBitmaps +{ +public: + ScrollBitmaps() : v_up(L"wasabi.scrollbar.vertical.background.top"), + v_down(L"wasabi.scrollbar.vertical.background.bottom"), + v_mid(L"wasabi.scrollbar.vertical.background.middle"), + h_left(L"wasabi.scrollbar.horizontal.background.left"), + h_mid(L"wasabi.scrollbar.horizontal.background.middle"), + h_right(L"wasabi.scrollbar.horizontal.background.right") + { + } + AutoSkinBitmap v_up, v_down, v_mid, h_left, h_mid, h_right; +}; + +static ScrollBitmaps *scrollBitmaps=0; + +void SkinnedScrollWnd_Init() +{ + scrollBitmaps = new ScrollBitmaps(); +} + +void SkinnedScrollWnd_Quit() +{ + if (scrollBitmaps) + { + delete scrollBitmaps; + scrollBitmaps=0; + } +} + +static HBITMAP hbmpCachedDib = NULL; + +// Paint a checkered rectangle, with each alternate pixel being assigned a different colour + +static BOOL DrawFrameCtrl(HDC hdc, LPRECT lprc, UINT uType, UINT state, BOOL hover, BOOL freeform) +{ + int startx, starty, alpha = 255; + + const wchar_t *bitmapid=0; + const wchar_t *backgroundid=0; + SkinBitmap *bg=0; + switch (GetBitmapEnum(state, hover)) + { + case HORIZ_LEFT: + bitmapid = L"wasabi.scrollbar.horizontal.left"; + if (scrollBitmaps) + bg=scrollBitmaps->h_left.getBitmap(); + startx = 0; starty = 45; break; + case HORIZ_LEFT_PRESSED: + bitmapid = L"wasabi.scrollbar.horizontal.left.pressed"; + if (scrollBitmaps) + bg=scrollBitmaps->h_left.getBitmap(); + startx = 28; starty = 45; break; + case HORIZ_LEFT_HOVER: + bitmapid = L"wasabi.scrollbar.horizontal.left.hover"; + if (scrollBitmaps) + bg=scrollBitmaps->h_left.getBitmap(); + startx = 0; starty = 45; break; + case HORIZ_LEFT_INACTIVE: + alpha = INACTIVEBAR_ALPHA; + bitmapid = L"wasabi.scrollbar.horizontal.left"; + if (scrollBitmaps) + bg=scrollBitmaps->h_left.getBitmap(); + startx = 0; starty = 45; break; + case HORIZ_RIGHT: + bitmapid = L"wasabi.scrollbar.horizontal.right"; + if (scrollBitmaps) + bg=scrollBitmaps->h_right.getBitmap(); + startx = 14; starty = 45; break; + case HORIZ_RIGHT_PRESSED: + bitmapid = L"wasabi.scrollbar.horizontal.right.pressed"; + if (scrollBitmaps) + bg=scrollBitmaps->h_right.getBitmap(); + startx = 42; starty = 45; break; + case HORIZ_RIGHT_HOVER: + bitmapid = L"wasabi.scrollbar.horizontal.right.hover"; + if (scrollBitmaps) + bg=scrollBitmaps->h_right.getBitmap(); + startx = 14; starty = 45; break; + case HORIZ_RIGHT_INACTIVE: + alpha = INACTIVEBAR_ALPHA; + bitmapid = L"wasabi.scrollbar.horizontal.right"; + if (scrollBitmaps) + bg=scrollBitmaps->h_right.getBitmap(); + startx = 14; starty = 45; break; + case VERT_UP: + bitmapid = L"wasabi.scrollbar.vertical.left"; + if (scrollBitmaps) + bg=scrollBitmaps->v_up.getBitmap(); + startx = 0; starty = 31; break; + case VERT_UP_PRESSED: + bitmapid = L"wasabi.scrollbar.vertical.left.pressed"; + if (scrollBitmaps) + bg=scrollBitmaps->v_up.getBitmap(); + startx = 28; starty = 31; break; + case VERT_UP_HOVER: + bitmapid = L"wasabi.scrollbar.vertical.left.hover"; + if (scrollBitmaps) + bg=scrollBitmaps->v_up.getBitmap(); + startx = 0; starty = 31; break; + case VERT_UP_INACTIVE: + alpha = INACTIVEBAR_ALPHA; + bitmapid = L"wasabi.scrollbar.vertical.left"; + if (scrollBitmaps) + bg=scrollBitmaps->v_up.getBitmap(); + startx = 0; starty = 31; break; + + case VERT_DOWN: + bitmapid = L"wasabi.scrollbar.vertical.right"; + if (scrollBitmaps) + bg=scrollBitmaps->v_down.getBitmap(); + startx = 14; starty = 31; break; + case VERT_DOWN_PRESSED: + bitmapid = L"wasabi.scrollbar.vertical.right.pressed"; + if (scrollBitmaps) + bg=scrollBitmaps->v_down.getBitmap(); + startx = 42; starty = 31; break; + case VERT_DOWN_HOVER: + bitmapid = L"wasabi.scrollbar.vertical.right.hover"; + if (scrollBitmaps) + bg=scrollBitmaps->v_down.getBitmap(); + startx = 14; starty = 31; break; + case VERT_DOWN_INACTIVE: + alpha = INACTIVEBAR_ALPHA; + bitmapid = L"wasabi.scrollbar.vertical.right"; + if (scrollBitmaps) + bg=scrollBitmaps->v_down.getBitmap(); + startx = 14; starty = 31; break; + } + + if (freeform) + { + SkinBitmap bmp(bitmapid); + if (!bmp.isInvalid() && bg && !bg->isInvalid()) + { + DCCanvas canvas(hdc); + bg->stretchToRectAlpha(&canvas, lprc, alpha); + bmp.stretchToRectAlpha(&canvas, lprc, alpha); + return 1; + } + } + + // fallback code + HDC hdcbmp; + HBITMAP hbmpOld, hbmp; + + hbmp = WADlg_getBitmap(); + if (!hbmp) return FALSE; + + hdcbmp = (HDC)MlStockObjects_Get(CACHED_DC); + if (!hdcbmp) return FALSE; + + hbmpOld = (HBITMAP)SelectObject(hdcbmp, hbmp); + + + + if (255 == alpha) + StretchBlt(hdc, lprc->left, lprc->top, lprc->right - lprc->left, lprc->bottom - lprc->top, hdcbmp, startx, starty, 14, 14, SRCCOPY); + else + { + HDC hdcTmp = CreateCompatibleDC(hdc); + + DIBSECTION dibSection; + if (NULL == hbmpCachedDib || + sizeof(DIBSECTION) != GetObjectW(hbmpCachedDib, sizeof(DIBSECTION), &dibSection) + || dibSection.dsBm.bmWidth < (lprc->right - lprc->left) || ABS(dibSection.dsBm.bmHeight) < (lprc->bottom - lprc->top)) + { + if (hbmpCachedDib) DeleteObject(hbmpCachedDib); + + BITMAPINFOHEADER bi; + ZeroMemory(&bi, sizeof(BITMAPINFOHEADER)); + bi.biSize = sizeof(BITMAPINFOHEADER); + bi.biWidth = lprc->right - lprc->left; + bi.biHeight = -(lprc->bottom - lprc->top); + bi.biPlanes = 1; + bi.biBitCount = 32; + bi.biCompression = BI_RGB; + hbmpCachedDib = CreateDIBSection(hdc, (BITMAPINFO*)&bi, DIB_RGB_COLORS, (VOID**)&dibSection.dsBm.bmBits, NULL, 0); + dibSection.dsBm.bmHeight = bi.biHeight; + dibSection.dsBm.bmWidth = bi.biWidth; + } + + ASSERT(hbmpCachedDib != 0); + + HBITMAP hbmpTmp = (HBITMAP)SelectObject(hdcTmp, hbmpCachedDib); + + StretchBlt(hdcTmp, 0, 0, lprc->right - lprc->left, lprc->bottom - lprc->top, hdcbmp, startx, starty, 14, 14, SRCCOPY); + + LONG pitch = dibSection.dsBm.bmWidth*4, cy = lprc->bottom - lprc->top, x; + LPBYTE cursor, line; + + COLORREF rgbBk = WADlg_getColor(WADLG_WNDBG);// BlendColors(GetSBBackColor(), WADlg_getColor(WADLG_ITEMBG), ((float)INACTIVEBAR_ALPHA)/255.0f); + + BYTE k = (((255 - alpha)*255 + 127)/255); + BYTE r = (GetRValue(rgbBk)*k + 127)/255, g = (GetGValue(rgbBk)*k + 127)>>8, b = (GetBValue(rgbBk)*k + 127)/255; + + for (line = (BYTE*)dibSection.dsBm.bmBits; cy-- != 0; line += pitch ) + { + for (x = (lprc->right - lprc->left), cursor = line; x-- != 0; cursor += 4) + { + cursor[0] = (cursor[0]*alpha)/255 + b; + cursor[1] = (cursor[1]*alpha)/255 + g; + cursor[2] = (cursor[2]*alpha)/255 + r; + cursor[3] = 0xFF; + } + } + + BitBlt(hdc, lprc->left, lprc->top, lprc->right - lprc->left, lprc->bottom - lprc->top, hdcTmp, 0, 0, SRCCOPY); + SelectObject(hdcTmp, hbmpTmp); + DeleteDC(hdcTmp); + } + + SelectObject(hdcbmp, hbmpOld); + + return 1; +} + +// Draw a standard scrollbar arrow +static int DrawScrollArrow(SCROLLBAR *sbar, HDC hdc, RECT *rect, UINT arrow, BOOL fMouseDown, BOOL fMouseOver, BOOL freeform) +{ + UINT ret; + UINT flags = arrow; + + //HACKY bit so this routine can be called by vertical and horizontal code + if (sbar->nBarType == SB_VERT) + { + if (flags & DFCS_SCROLLLEFT) flags = flags & ~DFCS_SCROLLLEFT | DFCS_SCROLLUP; + if (flags & DFCS_SCROLLRIGHT) flags = flags & ~DFCS_SCROLLRIGHT | DFCS_SCROLLDOWN; + } + + if (fMouseDown) flags |= (DFCS_FLAT | DFCS_PUSHED); + + ret = DrawFrameCtrl(hdc, rect, DFC_SCROLL, flags, fMouseOver, freeform); + + return ret; +} + +// Return the size in pixels for the specified scrollbar metric, for the specified scrollbar +static int GetScrollMetric(SCROLLBAR *psb, int metric, DWORD scrollFlags) +{ + int type (psb ? psb->nBarType : SB_VERT); + switch (type) + { + case SB_HORZ: + { + switch (metric) + { + case SM_CXHORZSB: + if (SWS_USEFREEFORM & scrollFlags) + { + SkinBitmap button(L"wasabi.scrollbar.horizontal.left"); // we assume symmetry which isn't necessary safe + if (!button.isInvalid()) + return WASABI_API_APP->getScaleX(button.getWidth()); + } + return WASABI_API_APP->getScaleX(14); // classic skin fixes this at 14 + default: + if (SWS_USEFREEFORM & scrollFlags) + { + SkinBitmap button(L"wasabi.scrollbar.horizontal.left"); // we assume symmetry which isn't necessary safe + if (!button.isInvalid()) + return WASABI_API_APP->getScaleY(button.getHeight()); + } + return WASABI_API_APP->getScaleY(14); // classic skin fixes this at 14 + break; + } + } + break; + + default: // case SB_VERT: + { + switch (metric) + { + case SM_CYVERTSB: + if (SWS_USEFREEFORM & scrollFlags) + { + SkinBitmap button(L"wasabi.scrollbar.vertical.left"); // we assume symmetry which isn't necessary safe + if (!button.isInvalid()) + return WASABI_API_APP->getScaleY(button.getHeight()); + } + return WASABI_API_APP->getScaleY(14); // classic skin fixes this at 14 + default: + if (SWS_USEFREEFORM & scrollFlags) + { + SkinBitmap button(L"wasabi.scrollbar.vertical.left"); // we assume symmetry which isn't necessary safe + if (!button.isInvalid()) + return WASABI_API_APP->getScaleX(button.getWidth()); + } + return WASABI_API_APP->getScaleX(14); // classic skin fixes this at 14 + break; + } + } + break; + } + + /* + if ((SB_HORZ == psb->nBarType && metric == SM_CXHORZSB) || (SB_VERT == psb->nBarType && metric == SM_CYVERTSB)) + return (psb->nArrowLength == SYSTEM_METRIC) * ((psb->nArrowLength < 0) ? -14 : 1); + else return psb->nArrowWidth * ((psb->nArrowWidth < 0) ? -14 : 1); + */ +} + +// Fill the specifed rectangle using a solid colour +static void PaintRect(HDC hdc, RECT *rect, COLORREF color) +{ + COLORREF oldcol = SetBkColor(hdc, color); + ExtTextOutW(hdc, 0, 0, ETO_OPAQUE, rect, L"", 0, 0); + SetBkColor(hdc, oldcol); +} + +// +// Set the minimum size, in pixels, that the thumb box will shrink to. +// + +// Draw a simple blank scrollbar push-button. Can be used +// to draw a push button, or the scrollbar thumb +// drawflag - could set to BF_FLAT to make flat scrollbars +static void DrawBlankButton(HDC hdc, const RECT *rect, UINT drawflag, int pushed, int vertical) +{ + HBITMAP hbmp, hbmpOld; + hbmp = WADlg_getBitmap(); + if (!hbmp) return; + + HDC hdcbmp = (HDC)MlStockObjects_Get(CACHED_DC); + if (!hdcbmp) return; + + hbmpOld = (HBITMAP)SelectObject(hdcbmp, hbmp); + +#define PART1SIZE 4 //copied top +#define PART2SIZE 5 //stretched top +#define PART3SIZE 10 //copied middle +#define PART4SIZE 5 //stretched bottom +#define PART5SIZE 4 //copied bottom + + if (vertical) + { + int middle = (rect->bottom - rect->top) / 2; + int startx = pushed ? 70 : 56; + //top + StretchBlt(hdc, rect->left, rect->top, rect->right - rect->left, PART1SIZE, hdcbmp, startx, 31, 14, PART1SIZE, SRCCOPY); + int p = PART1SIZE; + //stretched top + int l = middle - PART1SIZE - (PART3SIZE / 2); + if (l > 0) + { + StretchBlt(hdc, rect->left, rect->top + p, rect->right - rect->left, l, hdcbmp, startx, 31 + PART1SIZE, 14, PART2SIZE, SRCCOPY); + p += middle - PART1SIZE - (PART3SIZE / 2); + } + //copied middle + int m = (rect->bottom - rect->top) - PART1SIZE - PART5SIZE; //space that's available for middle + m = min(m, PART3SIZE); + if (m > 0) + { + StretchBlt(hdc, rect->left, rect->top + p, rect->right - rect->left, m, hdcbmp, startx, 31 + PART1SIZE + PART2SIZE, 14, m, SRCCOPY); + p += m; + } + //stretched bottom + l = rect->bottom - rect->top - p - PART5SIZE; + if (l > 0) StretchBlt(hdc, rect->left, rect->top + p, rect->right - rect->left, l, hdcbmp, startx, 31 + PART1SIZE + PART2SIZE + PART3SIZE, 14, PART4SIZE, SRCCOPY); + //bottom + StretchBlt(hdc, rect->left, rect->bottom - PART5SIZE, rect->right - rect->left, PART5SIZE, hdcbmp, startx, 31 + PART1SIZE + PART2SIZE + PART3SIZE + PART4SIZE, 14, PART5SIZE, SRCCOPY); + } + else + { + int middle = (rect->right - rect->left) / 2; + int starty = pushed ? 45 : 31; + //top + StretchBlt(hdc, rect->left, rect->top, PART1SIZE, rect->bottom - rect->top, hdcbmp, 84, starty, PART1SIZE, 14, SRCCOPY); + int p = PART1SIZE; + //stretched top + int l = middle - PART1SIZE - (PART3SIZE / 2); + if (l > 0) + { + StretchBlt(hdc, rect->left + p, rect->top, l, rect->bottom - rect->top, hdcbmp, 84 + PART1SIZE, starty, PART2SIZE, 14, SRCCOPY); + p += middle - PART1SIZE - (PART3SIZE / 2); + } + //copied middle + int m = (rect->right - rect->left) - PART1SIZE - PART5SIZE; //space that's available for middle + m = min(m, PART3SIZE); + if (m > 0) + { + StretchBlt(hdc, rect->left + p, rect->top, m, rect->bottom - rect->top, hdcbmp, 84 + PART1SIZE + PART2SIZE, starty, m, 14, SRCCOPY); + p += m; + } + //stretched bottom + l = rect->right - rect->left - p - PART5SIZE; + if (l > 0) StretchBlt(hdc, rect->left + p, rect->top, l, rect->bottom - rect->top, hdcbmp, 84 + PART1SIZE + PART2SIZE + PART3SIZE, starty, PART4SIZE, 14, SRCCOPY); + //bottom + StretchBlt(hdc, rect->right - PART5SIZE, rect->top, PART5SIZE, rect->bottom - rect->top, hdcbmp, 84 + PART1SIZE + PART2SIZE + PART3SIZE + PART4SIZE, starty, PART5SIZE, 14, SRCCOPY); + } + + SelectObject(hdcbmp, hbmpOld); +} + + +// Calculate the screen coordinates of the area taken by +// the horizontal scrollbar. Take into account the size +// of the window borders +static BOOL GetHScrollRect(SkinnedScrollWnd *pWnd, RECT *prc) +{ + if (pWnd->psbHorz->fScrollVisible) + { + GetClientRect(pWnd->hwnd, prc); + MapWindowPoints(pWnd->hwnd, HWND_DESKTOP, (POINT*)prc, 2); + prc->top = prc->bottom; + prc->bottom += GetScrollMetric(pWnd->psbHorz, SM_CYHORZSB, pWnd->scrollFlags); + } + else SetRect(prc, 0, 0, 0, 0); + return TRUE; +} + +// Calculate the screen coordinates of the area taken by the +// vertical scrollbar +static BOOL GetVScrollRect(SkinnedScrollWnd *pWnd, RECT *prc) +{ + if (pWnd->psbVert->fScrollVisible) + { + GetClientRect(pWnd->hwnd, prc); + MapWindowPoints(pWnd->hwnd, HWND_DESKTOP, (POINT*)prc, 2); + if (SWS_LEFT & pWnd->scrollFlags) + { + prc->right = prc->left; + prc->left -= GetScrollMetric(pWnd->psbVert, SM_CXVERTSB, pWnd->scrollFlags); + } + else + { + prc->left = prc->right; + prc->right += GetScrollMetric(pWnd->psbVert, SM_CXVERTSB, pWnd->scrollFlags); + } + } + else SetRect(prc, 0, 0, 0, 0); + + + return TRUE; +} + +// Depending on what type of scrollbar nBar refers to, call the +// appropriate Get?ScrollRect function +// +static BOOL GetScrollRect(SkinnedScrollWnd *pWnd, UINT nBar, RECT *prc) +{ + if (nBar == SB_HORZ) return GetHScrollRect(pWnd, prc); + else if (nBar == SB_VERT) return GetVScrollRect(pWnd, prc); + else return FALSE; +} + +// +// Work out the scrollbar width/height for either type of scrollbar (SB_HORZ/SB_VERT) +// rect - coords of the scrollbar. +// store results into *thumbsize and *thumbpos +// +static int CalcThumbSize(SCROLLBAR *sbar, const RECT *rect, int *pthumbsize, int *pthumbpos, DWORD scrollFlags) +{ + SCROLLINFO *si; + int scrollsize; //total size of the scrollbar including arrow buttons + int workingsize; //working area (where the thumb can slide) + int siMaxMin; + int butsize; + int startcoord; + int thumbpos = 0, thumbsize = 0; + + //work out the width (for a horizontal) or the height (for a vertical) + //of a standard scrollbar button + butsize = GetScrollMetric(sbar, SM_SCROLL_LENGTH, scrollFlags); + + if (1) //sbar->nBarType == SB_HORZ) + { + scrollsize = rect->right - rect->left; + startcoord = rect->left; + } + /*else if(sbar->nBarType == SB_VERT) + { + scrollsize = rect->bottom - rect->top; + startcoord = rect->top; + } + else + { + return 0; + }*/ + + si = &sbar->scrollInfo; + siMaxMin = si->nMax - si->nMin + 1; + + workingsize = scrollsize - butsize * 2; + + // + // Work out the scrollbar thumb SIZE + // + if (si->nPage == 0) + { + thumbsize = butsize; + } + else if (siMaxMin > 0) + { + /*if (SWS_HIDEHSCROLL & scrollFlags) + thumbsize = MulDiv(si->nPage, workingsize, si->nMax); + else*/ + thumbsize = MulDiv(si->nPage, workingsize, siMaxMin); + + if (SWS_USEFREEFORM & scrollFlags) + { + SkinBitmap thumb((sbar->nBarType == SB_VERT)?L"wasabi.scrollbar.vertical.button":L"wasabi.scrollbar.horizontal.button"); + if (!thumb.isInvalid()) + thumbsize = (sbar->nBarType == SB_VERT)?thumb.getHeight():thumb.getWidth(); + } + + if (thumbsize < sbar->nMinThumbSize) + thumbsize = sbar->nMinThumbSize; + } + + // + // Work out the scrollbar thumb position + // + if (siMaxMin > 0) + { + int pagesize = max(1, si->nPage); + thumbpos = MulDiv(si->nPos - si->nMin, workingsize - thumbsize, siMaxMin - pagesize); + /*if (SWS_HIDEHSCROLL & scrollFlags) + { + thumbpos = (si->nPos == (si->nMax - si->nPage)) ? + (workingsize - thumbsize) : MulDiv(si->nPos, workingsize, si->nMax); + }*/ + + if (thumbpos < 0) + thumbpos = 0; + + if (thumbpos >= workingsize - thumbsize) + thumbpos = workingsize - thumbsize; + } + + thumbpos += startcoord + butsize; + + *pthumbpos = thumbpos; + *pthumbsize = thumbsize; + + return 1; +} + +// +// return a hit-test value for whatever part of the scrollbar x,y is located in +// rect, x, y: SCREEN coordinates +// the rectangle must not include space for any inserted buttons +// (i.e, JUST the scrollbar area) +// +static UINT GetHorzScrollPortion(SCROLLBAR *sbar, HWND hwnd, const RECT *rect, int x, int y, DWORD scrollFlags) +{ + int thumbwidth, thumbpos; + int butwidth = GetScrollMetric(sbar, SM_SCROLL_LENGTH, scrollFlags); + int scrollwidth = rect->right - rect->left; + int workingwidth = scrollwidth - butwidth * 2; + + if (y < rect->top || y >= rect->bottom) + return HTSCROLL_NONE; + + CalcThumbSize(sbar, rect, &thumbwidth, &thumbpos, scrollFlags); + + //if we have had to scale the buttons to fit in the rect, + //then adjust the button width accordingly + if (scrollwidth <= butwidth * 2) + { + butwidth = scrollwidth / 2; + } + + //check for left button click + if (x >= rect->left && x < rect->left + butwidth) + { + return (ESB_DISABLE_LEFT & sbar->fScrollFlags) ? HTSCROLL_NONE : HTSCROLL_LEFT; + } + //check for right button click + else if (x >= rect->right - butwidth && x < rect->right) + { + return (ESB_DISABLE_RIGHT & sbar->fScrollFlags) ? HTSCROLL_NONE : HTSCROLL_RIGHT; + } + + //if the thumb is too big to fit (i.e. it isn't visible) + //then return a NULL scrollbar area + if (thumbwidth >= workingwidth) + return HTSCROLL_NONE; + + //check for point in the thumbbar + if (x >= thumbpos && x < thumbpos + thumbwidth) + { + return HTSCROLL_THUMB; + } + //check for left margin + else if (x >= rect->left + butwidth && x < thumbpos) + { + return HTSCROLL_PAGELEFT; + } + else if (x >= thumbpos + thumbwidth && x < rect->right - butwidth) + { + return HTSCROLL_PAGERIGHT; + } + + return HTSCROLL_NONE; +} + +// +// For vertical scrollbars, rotate all coordinates by -90 degrees +// so that we can use the horizontal version of this function +// +static UINT GetVertScrollPortion(SCROLLBAR *sb, HWND hwnd, RECT *rect, int x, int y, DWORD scrollFlags) +{ + UINT r; + + RotateRect(rect); + r = GetHorzScrollPortion(sb, hwnd, rect, y, x, scrollFlags); + RotateRect(rect); + return r; +} + +static const wchar_t *GetThumbID(UINT barType, UINT scrollFlags, BOOL hover) +{ + if (barType == SB_VERT) + { + if (scrollFlags & CSBS_TRACKING) + return L"wasabi.scrollbar.vertical.button.pressed"; + else if (hover) + return L"wasabi.scrollbar.vertical.button.hover"; + else + return L"wasabi.scrollbar.vertical.button"; + } + else + { + if (scrollFlags & CSBS_TRACKING) + return L"wasabi.scrollbar.horizontal.button.pressed"; + else if (hover) + return L"wasabi.scrollbar.horizontal.button.hover"; + else + return L"wasabi.scrollbar.horizontal.button"; + } +} + +static HBRUSH SetWindowPatternBrush(HWND hwnd, HDC hdc, UINT nBarType) +{ + RECT rw; + GetWindowRect(hwnd, &rw); + HWND hwndAncestor = GetAncestor(hwnd, GA_ROOT); + if (hwndAncestor) MapWindowPoints(HWND_DESKTOP, hwndAncestor, (POINT*)&rw, 2); + POINT ptOrg; + if (GetViewportOrgEx(hdc, &ptOrg)) OffsetRect(&rw, ptOrg.x, ptOrg.y); + if (nBarType == SB_VERT) + { + if (0 == (GetWindowLongPtrW(hwnd, GWL_EXSTYLE) & WS_EX_LEFTSCROLLBAR)) rw.left = rw.right; + } + else rw.top = rw.bottom; + + SetBrushOrgEx(hdc, rw.left, rw.top, (POINT*)&rw); + return (HBRUSH)SelectObject(hdc, hbrChecked); +} + +// +// Draw a complete HORIZONTAL scrollbar in the given rectangle +// Don't draw any inserted buttons in this procedure +// +// uDrawFlags - hittest code, to say if to draw the +// specified portion in an active state or not. +// +// +static LRESULT NCDrawHScrollbar(SCROLLBAR *sb, HWND hwnd, HDC hdc, const RECT *rect, UINT uDrawFlags, UINT hoverFlags, DWORD scrollFlags) +{ + SCROLLINFO *si; + RECT ctrl, thumb; + RECT sbm; + int butwidth = GetScrollMetric(sb, SM_SCROLL_LENGTH, scrollFlags); + int scrollwidth = rect->right - rect->left; + int workingwidth = scrollwidth - butwidth * 2; + int thumbwidth = 0, thumbpos = 0; + int siMaxMin; + + BOOL fMouseDownL = 0, fMouseOverL = (hoverFlags == HTSCROLL_LEFT); + BOOL fMouseDownR = 0, fMouseOverR = (hoverFlags == HTSCROLL_RIGHT); + BOOL fMouseOverThumb = (hoverFlags == HTSCROLL_THUMB); + + COLORREF crCheck1 = GetSBForeColor(); + COLORREF crCheck2 = GetSBBackColor(); + COLORREF crInverse1 = WADlg_getColor(WADLG_SCROLLBAR_INV_FGCOLOR); + COLORREF crInverse2 = WADlg_getColor(WADLG_SCROLLBAR_INV_BGCOLOR); + + UINT uDFCFlat = (CSBS_FLATSB & sb->fScrollFlags) ? DFCS_FLAT : 0; + UINT uDEFlat = (CSBS_FLATSB & sb->fScrollFlags) ? BF_FLAT : 0; + + //drawing flags to modify the appearance of the scrollbar buttons + UINT uLeftButFlags = DFCS_SCROLLLEFT; + UINT uRightButFlags = DFCS_SCROLLRIGHT; + + if (scrollwidth <= 0) + return 0; + + si = &sb->scrollInfo; + siMaxMin = si->nMax - si->nMin; + + if (hwnd != hwndCurCoolSB) + uDrawFlags = HTSCROLL_NONE; + // + // work out the thumb size and position + // + CalcThumbSize(sb, rect, &thumbwidth, &thumbpos, scrollFlags); + if ((CSBS_TRACKING & sb->fScrollFlags) && trackThumbPos != -1) + { + thumbpos=trackThumbPos; + } + + if (sb->fScrollFlags & ESB_DISABLE_LEFT) uLeftButFlags |= DFCS_INACTIVE; + if (sb->fScrollFlags & ESB_DISABLE_RIGHT) uRightButFlags |= DFCS_INACTIVE; + + //if we need to grey the arrows because there is no data to scroll + if ((0 == (DFCS_INACTIVE & uLeftButFlags) || 0 == (DFCS_INACTIVE & uRightButFlags)) && + !(sb->fScrollFlags & CSBS_THUMBALWAYS) && !IsScrollInfoActive(si)) + { + uLeftButFlags |= DFCS_INACTIVE; + uRightButFlags |= DFCS_INACTIVE; + } + + if ((DFCS_INACTIVE & uLeftButFlags) && (DFCS_INACTIVE & uRightButFlags)) + { + COLORREF rgbBk = WADlg_getColor(WADLG_WNDBG); + crCheck1 = BlendColors(crCheck1, rgbBk, INACTIVEBAR_ALPHA); + crCheck2 = BlendColors(crCheck2, rgbBk, INACTIVEBAR_ALPHA); + } + + if (hwnd == hwndCurCoolSB) + { + fMouseDownL = (uDrawFlags == HTSCROLL_LEFT); + fMouseDownR = (uDrawFlags == HTSCROLL_RIGHT); + } + + if (NULL == hbrChecked) //recreate pattern brush if needed + { + HBITMAP hbmp = CreateBitmap(8, 8, 1, 1, wCheckPat); + hbrChecked = CreatePatternBrush(hbmp); + DeleteObject(hbmp); + if (NULL == hbrChecked) return 0; + } + + + + HBRUSH hbrOld = NULL; + + COLORREF rgbFgOld, rgbBkOld; + rgbFgOld = SetTextColor(hdc, crCheck1); + rgbBkOld = SetBkColor(hdc, crCheck2); + + // + // Draw the scrollbar now + // + if (scrollwidth > butwidth*2) + { + DCCanvas canvas(hdc); + if (SWS_USEFREEFORM & scrollFlags) + { + CopyRect(&ctrl, rect); + RotateRect0(sb, &ctrl); + RenderBaseTexture(&canvas, &ctrl, hwnd); + } + + //LEFT ARROW + SetRect(&ctrl, rect->left, rect->top, rect->left + butwidth, rect->bottom); + + RotateRect0(sb, &ctrl); + + DrawScrollArrow(sb, hdc, &ctrl, uLeftButFlags, fMouseDownL, fMouseOverL, (SWS_USEFREEFORM & scrollFlags)); + + RotateRect0(sb, &ctrl); + + //MIDDLE PORTION + //if we can fit the thumbbar in, then draw it + if (thumbwidth > 0 && thumbwidth <= workingwidth + && IsScrollInfoActive(si) && ((sb->fScrollFlags & ESB_DISABLE_BOTH) != ESB_DISABLE_BOTH)) + { + SkinBitmap *bg = (scrollBitmaps && (SWS_USEFREEFORM & scrollFlags)) ? + ((sb->nBarType== SB_VERT) ? scrollBitmaps->v_mid.getBitmap(): scrollBitmaps->h_mid.getBitmap()) + : 0; + + if ((SWS_USEFREEFORM & scrollFlags) && bg && !bg->isInvalid()) + { + SetRect(&sbm, rect->left + butwidth, rect->top, rect->right-butwidth, rect->bottom); + RotateRect0(sb, &sbm); + + bg->stretchToRectAlpha(&canvas, &sbm); + + SkinBitmap thumbBitmap(GetThumbID(sb->nBarType, sb->fScrollFlags, fMouseOverThumb)); + SetRect(&sbm, thumbpos, rect->top, thumbpos+thumbwidth, rect->bottom); + RotateRect0(sb, &sbm); + if (!thumbBitmap.isInvalid()) + thumbBitmap.stretchToRectAlpha(&canvas, &sbm); + else + DrawBlankButton(hdc, &sbm, uDEFlat, (CSBS_TRACKING & sb->fScrollFlags), sb->nBarType == SB_VERT); + } + else + { + //Draw the scrollbar margin above the thumb + SetRect(&sbm, rect->left + butwidth, rect->top, thumbpos, rect->bottom); + + RotateRect0(sb, &sbm); + + if (HTSCROLL_PAGELEFT == uDrawFlags) + { + SetTextColor(hdc, crInverse1); + SetBkColor(hdc, crInverse2); + } + + if (GetTextColor(hdc) == GetBkColor(hdc)) ExtTextOutW(hdc, 0, 0, ETO_OPAQUE, &sbm, NULL, 0, NULL); + else + { + if (NULL == hbrOld) hbrOld = SetWindowPatternBrush(hwnd, hdc, sb->nBarType); + PatBlt(hdc, sbm.left, sbm.top, sbm.right - sbm.left, sbm.bottom - sbm.top, PATCOPY); + } + + if (HTSCROLL_PAGELEFT == uDrawFlags) + { + SetTextColor(hdc, crCheck1); + SetBkColor(hdc, crCheck2); + } + + RotateRect0(sb, &sbm); + + //Draw the margin below the thumb + sbm.left = thumbpos + thumbwidth; + sbm.right = rect->right - butwidth; + + RotateRect0(sb, &sbm); + + if (HTSCROLL_PAGERIGHT == uDrawFlags) + { + SetTextColor(hdc, crInverse1); + SetBkColor(hdc, crInverse2); + } + + if (GetTextColor(hdc) == GetBkColor(hdc)) ExtTextOutW(hdc, 0, 0, ETO_OPAQUE, &sbm, NULL, 0, NULL); + else + { + if (NULL == hbrOld) hbrOld = SetWindowPatternBrush(hwnd, hdc, sb->nBarType); + PatBlt(hdc, sbm.left, sbm.top, sbm.right - sbm.left, sbm.bottom - sbm.top, PATCOPY); + } + + if (HTSCROLL_PAGERIGHT == uDrawFlags) + { + SetTextColor(hdc, crCheck1); + SetBkColor(hdc, crCheck2); + } + + RotateRect0(sb, &sbm); + + //Draw the THUMB finally + SetRect(&thumb, thumbpos, rect->top, thumbpos + thumbwidth, rect->bottom); + + RotateRect0(sb, &thumb); + + DrawBlankButton(hdc, &thumb, uDEFlat, (CSBS_TRACKING & sb->fScrollFlags), sb->nBarType == SB_VERT); + RotateRect0(sb, &thumb); + } + + } + //otherwise, just leave that whole area blank + else + { + OffsetRect(&ctrl, butwidth, 0); + ctrl.right = rect->right - butwidth; + + //if we always show the thumb covering the whole scrollbar, + //then draw it that way + if (!IsScrollInfoActive(si) && (sb->fScrollFlags & CSBS_THUMBALWAYS) + && ctrl.right - ctrl.left > sb->nMinThumbSize) + { + //leave a 1-pixel gap between the thumb + right button + ctrl.right --; + RotateRect0(sb, &ctrl); + + DrawBlankButton(hdc, &ctrl, uDEFlat, 0, sb->nBarType == SB_VERT); + RotateRect0(sb, &ctrl); + + //draw the single-line gap + ctrl.left = ctrl.right; + ctrl.right += 1; + + RotateRect0(sb, &ctrl); + + PaintRect(hdc, &ctrl, GetSysColor(COLOR_SCROLLBAR)); + + RotateRect0(sb, &ctrl); + } + //otherwise, paint a blank if the thumb doesn't fit in + else + { + RotateRect0(sb, &ctrl); + BOOL classic(TRUE); + if (SWS_USEFREEFORM & scrollFlags) + { + SkinBitmap background(sb->nBarType== SB_VERT?L"wasabi.scrollbar.vertical.background.middle":L"wasabi.scrollbar.horizontal.background.middle"); + if (!background.isInvalid()) + { + background.stretchToRectAlpha(&canvas, &ctrl, + ((DFCS_INACTIVE & uLeftButFlags) && (DFCS_INACTIVE & uRightButFlags)) ? INACTIVEBAR_ALPHA : 255); + classic = FALSE; + } + } + + if (classic) + { + if (crCheck1 == crCheck2) ExtTextOutW(hdc, 0, 0, ETO_OPAQUE, &ctrl, NULL, 0, NULL); + else + { + if (NULL == hbrOld) hbrOld = SetWindowPatternBrush(hwnd, hdc, sb->nBarType); + PatBlt(hdc, ctrl.left, ctrl.top, ctrl.right - ctrl.left, ctrl.bottom - ctrl.top, PATCOPY); + } + } + + + RotateRect0(sb, &ctrl); + } + } + + //RIGHT ARROW + SetRect(&ctrl, rect->right - butwidth, rect->top, rect->right, rect->bottom); + + RotateRect0(sb, &ctrl); + + DrawScrollArrow(sb, hdc, &ctrl, uRightButFlags, fMouseDownR, fMouseOverR, (SWS_USEFREEFORM & scrollFlags)); + + RotateRect0(sb, &ctrl); + } + //not enough room for the scrollbar, so just draw the buttons (scaled in size to fit) + else + { + butwidth = scrollwidth / 2; + + //LEFT ARROW + SetRect(&ctrl, rect->left, rect->top, rect->left + butwidth, rect->bottom); + + RotateRect0(sb, &ctrl); + DrawScrollArrow(sb, hdc, &ctrl, uLeftButFlags, fMouseDownL, fMouseOverL, (SWS_USEFREEFORM & scrollFlags)); + RotateRect0(sb, &ctrl); + + //RIGHT ARROW + OffsetRect(&ctrl, scrollwidth - butwidth, 0); + + RotateRect0(sb, &ctrl); + DrawScrollArrow(sb, hdc, &ctrl, uRightButFlags, fMouseDownR, fMouseOverR, (SWS_USEFREEFORM & scrollFlags)); + RotateRect0(sb, &ctrl); + + //if there is a gap between the buttons, fill it with a solid color + //if(butwidth & 0x0001) + if (ctrl.left != rect->left + butwidth) + { + ctrl.left --; + ctrl.right -= butwidth; + RotateRect0(sb, &ctrl); + BOOL classic(TRUE); + + if (SWS_USEFREEFORM & scrollFlags) + { + SkinBitmap background(sb->nBarType== SB_VERT?L"wasabi.scrollbar.vertical.background.middle":L"wasabi.scrollbar.horizontal.background.middle"); + if (!background.isInvalid()) + { + DCCanvas canvas(hdc); + RenderBaseTexture(&canvas, &ctrl, hwnd); + background.stretchToRectAlpha(&canvas, &ctrl, + ((DFCS_INACTIVE & uLeftButFlags) && (DFCS_INACTIVE & uRightButFlags)) ? INACTIVEBAR_ALPHA : 255); + classic = FALSE; + } + } + + if (classic) + { + if (crCheck1 == crCheck2) ExtTextOutW(hdc, 0, 0, ETO_OPAQUE, &ctrl, NULL, 0, NULL); + else + { + if (NULL == hbrOld) hbrOld = SetWindowPatternBrush(hwnd, hdc, sb->nBarType); + PatBlt(hdc, ctrl.left, ctrl.top, ctrl.right - ctrl.left, ctrl.bottom - ctrl.top, PATCOPY); + } + } + + RotateRect0(sb, &ctrl); + } + + } + + SetBkColor(hdc, rgbBkOld); + SetTextColor(hdc, rgbFgOld); + if (hbrOld) + { + SelectObject(hdc, hbrOld); + SetBrushOrgEx(hdc, 0, 0, NULL); + } + return 0; +} + +// +// Draw a vertical scrollbar using the horizontal draw routine, but +// with the coordinates adjusted accordingly +// +static LRESULT NCDrawVScrollbar(SCROLLBAR *sb, HWND hwnd, HDC hdc, const RECT *rect, UINT uDrawFlags, UINT hoverFlags, DWORD scrollFlags) +{ + LRESULT ret; + RECT rc; + + rc = *rect; + RotateRect(&rc); + ret = NCDrawHScrollbar(sb, hwnd, hdc, &rc, uDrawFlags, hoverFlags, scrollFlags); + RotateRect(&rc); + + return ret; +} + +// +// Generic wrapper function for the scrollbar drawing +// +static LRESULT NCDrawScrollbar(SCROLLBAR *sb, HWND hwnd, HDC hdc, const RECT *rect, UINT uDrawFlags, UINT hoverFlags, DWORD scrollFlags) +{ + if (sb->nBarType == SB_HORZ) + return NCDrawHScrollbar(sb, hwnd, hdc, rect, uDrawFlags, hoverFlags, scrollFlags); + else + return NCDrawVScrollbar(sb, hwnd, hdc, rect, uDrawFlags, hoverFlags, scrollFlags); +} + + + +void SkinnedScrollWnd::PaintNonClient(HDC hdc) +{ + RECT winrect, rcH, rcV; + BOOL drawH = FALSE, drawV = FALSE; + + if (!psbHorz->fScrollVisible && !psbVert->fScrollVisible) + { + DrawBorder(hdc); + return; + } + + if (0 == (SWS_UPDATEFRAME & scrollFlags)) + { + HWND hwndActive; + hwndActive = GetActiveWindow(); + if (hwndActive != hwnd && !IsChild(hwndActive, hwnd)) scrollFlags |= SWS_UPDATEFRAME; + } + + GetWindowRect(hwnd, &winrect); + + + if (psbHorz->fScrollVisible) + { + GetHScrollRect(this, &rcH); + OffsetRect(&rcH, -winrect.left, -winrect.top); + if (rcH.right > rcH.left && rcH.bottom > rcH.top && RectVisible(hdc, &rcH)) drawH = TRUE; + } + + if (psbVert->fScrollVisible) + { + GetVScrollRect(this, &rcV); + OffsetRect(&rcV, -winrect.left, -winrect.top); + + if (rcV.right > rcV.left && rcV.bottom > rcV.top && RectVisible(hdc, &rcV)) drawV = TRUE; + } + DrawBorder(hdc); + + POINT ptOrg; + GetViewportOrgEx(hdc, &ptOrg); + + if (drawH) + { + UINT fDraw, fHover; + if (uCurrentScrollbar == SB_HORZ) { fDraw = uScrollTimerPortion; fHover = HTSCROLL_NONE; } + else + { + fDraw = HTSCROLL_NONE; + fHover = (NULL != psbHorz && 0 != (CSBS_HOVERING & psbHorz->fScrollFlags)) ? scrollPortionHover : HTSCROLL_NONE; + } + + if (SWS_USEFREEFORM & scrollFlags) + { + DCBltCanvas buffer; + buffer.cloneDC(hdc, &rcH); + NCDrawHScrollbar(psbHorz, hwnd, buffer.getHDC(), &rcH, fDraw, fHover, scrollFlags); + } + else + { + SetViewportOrgEx(hdc, ptOrg.x + rcH.left, ptOrg.y + rcH.top, NULL); + OffsetRect(&rcH, -rcH.left, -rcH.top); + NCDrawHScrollbar(psbHorz, hwnd, hdc, &rcH, fDraw, fHover, scrollFlags); + SetViewportOrgEx(hdc, ptOrg.x, ptOrg.y, NULL); + } + } + + if (drawV) + { + UINT fDraw, fHover; + if (uCurrentScrollbar == SB_VERT) { fDraw = uScrollTimerPortion; fHover = HTSCROLL_NONE; } + else + { + fDraw = HTSCROLL_NONE; + fHover = (NULL != psbVert && 0 != (CSBS_HOVERING & psbVert->fScrollFlags)) ? scrollPortionHover : HTSCROLL_NONE; + } + + if (SWS_USEFREEFORM & scrollFlags) + { + DCBltCanvas buffer; + buffer.cloneDC(hdc, &rcV); + NCDrawVScrollbar(psbVert, hwnd, buffer.getHDC(), &rcV, fDraw, fHover, scrollFlags); + + } + else + { + SetViewportOrgEx(hdc, ptOrg.x + rcV.left, ptOrg.y + rcV.top, NULL); + OffsetRect(&rcV, -rcV.left, -rcV.top); + NCDrawVScrollbar(psbVert, hwnd, hdc, &rcV, fDraw, fHover, scrollFlags); + SetViewportOrgEx(hdc, ptOrg.x, ptOrg.y, NULL); + } + + } + + SetViewportOrgEx(hdc, ptOrg.x, ptOrg.y, NULL); + + // DRAW THE DEAD AREA + // only do this if the horizontal and vertical bars are visible + if (psbHorz->fScrollVisible && psbVert->fScrollVisible) + { + GetClientRect(hwnd, &rcH); + MapWindowPoints(hwnd, HWND_DESKTOP, (POINT*)&rcH, 2); + OffsetRect(&rcH, -winrect.left, -winrect.top); + + rcH.top = rcH.bottom; + rcH.bottom += GetScrollMetric(psbHorz, SM_CYHORZSB, scrollFlags); + + if (SWS_LEFT & scrollFlags) + { + rcH.right = rcH.left; + rcH.left -= GetScrollMetric(psbVert, SM_CXVERTSB, scrollFlags); + } + else + { + rcH.left = rcH.right; + rcH.right += GetScrollMetric(psbVert, SM_CXVERTSB, scrollFlags); + } + + if (RectVisible(hdc, &rcH)) + { + + PaintRect(hdc, &rcH, WADlg_getColor(WADLG_SCROLLBAR_DEADAREA_COLOR)); + } + } + + +} + +void SkinnedScrollWnd::OnNcPaint(HRGN rgnUpdate) +{ + UINT flags = DCX_PARENTCLIP | DCX_CACHE | DCX_WINDOW | DCX_CLIPSIBLINGS | + DCX_INTERSECTUPDATE | DCX_VALIDATE; + + HDC hdc = GetDCEx(hwnd, ((HRGN)NULLREGION != rgnUpdate) ? rgnUpdate : NULL, flags); + + + if (NULL == hdc) + { + return; + } + + + PaintNonClient(hdc); + + + ReleaseDC(hwnd, hdc); +} + +// +// Need to detect if we have clicked in the scrollbar region or not +// +INT SkinnedScrollWnd::OnNcHitTest(POINTS pts) +{ + RECT rc; + + INT r = __super::OnNcHitTest(pts); + if (r == HTTRANSPARENT) + { + + + return r; + } + + if (psbHorz->fScrollVisible && GetHScrollRect(this, &rc) && + pts.x >= rc.left && pts.x <= rc.right && pts.y >= rc.top && pts.y <= rc.bottom) return HTHSCROLL; + if (psbVert->fScrollVisible && GetVScrollRect(this, &rc) && + pts.x >= rc.left && pts.x <= rc.right && pts.y >= rc.top && pts.y <= rc.bottom) return HTVSCROLL; + + return r; +} + +// +// Return a HT* value indicating what part of the scrollbar was clicked +// Rectangle is not adjusted +// +static UINT GetHorzPortion(SCROLLBAR *sb, HWND hwnd, RECT *rect, int x, int y, DWORD scrollFlags) +{ + RECT rc = *rect; + + if (y < rc.top || y >= rc.bottom) return HTSCROLL_NONE; + + //Now we have the rectangle for the scrollbar itself, so work out + //what part we clicked on. + return GetHorzScrollPortion(sb, hwnd, &rc, x, y, scrollFlags); +} + +// +// Just call the horizontal version, with adjusted coordinates +// +static UINT GetVertPortion(SCROLLBAR *sb, HWND hwnd, RECT *rect, int x, int y, DWORD scrollFlags) +{ + UINT ret; + RotateRect(rect); + ret = GetHorzPortion(sb, hwnd, rect, y, x, scrollFlags); + RotateRect(rect); + return ret; +} + +// +// Wrapper function for GetHorzPortion and GetVertPortion +// +static UINT GetPortion(SCROLLBAR *sb, HWND hwnd, RECT *rect, int x, int y, DWORD scrollFlags) +{ + if (sb->nBarType == SB_HORZ) return GetHorzPortion(sb, hwnd, rect, x, y, scrollFlags); + else if (sb->nBarType == SB_VERT) return GetVertPortion(sb, hwnd, rect, x, y, scrollFlags); + return HTSCROLL_NONE; +} + +// +// Input: rectangle of the total scrollbar area +// Output: adjusted to take the inserted buttons into account +// +static void GetRealHorzScrollRect(SCROLLBAR *sb, RECT *rect) +{ + if (CSBS_BTNVISBEFORE & sb->fScrollFlags) rect->left += sb->nButSizeBefore; + if (CSBS_BTNVISAFTER & sb->fScrollFlags) rect->right -= sb->nButSizeAfter; +} + +// +// Input: rectangle of the total scrollbar area +// Output: adjusted to take the inserted buttons into account +// +static void GetRealVertScrollRect(SCROLLBAR *sb, RECT *rect) +{ + if (CSBS_BTNVISBEFORE & sb->fScrollFlags) rect->top += sb->nButSizeBefore; + if (CSBS_BTNVISAFTER & sb->fScrollFlags) rect->bottom -= sb->nButSizeAfter; +} + +// +// Decide which type of scrollbar we have before calling +// the real function to do the job +// +static void GetRealScrollRect(SCROLLBAR *sb, RECT *rect, DWORD scrollFlags) +{ + if (sb->nBarType == SB_HORZ) + { + GetRealHorzScrollRect(sb, rect); + } + else if (sb->nBarType == SB_VERT) + { + GetRealVertScrollRect(sb, rect); + } +} + +// +// Left button click in the non-client area +// +void SkinnedScrollWnd::OnNcLButtonDown(UINT nHitTest, POINTS pts) +{ + RECT rect, winrect; + SCROLLBAR *psb; + + hwndCurCoolSB = hwnd; + + // + // HORIZONTAL SCROLLBAR PROCESSING + // + if (HTHSCROLL == nHitTest) + { + psb = psbHorz; + uScrollTimerMsg = WM_HSCROLL; + uCurrentScrollbar = SB_HORZ; + //get the total area of the normal Horz scrollbar area + GetHScrollRect(this, &rect); + uCurrentScrollPortion = GetHorzPortion(psbHorz, hwnd, &rect, pts.x, pts.y, scrollFlags); + } + // + // VERTICAL SCROLLBAR PROCESSING + // + else if (HTVSCROLL== nHitTest) + { + psb = psbVert; + uScrollTimerMsg = WM_VSCROLL; + uCurrentScrollbar = SB_VERT; + //get the total area of the normal Horz scrollbar area + GetVScrollRect(this, &rect); + uCurrentScrollPortion = GetVertPortion(psbVert, hwnd, &rect, pts.x, pts.y, scrollFlags); + } + // + // NORMAL PROCESSING + // + else + { + uCurrentScrollPortion = HTSCROLL_NONE; + __super::WindowProc(WM_NCLBUTTONDOWN, (WPARAM)nHitTest, *(LPARAM*)&pts); + return; + } + + // + // we can now share the same code for vertical + // and horizontal scrollbars + // + switch (uCurrentScrollPortion) + { + //inserted buttons to the left/right + case HTSCROLL_THUMB: + + //if the scrollbar is disabled, then do no further processing + if (!IsScrollbarActive(psb)) return; + + GetRealScrollRect(psb, &rect, scrollFlags); + RotateRect0(psb, &rect); + CalcThumbSize(psb, &rect, &nThumbSize, &nThumbPos, scrollFlags); + RotateRect0(psb, &rect); + + //remember the bounding rectangle of the scrollbar work area + rcThumbBounds = rect; + + trackThumbPos=-1; + psb->fScrollFlags |= CSBS_TRACKING; + psb->scrollInfo.nTrackPos = psb->scrollInfo.nPos; + + if (nHitTest == HTVSCROLL) nThumbMouseOffset = pts.y - nThumbPos; + else nThumbMouseOffset = pts.x - nThumbPos; + + nLastPos = psb->scrollInfo.nPos; + nThumbPos0 = nThumbPos; + + SCROLLINFO info; + info.cbSize = sizeof(SCROLLINFO); + info.fMask = SIF_POS; + info.nPos = nLastPos; + + SetScrollInfo(hwnd, psb->nBarType, &info, FALSE); + + SendScrollMessage(hwnd, uScrollTimerMsg, SB_THUMBTRACK, nLastPos); + //if(sb->fFlatScrollbar) + //{ + GetWindowRect(hwnd, &winrect); + OffsetRect(&rect, -winrect.left, -winrect.top); + InvalidateNC(InvalidateFlag_Normal, uCurrentScrollbar); + //} + + break; + + //Any part of the scrollbar + case HTSCROLL_LEFT: + if (psb->fScrollFlags & ESB_DISABLE_LEFT) return; + goto target1; + + case HTSCROLL_RIGHT: + if (psb->fScrollFlags & ESB_DISABLE_RIGHT) return; + goto target1; + + + case HTSCROLL_PAGELEFT: + case HTSCROLL_PAGERIGHT: + +target1: + + //if the scrollbar is disabled, then do no further processing + if (!IsScrollbarActive(psb)) + break; + + //ajust the horizontal rectangle to NOT include + //any inserted buttons + GetRealScrollRect(psb, &rect, scrollFlags); + + SendScrollMessage(hwnd, uScrollTimerMsg, uCurrentScrollPortion, 0); + + // Check what area the mouse is now over : + // If the scroll thumb has moved under the mouse in response to + // a call to SetScrollPos etc, then we don't hilight the scrollbar margin + if (uCurrentScrollbar == SB_HORZ) + uScrollTimerPortion = GetHorzScrollPortion(psb, hwnd, &rect, pts.x, pts.y, scrollFlags); + else + uScrollTimerPortion = GetVertScrollPortion(psb, hwnd, &rect, pts.x, pts.y, scrollFlags); + + GetWindowRect(hwnd, &winrect); + OffsetRect(&rect, -winrect.left, -winrect.top); + + + //if we aren't hot-tracking, then don't highlight + //the scrollbar thumb unless we click on it + if (uScrollTimerPortion == HTSCROLL_THUMB) uScrollTimerPortion = HTSCROLL_NONE; + + InvalidateNC(InvalidateFlag_Normal, uCurrentScrollbar); + + //Post the scroll message!!!! + uScrollTimerPortion = uCurrentScrollPortion; + + //set a timer going on the first click. + //if this one expires, then we can start off a more regular timer + //to generate the auto-scroll behaviour + uScrollTimerId = SetTimer(hwnd, COOLSB_TIMERID1, COOLSB_TIMERINTERVAL1, 0); + UpdateScrollBars(FALSE); + break; + default: + __super::WindowProc(WM_NCLBUTTONDOWN, (WPARAM)nHitTest, *(LPARAM*)&pts); + return; + + } + + if ((0 == (SWS_COMBOLBOX & scrollFlags)) && hwnd != GetCapture()) + { + ignoreCaptureChange = TRUE; + SetCapture(hwnd); + ignoreCaptureChange = FALSE; + captureSet = TRUE; + + } +} + +// +// Left button released +// +void SkinnedScrollWnd::Emulate_LeftButtonUp(UINT nFlags, POINTS pts, BOOL forwardMessage) +{ + //current scrollportion is the button that we clicked down on + if (uCurrentScrollPortion != HTSCROLL_NONE) + { + RECT rect; + //UINT thisportion; + POINT pt; + RECT winrect; + + SCROLLBAR *psb; + + if (captureSet && (0 == (SWS_COMBOLBOX & scrollFlags)) && hwnd == GetCapture()) + { + ignoreCaptureChange = TRUE; + ReleaseCapture(); + ignoreCaptureChange = FALSE; + } + captureSet = FALSE; + + GetWindowRect(hwnd, &winrect); + POINTSTOPOINT(pt, pts); + + //emulate the mouse input on a scrollbar here... + if (SB_VERT == uCurrentScrollbar) + { + //get the total area of the normal Horz scrollbar area + psb = psbVert; + GetVScrollRect(this, &rect); + } + else + { + //get the total area of the normal Horz scrollbar area + psb = psbHorz; + GetHScrollRect(this, &rect); + } + + //we need to do different things depending on if the + //user is activating the scrollbar itself, or one of + //the inserted buttons + switch (uCurrentScrollPortion) + { + //The scrollbar is active + case HTSCROLL_LEFT: + case HTSCROLL_RIGHT: + case HTSCROLL_PAGELEFT: + case HTSCROLL_PAGERIGHT: + case HTSCROLL_NONE: + KillTimer(hwnd, uScrollTimerId); + case HTSCROLL_THUMB: + UpdateScrollBars(FALSE); + + //In case we were thumb tracking, make sure we stop NOW + if (CSBS_TRACKING & psb->fScrollFlags) + { + SCROLLINFO info; + info.cbSize = sizeof(SCROLLINFO); + info.fMask = SIF_POS; + info.nPos = nLastPos; + + SetScrollInfo(hwnd, psb->nBarType, &info, FALSE); + SendScrollMessage(hwnd, uScrollTimerMsg, SB_THUMBPOSITION, nLastPos); + psb->fScrollFlags &= ~CSBS_TRACKING; + } + + //send the SB_ENDSCROLL message now that scrolling has finished + SendScrollMessage(hwnd, uScrollTimerMsg, SB_ENDSCROLL, 0); + + //adjust the total scroll area to become where the scrollbar + //really is (take into account the inserted buttons) + GetRealScrollRect(psb, &rect, scrollFlags); + OffsetRect(&rect, -winrect.left, -winrect.top); + InvalidateNC(InvalidateFlag_Normal, uCurrentScrollbar); + break; + } + + //reset our state to default + uCurrentScrollPortion = HTSCROLL_NONE; + uScrollTimerPortion = HTSCROLL_NONE; + uScrollTimerId = 0; + + uScrollTimerMsg = 0; + uCurrentScrollbar = COOLSB_NONE; + + return; + } + else + { + + /* + // Can't remember why I did this! + if(GetCapture() == hwnd) + { + ReleaseCapture(); + }*/ + } + + //sw->update(); + + if (FALSE != forwardMessage) + { + __super::WindowProc(WM_LBUTTONUP, (WPARAM)nFlags, *(LPARAM*)&pts); + } +} +void SkinnedScrollWnd::OnLButtonUp(UINT nFlags, POINTS pts) +{ + Emulate_LeftButtonUp(nFlags, pts, TRUE); +} + +static int +ListView_ScrollWindow(HWND hwnd, int dy) +{ + RECT rect; + + if (0 == dy) + return NULLREGION; + + if (FALSE == GetClientRect(hwnd, &rect)) + return ERROR; + + if (0 == (LVS_NOCOLUMNHEADER & GetWindowLongPtrW(hwnd, GWL_STYLE))) + { + HWND headerWindow; + headerWindow = (HWND)SendMessageW(hwnd, LVM_GETHEADER, 0, 0L); + if (NULL != headerWindow && + 0 != (WS_VISIBLE & GetWindowLongPtrW(headerWindow, GWL_STYLE))) + { + HDLAYOUT headerLayout; + WINDOWPOS headerPos; + + headerLayout.prc = ▭ + headerLayout.pwpos = &headerPos; + SendMessageW(headerWindow, HDM_LAYOUT, 0, (LPARAM)&headerLayout); + } + } + + return ScrollWindowEx(hwnd, 0, dy, &rect, &rect, NULL, NULL, SW_INVALIDATE); +} + +static BOOL ListView_ScrollReportModeVert(HWND hwnd, INT linesVert, BOOL horzBarHidden) +{ + int max, pos, page; + int itemHeight, prevPos, dy; + RECT rect; + unsigned long windowStyle; + + if (0 == linesVert) + return TRUE; + + windowStyle = GetWindowLongPtrW(hwnd, GWL_STYLE); + + pos = (int)SendMessageW(hwnd, LVM_GETTOPINDEX, 0, 0L); + max = (int)SendMessageW(hwnd, LVM_GETITEMCOUNT, 0, 0L); + page = (int)SendMessageW(hwnd, LVM_GETCOUNTPERPAGE, 0, 0L); + + if (FALSE == horzBarHidden) + max++; + + if ((linesVert < 0 && pos <= 0) || + (linesVert > 0 && (pos + page) >= max)) + { + return TRUE; + } + + if (linesVert < 0 && (pos + linesVert) < 0) + linesVert = -pos; + else if (linesVert > 0 && (pos + page + linesVert) > max) + linesVert = max - (page + pos); + + rect.left = LVIR_BOUNDS; + if (!SendMessageW(hwnd, LVM_GETITEMRECT, 0, (LPARAM)&rect)) + return FALSE; + + if (rect.top < 0) + OffsetRect(&rect, 0, -rect.top); + + itemHeight = rect.bottom - rect.top; + + if (0 != (WS_VISIBLE & windowStyle)) + SetWindowLongPtrW(hwnd, GWL_STYLE, windowStyle & ~WS_VISIBLE); + + dy = linesVert * itemHeight; + SendMessageW(hwnd, LVM_SCROLL, 0, dy); + + if (0 == (WS_VISIBLE & windowStyle)) + return TRUE; + + SetWindowLongPtrW(hwnd, GWL_STYLE, windowStyle); + + prevPos = pos; + pos = (int)SendMessageW(hwnd, LVM_GETTOPINDEX, 0, 0L); + linesVert = pos - prevPos; + + dy = linesVert * itemHeight; + + if (ERROR == ListView_ScrollWindow(hwnd, -dy)) + InvalidateRect(hwnd, NULL, FALSE); + + return TRUE; +} + +static BOOL +ListView_ScrollReportModeVertPx(HWND hwnd, int dy, BOOL horzBarHidden) +{ + int itemHeight, lines; + RECT rect; + unsigned long windowStyle; + + if (0 == dy) + return TRUE; + + windowStyle = GetWindowLongPtrW(hwnd, GWL_STYLE); + + rect.left = LVIR_BOUNDS; + if (!SendMessageW(hwnd, LVM_GETITEMRECT, 0, (LPARAM)&rect)) + return FALSE; + + if (rect.top < 0) + OffsetRect(&rect, 0, -rect.top); + + itemHeight = rect.bottom - rect.top; + + + lines = dy / itemHeight; + if (0 != lines) + { + if (0 != (WS_VISIBLE & windowStyle)) + SetWindowLongPtrW(hwnd, GWL_STYLE, windowStyle & ~WS_VISIBLE); + + SendMessageW(hwnd, LVM_SCROLL, 0, lines); + + if (0 != (WS_VISIBLE & windowStyle)) + { + windowStyle = GetWindowLongPtrW(hwnd, GWL_STYLE); + windowStyle |= WS_VISIBLE; + SetWindowLongPtrW(hwnd, GWL_STYLE, windowStyle); + } + } + + if (0 != (WS_VISIBLE & windowStyle)) + { + if (ERROR == ListView_ScrollWindow(hwnd, -dy)) + InvalidateRect(hwnd, NULL, FALSE); + } + + return TRUE; +} + +// +// This function is called whenever the mouse is moved and +// we are dragging the scrollbar thumb about. +// +static void ThumbTrack(SCROLLBAR *sbar, HWND hwnd, POINTS pts, UINT scrollFlags) +{ + POINT pt; + RECT rc, winrect, rc2; + int thumbpos = nThumbPos; + //int thumbDelta; + int pos; + int siMaxMin = 0; + //UINT flatflag = (CSBS_FLATSB & sbar->fScrollFlags) ? BF_FLAT : 0; + + SCROLLINFO *si; + si = &sbar->scrollInfo; + + POINTSTOPOINT(pt, pts); + MapWindowPoints(hwnd, HWND_DESKTOP, &pt, 1); + + if (SB_VERT == sbar->nBarType) + { + LONG t; + t= pt.x; pt.x = pt.y; pt.y = t; + RotateRect(&rcThumbBounds); + } + + //draw the thumb at whatever position + rc = rcThumbBounds; + + SetRect(&rc2, rc.left - THUMBTRACK_SNAPDIST*2, rc.top - THUMBTRACK_SNAPDIST, + rc.right + THUMBTRACK_SNAPDIST*2, rc.bottom + THUMBTRACK_SNAPDIST); + + int cxH = GetScrollMetric(sbar, SM_CXHORZSB, scrollFlags); + + rc.left += cxH; + rc.right -= cxH; + + //if the mouse is not in a suitable distance of the scrollbar, + //then "snap" the thumb back to its initial position +#ifdef SNAP_THUMB_BACK + if (!PtInRect(&rc2, pt)) + { + thumbpos = nThumbPos0; + } + //otherwise, move the thumb to where the mouse is + else +#endif //SNAP_THUMB_BACK + { + //keep the thumb within the scrollbar limits + thumbpos = pt.x - nThumbMouseOffset; + if (thumbpos < rc.left) thumbpos = rc.left; + if (thumbpos > rc.right - nThumbSize) thumbpos = rc.right - nThumbSize; + } + + GetClientRect(hwnd, &winrect); + + MapWindowPoints(hwnd, HWND_DESKTOP, (POINT*)&winrect, 2); + + RotateRect0(sbar, &winrect); + + OffsetRect(&rc, -winrect.left, -winrect.top); + thumbpos -= winrect.left; + + /*if (-1 == trackThumbPos) + thumbDelta = thumbpos - rc.left; + else + thumbDelta = thumbpos - trackThumbPos;*/ + + trackThumbPos = thumbpos; + + //post a SB_TRACKPOS message!!! + siMaxMin = si->nMax - si->nMin; + pos = (siMaxMin > 0) ? MulDiv(thumbpos - rc.left, siMaxMin - si->nPage + 1, rc.right - rc.left - nThumbSize) : (thumbpos - rc.left); + + if (si->nPage == 0) + pos = 0; // this supposed to protect from moving on empty scrollbar + + if (pos != nLastPos) + { + if (SWS_LISTVIEW & scrollFlags) // list view specific + { + // only for listviews + if (sbar->nBarType == SB_HORZ) + { + SCROLLINFO info; + info.cbSize = sizeof(SCROLLINFO); + info.fMask = SIF_TRACKPOS; + if (GetScrollInfo(hwnd, SB_HORZ, &info)) + { + INT dx = pos - info.nTrackPos; + if (LVS_LIST == (LVS_TYPEMASK & GetWindowLongPtrW(hwnd, GWL_STYLE))) + { + INT cw = (INT)(INT_PTR)SendMessageW(hwnd, LVM_GETCOLUMNWIDTH, 0, 0L); + dx = dx * cw; + } + SendMessageW(hwnd, LVM_SCROLL, dx, 0); + } + } + else if (sbar->nBarType == SB_VERT) + { + SCROLLINFO info; + info.cbSize = sizeof(SCROLLINFO); + info.fMask = SIF_TRACKPOS; + if (GetScrollInfo(hwnd, SB_VERT, &info) && pos != info.nTrackPos) + { + INT dy = pos - info.nTrackPos; + if (LVS_REPORT == (LVS_TYPEMASK & GetWindowLongPtrW(hwnd, GWL_STYLE))) + { + ListView_ScrollReportModeVert(hwnd, dy, (0 != (SWS_HIDEHSCROLL & scrollFlags))); + } + else + { + SendMessageW(hwnd, LVM_SCROLL, 0, dy); + } + } + } + } + else if ((SWS_TREEVIEW & scrollFlags) && + SB_VERT == sbar->nBarType && + ABS(nLastPos - pos) < 2) + { + INT i, cmd; + i = nLastPos - pos; + cmd = (i < 0) ? SB_LINEDOWN : SB_LINEUP; + if (i < 0) i = -i; + while (i--) + { + SendMessageW(hwnd, WM_VSCROLL, cmd, 0L); + } + } + else + { + si->nTrackPos = pos; + SCROLLINFO info; + info.cbSize = sizeof(SCROLLINFO); + info.fMask = SIF_TRACKPOS|SIF_POS; + info.nTrackPos = pos; + info.nPos = pos; + + SetScrollInfo(hwnd, sbar->nBarType, &info, FALSE); + SendScrollMessage(hwnd, uScrollTimerMsg, SB_THUMBTRACK, pos); + } + } + + nLastPos = pos; + + if (SB_VERT == sbar->nBarType) RotateRect(&rcThumbBounds); +} + +// +// remember to rotate the thumb bounds rectangle!! +// + +// +// Called when we have set the capture from the NCLButtonDown(...) +// +void SkinnedScrollWnd::OnMouseMove(UINT nFlags, POINTS pts) +{ + RECT rect; + //static UINT lastbutton = 0; + RECT winrect; + //UINT buttonIdx = 0; + SCROLLBAR *psb; + + if (nFlags) + { + if (MK_LBUTTON & nFlags) + { + UpdateScrollBars(TRUE); + } + } + psb = (uCurrentScrollbar == SB_VERT) ? psbVert : psbHorz; + if (CSBS_TRACKING & psb->fScrollFlags) + { + ThumbTrack(psb, hwnd, pts, scrollFlags); + InvalidateNC(InvalidateFlag_Normal, uCurrentScrollbar); + return; + } + if (uCurrentScrollPortion == HTSCROLL_NONE) + { + __super::WindowProc(WM_MOUSEMOVE, (WPARAM)nFlags, *(LPARAM*)&pts); + return; + } + else + { + static UINT lastportion = 0; + POINT pt; + + POINTSTOPOINT(pt, pts); + + MapWindowPoints(hwnd, HWND_DESKTOP, &pt, 1); + + GetWindowRect(hwnd, &winrect); + + //get the total area of the normal scrollbar area + GetScrollRect(this, psb->nBarType, &rect); + + //see if we clicked in the inserted buttons / normal scrollbar + //thisportion = GetPortion(sb, hwnd, &rect, LOWORD(lParam), HIWORD(lParam)); + UINT thisportion = GetPortion(psb, hwnd, &rect, pt.x, pt.y, scrollFlags); + + //we need to do different things depending on if the + //user is activating the scrollbar itself, or one of + //the inserted buttons + switch (uCurrentScrollPortion) + { + //The scrollbar is active + case HTSCROLL_LEFT: + case HTSCROLL_RIGHT: + case HTSCROLL_THUMB: + case HTSCROLL_PAGELEFT: + case HTSCROLL_PAGERIGHT: + case HTSCROLL_NONE: + + //adjust the total scroll area to become where the scrollbar + //really is (take into account the inserted buttons) + GetRealScrollRect(psb, &rect, scrollFlags); + + OffsetRect(&rect, -winrect.left, -winrect.top); + + if (thisportion != uCurrentScrollPortion) + { + uScrollTimerPortion = HTSCROLL_NONE; + if (lastportion != thisportion) + { + InvalidateNC(InvalidateFlag_Normal, uCurrentScrollbar); + } + } + //otherwise, draw the button in its depressed / clicked state + else + { + uScrollTimerPortion = uCurrentScrollPortion; + if (lastportion != thisportion) + { + InvalidateNC(InvalidateFlag_Normal, uCurrentScrollbar); + } + } + break; + } + + lastportion = thisportion; + //lastbutton = buttonIdx; + + //must return zero here, because we might get cursor anomilies + //CallWindowProc(sw->oldproc, hwnd, WM_MOUSEMOVE, wParam, lParam); + + return; + } +} + +// +// We must allocate from in the non-client area for our scrollbars +// Call the default window procedure first, to get the borders (if any) +// allocated some space, then allocate the space for the scrollbars +// if they fit +// +INT SkinnedScrollWnd::OnNcCalcSize(BOOL bCalcValidRects, NCCALCSIZE_PARAMS *pncsp) +{ + RECT *prc; + INT hcy, vcx, result; + BOOL bSizingDown; + prc = &pncsp->rgrc[0]; + UINT updateBars = -1; + + hcy = GetScrollMetric(psbHorz, SM_CYHORZSB, scrollFlags); + vcx = GetScrollMetric(psbVert, SM_CXVERTSB, scrollFlags); + + + if (SWS_UPDATEFRAME & scrollFlags) + { + // need to reset style + DWORD style; + + scrollFlags &= ~SWS_UPDATEFRAME; + style = (DWORD)GetWindowLongPtrW(hwnd, GWL_STYLE); + if ((WS_HSCROLL | WS_VSCROLL) & style) SetWindowLongPtrW(hwnd, GWL_STYLE, style & ~(WS_HSCROLL | WS_VSCROLL)); + CallDefWndProc(WM_NCCALCSIZE, (WPARAM)bCalcValidRects, (LPARAM)pncsp); + if ((WS_HSCROLL | WS_VSCROLL) & style) SetWindowLongPtrW(hwnd, GWL_STYLE, style); + } + + result = __super::OnNcCalcSize(bCalcValidRects, pncsp); + bSizingDown = (bCalcValidRects && + ((pncsp->rgrc[0].right - pncsp->rgrc[0].left) < (pncsp->rgrc[1].right - pncsp->rgrc[1].left) || + (pncsp->rgrc[0].bottom - pncsp->rgrc[0].top) < (pncsp->rgrc[1].bottom - pncsp->rgrc[1].top))); + + + //if there is room, allocate some space for the horizontal scrollbar + //NOTE: Change the ">" to a ">=" to make the horz bar totally fill the + //window before disappearing + if ((psbHorz->fScrollFlags & CSBS_VISIBLE) && (prc->bottom - prc->top) +#ifdef COOLSB_FILLWINDOW + >= +#else + > +#endif + hcy) + { + prc->bottom -= hcy; + if (TRUE != psbHorz->fScrollVisible) + { + psbHorz->fScrollVisible = TRUE; + updateBars = SB_HORZ; + } + } + else + { + if (FALSE != psbHorz->fScrollVisible) + { + psbHorz->fScrollVisible = FALSE; + updateBars = SB_HORZ; + } + } + + //if there is room, allocate some space for the vertical scrollbar + if ((psbVert->fScrollFlags & CSBS_VISIBLE) && (prc->right - prc->left) >= vcx) + { + if (SWS_LEFT & scrollFlags) prc->left += vcx; + else prc->right -= vcx; + if (TRUE != psbVert->fScrollVisible) + { + psbVert->fScrollVisible = TRUE; + updateBars = (SB_HORZ == updateBars) ? SB_BOTH : SB_VERT; + } + + } + else + { + if (FALSE != psbVert->fScrollVisible) + { + psbVert->fScrollVisible = FALSE; + updateBars = (SB_HORZ == updateBars) ? SB_BOTH : SB_VERT; + } + } + + if (-1 != updateBars) + { + if (SWS_COMBOLBOX & scrollFlags) + { + InvalidateNC(InvalidateFlag_RedrawNow, updateBars); + } + else if (bSizingDown) + { + PostMessageW(hwnd, WM_ML_IPC, TRUE, IPC_ML_SKINNEDSCROLLWND_UPDATEBARS); + } + + } + + return result; +} + +void SkinnedScrollWnd::OnNcMouseLeave() +{ + if (HTSCROLL_NONE != scrollPortionHover) + { + scrollPortionHover=HTSCROLL_NONE; + InvalidateNC(InvalidateFlag_Normal, SB_BOTH); + } +} +// +// used for hot-tracking over the scroll buttons +// +void SkinnedScrollWnd::OnNcMouseMove(UINT nHitTest, POINTS pts) +{ + if (!bDoHover) + { + __super::WindowProc(WM_NCMOUSEMOVE, nHitTest, *(LPARAM*)&pts); + return; + } + SCROLLBAR *psb=0; + UINT scrollbar=0; + RECT rect; + + if (psbHorz) psbHorz->fScrollFlags &= ~CSBS_HOVERING; + if (psbVert) psbVert->fScrollFlags &= ~CSBS_HOVERING; + if (HTHSCROLL == nHitTest) + { + psb = psbHorz; + scrollbar = SB_HORZ; + //get the total area of the normal Horz scrollbar area + GetHScrollRect(this, &rect); + } + // + // VERTICAL SCROLLBAR PROCESSING + // + else if (HTVSCROLL== nHitTest) + { + psb = psbVert; + scrollbar = SB_VERT; + //get the total area of the normal Horz scrollbar area + GetVScrollRect(this, &rect); + } + // + // NORMAL PROCESSING + // + else + { + + scrollPortionHover=HTSCROLL_NONE; + __super::WindowProc(WM_NCMOUSEMOVE, nHitTest, *(LPARAM*)&pts); + + return; + } + + if (NULL != psb) + { + psb->fScrollFlags |= CSBS_HOVERING; + UINT thisportion = GetPortion(psb, hwnd, &rect, pts.x, pts.y, scrollFlags); + if (thisportion != scrollPortionHover) + { + TRACKMOUSEEVENT tracker; + tracker.cbSize = sizeof(tracker); + tracker.hwndTrack = hwnd; + tracker.dwHoverTime=0; + tracker.dwFlags = TME_LEAVE |TME_NONCLIENT; // benski> TME_NONCLIENT doesn't work on NT4.0, and we can work around it anyway + if (TrackMouseEvent(&tracker)) + { + scrollPortionHover=thisportion; + InvalidateNC(InvalidateFlag_Normal, scrollbar); + } + } + } + + __super::WindowProc(WM_NCMOUSEMOVE, nHitTest, *(LPARAM*)&pts); +} + +// +// Timer routine to generate scrollbar messages +// +void SkinnedScrollWnd::OnTimer(UINT_PTR idEvent, TIMERPROC fnTimer) +{ + //let all timer messages go past if we don't have a timer installed ourselves + if (uScrollTimerId != 0) + { + //if the first timer goes off, then we can start a more + //regular timer interval to auto-generate scroll messages + //this gives a slight pause between first pressing the scroll arrow, and the + //actual scroll starting + if (idEvent == COOLSB_TIMERID1) + { + KillTimer(hwnd, uScrollTimerId); + uScrollTimerId = SetTimer(hwnd, COOLSB_TIMERID2, COOLSB_TIMERINTERVAL2, 0); + return; + } + //send the scrollbar message repeatedly + else if (idEvent == COOLSB_TIMERID2) + { + //need to process a spoof WM_MOUSEMOVE, so that + //we know where the mouse is each time the scroll timer goes off. + //This is so we can stop sending scroll messages if the thumb moves + //under the mouse. + POINT pt; + GetCursorPos(&pt); + MapWindowPoints(HWND_DESKTOP, hwnd, &pt, 1); + pt.x = POINTTOPOINTS(pt); + OnMouseMove(MK_LBUTTON, MAKEPOINTS(pt.x)); + if (uScrollTimerPortion != HTSCROLL_NONE) SendScrollMessage(hwnd, uScrollTimerMsg, uScrollTimerPortion, 0); + UpdateScrollBars(TRUE); + return; + } + } + __super::WindowProc(WM_TIMER, (WPARAM)idEvent, (LPARAM)fnTimer); +} + +// +// We must intercept any calls to SetWindowLong, to check if +// left-scrollbars are taking effect or not +// + +static UINT curTool = -1; + +static LRESULT SendToolTipMessage0(HWND hwndTT, UINT message, WPARAM wParam, LPARAM lParam) +{ + return SendMessageW(hwndTT, message, wParam, lParam); +} + +#ifdef COOLSB_TOOLTIPS +#define SendToolTipMessage SendToolTipMessage0 +#else +#define SendToolTipMessage 1 ? (void)0 : SendToolTipMessage0 +#endif + +void SkinnedScrollWnd::OnStyleChanged(INT styleType, STYLESTRUCT *pss) +{ + if (styleType == GWL_EXSTYLE) scrollFlags = (scrollFlags & ~SWS_LEFT) | ((WS_EX_LEFTSCROLLBAR & pss->styleNew) ? SWS_LEFT : 0); + __super::OnStyleChanged(styleType, pss); +} + +LRESULT SkinnedScrollWnd::OnEraseBackground(HDC hdc) +{ + if (0 == (CSBS_TRACKING & psbVert->fScrollFlags) && 0 == (CSBS_TRACKING & psbHorz->fScrollFlags) && uCurrentScrollPortion == HTSCROLL_NONE) + { + LRESULT result; + result = __super::WindowProc(WM_ERASEBKGND, (WPARAM)hdc, 0L); + UpdateScrollBars(TRUE); + return result; + } + return __super::WindowProc(WM_ERASEBKGND, (WPARAM)hdc, 0L); +} + + +void SkinnedScrollWnd::OnPrint(HDC hdc, UINT options) +{ + if ((PRF_NONCLIENT & options) && + (0 == (PRF_CHECKVISIBLE & options) || IsWindowVisible(hwnd))) + { + PaintNonClient(hdc); + if (PRF_CLIENT & options) + CallPrevWndProc(WM_PRINT, (WPARAM)hdc, (LPARAM)(~(PRF_NONCLIENT | PRF_CHECKVISIBLE) & options)); + } + else __super::OnPrint(hdc, options); +} + +LRESULT SkinnedScrollWnd::OnListViewScroll(INT dx, INT dy) +{ + if (0 != dy && + 0 != (SWS_LISTVIEW & scrollFlags) && + 0 != (SWS_HIDEHSCROLL & scrollFlags) && + (psbHorz->scrollInfo.nPage <= (UINT)psbHorz->scrollInfo.nMax)) + { + DWORD windowStyle = GetWindowLongPtr(hwnd, GWL_STYLE); + if (LVS_REPORT == (LVS_TYPEMASK & windowStyle)) + { + SCROLLINFO scrollInfo; + scrollInfo.cbSize = sizeof(SCROLLINFO); + scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE; + if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo)) + { + if (0 == dy || + (scrollInfo.nPos == scrollInfo.nMin && dy < 0) || + (scrollInfo.nPos >= (scrollInfo.nMax - (INT)scrollInfo.nPage) && dy > 0)) + return TRUE; + + RECT rc; + rc.left = LVIR_BOUNDS; + if (SendMessageW(hwnd, LVM_GETITEMRECT, 0, (LPARAM)&rc)) + { + dy = dy / (rc.bottom - rc.top); + if (dy < 0) + { + if ((scrollInfo.nPos - dy) < scrollInfo.nMin) + dy = scrollInfo.nMin - scrollInfo.nPos; + } + else if (dy > 0) + { + if ((scrollInfo.nPos + dy) >= (scrollInfo.nMax - (INT)scrollInfo.nPage)) + dy = scrollInfo.nMax - (INT)scrollInfo.nPage - scrollInfo.nPos; + } + + dy = dy * (rc.bottom - rc.top); + + if (0 == dy) + return TRUE; + } + } + } + } + + LRESULT result = __super::WindowProc(LVM_SCROLL, (WPARAM)dx, (LPARAM)dy); + if (result) UpdateScrollBars(TRUE); + return result; +} + +void SkinnedScrollWnd::OnVertScroll(UINT code, UINT pos, HWND hwndSB) +{ + if (0 != (SWS_LISTVIEW & scrollFlags) && + 0 != (SWS_HIDEHSCROLL & scrollFlags) && + (psbHorz->scrollInfo.nPage <= (UINT)psbHorz->scrollInfo.nMax)) + { + DWORD windowStyle = GetWindowLongPtr(hwnd, GWL_STYLE); + if (LVS_REPORT == (LVS_TYPEMASK & windowStyle)) + { + SCROLLINFO scrollInfo; + scrollInfo.cbSize = sizeof(SCROLLINFO); + + switch(code) + { + case SB_LINEDOWN: + scrollInfo.fMask = SIF_RANGE | SIF_POS | SIF_PAGE; + if (GetScrollInfo(hwnd, SB_VERT, &scrollInfo) + && scrollInfo.nPos >= (scrollInfo.nMax - (INT)scrollInfo.nPage)) + return; + break; + } + } + } + __super::WindowProc(WM_VSCROLL, MAKEWPARAM(code, pos), (LPARAM)hwndSB); +} + + +void SkinnedScrollWnd::OnMouseWheel(INT delta, UINT vtKey, POINTS pts) +{ + if (0 == delta) + return; + + if (0 != (SWS_LISTVIEW & scrollFlags)) + { + DWORD windowStyle = GetWindowLongPtr(hwnd, GWL_STYLE); + if (LVS_REPORT == (LVS_TYPEMASK & windowStyle)) + { + if (0 != (WS_VSCROLL & windowStyle)) + { + unsigned int wheelScroll; + int scrollLines, distance; + + if (!SystemParametersInfo(SPI_GETWHEELSCROLLLINES, 0, &wheelScroll, 0)) + wheelScroll = 3; + + if (0 == wheelScroll) + return; + + if (WHEEL_PAGESCROLL == wheelScroll) + { + SendMessageW(hwnd, WM_VSCROLL, MAKEWPARAM(((delta > 0) ? SB_PAGEUP : SB_PAGEDOWN), 0), 0L); + SendMessageW(hwnd, WM_VSCROLL, MAKEWPARAM(SB_ENDSCROLL, 0), 0L); + return; + } + + distance = delta + wheelCarryover; + scrollLines = distance * (INT)wheelScroll / WHEEL_DELTA; + + wheelCarryover = distance - scrollLines * WHEEL_DELTA / (INT)wheelScroll; + + if (ListView_ScrollReportModeVert(hwnd, -scrollLines, (0 != (SWS_HIDEHSCROLL & scrollFlags)))) + { + InvalidateNC(InvalidateFlag_RedrawNow, SB_VERT); + return; + } + } + else if (0 != (SWS_HIDEHSCROLL & scrollFlags)) + { + return; + } + } + + } + __super::WindowProc(WM_MOUSEWHEEL, MAKEWPARAM(vtKey, delta), *(LPARAM*)&pts); +} + +void SkinnedScrollWnd::UpdateFrame() +{ + if (SWS_UPDATEFRAME & scrollFlags) + { + SetWindowPos(hwnd, NULL, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED | SWP_NOSENDCHANGING | SWP_NOREDRAW); + } +} + +void SkinnedScrollWnd::OnSkinChanged(BOOL bNotifyChildren, BOOL bRedraw) +{ + UINT newFlag = scrollFlags & ~SWS_USEFREEFORM; + if (UseFreeformScrollbars()) + newFlag |= SWS_USEFREEFORM; + + if (newFlag != scrollFlags) + { + RECT rcOld; + GetClientRect(hwnd, &rcOld); + + scrollFlags = newFlag; + SetWindowPos(hwnd, NULL, 0, 0, 0, 0, + SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_FRAMECHANGED | SWP_NOREDRAW); + + } + __super::OnSkinChanged(bNotifyChildren, bRedraw); + + if (FALSE != bRedraw) + RedrawWindow(hwnd, NULL, NULL, RDW_INVALIDATE |RDW_ERASE | RDW_FRAME); +} + +LRESULT SkinnedScrollWnd::WindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch (uMsg) + { + /*case SBM_GETSCROLLINFO: + { + SCROLLINFO *scrollInfo = (SCROLLINFO *)lParam; + int x=0; + x=0; + } + break;*/ + + case WM_NCMOUSEMOVE: OnNcMouseMove((UINT)wParam, MAKEPOINTS(lParam)); return 0; + case WM_NCRBUTTONDOWN: + case WM_NCRBUTTONUP: + case WM_NCMBUTTONDOWN: + case WM_NCMBUTTONUP: if (wParam == HTHSCROLL || wParam == HTVSCROLL) return 0; + break; + case WM_NCLBUTTONDBLCLK: if (wParam != HTHSCROLL && wParam != HTVSCROLL) break; // else fall to the nclbuttondown + case WM_NCLBUTTONDOWN: OnNcLButtonDown((UINT)wParam, MAKEPOINTS(lParam)); return 0; + case WM_NCMOUSELEAVE: + case WM_MOUSELEAVE: OnNcMouseLeave(); break; + case WM_LBUTTONUP: OnLButtonUp((UINT)wParam, MAKEPOINTS(lParam)); return 0; + case WM_MOUSEMOVE: OnMouseMove((UINT)wParam, MAKEPOINTS(lParam)); return 0; + case WM_TIMER: OnTimer((UINT_PTR)wParam, (TIMERPROC)lParam); return 0; + case WM_ERASEBKGND: return OnEraseBackground((HDC)wParam); + case WM_DISPLAYCHANGE: + SetWindowPos(hwnd, NULL, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED | SWP_NOSENDCHANGING); + break; + case WM_CAPTURECHANGED: + if (!ignoreCaptureChange) + { + LONG pts = GetMessagePos(); + POINT pt; + POINTSTOPOINT(pt, pts); + MapWindowPoints(HWND_DESKTOP, hwnd, &pt, 1); + pts = POINTTOPOINTS(pt); + Emulate_LeftButtonUp((UINT)wParam, MAKEPOINTS(pts), FALSE); + } + break; + // sometimes update frame required when this messges arrive + case WM_ACTIVATE: + case WM_SETFOCUS: + case WM_HSCROLL: + { + LRESULT result = __super::WindowProc(uMsg, wParam, lParam); + UpdateFrame(); + return result; + } + break; + case WM_MOUSEWHEEL: + OnMouseWheel(GET_WHEEL_DELTA_WPARAM(wParam), GET_KEYSTATE_WPARAM(wParam), MAKEPOINTS(lParam)); + UpdateFrame(); + return 0; + case WM_VSCROLL: + OnVertScroll(LOWORD(wParam), HIWORD(wParam), (HWND)lParam); + UpdateFrame(); + return 0; + case LVM_SCROLL: + if (SWS_LISTVIEW & scrollFlags) + return OnListViewScroll((INT)wParam, (INT)lParam); + break; + case WM_KEYDOWN: + if (0 != ((SWS_LISTVIEW | SWS_TREEVIEW ) & scrollFlags)) + { + LRESULT result = __super::WindowProc(uMsg, wParam, lParam); + switch(wParam) + { + case VK_PRIOR: + case VK_NEXT: + case VK_UP: + case VK_DOWN: + case VK_HOME: + case VK_END: + UpdateScrollBars(TRUE); + break; + } + return result; + } + break; + case WM_USER + 0x3443: + UpdateScrollBars(TRUE); + break; + } + return __super::WindowProc(uMsg, wParam, lParam); +} + +// +// return the default minimum size of a scrollbar thumb +// +static int WINAPI CoolSB_GetDefaultMinThumbSize(void) +{ + DWORD dwVersion = GetVersion(); + // set the minimum thumb size for a scrollbar. This + // differs between NT4 and 2000, so need to check to see + // which platform we are running under + return (dwVersion >= 0x80000000 && LOBYTE(LOWORD(dwVersion)) >= 5) ? MINTHUMBSIZE_2000 : MINTHUMBSIZE_NT4; +} + +BOOL SkinnedScrollWnd::ShowScrollBar(int wBar, BOOL fShow) +{ + DWORD styleOld, styleNew; + + styleOld = GetWindowLongPtrW(hwnd, GWL_STYLE); + styleNew = styleOld; + + if (wBar == SB_HORZ || wBar == SB_BOTH) + { + psbHorz->fScrollFlags = (psbHorz->fScrollFlags & ~CSBS_VISIBLE) | ((fShow) ? CSBS_VISIBLE : 0); + styleNew = (styleNew & ~WS_HSCROLL) | ((fShow) ? WS_HSCROLL :0); + } + + if (wBar == SB_VERT || wBar == SB_BOTH) + { + psbVert->fScrollFlags = (psbVert->fScrollFlags & ~CSBS_VISIBLE) | ((fShow) ? CSBS_VISIBLE : 0); + styleNew = (styleNew & ~WS_VSCROLL) | ((fShow) ? WS_VSCROLL :0); + } + + if (styleNew != styleOld) + { + SetWindowLongPtrW(hwnd, GWL_STYLE, styleNew); + SetWindowPos(hwnd, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED | SWP_NOREDRAW); + } + + return TRUE; +} + +SkinnedScrollWnd::SkinnedScrollWnd(BOOL bIsDialog) + : SkinnedWnd(bIsDialog), psbHorz(0), psbVert(0), + scrollFlags(0), scrollPortionHover(0), wheelCarryover(0) +{ + if (-1 == bUseUpdateRgn) + { + OSVERSIONINFO osver = {0}; + osver.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + if (::GetVersionEx(&osver)) + { + bUseUpdateRgn = + ((VER_PLATFORM_WIN32_NT != osver.dwPlatformId || + (osver.dwMajorVersion < 6 && osver.dwMinorVersion != 2))); + bDoHover = !(VER_PLATFORM_WIN32_NT == osver.dwPlatformId && osver.dwMajorVersion == 4); // can't use TrackMouseEvent with non-client areas on Windows NT 4.0 + } + } +} + +BOOL SkinnedScrollWnd::Attach(HWND hwndToSkin) +{ + DWORD style; + + if (!SkinnedWnd::Attach(hwndToSkin)) + return FALSE; + + SetType(SKINNEDWND_TYPE_SCROLLWND); + + psbHorz = (SCROLLBAR*)calloc(1, sizeof(SCROLLBAR)); + psbVert = (SCROLLBAR*)calloc(1, sizeof(SCROLLBAR)); + if (!psbHorz || !psbVert) return FALSE; + + scrollFlags = 0; + wheelCarryover = 0; + + psbHorz->scrollInfo.cbSize = sizeof(SCROLLINFO); + psbHorz->scrollInfo.fMask = SIF_ALL; + if (!GetScrollInfo(hwnd, SB_HORZ, &psbHorz->scrollInfo)) + ZeroMemory(&psbHorz->scrollInfo, sizeof(SCROLLINFO)); + + psbVert->scrollInfo.cbSize = sizeof(SCROLLINFO); + psbVert->scrollInfo.fMask = SIF_ALL; + if (!GetScrollInfo(hwnd, SB_VERT, &psbVert->scrollInfo)) + ZeroMemory(&psbVert->scrollInfo, sizeof(SCROLLINFO)); + + scrollPortionHover = HTSCROLL_NONE; + + //check to see if the window has left-aligned scrollbars + if (GetWindowLongPtrW(hwnd, GWL_EXSTYLE) & WS_EX_LEFTSCROLLBAR) scrollFlags |= SWS_LEFT; + + style = GetWindowLongPtrW(hwnd, GWL_STYLE); + + if (WS_HSCROLL & style) psbHorz->fScrollFlags = CSBS_VISIBLE; + if (WS_VSCROLL & style) psbVert->fScrollFlags = CSBS_VISIBLE; + + psbHorz->nBarType = SB_HORZ; + psbVert->nBarType = SB_VERT; + + //set the default arrow sizes for the scrollbars + psbHorz->nMinThumbSize = CoolSB_GetDefaultMinThumbSize(); + psbVert->nMinThumbSize = psbHorz->nMinThumbSize; + + scrollFlags |= SWS_UPDATEFRAME; + SetWindowPos(hwnd, NULL, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED | SWP_NOSENDCHANGING | SWP_NOREDRAW); + InvalidateNC(InvalidateFlag_Frame | InvalidateFlag_RedrawNow, SB_BOTH); + + return TRUE; +} + +SkinnedScrollWnd::~SkinnedScrollWnd(void) +{ + InvalidateNC(InvalidateFlag_Frame | InvalidateFlag_RedrawNow, SB_BOTH); + if (psbHorz) free(psbHorz); + if (psbVert) free(psbVert); + if (hbrChecked) + { + DeleteObject(hbrChecked); + hbrChecked = NULL; + } +} + +void SkinnedScrollWnd::DisableNoScroll(BOOL bDisable) +{ + if (bDisable) scrollFlags |= SWS_DISABLENOSCROLL; + else scrollFlags &= ~SWS_DISABLENOSCROLL; +} +BOOL SkinnedScrollWnd::IsNoScrollDisabled() +{ + return (0 != (SWS_DISABLENOSCROLL & scrollFlags)); +} + +void SkinnedScrollWnd::InvalidateNC(InvalidateFlags invalidate, UINT bars) +{ + HRGN rgnH = NULL, rgnV = NULL; + RECT rc; + int scrollLength; + unsigned int flags; + long frameEdge, clientEdge; + + if (0 != (InvalidateFlag_Frame & invalidate)) + { + flags = SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE | + SWP_FRAMECHANGED | SWP_NOSENDCHANGING | SWP_NOREDRAW; + + SetWindowPos(hwnd, NULL, 0, 0, 0, 0, flags); + } + + if (FALSE == GetClientRect(hwnd, &rc)) + return; + + if (SB_HORZ == bars || SB_BOTH == bars) + { + scrollLength = GetScrollMetric(psbHorz, SM_CYVERTSB, scrollFlags); + + clientEdge = rc.bottom; + if (0 != (InvalidateFlag_HorzBarRemoved & invalidate)) + clientEdge -= scrollLength; + + frameEdge = clientEdge + scrollLength; + + rgnH = CreateRectRgn(rc.left, clientEdge, rc.right, frameEdge); + } + else + rgnH = NULL; + + if (SB_VERT == bars || SB_BOTH == bars) + { + scrollLength = GetScrollMetric(psbVert, SM_CXVERTSB, scrollFlags); + + if (0 != (SWS_LEFT & scrollFlags)) + { + clientEdge = rc.left; + if (0 == (InvalidateFlag_VertBarRemoved & invalidate)) + clientEdge -= scrollLength; + } + else + { + clientEdge = rc.right; + if (0 != (InvalidateFlag_VertBarRemoved & invalidate)) + clientEdge -= scrollLength; + + frameEdge = clientEdge + scrollLength; + } + + rgnV = CreateRectRgn(clientEdge, rc.top, frameEdge, rc.bottom); + + if (NULL != rgnV && SB_BOTH == bars) + CombineRgn(rgnH, rgnH, rgnV, RGN_OR); + } + else + rgnV = NULL; + + flags = RDW_INVALIDATE | /*RDW_INTERNALPAINT | RDW_ERASE |*/ RDW_FRAME | RDW_NOCHILDREN; + if (0 != (InvalidateFlag_RedrawNow & invalidate)) + flags |= (RDW_UPDATENOW | RDW_ERASENOW); + + HRGN rgn = ((NULL != rgnH) ? rgnH : rgnV); + if (rgn) + RedrawWindow(hwnd, NULL, rgn, flags); + + if (rgnH) + DeleteRgn(rgnH); + + if (rgnV) + DeleteRgn(rgnV); +} + +void SkinnedScrollWnd::UpdateScrollBars(BOOL fInvalidate) +{ + UINT bars; + InvalidateFlags invalidateFlags; + + SCROLLINFO tsi; + tsi.cbSize = sizeof(SCROLLINFO); + tsi.fMask = SIF_ALL; + + bars = -1; + invalidateFlags = InvalidateFlag_Normal /*| InvalidateFlag_RedrawNow*/; + + if (0 == (SWS_HIDEHSCROLL & scrollFlags)) + { + if (GetScrollInfo(hwnd, SB_HORZ, &tsi) && + memcmp(&tsi, &psbHorz->scrollInfo, sizeof(SCROLLINFO))) + { + memcpy(&psbHorz->scrollInfo, &tsi, sizeof(SCROLLINFO)); + UpdateScrollBar(psbHorz, &invalidateFlags); + + psbHorz->scrollInfo.cbSize = sizeof(SCROLLINFO); + psbHorz->scrollInfo.fMask = SIF_ALL; + if (!GetScrollInfo(hwnd, SB_HORZ, &psbHorz->scrollInfo)) + ZeroMemory(&psbHorz->scrollInfo, sizeof(SCROLLINFO)); + + bars = SB_HORZ; + } + } + else + { + psbHorz->scrollInfo.cbSize = sizeof(SCROLLINFO); + psbHorz->scrollInfo.fMask = SIF_ALL; + if (!GetScrollInfo(hwnd, SB_HORZ, &psbHorz->scrollInfo)) + ZeroMemory(&psbHorz->scrollInfo, sizeof(SCROLLINFO)); + } + + if (0 == (SWS_HIDEVSCROLL & scrollFlags)) + { + if (GetScrollInfo(hwnd, SB_VERT, &tsi)) + { + if (0 != (SWS_LISTVIEW & scrollFlags) && + LVS_REPORT == (LVS_TYPEMASK & GetWindowLongPtrW(hwnd, GWL_STYLE))) + { + if (0 != (SWS_HIDEHSCROLL & scrollFlags)) + { + tsi.nMax = (int)SendMessageW(hwnd, LVM_GETITEMCOUNT, 0, 0L); + tsi.nPage = (unsigned int)SendMessageW(hwnd, LVM_GETCOUNTPERPAGE, 0, 0L); + if(tsi.nMax > 0) + tsi.nMax--; + + // if (psbHorz->scrollInfo.nPage <= (UINT)psbHorz->scrollInfo.nMax) + + } + } + + if (memcmp(&tsi, &psbVert->scrollInfo, sizeof(SCROLLINFO))) + { + memcpy(&psbVert->scrollInfo, &tsi, sizeof(SCROLLINFO)); + + UpdateScrollBar(psbVert, &invalidateFlags); + bars = (SB_HORZ == bars) ? SB_BOTH : SB_VERT; + } + } + } + else + { + psbVert->scrollInfo.cbSize = sizeof(SCROLLINFO); + psbVert->scrollInfo.fMask = SIF_ALL; + if (!GetScrollInfo(hwnd, SB_VERT, &psbVert->scrollInfo)) + ZeroMemory(&psbVert->scrollInfo, sizeof(SCROLLINFO)); + } + + if ((fInvalidate || 0 != (InvalidateFlag_Frame & invalidateFlags)) && + -1 != bars) + { + InvalidateNC(invalidateFlags, bars); + + if (0 != (InvalidateFlag_Frame & invalidateFlags) && + 0 != (SWS_LISTVIEW & scrollFlags) && + LVS_REPORT == (LVS_TYPEMASK & GetWindowLongPtrW(hwnd, GWL_STYLE))) + { + if (0 == (LVS_NOCOLUMNHEADER & GetWindowLongPtrW(hwnd, GWL_STYLE))) + { + HWND hHeader = (HWND)SendMessageW(hwnd, LVM_GETHEADER, 0, 0L); + if (NULL != hHeader) + { + RECT clientRect; + WINDOWPOS wp; + + GetClientRect(hwnd, &clientRect); + + SCROLLINFO si; + si.cbSize = sizeof(si); + si.fMask = SIF_POS; + if (0 != GetScrollInfo(hwnd, SB_HORZ, &si)) + clientRect.left -= si.nPos; + + HDLAYOUT layout; + layout.prc = &clientRect; + layout.pwpos = ℘ + if (FALSE != SendMessageW(hHeader, HDM_LAYOUT, 0, (LPARAM)&layout)) + { + if (FALSE == fInvalidate) + wp.flags |= SWP_NOREDRAW; + SetWindowPos(hHeader, wp.hwndInsertAfter, wp.x, wp.y, wp.cx, wp.cy, wp.flags | SWP_NOZORDER | SWP_NOACTIVATE); + } + } + } + } + } +} + +void SkinnedScrollWnd::UpdateScrollBar(SCROLLBAR *psb, InvalidateFlags *invalidateFlags) +{ + SCROLLINFO *psi; + psi = &psb->scrollInfo; + + if ((psi->nPage > (UINT)psi->nMax || (psi->nPage == (UINT)psi->nMax && psi->nMax == 0) || psi->nMax <= psi->nMin)) + { + if (psb->fScrollVisible) + { + if (SWS_DISABLENOSCROLL & scrollFlags) + { + psb->fScrollFlags |= (ESB_DISABLE_LEFT | ESB_DISABLE_RIGHT); + } + else + { + ShowScrollBar(psb->nBarType, FALSE); + *invalidateFlags |= InvalidateFlag_Frame; + if (SB_VERT == psb->nBarType) + *invalidateFlags |= InvalidateFlag_VertBarRemoved; + else + *invalidateFlags |= InvalidateFlag_HorzBarRemoved; + } + } + } + else + { + if ((!psb->fScrollVisible || ((ESB_DISABLE_LEFT | ESB_DISABLE_RIGHT) & psb->fScrollFlags)) && psi->nPage > 0) + { + if ((SWS_DISABLENOSCROLL & scrollFlags) && ((ESB_DISABLE_LEFT | ESB_DISABLE_RIGHT) & psb->fScrollFlags)) + { + psb->fScrollFlags &= ~(ESB_DISABLE_LEFT | ESB_DISABLE_RIGHT); + } + + if (!psb->fScrollVisible) + { + if (SWS_LISTVIEW & scrollFlags) + { + DWORD ws = GetWindowLongPtrW(hwnd, GWL_STYLE); + if (LVS_ICON == (LVS_TYPEMASK & ws) || LVS_SMALLICON == (LVS_TYPEMASK & ws)) + { + switch(LVS_ALIGNMASK & ws) + { + case LVS_ALIGNLEFT: + if (SB_HORZ != psb->nBarType) psb->nBarType = ((SB_BOTH == psb->nBarType) ? SB_HORZ : -1); + break; + case LVS_ALIGNTOP: + if (SB_VERT != psb->nBarType) psb->nBarType = ((SB_BOTH == psb->nBarType) ? SB_VERT : -1); + break; + } + } + } + if (-1 != psb->nBarType) + { + ShowScrollBar(psb->nBarType, TRUE); + *invalidateFlags |= InvalidateFlag_Frame; + if (SB_VERT == psb->nBarType) + *invalidateFlags |= InvalidateFlag_VertBarAppeared; + else + *invalidateFlags |= InvalidateFlag_HorzBarAppeared; + } + } + } + else if (psb->fScrollVisible && 0 == psi->nPage) + { + if (SWS_DISABLENOSCROLL & scrollFlags) + { + psb->fScrollFlags |= (ESB_DISABLE_LEFT | ESB_DISABLE_RIGHT); + } + else + { + ShowScrollBar(psb->nBarType, FALSE); + *invalidateFlags |= InvalidateFlag_Frame; + if (SB_VERT == psb->nBarType) + *invalidateFlags |= InvalidateFlag_VertBarRemoved; + else + *invalidateFlags |= InvalidateFlag_HorzBarRemoved; + } + } + } +} + +void SkinnedScrollWnd::ShowHorzScroll(BOOL fEnable) +{ + scrollFlags = (scrollFlags & ~SWS_HIDEHSCROLL) | ((fEnable) ? 0 : SWS_HIDEHSCROLL); + psbHorz->fScrollFlags = 0; + InvalidateNC(InvalidateFlag_Frame | InvalidateFlag_RedrawNow, SB_HORZ); +} + +BOOL SkinnedScrollWnd::IsHorzBarHidden() +{ + return (0 != (SWS_HIDEHSCROLL & scrollFlags)); +} + +BOOL SkinnedScrollWnd::IsVertBarHidden() +{ + return (0 != (SWS_HIDEVSCROLL & scrollFlags)); +} + +void SkinnedScrollWnd::ShowVertScroll(BOOL fEnable) +{ + scrollFlags = (scrollFlags & ~SWS_HIDEVSCROLL) | ((fEnable) ? 0 : SWS_HIDEVSCROLL); + psbVert->fScrollFlags = 0; + InvalidateNC(InvalidateFlag_Frame | InvalidateFlag_RedrawNow, SB_VERT); +} + +BOOL SkinnedScrollWnd::SetMode(UINT nMode) +{ + scrollFlags &= ~(SWS_LISTVIEW | SWS_TREEVIEW | SWS_COMBOLBOX); + switch (0xFF & nMode) + { + case SCROLLMODE_STANDARD_I: return TRUE; + case SCROLLMODE_LISTVIEW_I: scrollFlags |= SWS_LISTVIEW; return TRUE; + case SCROLLMODE_TREEVIEW_I: scrollFlags |= SWS_TREEVIEW; return TRUE; + case SCROLLMODE_COMBOLBOX_I: scrollFlags |= SWS_COMBOLBOX; return TRUE; + } + + return FALSE; +} + +UINT SkinnedScrollWnd::GetMode() +{ + if (0 != (SWS_LISTVIEW & scrollFlags)) + return SCROLLMODE_STANDARD_I; + + if (0 != (SWS_TREEVIEW & scrollFlags)) + return SCROLLMODE_TREEVIEW_I; + + if (0 != (SWS_COMBOLBOX & scrollFlags)) + return SCROLLMODE_COMBOLBOX_I; + + return SCROLLMODE_STANDARD_I; +} + +INT SkinnedScrollWnd::AdjustHover(UINT nHitTest, POINTS pts) +{ + SCROLLBAR *psb = NULL; + RECT rect; + + if (psbHorz) psbHorz->fScrollFlags &= ~CSBS_HOVERING; + if (psbVert) psbVert->fScrollFlags &= ~CSBS_HOVERING; + scrollPortionHover = HTSCROLL_NONE; + + if (HTHSCROLL == nHitTest) + { + psb = psbHorz; + GetHScrollRect(this, &rect); + } + else if (HTVSCROLL== nHitTest) + { + psb = psbVert; + GetVScrollRect(this, &rect); + } + + if (psb) + { + psb->fScrollFlags |= CSBS_HOVERING; + scrollPortionHover = GetPortion(psb, hwnd, &rect, pts.x, pts.y, scrollFlags); + } + return scrollPortionHover; +} + +BOOL SkinnedScrollWnd::OnMediaLibraryIPC(INT msg, INT_PTR param, LRESULT *pResult) +{ + switch (msg) + { + case IPC_ML_SKINNEDSCROLLWND_UPDATEBARS: + UpdateScrollBars((BOOL)param); + *pResult = 1; + return TRUE; + case IPC_ML_SKINNEDSCROLLWND_SHOWHORZBAR: + ShowHorzScroll((BOOL)param); + *pResult = 1; + return TRUE; + case IPC_ML_SKINNEDSCROLLWND_SHOWVERTBAR: + ShowVertScroll((BOOL)param); + *pResult = 1; + return TRUE; + case IPC_ML_SKINNEDSCROLLWND_SETMODE: + *pResult = SetMode((UINT)param); + return TRUE; + case IPC_ML_SKINNEDSCROLLWND_DISABLENOSCROLL: + DisableNoScroll(0 != param); + *pResult = -1; + return TRUE; + case IPC_ML_SKINNEDSCROLLWND_ADJUSTHOVER: + ((SBADJUSTHOVER*)param)->nResult = AdjustHover(((SBADJUSTHOVER*)param)->hitTest, ((SBADJUSTHOVER*)param)->ptMouse); + *pResult = TRUE; + return TRUE; + case IPC_ML_SKINNEDSCROLLWND_GETHORZBARHIDDEN: + *pResult = IsHorzBarHidden(); + return TRUE; + case IPC_ML_SKINNEDSCROLLWND_GETVERTBARHIDDEN: + *pResult = IsVertBarHidden(); + return TRUE; + case IPC_ML_SKINNEDSCROLLWND_GETMODE: + *pResult = GetMode(); + return TRUE; + case IPC_ML_SKINNEDSCROLLWND_GETNOSCROLLDISABLED: + *pResult = IsNoScrollDisabled(); + return TRUE; + } + return __super::OnMediaLibraryIPC(msg, param, pResult); +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/skinnedscrollwnd.h b/Src/Plugins/General/gen_ml/skinnedscrollwnd.h new file mode 100644 index 00000000..c5f1644d --- /dev/null +++ b/Src/Plugins/General/gen_ml/skinnedscrollwnd.h @@ -0,0 +1,99 @@ +#ifndef NULLOSFT_MEDIALIBRARY_SKINNED_SCROLLWINDOW_HEADER +#define NULLOSFT_MEDIALIBRARY_SKINNED_SCROLLWINDOW_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include "./skinnedwnd.h" + +typedef struct _SCROLLBAR SCROLLBAR; + +// scroll modes +#define SCROLLMODE_STANDARD_I 0x00 +#define SCROLLMODE_LISTVIEW_I 0x01 +#define SCROLLMODE_TREEVIEW_I 0x02 +#define SCROLLMODE_COMBOLBOX_I 0x03 + +class SkinnedScrollWnd : public SkinnedWnd +{ +public: + typedef enum InvalidateFlags + { + InvalidateFlag_Normal = 0, + InvalidateFlag_RedrawNow = (1 << 0), + InvalidateFlag_Frame = (1 << 1), + InvalidateFlag_VertBarAppeared = (1 << 2), + InvalidateFlag_VertBarRemoved = (1 << 3), + InvalidateFlag_HorzBarAppeared = (1 << 4), + InvalidateFlag_HorzBarRemoved = (1 << 5), + } InvalidateFlags; + +protected: + SkinnedScrollWnd(BOOL bIsDialog); + virtual ~SkinnedScrollWnd(void); + +public: + void UpdateScrollBars(BOOL fInvalidate); + void ShowHorzScroll(BOOL fEnable); + void ShowVertScroll(BOOL fEnable); + BOOL IsHorzBarHidden(); + BOOL IsVertBarHidden(); + BOOL SetMode(UINT nMode); + UINT GetMode(); + void DisableNoScroll(BOOL bDisable); + BOOL IsNoScrollDisabled(); + +protected: + virtual BOOL Attach(HWND hwndHeader); + virtual LRESULT WindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam); + + virtual INT OnNcCalcSize(BOOL bCalcValidRects, NCCALCSIZE_PARAMS *pncsp); + virtual INT OnNcHitTest(POINTS pts); + virtual void OnNcPaint(HRGN rgnUpdate); + virtual void OnNcMouseMove(UINT nHitTest, POINTS pts); + virtual void OnNcLButtonDown(UINT nHitTest, POINTS pts); + virtual void OnNcMouseLeave(); + virtual void OnStyleChanged(INT styleType, STYLESTRUCT *pss); + virtual void OnLButtonUp(UINT nFlags, POINTS pts); + virtual void OnMouseMove(UINT nFlags, POINTS pts); + virtual void OnTimer(UINT_PTR idEvent, TIMERPROC fnTimer); + virtual LRESULT OnEraseBackground(HDC hdc); + virtual void OnPrint(HDC hdc, UINT options); + virtual void OnSkinChanged(BOOL bNotifyChildren, BOOL bRedraw); + + + virtual BOOL OnMediaLibraryIPC(INT msg, INT_PTR param, LRESULT *pResult); + + BOOL ShowScrollBar(int wBar, BOOL fShow); + void UpdateScrollBar(SCROLLBAR *psb, InvalidateFlags *invalidateFlags); + void InvalidateNC(InvalidateFlags invalidate, UINT bars); + void PaintNonClient(HDC hdc); + INT AdjustHover(UINT nHitTest, POINTS pts); + void UpdateFrame(); + + + LRESULT OnListViewScroll(INT dx, INT dy); + void OnVertScroll(UINT code, UINT pos, HWND hwndSB); + void OnMouseWheel(INT delta, UINT vtKey, POINTS pts); + + void Emulate_LeftButtonUp(UINT nFlags, POINTS pts, BOOL forwardMessage); + +private: + friend BOOL SkinWindowEx(HWND hwndToSkin, INT type, UINT style); + friend static BOOL GetHScrollRect(SkinnedScrollWnd *pWnd, RECT *prc); + friend static BOOL GetVScrollRect(SkinnedScrollWnd *pWnd, RECT *prc); + + +private: + + SCROLLBAR *psbHorz; + SCROLLBAR *psbVert; + UINT scrollFlags; + UINT scrollPortionHover; + int wheelCarryover; +}; + +DEFINE_ENUM_FLAG_OPERATORS(SkinnedScrollWnd::InvalidateFlags); + +#endif //NULLOSFT_MEDIALIBRARY_SKINNED_SCROLLWINDOW_HEADER diff --git a/Src/Plugins/General/gen_ml/skinnedstatic.cpp b/Src/Plugins/General/gen_ml/skinnedstatic.cpp new file mode 100644 index 00000000..864d7963 --- /dev/null +++ b/Src/Plugins/General/gen_ml/skinnedstatic.cpp @@ -0,0 +1,135 @@ +#include "./skinnedstatic.h" +#include "../winamp/wa_dlg.h" +#include "./skinning.h" +#include + +#define MARGIN_TOP 2 +#define MARGIN_BOTTOM 2 +#define MARGIN_LEFT 2 +#define MARGIN_RIGHT 2 + +SkinnedStatic::SkinnedStatic(void) : SkinnedWnd(FALSE) +{ +} + +SkinnedStatic::~SkinnedStatic(void) +{ +} + +BOOL SkinnedStatic::Attach(HWND hwndStatic) +{ + if(!SkinnedWnd::Attach(hwndStatic)) return FALSE; + SetType(SKINNEDWND_TYPE_STATIC); + + HWND hwndParent = GetParent(hwndStatic); + if (hwndParent) SkinWindow(hwndParent, SWS_NORMAL); + + SetWindowPos(hwnd, NULL, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED); + return TRUE; +} + +LRESULT SkinnedStatic::WindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + if (SWS_USESKINCOLORS & style) + { + switch(uMsg) + { + case REFLECTED_CTLCOLORSTATIC: + { + COLORREF rgbText, rgbTextBk; + rgbText = WADlg_getColor(WADLG_WNDFG); + rgbTextBk = WADlg_getColor(WADLG_WNDBG); + + if(!IsWindowEnabled(hwnd)) + { + rgbText = RGB((GetRValue(rgbText)+GetRValue(rgbTextBk))/2, + (GetGValue(rgbText)+GetGValue(rgbTextBk))/2, + (GetBValue(rgbText)+GetBValue(rgbTextBk))/2); + } + + SetBkColor((HDC)wParam, rgbTextBk); + SetTextColor((HDC)wParam, rgbText); + } + ((REFLECTPARAM*)lParam)->result = (LRESULT)MlStockObjects_Get(WNDBCK_BRUSH); + return TRUE; + } + } + return __super::WindowProc(uMsg, wParam, lParam); +} + +LRESULT SkinnedStatic::GetIdealSize(LPCWSTR pszText) +{ + INT cchText; + SIZE szButton; + szButton.cx = 0; + szButton.cy = 0; + + cchText = (pszText) ? lstrlenW(pszText) : (INT)SendMessageW(hwnd, WM_GETTEXTLENGTH, 0, 0L); + + { + HDC hdc = GetDCEx(hwnd, NULL, DCX_CACHE); + if (hdc) + { + wchar_t szText[STATIC_TEXT_MAX] = {0}; + if (NULL == pszText) + { + SendMessageW(hwnd, WM_GETTEXT, (WPARAM)STATIC_TEXT_MAX, (LPARAM)szText); + pszText = szText; + } + + HFONT hFont = (HFONT)SendMessageW(hwnd, WM_GETFONT, 0, 0L); + if (NULL == hFont) hFont = (HFONT)MlStockObjects_Get(DEFAULT_FONT); + HFONT hfo = (NULL != hFont) ? (HFONT)SelectObject(hdc, hFont) : NULL; + + if (0 != cchText) + { + RECT rt; + SetRect(&rt, 0, 0, 0, 0); + if (FALSE == DrawTextW(hdc, pszText, cchText, &rt, DT_CALCRECT | DT_SINGLELINE)) + { + szButton.cx = 0; + szButton.cy = 0; + } + else + { + szButton.cx = rt.right - rt.left; + szButton.cy = rt.bottom - rt.top; + } + } + else + { + TEXTMETRIC metrics; + + szButton.cx = 0; + if (FALSE == GetTextMetrics(hdc, &metrics)) + szButton.cy = 0; + else + szButton.cy = metrics.tmHeight; + } + + if (0 != szButton.cy) + szButton.cy += (MARGIN_TOP + MARGIN_BOTTOM); + + if (0 != szButton.cx) + szButton.cx += (MARGIN_LEFT + MARGIN_RIGHT) + 2; + + if (NULL != hfo) + SelectObject(hdc, hfo); + + ReleaseDC(hwnd, hdc); + } + } + + return MAKELPARAM(szButton.cx, szButton.cy); +} + +BOOL SkinnedStatic::OnMediaLibraryIPC(INT msg, INT_PTR param, LRESULT *pResult) +{ + switch(msg) + { + case ML_IPC_SKINNEDSTATIC_GETIDEALSIZE: + *pResult = GetIdealSize((LPCWSTR)param); + return TRUE; + } + return __super::OnMediaLibraryIPC(msg, param, pResult); +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/skinnedstatic.h b/Src/Plugins/General/gen_ml/skinnedstatic.h new file mode 100644 index 00000000..7a50424d --- /dev/null +++ b/Src/Plugins/General/gen_ml/skinnedstatic.h @@ -0,0 +1,30 @@ +#ifndef NULLOSFT_MEDIALIBRARY_SKINNED_STATIC_HEADER +#define NULLOSFT_MEDIALIBRARY_SKINNED_STATIC_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include "./skinnedwnd.h" + +#define STATIC_TEXT_MAX 1024 + +class SkinnedStatic : public SkinnedWnd +{ +protected: + SkinnedStatic(void); + virtual ~SkinnedStatic(void); + +protected: + virtual BOOL Attach(HWND hwndStatic); + virtual LRESULT WindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam); // treat this as dialog proc + virtual BOOL OnMediaLibraryIPC(INT msg, INT_PTR param, LRESULT *pResult); + virtual LRESULT GetIdealSize(LPCWSTR pszText); + +private: + friend BOOL SkinWindowEx(HWND hwndToSkin, INT type, UINT style); + +protected: +}; + +#endif // NULLOSFT_MEDIALIBRARY_SKINNED_STATIC_HEADER \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/skinnedtooltip.cpp b/Src/Plugins/General/gen_ml/skinnedtooltip.cpp new file mode 100644 index 00000000..a672723c --- /dev/null +++ b/Src/Plugins/General/gen_ml/skinnedtooltip.cpp @@ -0,0 +1,190 @@ +#include "main.h" +#include "./skinnedtooltip.h" +#include "./skinning.h" +#include "../winamp/wa_dlg.h" +#include "./colors.h" +#include + +SkinnedToolTip::SkinnedToolTip(void) + : SkinnedWnd(FALSE), skinCursor(NULL), buffer(NULL), bufferSizeCch(0) +{ +} + +SkinnedToolTip::~SkinnedToolTip(void) +{ + if (NULL != buffer) + free(buffer); +} + +BOOL SkinnedToolTip::Attach(HWND hToolTip) +{ + if(!__super::Attach(hToolTip)) return FALSE; + + SetType(SKINNEDWND_TYPE_TOOLTIP); + return TRUE; +} + +HPEN SkinnedToolTip::GetBorderPen(void) +{ + return (HPEN)MlStockObjects_Get(TOOLTIPBORDER_PEN); +} + +void SkinnedToolTip::OnSkinChanged(BOOL bNotifyChildren, BOOL bRedraw) +{ + if (0 != (SWS_USESKINCOLORS & style)) + { + HRESULT hr; + COLORREF rgbText, rgbBk; + + hr = MLGetSkinColor(MLSO_TOOLTIP, TTP_BACKGROUND, MBS_NORMAL, &rgbBk); + + if (SUCCEEDED(hr)) + hr = MLGetSkinColor(MLSO_TOOLTIP, TTP_TEXT, MBS_NORMAL, &rgbText); + + if (SUCCEEDED(hr)) + { + if (rgbBk != (COLORREF)CallPrevWndProc(TTM_GETTIPBKCOLOR, 0, 0L)) + CallPrevWndProc(TTM_SETTIPBKCOLOR, rgbBk, 0L); + + if (rgbText != (COLORREF)CallPrevWndProc(TTM_GETTIPTEXTCOLOR, 0, 0L)) + CallPrevWndProc(TTM_SETTIPTEXTCOLOR, rgbText, 0L); + } + } + + skinCursor = (0 != (SWS_USESKINCURSORS & style)) ? + (HCURSOR)SENDWAIPC(plugin.hwndParent, IPC_GETSKINCURSORS, WACURSOR_NORMAL) : NULL; + + HFONT hfOld = (HFONT)CallPrevWndProc(WM_GETFONT, 0, 0L); + + __super::OnSkinChanged(bNotifyChildren, bRedraw); + + if (hfOld != (HFONT)CallPrevWndProc(WM_GETFONT, 0, 0L)) + CallPrevWndProc(TTM_UPDATE, 0, 0L); +} + +void SkinnedToolTip::OnPaint() +{ + BOOL defaultPaint = TRUE; + DWORD windowStyle = GetWindowLongPtr(hwnd, GWL_STYLE); + if (0 == ((WS_BORDER | WS_DLGFRAME | WS_THICKFRAME) & windowStyle)) + { + DWORD windowExStyle = GetWindowLongPtr(hwnd, GWL_EXSTYLE); + if (0 == ((WS_EX_CLIENTEDGE | WS_EX_STATICEDGE | WS_EX_WINDOWEDGE | WS_EX_DLGMODALFRAME) & windowExStyle)) + defaultPaint = FALSE; + } + + if (FALSE != defaultPaint) + { + CallPrevWndProc(WM_PAINT, 0, 0L); + return; + } + + PAINTSTRUCT ps; + HDC hdc = BeginPaint(hwnd, &ps); + if (NULL == hdc) return; + + COLORREF rgbBk; + rgbBk = (COLORREF)CallPrevWndProc(TTM_GETTIPBKCOLOR, 0, 0L); + SetBkColor(hdc, rgbBk); + + RECT rc, rcText; + GetClientRect(hwnd, &rc); + + if (FALSE != ps.fErase) + { + ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &ps.rcPaint, NULL, 0, NULL); + DrawBorder(hdc, &rc, BORDER_FLAT, GetBorderPen()); + } + + unsigned int textLength = (unsigned int)CallPrevWndProc(WM_GETTEXTLENGTH, 0, 0L); + if (0 != textLength) + { + if (textLength >= bufferSizeCch) + { + if (NULL != buffer) + free(buffer); + bufferSizeCch = textLength + 1; + buffer = (wchar_t*)calloc(bufferSizeCch, sizeof(wchar_t)); + if (NULL == buffer) + bufferSizeCch = 0; + } + + if (NULL != buffer) + textLength = (long)CallPrevWndProc(WM_GETTEXT, (WPARAM)bufferSizeCch, (LPARAM)buffer); + else + textLength = 0; + + COLORREF rgbFg = (COLORREF)CallPrevWndProc(TTM_GETTIPTEXTCOLOR, 0, 0L); + + SetRectEmpty(&rcText); + CallPrevWndProc(TTM_GETMARGIN, 0, (LPARAM)&rcText); + + if (rcText.left < 2) + rcText.left = 2; + + if (rcText.right < 2) + rcText.right = 2; + + if (rcText.bottom < 1) + rcText.bottom = 1; + + if (rcText.top < 1) + rcText.top = 1; + + rcText.left = rc.left + rcText.left; + rcText.top = rc.top + rcText.top; + rcText.right = rc.right - rcText.right; + rcText.bottom = rc.bottom - rcText.bottom; + + HFONT textFont = (HFONT)CallPrevWndProc(WM_GETFONT, 0, 0L); + if (NULL == textFont) + textFont = (HFONT)MlStockObjects_Get(DEFAULT_FONT); + + HFONT textFontOld = (HFONT)SelectObject(hdc, textFont); + COLORREF rgbFgOld = SetTextColor(hdc, rgbFg); + + unsigned int textFormat; + + textFormat = DT_TOP | DT_LEFT | DT_WORDBREAK; + if (0 != (TTS_NOPREFIX & windowStyle)) + textFormat |= DT_NOPREFIX; + + + DrawTextW(hdc, buffer, textLength, &rcText, textFormat); + + SelectObject(hdc, textFontOld); + if (rgbFg != rgbFgOld) + SetTextColor(hdc, rgbFgOld); + } + + EndPaint(hwnd, &ps); +} + +LRESULT SkinnedToolTip::WindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch(uMsg) + { + case WM_ERASEBKGND: + return 0; + case WM_PAINT: + OnPaint(); + return 0; + case WM_WINDOWPOSCHANGED: + if (0 != (SWP_SHOWWINDOW & ((WINDOWPOS*)lParam)->flags)) + SkinChanged(FALSE, TRUE); + break; + case WM_SHOWWINDOW: + if (0 != wParam) + SkinChanged(FALSE, TRUE); + break; + case WM_SETCURSOR: + if (NULL != skinCursor) + { + if (skinCursor != GetCursor()) + SetCursor(skinCursor); + return TRUE; + } + break; + } + return __super::WindowProc(uMsg, wParam, lParam); +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/skinnedtooltip.h b/Src/Plugins/General/gen_ml/skinnedtooltip.h new file mode 100644 index 00000000..cddd637b --- /dev/null +++ b/Src/Plugins/General/gen_ml/skinnedtooltip.h @@ -0,0 +1,35 @@ +#ifndef NULLOSFT_MEDIALIBRARY_SKINNED_TOOLTIP_HEADER +#define NULLOSFT_MEDIALIBRARY_SKINNED_TOOLTIP_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include "./skinnedwnd.h" + + +class SkinnedToolTip : public SkinnedWnd +{ + +protected: + SkinnedToolTip(void); + virtual ~SkinnedToolTip(void); + +protected: + virtual BOOL Attach(HWND hToolTip); + virtual LRESULT WindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam); // treat this as dialog proc + void OnSkinChanged(BOOL bNotifyChildren, BOOL bRedraw); + + HPEN GetBorderPen(void); + void OnPaint(); + +private: + friend BOOL SkinWindowEx(HWND hwndToSkin, INT type, UINT style); + +protected: + HCURSOR skinCursor; + wchar_t *buffer; + size_t bufferSizeCch; +}; + +#endif // NULLOSFT_MEDIALIBRARY_SKINNED_TOOLTIP_HEADER \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/skinnedwnd.cpp b/Src/Plugins/General/gen_ml/skinnedwnd.cpp new file mode 100644 index 00000000..0e16bb25 --- /dev/null +++ b/Src/Plugins/General/gen_ml/skinnedwnd.cpp @@ -0,0 +1,569 @@ +#include "./skinnedwnd.h" +#include "../winamp/wa_dlg.h" +#include "../nu/trace.h" +#include "./mldwm.h" +#include "../nu/CGlobalAtom.h" + +static CGlobalAtom WNDDATAPROPW(L"SWDATA"); + +static UINT WINAMP_WM_DIRECT_MOUSE_WHEEL = WM_NULL; + + +#ifndef LONGX86 +#ifdef _WIN64 + #define LONGX86 LONG_PTR +#else /*_WIN64*/ + #define LONGX86 LONG +#endif /*_WIN64*/ +#endif // LONGX86 + +#define SWS_ATTACHED 0x00010000 // window attached +#define SWS_UNICODE 0x00020000 // winodow is unicode +#define SWS_THEMED 0x00040000 // was themed before +#define SWS_REFLECT 0x00080000 // support message reflection +#define SWS_DIALOG 0x00100000 // treat this as dialog + +#define BORDER_WIDTH 1 + +extern HRESULT(WINAPI *SetWindowTheme)(HWND hwnd, LPCWSTR pszSubAppName, LPCWSTR pszSubIdList); //xp theme shit +extern BOOL (__stdcall *IsAppThemed)(void); + + +#define DWM_COMPOSITION_CHECK ((UINT)-1) +#define DWM_COMPOSITION_DISABLED ((UINT)0) +#define DWM_COMPOSITION_ENABLED ((UINT)1) + +static UINT dwmCompositionEnabled = DWM_COMPOSITION_CHECK; + + +static BOOL CALLBACK SkinChangedNotifyCB(HWND hwnd, LPARAM param) +{ + SendMessageW(hwnd, (UINT)WM_ML_IPC, MAKEWPARAM(TRUE, param), (LPARAM)ML_IPC_SKINNEDWND_SKINCHANGED); + return TRUE; +} + +SkinnedWnd *SkinnedWnd::GetFromHWND(HWND hwndSkinned) +{ + return (hwndSkinned && IsWindow(hwndSkinned)) ? (SkinnedWnd*)GetPropW(hwndSkinned, WNDDATAPROPW) : NULL; +} + +BOOL SkinnedWnd::IsDwmCompositionEnabled() +{ + if (DWM_COMPOSITION_CHECK == dwmCompositionEnabled) + { + dwmCompositionEnabled = DWM_COMPOSITION_DISABLED; + BOOL bEnabled; + if (S_OK == MlDwm_LoadLibrary() && S_OK == MlDwm_IsCompositionEnabled(&bEnabled) && bEnabled) + dwmCompositionEnabled = DWM_COMPOSITION_ENABLED; + } + return (DWM_COMPOSITION_ENABLED == dwmCompositionEnabled); +} + +SkinnedWnd::SkinnedWnd(BOOL bIsDialog) + : hwnd(NULL), style(SWS_NORMAL), uiState(NULL), redrawLock(0), + fnWndProc(NULL), wnddata(SKINNEDWND_TYPE_WINDOW | ((bIsDialog) ? SWS_DIALOG : 0)) +{ + minSize.cx = 0; + minSize.cy = 0; + maxSize.cx = 0; + maxSize.cy = 0; + + if (WM_NULL == WINAMP_WM_DIRECT_MOUSE_WHEEL) + WINAMP_WM_DIRECT_MOUSE_WHEEL = RegisterWindowMessageW(L"WINAMP_WM_DIRECT_MOUSE_WHEEL"); +} + +SkinnedWnd::~SkinnedWnd(void) +{ + if (!hwnd || !IsWindow(hwnd)) return; + RemovePropW(hwnd, WNDDATAPROPW); + if (fnWndProc) + { + INT index; + index = (SWS_DIALOG & wnddata) ? DWLP_DLGPROC : GWLP_WNDPROC; + (SWS_UNICODE & wnddata) ? SetWindowLongPtrW(hwnd, index, (LONGX86)(LONG_PTR)fnWndProc) : SetWindowLongPtrA(hwnd, index, (LONGX86)(LONG_PTR)fnWndProc); + } + if ((SWS_THEMED & wnddata) && IsAppThemed && IsAppThemed() && SetWindowTheme) SetWindowTheme(hwnd, NULL, NULL); +} + +BOOL SkinnedWnd::IsUnicode(void) +{ + return ( 0 != (SWS_UNICODE & wnddata)); +} + +BOOL SkinnedWnd::IsAttached(void) +{ + return ( 0 != (SWS_ATTACHED & wnddata)); +} + +BOOL SkinnedWnd::Attach(HWND hwndToSkin) +{ + INT index; + if (hwnd) return FALSE; + + hwnd = hwndToSkin; + if(!hwnd || GetPropW(hwnd, WNDDATAPROPW)) return FALSE; + + wnddata &= (SKINNEDWND_TYPE_WINDOW | SWS_DIALOG); + + if(IsWindowUnicode(hwnd)) wnddata |= SWS_UNICODE; + + index = (SWS_DIALOG & wnddata) ? DWLP_DLGPROC : GWLP_WNDPROC; + + + fnWndProc= (WNDPROC)(LONG_PTR)((SWS_UNICODE & wnddata) ? SetWindowLongPtrW(hwnd, index, (LONGX86)(LONG_PTR)WindowProcReal) : SetWindowLongPtrA(hwnd, index, (LONGX86)(LONG_PTR)WindowProcReal)); + if (!fnWndProc || !SetPropW(hwnd, WNDDATAPROPW, this)) return FALSE; + + RemoveReflector(hwnd); // we will use this refelector + + wnddata |= (SWS_ATTACHED | SWS_REFLECT); + + + if (S_OK == MlDwm_LoadLibrary()) + { + DWMNCRENDERINGPOLICY ncrp = DWMNCRP_DISABLED; + DWORD allow = FALSE; + MlDwm_SetWindowAttribute(hwnd, DWMWA_NCRENDERING_POLICY, &ncrp, sizeof(ncrp)); + MlDwm_SetWindowAttribute(hwnd, DWMWA_ALLOW_NCPAINT, &allow, sizeof(allow)); + } + + if (IsAppThemed && IsAppThemed() && SetWindowTheme) + { + SetWindowTheme(hwnd, NULL, L""); + wnddata |= SWS_THEMED; + } + + uiState =(WORD)SendMessageW(hwnd, WM_QUERYUISTATE, 0, 0L); + + return TRUE; +} + +LRESULT SkinnedWnd::CallPrevWndProc(UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + return (SWS_UNICODE & wnddata) ? CallWindowProcW(fnWndProc, hwnd, uMsg, wParam, lParam) : CallWindowProcA(fnWndProc, hwnd, uMsg, wParam, lParam); +} + +LRESULT SkinnedWnd::CallDefWndProc(UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + return (SWS_UNICODE & wnddata) ? DefWindowProcW(hwnd, uMsg, wParam, lParam) : DefWindowProcA(hwnd, uMsg, wParam, lParam); +} + +void SkinnedWnd::OnSkinChanged(BOOL bNotifyChildren, BOOL bRedraw) +{ + if (SWS_USESKINFONT & style) + { + HFONT skinFont; + + skinFont = (HFONT)MlStockObjects_Get(SKIN_FONT); + + if (NULL == skinFont) + skinFont = (HFONT)MlStockObjects_Get(DEFAULT_FONT); + + if (NULL != skinFont) + { + HFONT windowFont = (HFONT)CallPrevWndProc(WM_GETFONT, 0, 0L); + if (skinFont != windowFont) + { + DisableRedraw(); + + SendMessageW(hwnd, WM_SETFONT, (WPARAM)skinFont, MAKELPARAM(0, bRedraw)); + + if (FALSE != bRedraw) + { + SetWindowPos(hwnd, NULL, 0, 0, 0, 0, + SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | + SWP_FRAMECHANGED | SWP_NOREDRAW); + } + + EnableRedraw(SWR_NONE); + } + } + } + + if (bNotifyChildren) + EnumChildWindows(hwnd, SkinChangedNotifyCB, bRedraw); + + SendMessageW(hwnd, (UINT)WM_ML_IPC, + MAKEWPARAM(bNotifyChildren, bRedraw), + (LPARAM)ML_IPC_SKINNEDWND_SKINUPDATED); + + if (bRedraw) + InvalidateRect(hwnd, NULL, TRUE); +} + +void SkinnedWnd::OnSkinUpdated(BOOL bNotifyChildren, BOOL bRedraw) +{ +} + +void SkinnedWnd::SkinChanged(BOOL bNotifyChildren, BOOL bRedraw) +{ + OnSkinChanged(bNotifyChildren, bRedraw); +} + +void SkinnedWnd::EnableReflection(BOOL bEnable) +{ + wnddata = (wnddata & ~SWS_REFLECT) | ((bEnable) ? SWS_REFLECT : 0); +} + +BOOL SkinnedWnd::SetStyle(UINT newStyle, BOOL bRedraw) +{ + style = newStyle; + + if (NULL != hwnd) + SkinChanged(FALSE, bRedraw); + + return TRUE; +} + +void SkinnedWnd::SetMinMaxInfo(MLSKINNEDMINMAXINFO *minMax) +{ + if (NULL != minMax) + { + minSize.cx = minMax->min.cx; + if (minSize.cx < 0) + minSize.cx = 0; + + minSize.cy = minMax->min.cy; + if (minSize.cy < 0) + minSize.cy = 0; + + maxSize.cx = minMax->max.cx; + if (maxSize.cx < 0) + maxSize.cx = 0; + + maxSize.cy = minMax->max.cy; + if (maxSize.cy < 0) + maxSize.cy = 0; + } + else + { + memset(&minSize, 0, sizeof(minSize)); + memset(&maxSize, 0, sizeof(maxSize)); + } +} + + +BOOL SkinnedWnd::OnMediaLibraryIPC(INT msg, INT_PTR param, LRESULT *pResult) +{ + switch(msg) + { + case ML_IPC_SKINNEDWND_ISSKINNED: *pResult = IsAttached(); return TRUE; + case ML_IPC_SKINNEDWND_SKINCHANGED: SkinChanged(LOWORD(param), HIWORD(param)); *pResult = 1; return TRUE; + case ML_IPC_SKINNEDWND_SKINUPDATED: OnSkinUpdated(LOWORD(param), HIWORD(param)); break; + case ML_IPC_SKINNEDWND_GETTYPE: *pResult = GetType(); return TRUE; + case ML_IPC_SKINNEDWND_ENABLEREFLECTION: EnableReflection((BOOL)param); *pResult = 1; return TRUE; + case ML_IPC_SKINNEDWND_GETPREVWNDPROC: + if (param) *((BOOL*)param) = (SWS_UNICODE & wnddata); + *pResult = (INT_PTR)fnWndProc; + return TRUE; + case ML_IPC_SKINNEDWND_SETSTYLE: *pResult = style; style = (UINT)param; return TRUE; + case ML_IPC_SKINNEDWND_GETSTYLE: *pResult = style; return TRUE; + case ML_IPC_SKINNEDWND_SETMINMAXINFO: + SetMinMaxInfo((MLSKINNEDMINMAXINFO*)param); + *pResult = TRUE; + return TRUE; + } + return FALSE; +} + + +INT SkinnedWnd::OnNcHitTest(POINTS pts) +{ + return (INT)CallPrevWndProc(WM_NCHITTEST, 0, *(LPARAM*)&pts); +} + +void SkinnedWnd::DrawBorder(HDC hdc, RECT *prc, UINT type, HPEN pen) +{ + HPEN penOld; + INT o = (BORDER_WIDTH/2) + ((BORDER_WIDTH%2) ? 1 : 0); + + switch(type) + { + case BORDER_SUNKEN: + case BORDER_FLAT: + penOld = (HPEN)SelectObject(hdc, pen); + + MoveToEx(hdc, prc->right - o, prc->top, NULL); + LineTo(hdc, prc->right - o, prc->bottom); + MoveToEx(hdc, prc->right - BORDER_WIDTH, prc->bottom - o, NULL); + LineTo(hdc, prc->left - 1, prc->bottom - o); + + if (BORDER_FLAT == type) + { + MoveToEx(hdc, prc->left + BORDER_WIDTH/2, prc->bottom - BORDER_WIDTH, NULL); + LineTo(hdc, prc->left + BORDER_WIDTH/2, prc->top); + MoveToEx(hdc, prc->left, prc->top + BORDER_WIDTH/2, NULL); + LineTo(hdc, prc->right - BORDER_WIDTH, prc->top + BORDER_WIDTH/2); + } + SelectObject(hdc, penOld); + break; + } +} + +UINT SkinnedWnd::GetBorderType(void) +{ + DWORD ws = (DWORD)GetWindowLongPtrW(hwnd, GWL_EXSTYLE); + if (WS_EX_STATICEDGE & ws) return BORDER_FLAT; + else if (WS_EX_CLIENTEDGE & ws) return BORDER_SUNKEN; + ws = (DWORD)GetWindowLongPtrW(hwnd, GWL_STYLE); + return (WS_BORDER & ws) ? BORDER_FLAT : BORDER_NONE; +} + +void SkinnedWnd::DrawBorder(HDC hdc) +{ + UINT borderType = GetBorderType(); + + if (BORDER_NONE != borderType) + { + RECT rc; + GetWindowRect(hwnd, &rc); + OffsetRect(&rc, -rc.left, -rc.top); + DrawBorder(hdc, &rc, borderType, GetBorderPen()); + } +} + +void SkinnedWnd::OnNcPaint(HRGN rgnUpdate) +{ + UINT borderType = GetBorderType(); + if (BORDER_NONE == borderType) return; + + UINT flags = DCX_PARENTCLIP | DCX_CACHE | DCX_WINDOW | DCX_CLIPSIBLINGS | + DCX_INTERSECTUPDATE | DCX_VALIDATE; + + HDC hdc = GetDCEx(hwnd, ((HRGN)NULLREGION != rgnUpdate) ? rgnUpdate : NULL, flags); + + if (NULL == hdc) return; + + DrawBorder(hdc); + ReleaseDC(hwnd, hdc); +} + + +INT SkinnedWnd::OnNcCalcSize(BOOL bCalcValidRects, NCCALCSIZE_PARAMS *pncsp) +{ + UINT borderType = GetBorderType(); + switch(borderType) + { + case BORDER_SUNKEN: + case BORDER_FLAT: + if (bCalcValidRects) + { + SetRect(&pncsp->rgrc[0], + pncsp->lppos->x, pncsp->lppos->y, + pncsp->lppos->x + pncsp->lppos->cx - BORDER_WIDTH, pncsp->lppos->y + pncsp->lppos->cy - BORDER_WIDTH); + } + else + { + GetWindowRect(hwnd, &pncsp->rgrc[0]); + pncsp->rgrc[0].right -= BORDER_WIDTH; + pncsp->rgrc[0].bottom -= BORDER_WIDTH; + + } + if (BORDER_FLAT == borderType) + { + pncsp->rgrc[0].left += BORDER_WIDTH; + pncsp->rgrc[0].top += BORDER_WIDTH; + } + break; + } + + return 0; +} + +void SkinnedWnd::OnPrint(HDC hdc, UINT options) +{ + if ((PRF_CHECKVISIBLE & options) && !IsWindowVisible(hwnd)) return; + if (PRF_NONCLIENT & options) DrawBorder(hdc); + if (PRF_CLIENT & options) + { + CallPrevWndProc(WM_PRINT, (WPARAM)hdc, (LPARAM)(~(PRF_NONCLIENT | PRF_CHECKVISIBLE) & options)); + } +} + +HPEN SkinnedWnd::GetBorderPen(void) +{ + return (HPEN)MlStockObjects_Get(HILITE_PEN); +} + + +void SkinnedWnd::OnUpdateUIState(UINT uAction, UINT uState) +{ + CallPrevWndProc(WM_UPDATEUISTATE, MAKEWPARAM(uAction, uState), 0L); + uiState =(WORD)SendMessageW(hwnd, WM_QUERYUISTATE, 0, 0L); +} + +void SkinnedWnd::OnStyleChanged(INT styleType, STYLESTRUCT *pss) +{ + if (0 != redrawLock) + { + if (0 != (GWL_STYLE & styleType) && + (WS_VISIBLE & pss->styleOld) != (WS_VISIBLE & pss->styleNew)) + { + redrawLock = 0; + } + } + + CallPrevWndProc(WM_STYLECHANGED, (WPARAM)styleType, (LPARAM)pss); +} + +void SkinnedWnd::OnDwmCompositionChanged(void) +{ + dwmCompositionEnabled = DWM_COMPOSITION_CHECK; +} + +void SkinnedWnd::DisableRedraw() +{ + if (0 == redrawLock) + { + UINT windowStyle = (UINT)GetWindowLongPtrW(hwnd, GWL_STYLE); + if (0 == (WS_VISIBLE & windowStyle)) + return; + + CallDefWndProc(WM_SETREDRAW, FALSE, 0L); + } + + redrawLock++; +} + +void SkinnedWnd::EnableRedraw(SkinnedWndRedraw redrawFlags) +{ + UINT rdwFlags(0); + + if (0 == redrawLock) + return; + + redrawLock--; + if (0 != redrawLock) + return; + + CallDefWndProc(WM_SETREDRAW, TRUE, 0L); + + if (0 != (SWR_INVALIDATE & redrawFlags)) + { + rdwFlags |= RDW_INVALIDATE; + if (0 != (SWR_UPDATE & redrawFlags)) + rdwFlags |= RDW_UPDATENOW; + } + + if (0 != (SWR_ERASE & redrawFlags)) + { + rdwFlags |= RDW_ERASE; + if (0 != (SWR_UPDATE & redrawFlags)) + rdwFlags |= RDW_ERASENOW; + } + + if (0 != rdwFlags) + { + if (0 != (SWR_ALLCHILDREN & redrawFlags)) + rdwFlags |= RDW_ALLCHILDREN; + + RedrawWindow(hwnd, NULL, NULL, rdwFlags); + } + +} + +BOOL SkinnedWnd::OnDirectMouseWheel(INT delta, UINT vtKey, POINTS pts) +{ + SendMessageW(hwnd, WM_MOUSEWHEEL, MAKEWPARAM(vtKey, delta), *((LPARAM*)&pts)); + return TRUE; +} + +void SkinnedWnd::OnGetMinMaxInfo(MINMAXINFO *minMax) +{ + if (NULL == minMax) + return; + + CallPrevWndProc(WM_GETMINMAXINFO, 0, (LPARAM)minMax); + + if (0 != minSize.cx) + minMax->ptMinTrackSize.x = minSize.cx; + + if (0 != minSize.cy) + minMax->ptMinTrackSize.y = minSize.cy; + + if (0 != maxSize.cx) + minMax->ptMaxTrackSize.x = maxSize.cx; + + if (0 != maxSize.cy) + minMax->ptMaxTrackSize.y = maxSize.cy; +} + +LRESULT SkinnedWnd::WindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch(uMsg) + { + case WM_NCHITTEST: return OnNcHitTest(MAKEPOINTS(lParam)); + case WM_NCPAINT: OnNcPaint((HRGN)wParam); return 0; + case WM_NCCALCSIZE: return OnNcCalcSize((BOOL)wParam, (NCCALCSIZE_PARAMS*)lParam); + case WM_PRINT: OnPrint((HDC)wParam, (UINT)lParam); return 0; + case WM_UPDATEUISTATE: OnUpdateUIState(LOWORD(wParam), HIWORD(wParam)); return 0; + case WM_STYLECHANGED: OnStyleChanged((INT)wParam, (STYLESTRUCT*)lParam); return 0; + case WM_SUPPORTREFLECT: + { + BOOL reflectionSupported = (0 != (SWS_REFLECT & wnddata) && CanReflect((UINT)wParam)); + if (0 != (SWS_DIALOG & wnddata)) + { + SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, reflectionSupported); + return TRUE; + } + return reflectionSupported; + } + case WM_ML_IPC: + { + LRESULT result; + if (OnMediaLibraryIPC((INT)lParam, (INT_PTR)wParam, &result)) + { + if (SWS_DIALOG & wnddata) + { + SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, (LONGX86)result); + return TRUE; + } + return result; + } + break; + } + + case WM_DWMCOMPOSITIONCHANGED: OnDwmCompositionChanged(); return 0; + case WM_GETMINMAXINFO: OnGetMinMaxInfo((MINMAXINFO*)lParam); return 0; + + } + + // Reflection + if (0 != (SWS_REFLECT & wnddata)) + { + LRESULT result; + if (ReflectMessage(hwnd, uMsg, wParam, lParam, (0 != (SWS_DIALOG & wnddata)), &result)) + return result; + } + + if ( 0 == (SWS_NO_DIRECT_MOUSE_WHEEL & style) && + WINAMP_WM_DIRECT_MOUSE_WHEEL == uMsg && + WM_NULL != WINAMP_WM_DIRECT_MOUSE_WHEEL && + FALSE != OnDirectMouseWheel(GET_WHEEL_DELTA_WPARAM(wParam), GET_KEYSTATE_WPARAM(wParam), MAKEPOINTS(lParam))) + { + if (0 != (SWS_DIALOG & wnddata)) + SetWindowLongPtrW(hwnd, DWLP_MSGRESULT, TRUE); + return TRUE; + } + + return CallPrevWndProc(uMsg, wParam, lParam); +} + +LRESULT WINAPI SkinnedWnd::WindowProcReal(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + SkinnedWnd *pWnd = (SkinnedWnd*)GetPropW(hwnd, WNDDATAPROPW); + if (!pWnd) + return (IsWindowUnicode(hwnd)) ? DefWindowProcW(hwnd, uMsg, wParam, lParam) : DefWindowProcA(hwnd, uMsg, wParam, lParam); + + switch(uMsg) + { + case WM_NCDESTROY: + { + WNDPROC fnWndProc = pWnd->fnWndProc; + delete(pWnd); + + return IsWindowUnicode(hwnd) ? CallWindowProcW(fnWndProc, hwnd, uMsg, wParam, lParam) : CallWindowProcA(fnWndProc, hwnd, uMsg, wParam, lParam); + } + break; + } + return pWnd->WindowProc(uMsg, wParam, lParam); +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/skinnedwnd.h b/Src/Plugins/General/gen_ml/skinnedwnd.h new file mode 100644 index 00000000..dda147f5 --- /dev/null +++ b/Src/Plugins/General/gen_ml/skinnedwnd.h @@ -0,0 +1,101 @@ +#ifndef NULLOSFT_MEDIALIBRARY_SKINNED_WINDOW_HEADER +#define NULLOSFT_MEDIALIBRARY_SKINNED_WINDOW_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include +#include "./ml.h" +#include "./ml_ipc_0313.h" +#include "./reflectmsg.h" +#include "./stockobjects.h" + + +#define MLWM_SKINCHANGED (WM_APP + 0x2FFF) + +#define BORDER_NONE 0 +#define BORDER_SUNKEN 1 +#define BORDER_FLAT 2 + +typedef enum SkinnedWndRedraw +{ + SWR_NONE = 0, + SWR_INVALIDATE = (1 << 0), + SWR_ERASE = (1 << 1), + SWR_UPDATE = (1 << 2), + SWR_ALLCHILDREN = (1 << 3), +}SkinnedWndRedraw; + +DEFINE_ENUM_FLAG_OPERATORS(SkinnedWndRedraw); + + +class SkinnedWnd +{ +protected: + SkinnedWnd(BOOL bIsDialog); + virtual ~SkinnedWnd(void); + +public: + static SkinnedWnd *GetFromHWND(HWND hwndSkinned); + static BOOL IsDwmCompositionEnabled(); + + BOOL IsUnicode(void); + BOOL IsAttached(void); + INT GetType(void) { return (INT)LOWORD(wnddata); } + HWND GetHWND(void) { return hwnd; } + void SkinChanged(BOOL bNotifyChildren, BOOL bRedraw); + void EnableReflection(BOOL bEnable); + virtual BOOL SetStyle(UINT newStyle, BOOL bRedraw); + virtual UINT GetStyle(void) { return style; } + void SetMinMaxInfo(MLSKINNEDMINMAXINFO *minMax); + +protected: + virtual BOOL Attach(HWND hwndToSkin); + void SetType(INT newType) { ((wnddata & 0xFFFF0000) | (newType & 0xFFFF)); } + LRESULT CallPrevWndProc(UINT uMsg, WPARAM wParam, LPARAM lParam); + LRESULT CallDefWndProc(UINT uMsg, WPARAM wParam, LPARAM lParam); + + virtual LRESULT WindowProc(UINT uMsg, WPARAM wParam, LPARAM lParam); + virtual void OnSkinChanged(BOOL bNotifyChildren, BOOL bRedraw); + virtual void OnSkinUpdated(BOOL bNotifyChildren, BOOL bRedraw); + virtual BOOL OnMediaLibraryIPC(INT msg, INT_PTR param, LRESULT *pResult); + + virtual INT OnNcCalcSize(BOOL bCalcValidRects, NCCALCSIZE_PARAMS *pncsp); + virtual INT OnNcHitTest(POINTS pts); + virtual void OnNcPaint(HRGN rgnUpdate); + virtual void OnPrint(HDC hdc, UINT options); + virtual void DrawBorder(HDC hdc); + virtual void OnDwmCompositionChanged(void); + virtual UINT GetBorderType(void); + virtual HPEN GetBorderPen(void); + void OnUpdateUIState(UINT uAction, UINT uState); + virtual void OnStyleChanged(INT styleType, STYLESTRUCT *pss); + + void DisableRedraw(); + void EnableRedraw(SkinnedWndRedraw redrawFlags); + + virtual BOOL OnDirectMouseWheel(INT delta, UINT vtKey, POINTS pts); + virtual void OnGetMinMaxInfo(MINMAXINFO *minMax); + +public: + static void DrawBorder(HDC hdc, RECT *prc, UINT type, HPEN pen); + +private: + static LRESULT WINAPI WindowProcReal(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + friend BOOL SkinWindowEx(HWND hwndToSkin, INT type, UINT style); + friend BOOL UnskinWindow(HWND hwndToUnskin); + +protected: + HWND hwnd; + UINT style; + WORD uiState; + ULONG redrawLock; + SIZE minSize; + SIZE maxSize; +private: + WNDPROC fnWndProc; + LONG wnddata; +}; + +#endif // NULLOSFT_MEDIALIBRARY_SKINNED_WINDOW_HEADER \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/skinning.cpp b/Src/Plugins/General/gen_ml/skinning.cpp new file mode 100644 index 00000000..5cd180a7 --- /dev/null +++ b/Src/Plugins/General/gen_ml/skinning.cpp @@ -0,0 +1,151 @@ +#include "./skinning.h" +#include "./skinneddlg.h" +#include "./skinnedheader.h" +#include "./skinnedlistview.h" +#include "./skinnedbutton.h" +#include "./skinneddivider.h" +#include "./skinnededit.h" +#include "./skinnedstatic.h" +#include "./skinnedlistbox.h" +#include "./skinnedcombo.h" +#include "./skinnedfolder.h" +#include "./skinnedmenu.h" +#include "./skinnedtooltip.h" +#include "./skinnedprogressbar.h" + +#include "./config.h" +extern C_Config *g_config; + +BOOL SkinWindow(HWND hwndToSkin, UINT style) +{ + return SkinWindowEx(hwndToSkin, SKINNEDWND_TYPE_AUTO, style); +} + +BOOL SkinWindowEx(HWND hwndToSkin, INT type, UINT style) +{ + SkinnedWnd *psw; + if (!hwndToSkin || !IsWindow(hwndToSkin) || SkinnedWnd::GetFromHWND(hwndToSkin)) return FALSE; + + if (SKINNEDWND_TYPE_AUTO == type) + { + wchar_t szName[256] = {0}; + + if (!RealGetWindowClassW(hwndToSkin, szName, sizeof(szName)/sizeof(wchar_t))) + return FALSE; + + if (0== lstrcmpW(szName, L"#32770")) type = SKINNEDWND_TYPE_DIALOG; + else if (0== lstrcmpW(szName, WC_HEADERW)) type = SKINNEDWND_TYPE_HEADER; + else if (0== lstrcmpW(szName, WC_LISTVIEWW)) type = SKINNEDWND_TYPE_LISTVIEW; + else if (0== lstrcmpW(szName, WC_BUTTONW)) type = SKINNEDWND_TYPE_BUTTON; + else if (0== lstrcmpW(szName, WC_EDITW)) type = SKINNEDWND_TYPE_EDIT; + else if (0== lstrcmpW(szName, WC_STATICW)) type = SKINNEDWND_TYPE_STATIC; + else if (0== lstrcmpW(szName, WC_LISTBOXW)) type = SKINNEDWND_TYPE_LISTBOX; + else if (0== lstrcmpW(szName, WC_COMBOBOXW)) type = SKINNEDWND_TYPE_COMBOBOX; + else if (0== lstrcmpW(szName, TOOLTIPS_CLASSW)) type = SKINNEDWND_TYPE_TOOLTIP; + else if (0== lstrcmpW(szName, FOLDERBROWSER_NAME)) type = SKINNEDWND_TYPE_FOLDERBROWSER; + else if (0== lstrcmpW(szName, PROGRESS_CLASSW)) type = SKINNEDWND_TYPE_PROGRESSBAR; + else + { + if (0 != ((WS_VSCROLL | WS_HSCROLL) & GetWindowLongPtrW(hwndToSkin, GWL_STYLE))) + type = SKINNEDWND_TYPE_SCROLLWND; + else + type = SKINNEDWND_TYPE_WINDOW; + } + } + + switch(type) + { + case SKINNEDWND_TYPE_WINDOW: psw = new SkinnedWnd(FALSE); break; + case SKINNEDWND_TYPE_SCROLLWND: psw = new SkinnedScrollWnd(FALSE); break; + case SKINNEDWND_TYPE_DIALOG: psw = new SkinnedDialog(); break; + case SKINNEDWND_TYPE_HEADER: psw = new SkinnedHeader(); break; + case SKINNEDWND_TYPE_LISTVIEW: psw = new SkinnedListView(); break; + case SKINNEDWND_TYPE_BUTTON: psw = new SkinnedButton(); break; + case SKINNEDWND_TYPE_DIVIDER: psw = new SkinnedDivider(); break; + case SKINNEDWND_TYPE_EDIT: psw = new SkinnedEdit(); break; + case SKINNEDWND_TYPE_STATIC: psw = new SkinnedStatic(); break; + case SKINNEDWND_TYPE_LISTBOX: psw = new SkinnedListbox(); break; + case SKINNEDWND_TYPE_COMBOBOX: psw = new SkinnedCombobox(); break; + case SKINNEDWND_TYPE_FOLDERBROWSER: psw = new SkinnedFolderBrowser(); break; + case SKINNEDWND_TYPE_TOOLTIP: psw = new SkinnedToolTip(); break; + case SKINNEDWND_TYPE_PROGRESSBAR: psw = new SkinnedProgressBar(); break; + default: psw = NULL; break; + } + + if (!psw) return FALSE; + + if (!psw->Attach(hwndToSkin)) + { + delete(psw); + return FALSE; + } + psw->SetStyle(style, FALSE); + + return TRUE; +} + +BOOL UnskinWindow(HWND hwndToUnskin) +{ + SkinnedWnd *psw; + + psw = SkinnedWnd::GetFromHWND(hwndToUnskin); + if (!psw) return FALSE; + delete (psw); + + return TRUE; +} + +BOOL TrackSkinnedPopupMenuEx(HMENU hmenu, UINT fuFlags, INT x, INT y, HWND hwnd, LPTPMPARAMS lptpm, HMLIMGLST hmlil, + INT width, UINT skinStyle, MENUCUSTOMIZEPROC customProc, ULONG_PTR customParam) +{ + if (!hmenu) return FALSE; + SkinnedMenu skinnedMenu; + return skinnedMenu.TrackMenuPopupEx(hmenu, fuFlags, x, y, hwnd, lptpm, skinStyle, hmlil, width, customProc, customParam); +} + +HANDLE InitSkinnedPopupHook(HWND hwndOwner, HMLIMGLST hmlil, INT width, UINT skinStyle, MENUCUSTOMIZEPROC customProc, ULONG_PTR customParam) +{ + SkinnedMenu *psm = new SkinnedMenu(); + if (NULL != psm && + FALSE == psm->InitializeHook(hwndOwner, skinStyle, hmlil, width, customProc, customParam)) + { + delete(psm); + psm = NULL; + } + + return (HANDLE)psm; +} + +void RemoveSkinnedPopupHook(HANDLE hPopupHook) +{ + SkinnedMenu *psm = (SkinnedMenu*)hPopupHook; + if (NULL != psm) + delete(psm); +} + +#define SKINNEDMENU_DEFAULT TRUE +BOOL IsSkinnedPopupEnabled(BOOL fIgnoreCache) +{ + static INT fUseSkinnedMenus = -1; + + if (FALSE != fIgnoreCache) + fUseSkinnedMenus = -1; + + if (-1 == fUseSkinnedMenus) + { + fUseSkinnedMenus = (NULL != g_config) ? + (0 != g_config->ReadInt(L"skinned_menus", SKINNEDMENU_DEFAULT)) : + SKINNEDMENU_DEFAULT; + } + + return (FALSE != fUseSkinnedMenus); +} + +BOOL EnableSkinnedPopup(BOOL fEnable) +{ + if (NULL == g_config) + return FALSE; + + g_config->WriteInt(L"skinned_menus", (FALSE != fEnable)); + return (fEnable == IsSkinnedPopupEnabled(TRUE)); +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/skinning.h b/Src/Plugins/General/gen_ml/skinning.h new file mode 100644 index 00000000..ccb0d7c9 --- /dev/null +++ b/Src/Plugins/General/gen_ml/skinning.h @@ -0,0 +1,26 @@ +#ifndef NULLOSFT_MEDIALIBRARY_SKINNING_HEADER +#define NULLOSFT_MEDIALIBRARY_SKINNING_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include + +typedef LPVOID HMLIMGLST; +typedef INT (CALLBACK *MENUCUSTOMIZEPROC)(INT /*action*/, HMENU /*hMenu*/, HDC /*hdc*/, LPARAM /*param*/, ULONG_PTR /*user*/); + +BOOL SkinWindow(HWND hwndToSkin, UINT style); +BOOL SkinWindowEx(HWND hwndToSkin, INT type, UINT style); +BOOL UnskinWindow(HWND hwndToUnskin); +BOOL TrackSkinnedPopupMenuEx(HMENU hmenu, UINT fuFlags, INT x, INT y, HWND hwnd, LPTPMPARAMS lptpm, + HMLIMGLST hmlil, INT width, UINT skinStyle, MENUCUSTOMIZEPROC customProc, ULONG_PTR customParam); + +BOOL IsSkinnedPopupEnabled(BOOL fIgnoreCache); +BOOL EnableSkinnedPopup(BOOL fEnable); + +// you can call this from WM_CONTEXTMENU +HANDLE InitSkinnedPopupHook(HWND hwndOwner, HMLIMGLST hmlil, INT width, UINT skinStyle, MENUCUSTOMIZEPROC customProc, ULONG_PTR customParam); +void RemoveSkinnedPopupHook(HANDLE hPopupHook); + +#endif //NULLOSFT_MEDIALIBRARY_SKINNING_HEADER \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/stockobjects.cpp b/Src/Plugins/General/gen_ml/stockobjects.cpp new file mode 100644 index 00000000..80da7353 --- /dev/null +++ b/Src/Plugins/General/gen_ml/stockobjects.cpp @@ -0,0 +1,215 @@ +#include "main.h" +#include "./stockobjects.h" +#include "./colors.h" + + +typedef struct MlStockObjects +{ + CRITICAL_SECTION lock; + HDC cachedDc; + HFONT skinFont; + HFONT systemFont; + HBRUSH brushes[BRUSH_MAX - BRUSH_MIN + 1]; + HPEN pens[PEN_MAX - PEN_MIN + 1]; +} MlStockObjects; + +static MlStockObjects stockObjects; + +static void +MlStockObjects_ResetEx(MlStockObjects *self) +{ + INT index; + if (NULL != self->cachedDc) + { + DeleteDC(self->cachedDc); + self->cachedDc = NULL; + } + + if (NULL != self->skinFont) + { + DeleteObject(self->skinFont); + self->skinFont = NULL; + } + + if (NULL != self->systemFont) + { + DeleteObject(self->systemFont); + self->systemFont = NULL; + } + + for (index = 0; index < ARRAYSIZE(self->brushes); index++) + { + if (NULL != self->brushes[index]) + DeleteObject(self->brushes[index]); + } + ZeroMemory(self->brushes, sizeof(self->brushes)); + + for (index = 0; index < ARRAYSIZE(self->pens); index++) + { + if (NULL != self->pens[index]) + DeleteObject(self->pens[index]); + } + ZeroMemory(self->pens, sizeof(self->pens)); + +} + +static BYTE +MlStockObjects_GetSysFontQuality() +{ + BOOL smoothingEnabled; + if (FALSE == SystemParametersInfoW(SPI_GETFONTSMOOTHING, 0, &smoothingEnabled, 0) || + FALSE == smoothingEnabled) + { + return DEFAULT_QUALITY; + } + + UINT smootingType; + if (FALSE == SystemParametersInfoW(SPI_GETFONTSMOOTHINGTYPE, 0, &smootingType, 0)) + return DEFAULT_QUALITY; + + if (FE_FONTSMOOTHINGCLEARTYPE == smootingType) + return CLEARTYPE_QUALITY; + + return ANTIALIASED_QUALITY; +} + +static HDC +MlStockObjects_GetCachedDc(MlStockObjects *self) +{ + if (NULL == self->cachedDc) + { + HDC baseDC = GetDCEx(g_hwnd, NULL, DCX_WINDOW | DCX_CACHE | DCX_NORESETATTRS); + self->cachedDc = CreateCompatibleDC(baseDC); + ReleaseDC(g_hwnd, baseDC); + } + return self->cachedDc; +} + +static HFONT +MlStockObjects_GetSkinFont(MlStockObjects *self) +{ + if (NULL == self->skinFont && + NULL != g_config && + 0 != g_config->ReadInt(L"plfont_everywhere", 1)) + { + INT height = (INT)SendMessageW(plugin.hwndParent, WM_WA_IPC, 3, IPC_GET_GENSKINBITMAP); + DWORD charset = (DWORD)SendMessageW(plugin.hwndParent, WM_WA_IPC, 2, IPC_GET_GENSKINBITMAP); + char *fontname = (char*)SendMessageW(plugin.hwndParent, WM_WA_IPC, 1, IPC_GET_GENSKINBITMAP); + self->skinFont = CreateFontA(-height, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, charset, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DRAFT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, fontname); + } + + return self->skinFont; +} + + +static HFONT +MlStockObjects_GetSystemFont(MlStockObjects *self) +{ + if (NULL == self->systemFont) + { + LOGFONTW lf; + + if (FALSE == SystemParametersInfoW(SPI_GETICONTITLELOGFONT, sizeof(lf), &lf, 0)) + return NULL; + + lf.lfQuality = MlStockObjects_GetSysFontQuality(); + self->systemFont = CreateFontIndirectW(&lf); + } + + return self->systemFont; +} + +static HBRUSH +MlStockObjects_GetBrush(MlStockObjects *self, UINT type) +{ + if (NULL == self->brushes[type - BRUSH_MIN]) + { + INT color; + switch(type) + { + case WNDBCK_BRUSH: color = WADLG_WNDBG; break; + case ITEMBCK_BRUSH: color = WADLG_ITEMBG; break; + case HILITE_BRUSH: color = WADLG_HILITE; break; + case ITEMTEXT_BRUSH: color = WADLG_ITEMFG; break; + default: return NULL; + } + self->brushes[type - BRUSH_MIN] = CreateSolidBrush(WADlg_getColor(color)); + } + return self->brushes[type - BRUSH_MIN]; +} + +static HPEN +MlStockObjects_GetPen(MlStockObjects *self, UINT type) +{ + if (NULL == self->pens[type - PEN_MIN]) + { + COLORREF rgb; + switch(type) + { + case HILITE_PEN: rgb = WADlg_getColor(WADLG_HILITE); break; + case HEADERTOP_PEN: rgb = WADlg_getColor(WADLG_LISTHEADER_FRAME_TOPCOLOR); break; + case HEADERMIDDLE_PEN: rgb = WADlg_getColor(WADLG_LISTHEADER_FRAME_MIDDLECOLOR); break; + case HEADERBOTTOM_PEN: rgb = WADlg_getColor(WADLG_LISTHEADER_FRAME_BOTTOMCOLOR); break; + case WNDBCK_PEN: rgb = WADlg_getColor(WADLG_WNDBG); break; + case MENUBORDER_PEN: MLGetSkinColor(MLSO_MENU, MP_FRAME, 0, &rgb); break; + case TOOLTIPBORDER_PEN: MLGetSkinColor(MLSO_TOOLTIP, TTP_FRAME, 0, &rgb); break; + case ITEMBCK_PEN: rgb = WADlg_getColor(WADLG_ITEMBG); break; + case ITEMTEXT_PEN: rgb = WADlg_getColor(WADLG_ITEMFG); break; + default: return NULL; + } + self->pens[type - PEN_MIN] = CreatePen(PS_SOLID, 0, rgb); + } + + return self->pens[type - PEN_MIN]; +} + +static HANDLE +MlStockObjects_GetEx(MlStockObjects *self, UINT type) +{ + HANDLE handle; + EnterCriticalSection(&self->lock); + + if (CACHED_DC == type) + handle = MlStockObjects_GetCachedDc(self); + else if (SKIN_FONT == type) + handle = MlStockObjects_GetSkinFont(self); + else if (DEFAULT_FONT == type) + handle = MlStockObjects_GetSystemFont(self); + else if (BRUSH_MIN <= type && BRUSH_MAX >= type) + handle = MlStockObjects_GetBrush(self, type); + else if (PEN_MIN <= type && PEN_MAX >= type) + handle = MlStockObjects_GetPen(self, type); + else + handle = NULL; + + LeaveCriticalSection(&self->lock); + + return handle; +} + + +void +MlStockObjects_Init() +{ + ZeroMemory(&stockObjects, sizeof(MlStockObjects)); + InitializeCriticalSection(&stockObjects.lock); +} + +void +MlStockObjects_Free() +{ + MlStockObjects_ResetEx(&stockObjects); + DeleteCriticalSection(&stockObjects.lock); +} + +HANDLE +MlStockObjects_Get(UINT type) +{ + return MlStockObjects_GetEx(&stockObjects, type); +} + +void +MlStockObjects_Reset() +{ + MlStockObjects_ResetEx(&stockObjects); +} diff --git a/Src/Plugins/General/gen_ml/stockobjects.h b/Src/Plugins/General/gen_ml/stockobjects.h new file mode 100644 index 00000000..1f4295d3 --- /dev/null +++ b/Src/Plugins/General/gen_ml/stockobjects.h @@ -0,0 +1,42 @@ +#ifndef NULLOSFT_MEDIALIBRARY_STOCK_OBJECTS_HEADER +#define NULLOSFT_MEDIALIBRARY_STOCK_OBJECTS_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include + +#define CACHED_DC 0x0000 + +#define WNDBCK_BRUSH 0x0001 +#define ITEMBCK_BRUSH 0x0002 +#define HILITE_BRUSH 0x0003 +#define ITEMTEXT_BRUSH 0x0004 + +#define BRUSH_MIN 0x0001 +#define BRUSH_MAX 0x0004 + +#define PEN_MIN 0x0020 +#define PEN_MAX 0x0028 + +#define HILITE_PEN 0x0020 +#define HEADERTOP_PEN 0x0021 +#define HEADERMIDDLE_PEN 0x0022 +#define HEADERBOTTOM_PEN 0x0023 +#define WNDBCK_PEN 0x0024 +#define MENUBORDER_PEN 0x0025 +#define TOOLTIPBORDER_PEN 0x0026 +#define ITEMBCK_PEN 0x0027 +#define ITEMTEXT_PEN 0x0028 + +#define SKIN_FONT 0x0100 +#define DEFAULT_FONT 0x0101 + + +void MlStockObjects_Init(); +void MlStockObjects_Free(); +void MlStockObjects_Reset(); +HANDLE MlStockObjects_Get(UINT type); + +#endif // NULLOSFT_MEDIALIBRARY_STOCK_OBJECTS_HEADER \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/stringvector.cpp b/Src/Plugins/General/gen_ml/stringvector.cpp new file mode 100644 index 00000000..a6a6ffb9 --- /dev/null +++ b/Src/Plugins/General/gen_ml/stringvector.cpp @@ -0,0 +1,130 @@ +#include "./stringvector.h" +#include + +StringVector::StringVector(size_t cchAllocate, int cchAllocateStep) : pointers(NULL), ptCount(0), ptAllocated(0) +{ + if (cchAllocate) + { + cchBuffer = cchAllocate; + buffer = (wchar_t*)HeapAlloc(GetProcessHeap(), NULL, cchBuffer * sizeof(wchar_t)); + } + else + { + cchBuffer = 0; + buffer = NULL; + } + + tail = buffer; + allocateStep = cchAllocateStep; +} + +StringVector::~StringVector(void) +{ + if (buffer) + { + HANDLE hHeap = GetProcessHeap(); + HeapFree(hHeap, NULL, buffer); + cchBuffer = 0; + tail = NULL; + buffer = NULL; + if (pointers) + { + HeapFree(hHeap, NULL, pointers); + ptCount = 0; + ptAllocated =0; + pointers = NULL; + } + } +} +size_t StringVector::Count(void) { return ptCount; } +size_t StringVector::GetCbAllocated(void){ return cchBuffer * sizeof(wchar_t); } +size_t StringVector::GetCchAllocated(void) { return cchBuffer; } +LPCWSTR StringVector::GetString(size_t index) { return (index >= 0 && index < ptCount) ? buffer + pointers[index].offset : NULL; } +int StringVector::GetStringLength(size_t index) { return (index >= 0 && index < ptCount) ? pointers[index].length : -1; } + +void StringVector::Clear(void) +{ + tail = buffer; + ptCount = 0; +} + +int StringVector::SetAllocateStep(int cchNewStep) +{ + if (cchNewStep < 1) return allocateStep; + int tmp = allocateStep; + allocateStep = cchNewStep; + return tmp; +} + +size_t StringVector::Add(const wchar_t *entry, int cchLen) +{ + if (!entry) return -1; + if (cchLen < 0) cchLen = lstrlenW(entry); + if (cchLen == 0) return -1; + + size_t cchNeed = (size_t)(tail - buffer) + cchLen + 2; + + if (cchBuffer < cchNeed) + { + while ( cchBuffer < cchNeed) cchBuffer += allocateStep; + size_t offset_tail = (size_t)(tail - buffer); + buffer = (wchar_t*)( (buffer) ? HeapReAlloc(GetProcessHeap(), NULL, buffer, cchBuffer*sizeof(wchar_t)) : HeapAlloc(GetProcessHeap(), NULL, cchBuffer*sizeof(wchar_t))); + tail = buffer + offset_tail; + } + if (S_OK != StringCchCopyNW(tail, cchBuffer - (size_t)(tail - buffer), entry, cchLen)) return -1; + + if (ptCount == ptAllocated) + { + ptAllocated += 24; + pointers = (HRECORD)( (pointers) ? HeapReAlloc(GetProcessHeap(), NULL, pointers, ptAllocated*sizeof(RECORD)) : HeapAlloc(GetProcessHeap(), NULL, ptAllocated*sizeof(RECORD))); + if (pointers == NULL) + { + DWORD err = GetLastError(); + err += 1; + } + } + pointers[ptCount].offset = tail - buffer; + pointers[ptCount].length = cchLen; + + tail += cchLen + 1; + ptCount++; + + return ptCount -1; +} + +BOOL StringVector::Remove(size_t index) +{ + if (index < 0 || index >= ptCount) return FALSE; + + ptCount--; + for (size_t i = index; i < ptCount; i++) pointers[i] = pointers[i + 1]; + return TRUE; +} + +void StringVector::TrimCount(size_t newCount) +{ + if (newCount >= ptCount) return; + if (newCount <= 0) { Clear(); return; } + tail = buffer + pointers[newCount].offset; + ptCount = newCount; +} + +size_t StringVector::FindString(LCID lcid, LPCWSTR string, int cchLen, BOOL igonreCase) +{ + if (!string) return -1; + if (cchLen < 0) cchLen = lstrlenW(string); + if (cchLen == 0) return -1; + + for (size_t i = 0; i < ptCount; i ++) + { + if ((cchLen == pointers[i].length) && + ( string == (buffer + pointers[i].offset) || CSTR_EQUAL == CompareStringW(lcid, (igonreCase) ? NORM_IGNORECASE : NULL, + buffer + pointers[i].offset, + cchLen, + string, + cchLen))) + return i; + + } + return -1; +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/stringvector.h b/Src/Plugins/General/gen_ml/stringvector.h new file mode 100644 index 00000000..499d1b05 --- /dev/null +++ b/Src/Plugins/General/gen_ml/stringvector.h @@ -0,0 +1,51 @@ +#ifndef NULLOSFT_MEDIALIBRARY_STRINGVECTOR_HEADER +#define NULLOSFT_MEDIALIBRARY_STRINGVECTOR_HEADER + + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +// Manages strings as array. Strings a copied to the continues buffer which make it fast to access data + +#include + +class StringVector +{ + +public: + StringVector(size_t cchAllocate = 0, int cchAllocateStep = 32); + virtual ~StringVector(void); + +public: + + size_t Add(const wchar_t *entry, int cchLen); // returns index of a new created string object; + size_t Add(const wchar_t *entry) { return Add(entry, -1); } + + BOOL Remove(size_t index); + size_t Count(void); // returns count; + + void TrimCount(size_t newCount); + void Clear(void); // makes it empty; + // void Vacuum(void); // compact space; + LPCWSTR GetString(size_t index); // returns string from index; + int GetStringLength(size_t index); // returns string length; + size_t FindString(LCID lcid, LPCWSTR string, int cchLen = -1, BOOL igonreCase = FALSE); // returns index of the string if found or -1 if no string. (uses CompareString() function) + int SetAllocateStep(int cchNewStep); // Sets new allocate step and returns previous one (if newStep < 1 then just return current one). + size_t GetCbAllocated(void); // returns amount of allocated memory + size_t GetCchAllocated(void); // returns amount of allocated memory + +private: + typedef struct _RECORD { size_t offset; int length; } RECORD, *HRECORD; + +protected: + HRECORD pointers; + size_t ptCount; + size_t ptAllocated; + LPWSTR buffer; + size_t cchBuffer; + LPWSTR tail; + int allocateStep; +}; + +#endif //NULLOSFT_MEDIALIBRARY_STRINGVECTOR_HEADER \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/unused.cpp b/Src/Plugins/General/gen_ml/unused.cpp new file mode 100644 index 00000000..0b76cef7 --- /dev/null +++ b/Src/Plugins/General/gen_ml/unused.cpp @@ -0,0 +1,7 @@ +#if 0 + + + + + +#endif \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/util.cpp b/Src/Plugins/General/gen_ml/util.cpp new file mode 100644 index 00000000..6737bfb5 --- /dev/null +++ b/Src/Plugins/General/gen_ml/util.cpp @@ -0,0 +1,84 @@ +#include "./main.h" +#include +#include +#include "./config.h" +#include "./resource.h" + +#include "../nu/AutoChar.h" +#include "../nu/AutoWide.h" +#include "api__gen_ml.h" +#include +#include + +void myOpenURLWithFallback(HWND hwnd, wchar_t *loc, wchar_t *fallbackLoc) +{ + bool override=false; + if (loc) + { + WASABI_API_SYSCB->syscb_issueCallback(SysCallback::BROWSER, BrowserCallback::ONOPENURL, reinterpret_cast(loc), reinterpret_cast(&override)); + } + if (!override && fallbackLoc) + ShellExecuteW(hwnd, L"open", fallbackLoc, NULL, NULL, SW_SHOWNORMAL); +} + +void FixAmps(char *str, size_t len) +{ + size_t realSize=0; + size_t extra=0; + char *itr = str; + while (itr && *itr) + { + if (*itr == '&') + extra++; + itr++; + realSize++; + } + + extra = min(len-(realSize+1), extra); + + while (extra) + { + str[extra+realSize]=str[realSize]; + if (str[realSize] == '&') + { + extra--; + str[extra+realSize]='&'; + } + realSize--; + } +} + +LPWSTR FixAmpsW(LPWSTR pszText, INT cchMaxText) +{ + INT realSize, extra; + LPWSTR itr; + + for (itr = pszText, extra = 0; NULL != *itr; itr++) if (L'&' == *itr) extra++; + + realSize = (INT)(itr - pszText); + + for (extra = min(cchMaxText - (realSize + 1), extra); extra > 0; realSize--) + { + pszText[extra+realSize] = pszText[realSize]; + if (L'&' == pszText[realSize]) + { + extra--; + pszText[extra+realSize] = L'&'; + } + } + return pszText; +} + +bool IsVista() +{ + static INT fVista = -1; + + if (-1 == fVista) + { + OSVERSIONINFO osver; + osver.dwOSVersionInfoSize = sizeof( OSVERSIONINFO ); + fVista = ( ::GetVersionEx(&osver) && osver.dwPlatformId == VER_PLATFORM_WIN32_NT && (osver.dwMajorVersion >= 6 )) ? 1 : 0; + } + + return (1 == fVista); +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/version.rc2 b/Src/Plugins/General/gen_ml/version.rc2 new file mode 100644 index 00000000..a7b753d4 --- /dev/null +++ b/Src/Plugins/General/gen_ml/version.rc2 @@ -0,0 +1,39 @@ + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// +#include "..\..\..\Winamp/buildType.h" +VS_VERSION_INFO VERSIONINFO + FILEVERSION 3,78,0,0 + PRODUCTVERSION WINAMP_PRODUCTVER + FILEFLAGSMASK 0x17L +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", "Winamp SA" + VALUE "FileDescription", "Winamp General Purpose Plug-in" + VALUE "FileVersion", "3,78,0,0" + VALUE "InternalName", "Nullsoft Media Library" + VALUE "LegalCopyright", "Copyright © 2003-2023 Winamp SA" + VALUE "LegalTrademarks", "Nullsoft and Winamp are trademarks of Winamp SA" + VALUE "OriginalFilename", "gen_ml.dll" + VALUE "ProductName", "Winamp" + VALUE "ProductVersion", STR_WINAMP_PRODUCTVER + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END diff --git a/Src/Plugins/General/gen_ml/view_mb.h b/Src/Plugins/General/gen_ml/view_mb.h new file mode 100644 index 00000000..ce4a599b --- /dev/null +++ b/Src/Plugins/General/gen_ml/view_mb.h @@ -0,0 +1,129 @@ +#ifndef _VIEW_MB_H_ +#define _VIEW_MB_H_ + +#include "main.h" +#include "contnr.h" +#include "evntsink.h" +#include "childwnd.h" +#include "resource.h" +#include "..\winamp\wa_dlg.h" +#include +#include "mbutil.h" +#include "config.h" +extern void gracenoteCancelRequest(); +// --------------------------------------------------------------- +// class viewMBHandler is a singleton class - +// All public members are static, including +// viewMBHandler :: DialogProc. +// CTOR and DTOR are public (and not static) +// the only instance of viewMBHandler is private, +// theVmb; +// --------------------------------------------------------- +// --------------------------------------------------------- +// this class has the basic responsibiliity of creating (and +// handling the messages for) an IEControl window. +// It is a prototype for the Advertising window, +// which will be a similar class, but without the extraneous +// stuff. ie There are currently navigation buttons for which +// messages must be handled. These should disappear +// in the final class, along with the gracenote stuff. +// +// Ben Pontius +// --------------------------------------------------------------- +class viewMBHandler +{ +public: + // --------------------------------------------------------------- + friend BOOL view_mbDialogProc (HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); +private: + // --------------------------------------------------------------- + // CTOR + // --------------------------------------------------------------- + viewMBHandler () + : m_hwnd (0), + m_pweb (0), + m_contnr (0), + m_event (0), + m_eventCookie (0), + m_defurl (1), + m_tmpurl (0), + loc_oldWndProc (0) + { + } + // --------------------------------------------------------------- + // DTOR - public (because the compiler demanded it.) + // --------------------------------------------------------------- + ~viewMBHandler () + { + // --------------------------------------------------------------- + gracenoteCancelRequest (); + // --------------------------------------------------------------- + if (m_contnr != 0) + { + destroyIEControl (); + } + if (m_tmpurl) + { + free (m_tmpurl); + m_tmpurl = 0; + } + m_hwnd = 0; + } +public: + // --------------------------------------------------------------- + static void Refresh (int defurl); + // --------------------------------------------------------------- + static void SetDesc (char *desc); + // --------------------------------------------------------------- + static void Navigate (char *s); + // --------------------------------------------------------------- + static void navigateGracenoteTuid (); + // --------------------------------------------------------------- + // the central (CALLBACK) function + // --------------------------------------------------------------- + static BOOL CALLBACK DialogProc (HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); + // --------------------------------------------------------------- + static BOOL CALLBACK newWndProc (HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); + // --------------------------------------------------------------- + static void ContextMenu (HWND parent); + // --------------------------------------------------------------- + static viewMBHandler &getInstance () + { + if (0 == pVmb) + { + pVmb = new viewMBHandler; + } + return *pVmb; + } + +protected: + // --------------------------------------------------------------- + IConnectionPoint *GetConnectionPoint(REFIID riid); + // --------------------------------------------------------------- + void ConnectEvents (); + // --------------------------------------------------------------- + // bp IEControl creation: + // --------------------------------------------------------------- + void createIEControl (); + // --------------------------------------------------------------- + // BP - IEControl destruction + // --------------------------------------------------------------- + void destroyIEControl (); + // --------------------------------------------------------------- + void NavigateToName (LPCTSTR pszUrl); + +private: + + IWebBrowser2 *m_pweb; + HWND m_hwnd; + CContainer *m_contnr; + CEventSink *m_event; + DWORD m_eventCookie; + int m_defurl; + char *m_tmpurl; + WNDPROC loc_oldWndProc; + static viewMBHandler *pVmb; + +}; + +#endif // _VIEW_MB_H_ diff --git a/Src/Plugins/General/gen_ml/view_ml.cpp b/Src/Plugins/General/gen_ml/view_ml.cpp new file mode 100644 index 00000000..26a65c04 --- /dev/null +++ b/Src/Plugins/General/gen_ml/view_ml.cpp @@ -0,0 +1,1723 @@ +#include "main.h" +#include "api__gen_ml.h" +#include +#include +#include +#include "../winamp/gen.h" +#include "resource.h" +#include "../nu/ns_wc.h" +#include "config.h" +#include "../winamp/ipc_pe.h" +#include "../winamp/wa_dlg.h" + +#include "ml.h" +#include "ml_ipc.h" +#include "sendto.h" +#include "../gen_hotkeys/wa_hotkeys.h" +#include "MediaLibraryCOM.h" +#include "../nu/CCVersion.h" +#include "../nu/AutoWide.h" +#include "../nu/AutoChar.h" + +#include "./navigation.h" + +#include "./ml_imagelist.h" +#include "./ml_imagefilter.h" +#include "./imagefilters.h" // default filters + +#include +#include "./ml_ipc_0313.h" +#include "./skinning.h" +#include "./ml_ratingcolumn.h" +#include "./ml_cloudcolumn.h" +#include "./colors.h" +#include "./stockobjects.h" +#include "./service.h" +#include "./skinnedbutton.h" +#include "../Winamp/wasabicfg.h" +#ifdef _DEBUG +#define BETA +#endif + +// messages +#define WMML_FIRST (WM_APP + 0) +#define WMML_UPDATEVIEW (WMML_FIRST + 1) +#define WMML_SHOWCONTAINER (WMML_FIRST + 10) + +#define BLOCK_ACCELTOGGLE_PROP TEXT("BLOCK_ACCELTOGGLE") + +// timers +#define TIMER_UPDATEVIEW_ID 1970 +#define TIMER_UPDATEVIEW_DELAY 7 +#define TIMER_NAVAUTOSCROLL_ID 1971 +#define TIMER_NAVAUTOSCROLL_DELAY 80 +#define TIMER_NAVAUTOEXPAND_ID 1972 +#define TIMER_NAVAUTOEXPAND_DELAY 500 +#define TIMER_UNBLOCKACCELTOGGLE_ID 1973 +#define TIMER_UNBLOCKACCELTOGGLE_DELAY 200 + +#define IDC_CURRENTVIEW 0x1001 +#define IDC_NAVIGATION 0x03FD // (this is the same that old versions used) + + +#define MEDIALIBRARY_HELP_URL L"https://help.winamp.com/hc/articles/8105304048660-The-Winamp-Media-Library" + +SendToMenu *main_sendtomenu; +HMENU main_sendto_hmenu; +int main_sendto_mode; + +extern "C" HWND g_ownerwnd; + +static int ldiv_clickoffs, ldiv_paintleftborder; +static WNDPROC ldiv_oldWndProc; +static WNDPROC add_oldWndProc; + +HNAVITEM g_treedrag_lastSel; +static HNAVITEM m_query_moving_dragplace; +static HNAVITEM m_query_moving_item, m_query_moving_lastdest; +static int m_query_moving_dragplaceisbelow; +static int m_query_moving; +static int m_query_moving_type; + +HWND m_curview_hwnd = NULL; + +HNAVCTRL hNavigation = NULL; // navigation control +HMLIMGLST hmlilRating = NULL; // default rating images +HMLIMGLST hmlilCloud = NULL; // default cloud images +UINT ratingGlobalStyle = RATING_DEFAULT_STYLE; + +static HMLIMGLST hmlilNavigation = NULL; // default navigation image list +HMLIMGFLTRMNGR hmlifMngr = NULL; /// default image filters + +static HRGN g_rgnUpdate = NULL; +static INT divider_pos; + +static BOOL firstShow = TRUE; +static BOOL m_nav_autoscroll = FALSE; +static HNAVITEM m_nav_autoexpand_item = NULL; + +static HIMAGELIST m_nav_dragitem_imagelist = NULL; + +void OpenMediaLibraryPreferences(); + +typedef struct _LAYOUT +{ + INT id; + HWND hwnd; + INT x; + INT y; + INT cx; + INT cy; + DWORD flags; + HRGN rgn; +}LAYOUT, PLAYOUT; + +#define SETLAYOUTPOS(_layout, _x, _y, _cx, _cy) { _layout->x=_x; _layout->y=_y;_layout->cx=_cx;_layout->cy=_cy;_layout->rgn=NULL; } + +static void LayoutWindows( HWND hwnd, INT divX, BOOL fRedraw ) +{ + static BOOL useDefer = ( GetVersion() < 0x80000000 ); + static INT controls[] = { IDC_BTN_LIB, IDC_NAVIGATION, IDC_VDELIM, IDC_CURRENTVIEW, IDC_NO_VIEW }; + RECT rc, ri, roTree; + LAYOUT layout[ sizeof( controls ) / sizeof( controls[ 0 ] ) ], *pl; + + INT divCX = 0, btnY; + + GetClientRect( hwnd, &rc ); + if ( rc.bottom == rc.top || rc.left == rc.right ) + return; + + if ( rc.bottom > WASABI_API_APP->getScaleY( 2 ) ) + rc.bottom -= WASABI_API_APP->getScaleY( 2 ); + + HRGN rgn = CreateRectRgn( 0, 0, 0, 0 ); + + if ( divX > rc.right - WASABI_API_APP->getScaleY( 41 ) ) + divX = rc.right - WASABI_API_APP->getScaleY( 9 ); + + if ( divX < WASABI_API_APP->getScaleY( 32 ) ) + divX = 0; + else if ( divX > ( rc.right - rc.left ) ) + divX = ( rc.right - rc.left ); + + pl = layout; + btnY = rc.bottom; // in case button is broken + + InvalidateRgn( hwnd, NULL, TRUE ); + + for ( int i = 0; i < sizeof( controls ) / sizeof( controls[ 0 ] ); i++ ) + { + pl->id = controls[ i ]; + switch ( pl->id ) + { + case IDC_NAVIGATION: pl->hwnd = NavCtrlI_GetHWND( hNavigation ); break; + case IDC_CURRENTVIEW: pl->hwnd = m_curview_hwnd; break; + default: pl->hwnd = GetDlgItem( hwnd, pl->id ); break; + } + + if ( !pl->hwnd ) + continue; + + pl->rgn = NULL; + GetWindowRect( pl->hwnd, &ri ); + MapWindowPoints( HWND_DESKTOP, hwnd, (LPPOINT)&ri, 2 ); + + pl->flags = SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOREDRAW | SWP_NOCOPYBITS; + + switch ( pl->id ) + { + case IDC_BTN_LIB: + { + wchar_t buffer[ 128 ] = { 0 }; + GetWindowTextW( pl->hwnd, buffer, ARRAYSIZE( buffer ) ); + LRESULT idealSize = MLSkinnedButton_GetIdealSize( pl->hwnd, buffer ); + btnY = WASABI_API_APP->getScaleY( HIWORD( idealSize ) ); + SETLAYOUTPOS( pl, rc.left, rc.bottom - btnY, rc.left + divX, btnY ); + break; + } + case IDC_NAVIGATION: + GetClientRect( pl->hwnd, &roTree ); + NavCtrlI_MapPointsTo( hNavigation, hwnd, (LPPOINT)&roTree, 2 ); + SETLAYOUTPOS( pl, rc.left, rc.top, rc.left + divX, rc.bottom - btnY - WASABI_API_APP->getScaleY( 3 ) ); + ldiv_paintleftborder = ( pl->cx > 0 ); + break; + case IDC_VDELIM: + divCX = ( ri.right - ri.left ); + SETLAYOUTPOS( pl, divX + WASABI_API_APP->getScaleX( 1 ), rc.top, divCX, max( 0, rc.bottom - rc.top ) ); + break; + case IDC_CURRENTVIEW: // current view; + SETLAYOUTPOS( pl, divX + divCX, rc.top, max( 0, rc.right - pl->x - WASABI_API_APP->getScaleX( 2 ) ), max( 0, rc.bottom - rc.top ) ); + if ( !SendMessage( pl->hwnd, WM_USER + 0x200, 0, 0 ) ) pl->flags &= ~( SWP_NOREDRAW ); + break; + case IDC_NO_VIEW: + SETLAYOUTPOS( pl, divX + divCX + 20, rc.top, max( 0, rc.right - pl->x - WASABI_API_APP->getScaleX( 2 ) - 20 ), max( 0, rc.bottom - rc.top ) ); + if ( !SendMessage( pl->hwnd, WM_USER + 0x200, 0, 0 ) ) pl->flags &= ~( SWP_NOREDRAW ); + break; + } + + if ( pl->x == ri.left && pl->y == ri.top ) + pl->flags |= SWP_NOMOVE; + + if ( pl->cx == ( ri.right - ri.left ) && pl->cy == ( ri.bottom - ri.top ) ) + pl->flags |= SWP_NOSIZE; + + if ( ( SWP_NOMOVE | SWP_NOSIZE ) != ( ( SWP_NOMOVE | SWP_NOSIZE ) & pl->flags ) ) + pl++; + else if ( IsWindowVisible( pl->hwnd ) ) + { + ValidateRect( hwnd, &ri ); + if ( GetUpdateRect( pl->hwnd, NULL, FALSE ) ) + { + GetUpdateRgn( pl->hwnd, rgn, FALSE ); + OffsetRgn( rgn, pl->x, pl->y ); + InvalidateRgn( hwnd, rgn, FALSE ); + } + } + } + + if ( pl != layout ) + { + LAYOUT *pc; + HDWP hdwp = ( useDefer ) ? BeginDeferWindowPos( (INT)( pl - layout ) ) : NULL; + for ( pc = layout; pc < pl && ( !useDefer || hdwp ); pc++ ) + { + if ( IDC_CURRENTVIEW == pc->id && ( SWP_NOREDRAW & pc->flags ) && IsWindowVisible( pc->hwnd ) ) + { + if ( !pc->rgn ) + pc->rgn = CreateRectRgn( 0, 0, pc->cx, pc->cy ); + + GetWindowRect( pc->hwnd, &ri ); + NavCtrlI_MapPointsFrom( hNavigation, HWND_DESKTOP, (LPPOINT)&ri, 1 ); + SendMessage( pc->hwnd, WM_USER + 0x201, MAKEWPARAM( pc->x - ri.left, pc->y - ri.top ), (LPARAM)pc->rgn ); + } + if ( useDefer ) + hdwp = DeferWindowPos( hdwp, pc->hwnd, NULL, pc->x, pc->y, pc->cx, pc->cy, pc->flags ); + else + SetWindowPos( pc->hwnd, NULL, pc->x, pc->y, pc->cx, pc->cy, pc->flags ); + } + + if ( hdwp ) + EndDeferWindowPos( hdwp ); + + for ( pc = layout; pc < pl; pc++ ) + { + if ( IDC_NAVIGATION == pc->id ) + { + GetWindowRect( pc->hwnd, &ri ); + OffsetRect( &ri, -ri.left, -ri.top ); + pc->rgn = CreateRectRgnIndirect( &ri ); + + GetClientRect( pc->hwnd, &ri ); + + NavCtrlI_MapPointsTo( hNavigation, hwnd, (LPPOINT)&ri, 1 ); + IntersectRect( &ri, &roTree, &ri ); + SetRectRgn( rgn, ri.left, ri.top, ri.right, ri.bottom ); + CombineRgn( pc->rgn, pc->rgn, rgn, RGN_DIFF ); + if ( GetUpdateRect( pc->hwnd, NULL, FALSE ) ) + { + GetUpdateRgn( pc->hwnd, rgn, FALSE ); + CombineRgn( pc->rgn, pc->rgn, rgn, RGN_OR ); + } + } + else if ( IDC_CURRENTVIEW == pc->id && pc->rgn ) + SendMessage( pc->hwnd, WM_USER + 0x201, 0, 0L ); + + if ( IsWindowVisible( pc->hwnd ) ) + { + GetWindowRect( pc->hwnd, &ri ); + MapWindowPoints( HWND_DESKTOP, hwnd, (LPPOINT)&ri, 2 ); + ValidateRect( hwnd, &ri ); + } + } + + if ( fRedraw ) + { + HRGN rgnTmp = CreateRectRgn( 0, 0, 0, 0 ); + GetUpdateRgn( hwnd, rgn, FALSE ); + for ( pc = layout; pc < pl; pc++ ) + { + if ( SWP_NOREDRAW & pc->flags ) + { + if ( pc->rgn ) + OffsetRgn( pc->rgn, pc->x, pc->y ); + else + SetRectRgn( rgnTmp, pc->x, pc->y, pc->x + pc->cx, pc->y + pc->cy ); + + CombineRgn( rgn, rgn, ( pc->rgn ) ? pc->rgn : rgnTmp, RGN_OR ); + } + } + + DeleteObject( rgnTmp ); + RedrawWindow( hwnd, NULL, rgn, RDW_INVALIDATE | RDW_ERASE | RDW_UPDATENOW | RDW_ERASENOW | RDW_ALLCHILDREN ); + } + else if ( g_rgnUpdate ) + { + GetUpdateRgn( hwnd, g_rgnUpdate, FALSE ); + ValidateRect( hwnd, NULL ); + for ( pc = layout; pc < pl; pc++ ) + { + if ( SWP_NOREDRAW & pc->flags ) + { + if ( pc->rgn ) + OffsetRgn( pc->rgn, pc->x, pc->y ); + else + SetRectRgn( rgn, pc->x, pc->y, pc->x + pc->cx, pc->y + pc->cy ); + + CombineRgn( g_rgnUpdate, g_rgnUpdate, ( pc->rgn ) ? pc->rgn : rgn, RGN_OR ); + } + } + } + + for ( pc = layout; pc < pl; pc++ ) + if ( pc->rgn ) + DeleteObject( pc->rgn ); + } + + if ( rgn ) + DeleteObject( rgn ); +} + +// a default right-pane view to show if a plugin failed to return us an HWND +static INT_PTR CALLBACK view_Error( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam ) +{ + if ( uMsg == WM_INITDIALOG ) + { + ShowWindow( GetDlgItem( g_hwnd, IDC_NO_VIEW ), SW_SHOW ); + } + + return WADlg_handleDialogMsgs( hwndDlg, uMsg, wParam, lParam ); +} + +static void CreateCurrentView( HWND hwndDlg ) +{ + HNAVITEM hItem; + DLGPROC proc; + proc = view_Error; + + hItem = NavCtrlI_GetSelection( hNavigation ); + if ( hItem ) + { + INT itemId = NavItemI_GetId( hItem ); + +#if 0 +#ifdef BETA + wchar_t pszText[ 32 ] = { 0 }; + if ( NavItemI_GetInvariantText( hItem, pszText, 32 ) && !lstrcmpW( pszText, L"winamp_labs" ) ) + { + OmService *om_service; + OmService::CreateInstance( SERVICE_LABS, L"Winamp Labs", &om_service ); + + if ( AGAVE_OBJ_BROWSER ) + { + HWND hView = 0; + HRESULT hr = AGAVE_OBJ_BROWSER->CreateView( om_service, (HWND)hwndDlg, 0, 0, &hView ); + om_service->Release(); + if ( SUCCEEDED( hr ) ) + { + m_curview_hwnd = hView; + } + } + } + else +#endif +#endif + m_curview_hwnd = (HWND)plugin_SendMessage( ML_MSG_TREE_ONCREATEVIEW, (INT_PTR)itemId, (INT_PTR)hwndDlg, 0 ); + } + + if ( !IsWindow( m_curview_hwnd ) ) + m_curview_hwnd = WASABI_API_CREATEDIALOGPARAMW( IDD_VIEW_EMPTY, hwndDlg, proc, 0 ); + else + ShowWindow( GetDlgItem( g_hwnd, IDC_NO_VIEW ), SW_HIDE ); + + if ( IsWindow( m_curview_hwnd ) ) + { + SetWindowPos( m_curview_hwnd, NavCtrlI_GetHWND( hNavigation ), 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOMOVE | SWP_NOSIZE | SWP_NOREDRAW ); + LayoutWindows( hwndDlg, divider_pos, FALSE ); + // moved 08/12/09 from start of block to here along with SkinnedWnd::OnSkinChanged(..) change to hopefully resolve bold font issues + MLSkinnedWnd_SkinChanged( m_curview_hwnd, TRUE, FALSE ); + ShowWindow( m_curview_hwnd, SW_SHOWNORMAL ); + RedrawWindow( m_curview_hwnd, NULL, NULL, RDW_INVALIDATE | RDW_ERASE | RDW_ALLCHILDREN ); + PostMessage( m_curview_hwnd, WMML_UPDATEVIEW, 0, 0 ); //refresh view + } +} + +static void DisplayItemDragImage( HNAVITEM hItem, LPCWSTR pszTip, HWND hwndOwner, POINT *ppt ) // ppt in hwndOwner coordinates +{ + if ( !hwndOwner ) + return; + + if ( m_nav_dragitem_imagelist ) + { + ImageList_DragLeave( hwndOwner ); + ImageList_EndDrag(); + ImageList_Destroy( m_nav_dragitem_imagelist ); + } + + m_nav_dragitem_imagelist = NavItemI_CreateDragImage( hItem, pszTip ); + + if ( m_nav_dragitem_imagelist ) + { + INT cx, cy; + ImageList_GetIconSize( m_nav_dragitem_imagelist, &cx, &cy ); + if ( ImageList_BeginDrag( m_nav_dragitem_imagelist, 0, cx / 2, cy / 2 ) ) + { + NavCtrlI_Update( hNavigation ); + ImageList_DragEnter( hwndOwner, ppt->x, ppt->y ); + } + } +} + +VOID CALLBACK CreateViewTimerProc( HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime ) +{ + if ( idEvent == TIMER_UPDATEVIEW_ID ) + { + KillTimer( hwnd, TIMER_UPDATEVIEW_ID ); + CreateCurrentView( hwnd ); + } +} + + +static void CALLBACK OnNavCtrl_Selected( HNAVCTRL hMngr, HNAVITEM hItemOld, HNAVITEM hItemNew ) +{ + KillTimer( g_hwnd, TIMER_UPDATEVIEW_ID ); + if ( IsWindow( m_curview_hwnd ) ) + { + RedrawWindow( g_hwnd, NULL, NULL, RDW_UPDATENOW | RDW_ALLCHILDREN ); + SendMessageW( g_hwnd, WM_SETREDRAW, FALSE, 0L ); // freeze window + ShowWindow( m_curview_hwnd, SW_HIDE ); + SendMessageW( g_hwnd, WM_SETREDRAW, TRUE, 0L ); + DestroyWindow( m_curview_hwnd ); + + m_curview_hwnd = NULL; + } + // 07/05/2010 DRO - changed to use a callback proc as ml_disc was managing to trigger + // the same timer message multiple times on (at least) older XP machines which was + // causing multiple ml view dialogs to be created but hidden behind the currently + // opened view - intermittent issue i've been seeing for a year on my XP machine. + SetTimer( g_hwnd, TIMER_UPDATEVIEW_ID, TIMER_UPDATEVIEW_DELAY, CreateViewTimerProc ); +} + +static BOOL CALLBACK OnNavCtrl_Click( HNAVCTRL hMngr, HNAVITEM hItem, INT actionId ) +{ + if ( hItem ) + { + INT mlAction = -1; + + switch ( actionId ) + { + case ACTION_CLICKL_I: mlAction = ML_ACTION_LCLICK; break; + case ACTION_CLICKR_I: mlAction = ML_ACTION_RCLICK; break; + case ACTION_ENTER_I: mlAction = ML_ACTION_ENTER; break; + case ACTION_DBLCLICKL_I: mlAction = ML_ACTION_DBLCLICK; break; + case ACTION_DBLCLICKR_I: break; + } + + if ( -1 != mlAction ) + { + BOOL ret = (BOOL)plugin_SendMessage( ML_MSG_TREE_ONCLICK, + NavItemI_GetId( hItem ), + mlAction, + (INT_PTR)GetParent( NavCtrlI_GetHWND( hMngr ) ) ); + + if ( mlAction != ML_ACTION_LCLICK ) + return ret; + } + } + return FALSE; +} + +static BOOL CALLBACK OnNavCtrl_KeyDown( HNAVCTRL hMngr, HNAVITEM hItem, NMTVKEYDOWN *ptvkd ) +{ + return ( hItem ) ? (BOOL)plugin_SendMessage( ML_MSG_TREE_ONKEYDOWN, + NavItemI_GetId( hItem ), + (INT_PTR)ptvkd, + (INT_PTR)NavCtrlI_GetHWND( hMngr ) ) : FALSE; +} + +static BOOL CALLBACK OnNavCtrl_BeginTitleEdit( HNAVCTRL hMngr, HNAVITEM hItem ) +{ + return !(BOOL)plugin_SendMessage( ML_MSG_NAVIGATION_ONBEGINTITLEEDIT, (INT_PTR)hItem, 0, 0 ); +} + +static BOOL CALLBACK OnNavCtrl_EndTitleEdit( HNAVCTRL hMngr, HNAVITEM hItem, LPCWSTR pszNewTitle ) +{ + return (BOOL)plugin_SendMessage( ML_MSG_NAVIGATION_ONENDTITLEEDIT, (INT_PTR)hItem, (INT_PTR)pszNewTitle, 0 ); +} + +static void CALLBACK OnNavItem_Delete( HNAVCTRL hMngr, HNAVITEM hItem ) +{ + plugin_SendMessage( ML_MSG_NAVIGATION_ONDELETE, (INT_PTR)hItem, 0, 0 ); +} + +static void CALLBACK OnNavCtrl_Destroy( HNAVCTRL hMngr ) +{ + plugin_SendMessage( ML_MSG_NAVIGATION_ONDESTROY, 0, 0, 0 ); +} + +static INT CALLBACK OnNavItem_CustomDraw( HNAVCTRL hMngr, HNAVITEM hItem, NAVITEMDRAW_I *pnicd, LPARAM lParam ) +{ + INT result = (INT)plugin_SendMessage( ML_MSG_NAVIGATION_ONCUSTOMDRAW, (INT_PTR)hItem, (INT_PTR)pnicd, (INT_PTR)lParam ); + + if ( NICDRF_NOTMINE == result ) + result = NICDRF_DODEFAULT_I; + + return result; +} + +static INT CALLBACK OnNavItem_SetCursor( HNAVCTRL hMngr, HNAVITEM hItem, LPARAM lParam ) +{ + INT result = (INT)plugin_SendMessage( ML_MSG_NAVIGATION_ONSETCURSOR, (INT_PTR)hItem, (INT_PTR)0, (INT_PTR)lParam ); + + return ( result > 0 ); +} + +static void CALLBACK OnNavItem_HitTest( HNAVCTRL hMngr, POINT pt, UINT *pHitFlags, HNAVITEM *phItem, LPARAM lParam ) +{ + NAVHITTEST ht; + + ht.flags = *pHitFlags; + ht.pt = pt; + ht.hItem = *phItem; + + if ( 0 != plugin_SendMessage( ML_MSG_NAVIGATION_ONHITTEST, (INT_PTR)*phItem, (INT_PTR)&ht, (INT_PTR)lParam ) ) + { + *pHitFlags = ht.flags; + *phItem = ht.hItem; + } +} + +static void CALLBACK OnNavCtrl_BeginDrag( HNAVCTRL hMngr, HNAVITEM hItem, POINT pt ) +{ +#ifdef BETA + wchar_t pszText[ 32 ] = { 0 }; + if ( ( !( sneak & 4 ) ) && NavItemI_GetInvariantText( hItem, pszText, 32 ) && !lstrcmpW( pszText, L"winamp_labs" ) ) + { + return; + } + +#endif + if ( plugin_SendMessage( ML_MSG_TREE_ONDRAG, NavItemI_GetId( hItem ), (INT_PTR)&pt, (INT_PTR)&m_query_moving_type ) < 1 ) + { + HNAVITEM hParent; + hParent = NavItemI_GetParent( hItem ); + m_query_moving_type = ( NULL == hParent || NIS_ALLOWCHILDMOVE_I == NavItemI_GetStyle( hParent, NIS_ALLOWCHILDMOVE_I ) ) ? + ML_TYPE_TREEITEM : ML_TYPE_UNKNOWN; + } + + m_query_moving = 1; + m_query_moving_item = hItem; + m_query_moving_dragplace = NULL; + m_query_moving_lastdest = NULL; + m_query_moving_dragplaceisbelow = FALSE; + + HWND hwndDlg = GetParent( NavCtrlI_GetHWND( hMngr ) ); + NavCtrlI_MapPointsTo( hMngr, hwndDlg, &pt, 1 ); + DisplayItemDragImage( hItem, NULL, hwndDlg, &pt ); + SetCapture( g_hwnd ); +} + +static INT CALLBACK OnNavCtrl_GetImageIndex( HNAVCTRL hMngr, HNAVITEM hItem, INT imageType ) +{ + if ( NavItemI_HasChildren( hItem ) ) + { + INT_PTR tag; + tag = ( NavItemI_HasChildrenReal( hItem ) ) ? + ( ( NavItemI_IsExpanded( hItem ) ) ? MLTREEIMAGE_BRANCH_EXPANDED : MLTREEIMAGE_BRANCH_COLLAPSED ) : + MLTREEIMAGE_BRANCH_NOCHILD; + INT mlilIndex = MLImageListI_GetIndexFromTag( NavCtrlI_GetImageList( hMngr ), tag ); + + return ( -1 != mlilIndex ) ? mlilIndex : 0; + } + + return 0; +} + +static void OnWindowPosChanged( HWND hwnd, WINDOWPOS *pwp ) +{ + if ( SWP_NOSIZE != ( ( SWP_NOSIZE | SWP_FRAMECHANGED | SWP_SHOWWINDOW ) & pwp->flags ) ) + { + LayoutWindows( hwnd, divider_pos, ( 0 == ( SWP_NOREDRAW & pwp->flags ) ) ); + } +} + +HWND wa3wnd_fixwnd( HWND h ) +{ + if ( IsChild( h, g_hwnd ) ) + return h; // if the library window is a child of this window, don't touch it + + if ( IsChild( h, g_PEWindow ) ) + return g_PEWindow; // if the playlist window is a child of this window, treat it as the playlist window + + HWND hParent = (HWND)SENDWAIPC( plugin.hwndParent, IPC_GETDIALOGBOXPARENT, 0 ); + if ( plugin.hwndParent != hParent && h == hParent ) + h = plugin.hwndParent; + + //DWORD pid; + //DWORD threadID = GetWindowThreadProcessId(h, &pid); + //if (pid != GetCurrentProcessId() || threadID != GetCurrentThreadId()) return h; // if other process, dont mangle + //char buf[64] = {0}; + //GetClassName(h, buf, sizeof(buf) - 1); + //if (!strcmp(buf, "BaseWindow_RootWnd")) return plugin.hwndParent; // if a wa3 window, treat as main window + return h; +} + + +static void CALLBACK OnDividerMoved( HWND hdiv, INT nPos, LPARAM param ) +{ + HWND hwndParent; + divider_pos = nPos; + hwndParent = GetParent( hdiv ); + LayoutWindows( hwndParent, nPos, TRUE ); +} + +static void Navigation_DropHelper( HWND hwndDlg, HWND hwndNav, HNAVITEM hItemHit, UINT hitFlags, POINT pt ) +{ + if ( hItemHit && ( ( NAVHT_ONITEMINDENT_I | NAVHT_ONITEMRIGHT_I | NAVHT_ONITEM_I | NAVHT_ONITEMBUTTON_I ) & hitFlags ) ) + { + RECT rc; + GetClientRect( hwndNav, &rc ); + if ( !m_nav_autoscroll && ( rc.left <= pt.x && rc.right >= pt.x ) ) + { + INT sbCommand; + if ( rc.top <= pt.y && pt.y < ( rc.top + 24 ) ) + sbCommand = 1; + else if ( ( rc.bottom - 24 ) < pt.y && pt.y <= rc.bottom ) + sbCommand = 2; + else + sbCommand = 0; + + if ( sbCommand ) + { + SCROLLINFO si = { 0 }; + si.cbSize = sizeof( SCROLLINFO ); + si.fMask = SIF_POS | SIF_RANGE | SIF_PAGE; + if ( !GetScrollInfo( hwndNav, SB_VERT, &si ) ) + ZeroMemory( &si, sizeof( SCROLLINFO ) ); + + if ( ( 1 == sbCommand && si.nMin < si.nPos ) || ( 2 == sbCommand && si.nMax >= (INT)( si.nPos + si.nPage ) ) ) + { + if ( SetTimer( hwndDlg, TIMER_NAVAUTOSCROLL_ID, TIMER_NAVAUTOSCROLL_DELAY, NULL ) ) + m_nav_autoscroll = TRUE; + } + } + } + + if ( ( NAVHT_ONITEMBUTTON_I & hitFlags ) && m_nav_autoexpand_item != hItemHit && !NavItemI_IsExpanded( hItemHit ) ) + { + KillTimer( hwndDlg, TIMER_NAVAUTOEXPAND_ID ); + if ( SetTimer( hwndDlg, TIMER_NAVAUTOEXPAND_ID, TIMER_NAVAUTOEXPAND_DELAY, NULL ) ) + m_nav_autoexpand_item = hItemHit; + } + } +} + +int handleDragDropMove( HWND hwndDlg, int type, POINT p, int do_cursors ) +{ + BOOL valid = FALSE; + HWND h = WindowFromPoint( p ); + HNAVITEM hItemPrevHilited = g_treedrag_lastSel; + g_treedrag_lastSel = NULL; + + if ( h ) + { + h = wa3wnd_fixwnd( h ); + + if ( IsChild( plugin.hwndParent, h ) ) + h = plugin.hwndParent; + else if ( g_PEWindow && IsChild( g_PEWindow, h ) ) + h = g_PEWindow; + else + { + HWND vid = (HWND)SendMessage( plugin.hwndParent, WM_WA_IPC, IPC_GETWND_VIDEO, IPC_GETWND ); + if ( vid ) + { + if ( h == vid || IsChild( vid, h ) ) + h = plugin.hwndParent; + } + } + + if ( h && ( h == plugin.hwndParent || h == g_PEWindow ) ) + { + valid = ( type == ML_TYPE_ITEMRECORDLISTW || type == ML_TYPE_ITEMRECORDLIST || + type == ML_TYPE_FILENAMES || type == ML_TYPE_STREAMNAMES || + type == ML_TYPE_CDTRACKS || + type == ML_TYPE_FILENAMESW || type == ML_TYPE_STREAMNAMESW ); + } + else if ( h == NavCtrlI_GetHWND( hNavigation ) ) + { + HNAVITEM hItemHit, hItemSel; + UINT hitFlags; + POINT pt = p; + MapWindowPoints( HWND_DESKTOP, h, &pt, 1 ); + + hItemHit = NavCtrlI_HitTest( hNavigation, &pt, &hitFlags ); + hItemSel = NavCtrlI_GetSelection( hNavigation ); + + Navigation_DropHelper( hwndDlg, h, hItemHit, hitFlags, pt ); + if ( hItemHit && hItemHit != hItemSel && ( !m_query_moving_item || NavItemI_GetParent( m_query_moving_item ) != hItemHit ) ) + { + valid = ( plugin_SendMessage( ML_MSG_TREE_ONDROPTARGET, NavItemI_GetId( hItemHit ), type, NULL ) > 0 ); + if ( valid ) + g_treedrag_lastSel = hItemHit; + } + } + else if ( IsWindow( m_curview_hwnd ) && IsWindow( h ) && ( h == m_curview_hwnd || IsChild( m_curview_hwnd, h ) ) ) + { + mlDropItemStruct dis = { 0, }; + dis.type = type; + dis.p = p; + while ( 1 ) + { + SendMessage( h, WM_ML_CHILDIPC, (WPARAM)&dis, ML_CHILDIPC_DROPITEM ); + + if ( dis.result || h == m_curview_hwnd ) + break; + + h = GetParent( h ); // traverse up the tree + } + + valid = dis.result > 0; + } + } + + if ( g_treedrag_lastSel != hItemPrevHilited ) + { + ImageList_DragShowNolock( FALSE ); + if ( hItemPrevHilited ) + NavItemI_SetState( hItemPrevHilited, 0, NIS_DROPHILITED_I ); + + if ( g_treedrag_lastSel ) + NavItemI_SetState( g_treedrag_lastSel, NIS_DROPHILITED_I, NIS_DROPHILITED_I ); + + NavCtrlI_Update( hNavigation ); + ImageList_DragShowNolock( TRUE ); + } + + if ( do_cursors ) + SetCursor( ( valid ) ? hDragNDropCursor : LoadCursor( NULL, IDC_NO ) ); + + return valid; +} + +static void CancelNavigationMove( void ) +{ + m_query_moving_dragplace = NULL; + ImageList_DragShowNolock( FALSE ); + NavCtrlI_SetInsertMark( hNavigation, NULL, FALSE ); + NavCtrlI_Update( hNavigation ); + ImageList_DragShowNolock( TRUE ); +} + +static void OnInitMenuPopUp( HMENU menu ); +static void OnDisplayChange( HWND hwndDlg, UINT imgDepth, UINT hRes, UINT vRes ); +static int OnInitDialog( HWND hwndDlg ); +static void OnDestroy( HWND hwndDlg ); +static void OnClose( HWND hwndDlg ); +static void OnSize( HWND hwndDlg, UINT type, int cx, int cy ); +static void OnShowWindow( HWND hwndDlg, BOOL fShow ); +LRESULT OnMediaLibraryIPC( HWND hwndDlg, WPARAM wParam, LPARAM lParam ); +static void OnGetMaxMinInfo( MINMAXINFO *info ); +static void OnBtnLibraryClick( HWND hwndBtn ); + +static HWND hwndTweak = NULL; +static BOOL CALLBACK RatingTweak_OnApplyChanges( UINT fStyle, BOOL bClosing ) +{ + ratingGlobalStyle = fStyle; + g_config->WriteInt( L"rating_style", ratingGlobalStyle ); + + MLRatingColumnI_Update(); + + if ( IsWindow( m_curview_hwnd ) ) + PostMessage( m_curview_hwnd, WM_DISPLAYCHANGE, 0, MAKELPARAM( 0, 0 ) ); + + if ( bClosing ) + hwndTweak = NULL; + + return TRUE; +} + +static void OnTimer_NavAutoScroll( HWND hwndDlg ) +{ + RECT rc; + POINT pt; + HWND hwndNav = NavCtrlI_GetHWND( hNavigation ); + INT command = 0; + + KillTimer( hwndDlg, TIMER_NAVAUTOSCROLL_ID ); + + if ( !hwndNav || !IsWindowVisible( hwndNav ) ) + { + m_nav_autoscroll = FALSE; + return; + } + + GetCursorPos( &pt ); + NavCtrlI_MapPointsFrom( hNavigation, HWND_DESKTOP, &pt, 1 ); + + GetClientRect( hwndNav, &rc ); + + if ( rc.left <= pt.x && rc.right >= pt.x ) + { + if ( rc.top <= pt.y && pt.y < ( rc.top + 24 ) ) + command = 1; + else if ( ( rc.bottom - 24 ) < pt.y && pt.y <= rc.bottom ) + command = 2; + } + + if ( command ) + { + SCROLLINFO si = { 0 }; + si.cbSize = sizeof( SCROLLINFO ); + si.fMask = SIF_POS | SIF_RANGE | SIF_PAGE; + if ( !GetScrollInfo( hwndNav, SB_VERT, &si ) ) + ZeroMemory( &si, sizeof( SCROLLINFO ) ); + + if ( ( 1 == command && si.nMin == si.nPos ) || ( 2 == command && si.nMax < (INT)( si.nPos + si.nPage ) ) ) + command = 0; + else + { + ImageList_DragShowNolock( FALSE ); + SendMessageW( hwndNav, WM_VSCROLL, MAKEWPARAM( ( 1 == command ) ? SB_LINEUP : SB_LINEDOWN, 0 ), NULL ); + NavCtrlI_Update( hNavigation ); + ImageList_DragShowNolock( TRUE ); + SetTimer( hwndDlg, TIMER_NAVAUTOSCROLL_ID, TIMER_NAVAUTOSCROLL_DELAY, NULL ); + } + } + + if ( !command ) + m_nav_autoscroll = FALSE; +} + +static void OnTimer_NavAutoExpand( HWND hwndDlg ) +{ + POINT pt; + HWND hwndNav = NavCtrlI_GetHWND( hNavigation ); + HNAVITEM hItem; + UINT hitFlags; + + KillTimer( hwndDlg, TIMER_NAVAUTOEXPAND_ID ); + + if ( !hwndNav || !IsWindowVisible( hwndNav ) ) + return; + + GetCursorPos( &pt ); + NavCtrlI_MapPointsFrom( hNavigation, HWND_DESKTOP, &pt, 1 ); + + hItem = NavCtrlI_HitTest( hNavigation, &pt, &hitFlags ); + + if ( NAVHT_ONITEMBUTTON_I & hitFlags && hItem == m_nav_autoexpand_item ) + { + ImageList_DragShowNolock( FALSE ); + NavCtrlI_SetInsertMark( hNavigation, NULL, FALSE ); + NavItemI_Expand( hItem, NAVITEM_EXPAND_I ); + NavCtrlI_Update( hNavigation ); + ImageList_DragShowNolock( TRUE ); + } + m_nav_autoexpand_item = NULL; +} + + +static void OnTimer( HWND hwndDlg, UINT_PTR idTimer ) +{ + switch ( idTimer ) + { + case TIMER_NAVAUTOSCROLL_ID: OnTimer_NavAutoScroll( hwndDlg ); break; + case TIMER_NAVAUTOEXPAND_ID: OnTimer_NavAutoExpand( hwndDlg ); break; + case TIMER_UNBLOCKACCELTOGGLE_ID: + KillTimer( hwndDlg, idTimer ); + RemoveProp( hwndDlg, BLOCK_ACCELTOGGLE_PROP ); + break; + } +} + +static HMLIMGLST CreateNavigationImages( HMLIMGFLTRMNGR filterManager ) +{ + MLIMAGESOURCE_I is = { 0 }; + static INT resourceId[] = { IDB_TREEITEM_DEFAULT, IDB_TREEITEM_COLLAPSED, IDB_TREEITEM_EXPANDED, IDB_TREEITEM_NOCHILD }; + static INT_PTR imageTag[] = { MLTREEIMAGE_DEFAULT, MLTREEIMAGE_BRANCH_COLLAPSED, MLTREEIMAGE_BRANCH_EXPANDED, MLTREEIMAGE_BRANCH_NOCHILD }; + HMLIMGLST hmlil = MLImageListI_Create( 16, 16, 24, 30, 2, 3, filterManager ); + + if ( !hmlil ) + return NULL; + + is.bpp = 24; + is.hInst = plugin.hDllInstance; + is.type = SRC_TYPE_BMP_I; + is.flags = ISF_FORCE_BPP_I; + for ( INT index = 0; index < sizeof( resourceId ) / sizeof( resourceId[ 0 ] ); index++ ) + { + is.lpszName = MAKEINTRESOURCEW( resourceId[ index ] ); + MLImageListI_Add( hmlil, &is, MLIF_FILTER1_UID, imageTag[ index ] ); + } + return hmlil; +} + +static void OnMouseMove( HWND hwndDlg, POINT pt, UINT flags ) +{ + RECT rc; + if ( !m_query_moving ) return; + + MapWindowPoints( hwndDlg, HWND_DESKTOP, &pt, 1 ); + + GetWindowRect( hwndDlg, &rc ); + ImageList_DragMove( pt.x - rc.left, pt.y - rc.top ); + + HWND hWndHit = WindowFromPoint( pt ); + + if ( hWndHit == NavCtrlI_GetHWND( hNavigation ) ) + { + POINT ptMy = pt; + UINT hitFlags; + HNAVITEM hItem; + + MapWindowPoints( HWND_DESKTOP, hWndHit, &ptMy, 1 ); + + hItem = NavCtrlI_HitTest( hNavigation, &ptMy, &hitFlags ); + + if ( !hItem ) + { + HNAVITEM hItemLast; + hItemLast = NavCtrlI_GetLastVisible( hNavigation ); + if ( hItemLast ) + { + RECT ri; + if ( NavItemI_GetRect( hItemLast, &ri, FALSE ) && ri.bottom < ptMy.y ) + { + HNAVITEM hMovingParent, hLastParent; + hMovingParent = NavItemI_GetParent( m_query_moving_item ); + while ( NULL != ( hLastParent = NavItemI_GetParent( hItemLast ) ) && hLastParent != hMovingParent ) hItemLast = hLastParent; + if ( hMovingParent == hLastParent ) + { + hItem = hItemLast; + hitFlags = NAVHT_ONITEM_I; + } + } + } + } + + if ( ( NAVHT_ONITEMINDENT_I | NAVHT_ONITEMRIGHT_I | NAVHT_ONITEM_I | NAVHT_ONITEMBUTTON_I ) & hitFlags ) + { + HNAVITEM tempItem; + + Navigation_DropHelper( hwndDlg, hWndHit, hItem, hitFlags, ptMy ); + + if ( ML_TYPE_UNKNOWN == m_query_moving_type ) + { + SetCursor( LoadCursor( NULL, IDC_NO ) ); + return; + } + tempItem = hItem; + while ( tempItem && tempItem != m_query_moving_item ) tempItem = NavItemI_GetParent( tempItem ); + if ( tempItem == m_query_moving_item ) + hItem = m_query_moving_item; + + if ( hItem ) + { + BOOL hitBelow; + + if ( NavItemI_GetParent( hItem ) != NavItemI_GetParent( m_query_moving_item ) ) + { + // TODO: I think regular drag-n-drop goes here? + //SetCursor(LoadCursor(NULL, MAKEINTRESOURCE(IDC_NO))); + CancelNavigationMove(); + m_query_moving_lastdest = NULL; + mlDropItemStruct dropItem; + ZeroMemory( &dropItem, sizeof( mlDropItemStruct ) ); + dropItem.p = pt; + dropItem.type = m_query_moving_type; + OnMediaLibraryIPC( hwndDlg, (WPARAM)&dropItem, ML_IPC_HANDLEDRAG ); + //handleDragDropMove(hwndDlg, m_query_moving_type, pt, 1); + //goto outside_window; + return; + } + + if ( g_treedrag_lastSel ) + { + NavItemI_SetState( g_treedrag_lastSel, 0, NIS_DROPHILITED_I ); + g_treedrag_lastSel = NULL; + } + + NavItemI_GetRect( hItem, &rc, FALSE ); + hitBelow = ( ptMy.y > ( rc.bottom - ( rc.bottom - rc.top ) / 2 ) ); + + SetCursor( LoadCursor( NULL, MAKEINTRESOURCE( IDC_ARROW ) ) ); + + m_query_moving_lastdest = hItem; + + if ( m_query_moving_dragplace != hItem || m_query_moving_dragplaceisbelow != hitBelow ) + { + m_query_moving_dragplace = hItem; + m_query_moving_dragplaceisbelow = hitBelow; + ImageList_DragShowNolock( FALSE ); + NavCtrlI_SetInsertMark( hNavigation, hItem, hitBelow ); + NavCtrlI_Update( hNavigation ); + ImageList_DragShowNolock( TRUE ); + } + } + } + } + else + { + int type = ML_TYPE_TREEITEM; + bool canDrag; + + CancelNavigationMove(); + + canDrag = ( m_query_moving_item && + ( plugin_SendMessage( ML_MSG_TREE_ONDRAG, NavItemI_GetId( m_query_moving_item ), (INT_PTR)&pt, (INT_PTR)&type ) < 0 ) ); + + if ( !canDrag ) + { + mlDropItemStruct dropItem; + ZeroMemory( &dropItem, sizeof( mlDropItemStruct ) ); + dropItem.p = pt; + dropItem.type = type; + OnMediaLibraryIPC( hwndDlg, (WPARAM)&dropItem, ML_IPC_HANDLEDRAG ); + + //pluginHandleIpcMessage(hwndDlg, ML_IPC_HANDLEDRAG, (WPARAM)&m); + //SetCursor(dropItem.result > 0 ? hDragNDropCursor : LoadCursor(NULL, IDC_NO)); + //SetCursor(LoadCursor(NULL, IDC_NO)); + } + + m_query_moving_dragplace = NULL; + m_query_moving_lastdest = NULL; + } +} + +static void OnLButtonUp( HWND hwndDlg, POINT pt, UINT flags ) +{ + HWND hwndHit; + + if ( !m_query_moving ) return; + + SetCursor( LoadCursor( NULL, MAKEINTRESOURCE( IDC_ARROW ) ) ); + + ImageList_DragLeave( hwndDlg ); + ImageList_EndDrag(); + + if ( m_nav_dragitem_imagelist ) + { + ImageList_Destroy( m_nav_dragitem_imagelist ); + m_nav_dragitem_imagelist = NULL; + } + + MapWindowPoints( hwndDlg, HWND_DESKTOP, &pt, 1 ); + + hwndHit = WindowFromPoint( pt ); + + if ( ML_TYPE_UNKNOWN != m_query_moving_type && hwndHit && m_query_moving_item ) + { + if ( m_query_moving_dragplace ) + { + //move item in the tree + + if ( ML_TYPE_TREEITEM == m_query_moving_type && m_query_moving_item != m_query_moving_lastdest ) + { + WORD orderOld; + orderOld = NavItemI_GetOrder( m_query_moving_item ); + if ( NavItemI_Move( m_query_moving_item, m_query_moving_lastdest, m_query_moving_dragplaceisbelow ) ) + { + plugin_SendMessage( ML_MSG_NAVIGATION_ONMOVE, (INT_PTR)m_query_moving_item, orderOld, NavItemI_GetOrder( m_query_moving_item ) ); + } + } + else + { + HNAVITEM whereinsert; + whereinsert = m_query_moving_lastdest; + + if ( !m_query_moving_dragplaceisbelow ) + { + BOOL fTest; + fTest = ( whereinsert == m_query_moving_item ); + whereinsert = NavItemI_GetPrevious( whereinsert ); + + if ( !whereinsert && !fTest ) + whereinsert = NavItemI_GetParent( m_query_moving_lastdest ); + } + + if ( whereinsert && m_query_moving_item != whereinsert ) + plugin_SendMessage( ML_MSG_TREE_ONDROP, NavItemI_GetId( m_query_moving_item ), (INT_PTR)&pt, NavItemI_GetId( whereinsert ) ); + } + } + else + plugin_SendMessage( ML_MSG_TREE_ONDROP, NavItemI_GetId( m_query_moving_item ), (INT_PTR)&pt, 0 ); + } + m_query_moving = 0; + + if ( g_treedrag_lastSel ) + { + NavItemI_SetState( g_treedrag_lastSel, 0, NIS_DROPHILITED_I ); + g_treedrag_lastSel = NULL; + } + + if ( m_query_moving_dragplace ) + CancelNavigationMove(); + + ReleaseCapture(); +} + + +void listbuild( wchar_t **buf, int &buf_size, int &buf_pos, const wchar_t *tbuf ) +{ + if ( !*buf ) + { + *buf = (wchar_t *)calloc( 4096, sizeof( wchar_t ) ); + if ( *buf ) + { + buf_size = 4096; + buf_pos = 0; + } + else + { + buf_size = buf_pos = 0; + } + } + + int newsize = buf_pos + lstrlenW( tbuf ) + 1; + if ( newsize < buf_size ) + { + size_t old_buf_size = buf_size; + buf_size = newsize + 4096; + wchar_t *data = (wchar_t *)realloc( *buf, ( buf_size + 1 ) * sizeof( wchar_t ) ); + if ( data ) + { + *buf = data; + } + else + { + data = (wchar_t *)malloc( ( buf_size + 1 ) * sizeof( wchar_t ) ); + if ( data ) + { + memcpy( data, *buf, sizeof( wchar_t ) * old_buf_size ); + free( *buf ); + *buf = data; + } + else + buf_size = old_buf_size; + } + } + StringCchCopyW( *buf + buf_pos, buf_size, tbuf ); + buf_pos = newsize; +} + +/*wchar_t * getSelectedList() +{ + wchar_t* path=NULL; + int buf_pos=0, buf_size=0; + int download=-1; + while (GetDownload(download)) + { + if(listContents[download]->f) + listbuild(&path,buf_size,buf_pos,listContents[download]->f->path); + } + if(path) path[buf_pos] = 0; + return path; +}*/ + +static void OnDragDrop( HWND hwndDlg, HDROP hdrop ) +{ + UINT hitFlags = NAVHT_NOWHERE_I; + POINT pt = { 0 }; + DragQueryPoint( hdrop, &pt ); + + HNAVITEM hItemHit = NavCtrlI_HitTest( hNavigation, &pt, &hitFlags ); + if ( hItemHit ) + { + if ( plugin_SendMessage( ML_MSG_TREE_ONDROPTARGET, NavItemI_GetId( hItemHit ), ML_TYPE_FILENAMESW, NULL ) > 0 ) + { + wchar_t temp[ MAX_PATH ] = { 0 }; + int y = DragQueryFileW( hdrop, 0xffffffff, temp, 1024 ); + if ( y > 0 ) + { + wchar_t *paths = NULL; + int buf_pos = 0, buf_size = 0; + for ( int x = 0; x < y; x++ ) + { + if ( DragQueryFileW( hdrop, x, temp, MAX_PATH ) ) + { + listbuild( &paths, buf_size, buf_pos, temp ); + } + } + + if ( paths ) + { + paths[ buf_pos ] = 0; + plugin_SendMessage( ML_MSG_TREE_ONDROPTARGET, NavItemI_GetId( hItemHit ), ML_TYPE_FILENAMESW, (INT_PTR)paths ); + + if ( IsWindow( m_curview_hwnd ) ) + SendMessage( m_curview_hwnd, WM_APP + 1, 0, 0 ); //update current view + + free( paths ); + } + } + + DragFinish( hdrop ); + } + } +} + +static void MlView_ShowWindow( HWND hwnd, BOOL fShow, UINT nStatus ) +{ + if ( SW_PARENTOPENING == nStatus ) + { + BOOL fStored = ( 0 != g_config->ReadInt( L"visible", 1 ) ); + if ( fShow != fStored ) + { + PostMessageW( hwnd, WMML_SHOWCONTAINER, ( 0 == fStored ) ? SW_HIDE : SW_SHOWNA, 0 ); + return; + } + } + + ShowWindow( hwnd, ( FALSE != fShow ) ? SW_SHOWNA : SW_HIDE ); + + if ( 0 == nStatus ) + { + if ( NULL != g_config ) + g_config->WriteInt( L"visible", ( FALSE != fShow ) ); + + UINT menuFlags = ( FALSE != fShow ) ? MF_CHECKED : MF_UNCHECKED; + menuFlags |= MF_BYCOMMAND; + + INT szMenu[] = { 0, 4, }; + for ( INT i = 0; i < ARRAYSIZE( szMenu ); i++ ) + { + HMENU hMenu = (HMENU)SendMessage( plugin.hwndParent, WM_WA_IPC, szMenu[ i ], IPC_GET_HMENU ); + if ( NULL != hMenu ) + CheckMenuItem( hMenu, WA_MENUITEM_ID, menuFlags ); + } + + MLVisibleChanged( FALSE != fShow ); + + if ( FALSE != fShow ) + { + SendMessageW( g_ownerwnd, 0x0127/*WM_CHANGEUISTATE*/, MAKEWPARAM( 1/*UIS_SET*/, 3/*(UISF_HIDEACCEL | UISF_HIDEFOCUS)*/ ), 0L ); + HWND hRoot = GetAncestor( g_ownerwnd, GA_ROOT ); + if ( NULL != hRoot ) + SetForegroundWindow( hRoot ); + } + } +} + +static LRESULT MlView_OnContainerNotify( HWND hwnd, NMHDR *pnmh ) +{ + switch ( pnmh->code ) + { + case EWN_SHOWWINDOW: + MlView_ShowWindow( hwnd, ( (EMBEDSHOW *)pnmh )->fShow, ( (EMBEDSHOW *)pnmh )->nStatus ); + break; + } + + return 0; +} + +static LRESULT MlView_OnNotify( HWND hwnd, INT controlId, NMHDR *pnmh ) +{ + if ( pnmh->hwndFrom == g_ownerwnd ) + { + return MlView_OnContainerNotify( hwnd, pnmh ); + } + + return 0; +} + +static LRESULT MlView_OnContextMenu( HWND hwnd, HWND hOwner, POINTS pts ) +{ + HWND hNav = NavCtrlI_GetHWND( hNavigation ); + + if ( NULL != hNav && hOwner == hNav ) + { + HNAVITEM hItem; + POINT pt; + POINTSTOPOINT( pt, pts ); + + if ( -1 == pt.x || -1 == pt.y ) + { + hItem = NavCtrlI_GetSelection( hNavigation ); + if ( NULL != hItem ) + { + RECT itemRect; + if ( NavItemI_GetRect( hItem, &itemRect, FALSE ) ) + { + pt.x = itemRect.left + 1; + pt.y = itemRect.top + 1; + MapWindowPoints( hNav, HWND_DESKTOP, &pt, 1 ); + } + } + } + else + { + UINT hitFlags; + MapWindowPoints( HWND_DESKTOP, hNav, &pt, 1 ); + hItem = NavCtrlI_HitTest( hNavigation, &pt, &hitFlags ); + pt.x = pts.x; + pt.y = pts.y; + } + + if ( NULL == hItem ) return 0; + + UINT itemState = NavItemI_GetState( hItem, NIS_DROPHILITED_I | NIS_SELECTED_I ); + + if ( 0 == ( ( NIS_DROPHILITED_I | NIS_SELECTED_I ) & itemState ) ) + NavItemI_SetState( hItem, NIS_DROPHILITED_I, NIS_DROPHILITED_I ); + + INT_PTR result = plugin_SendMessage( ML_MSG_NAVIGATION_CONTEXTMENU, + (INT_PTR)hItem, + (INT_PTR)hNav, + MAKELONG( pt.x, pt.y ) ); + + if ( 0 == ( ( NIS_DROPHILITED_I | NIS_SELECTED_I ) & itemState ) ) + NavItemI_SetState( hItem, 0, NIS_DROPHILITED_I ); + + return ( 0 != result ); + } + + return 0; +} + +static BOOL +MlView_OnHelp( HWND hwnd, HELPINFO *helpInfo ) +{ + // TODO review this for handling Shift+F1 as view specific help? + // make sure we're only doing it for the expect help actions + // i.e. not Ctrl+F1 which opens the global about dialog + if ( ( GetAsyncKeyState( VK_CONTROL ) & 0x8000 ) ) + return FALSE; + + // for view specific handling, we use Shift+F1 + if ( NULL != helpInfo && HELPINFO_WINDOW == helpInfo->iContextType && ( GetAsyncKeyState( VK_SHIFT ) & 0x8000 ) ) + { + HWND navigationWindow; + navigationWindow = NavCtrlI_GetHWND( hNavigation ); + + if ( navigationWindow == helpInfo->hItemHandle ) + { + HNAVITEM selectedItem; + selectedItem = NavCtrlI_GetSelection( hNavigation ); + if ( NULL != selectedItem && 0 != plugin_SendMessage( ML_MSG_NAVIGATION_HELP, (INT_PTR)selectedItem, (INT_PTR)navigationWindow, MAKELONG( helpInfo->MousePos.x, helpInfo->MousePos.y ) ) ) + { + return TRUE; + } + return TRUE; + } + } + // otherwise we treat it as a generic help request + return MediaLibrary_OpenHelpUrl( MEDIALIBRARY_HELP_URL ); +} + +INT_PTR CALLBACK dialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam ) +{ + + switch ( uMsg ) + { + case WM_MOUSEMOVE: + case WM_LBUTTONUP: + { + POINT pt = { GET_X_LPARAM( lParam ),GET_Y_LPARAM( lParam ) }; + if ( WM_MOUSEMOVE == uMsg ) + OnMouseMove( hwndDlg, pt, (UINT)wParam ); + else if ( WM_LBUTTONUP == uMsg ) + OnLButtonUp( hwndDlg, pt, (UINT)wParam ); + } + return TRUE; + + case WM_COMMAND: + switch ( LOWORD( wParam ) ) + { + case IDC_BTN_LIB: if ( BN_CLICKED == HIWORD( wParam ) ) OnBtnLibraryClick( (HWND)lParam ); break; + case IDM_LIBRARY_CONFIG: OpenMediaLibraryPreferences(); break; + case IDM_LIBRARY_HELP: MediaLibrary_OpenHelpUrl( MEDIALIBRARY_HELP_URL ); break; + case ID_TOGGLE_LIBRARY: + if ( 0 == GetProp( hwndDlg, BLOCK_ACCELTOGGLE_PROP ) ) + { + toggleVisible( 0 ); + SetProp( hwndDlg, BLOCK_ACCELTOGGLE_PROP, (HANDLE)(INT_PTR)1 ); + SetTimer( hwndDlg, TIMER_UNBLOCKACCELTOGGLE_ID, TIMER_UNBLOCKACCELTOGGLE_DELAY, NULL ); + } + break; + case ID_WINDOW_CLOSE: + if ( IsVisible() ) + toggleVisible( 0 ); + break; + case ID_SHOW_RATINGTWEAK: + if ( !IsWindow( hwndTweak ) ) + hwndTweak = MLRatingColumnI_TweakDialog( hwndDlg, ratingGlobalStyle, RatingTweak_OnApplyChanges, TRUE ); + else SetWindowPos( hwndTweak, HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_SHOWWINDOW ); + break; + case ID_GO_TO_VIEW_SEARCHBAR: + SendMessage( m_curview_hwnd, WM_ML_CHILDIPC, 0, ML_CHILDIPC_GO_TO_SEARCHBAR ); + break; + case ID_REFRESH_SEARCH: + SendMessage( m_curview_hwnd, WM_ML_CHILDIPC, 0, ML_CHILDIPC_REFRESH_SEARCH ); + break; + case ID_NEW_PLAYLIST: + { + // only process if not in an edit control + // (as shift+insert is a legacy OS paste shortcut) + wchar_t szClass[ 32 ] = { 0 }; + HWND hFocus = GetFocus(); + if ( GetClassNameW( hFocus, szClass, sizeof( szClass ) / sizeof( szClass[ 0 ] ) ) && + CSTR_EQUAL != CompareStringW( CSTR_INVARIANT, NORM_IGNORECASE, szClass, -1, WC_EDITW, -1 ) ) + { +#define ID_MLFILE_NEWPLAYLIST 40359 + SendMessageW( plugin.hwndParent, WM_COMMAND, MAKEWPARAM( ID_MLFILE_NEWPLAYLIST, 0 ), 0 ); + } + else SendMessageW( hFocus, WM_PASTE, 0, 0L ); + } + break; + case ID_SHOW_HELP: + // do nothing, just need to still F1 from Winamp + break; + + } + break; + case WM_NOTIFY: + { + LRESULT result; + result = 0; + if ( !NavCtrlI_ProcessNotifications( hNavigation, (LPNMHDR)lParam, &result ) ) + result = MlView_OnNotify( hwndDlg, (INT)wParam, (NMHDR *)lParam ); + + SetWindowLongPtr( hwndDlg, DWLP_MSGRESULT, (LONGX86)(LONG_PTR)result ); + return TRUE; + } + + case WM_CAPTURECHANGED: + { + POINT pt = { MAXLONG,MAXLONG }; + OnLButtonUp( hwndDlg, pt, 0 ); + break; + } + + case WM_DROPFILES: OnDragDrop( hwndDlg, (HDROP)wParam ); break; + case WM_INITMENUPOPUP: OnInitMenuPopUp( (HMENU)wParam ); return TRUE; + case WM_DISPLAYCHANGE: OnDisplayChange( hwndDlg, (UINT)wParam, LOWORD( lParam ), HIWORD( lParam ) ); return TRUE; + case WM_INITDIALOG: return OnInitDialog( hwndDlg ); + case WM_DESTROY: OnDestroy( hwndDlg ); break; + case WM_WINDOWPOSCHANGED: OnWindowPosChanged( hwndDlg, (WINDOWPOS *)lParam ); return TRUE; + case WM_CLOSE: OnClose( hwndDlg ); break; + case WM_GETMINMAXINFO: OnGetMaxMinInfo( (LPMINMAXINFO)lParam ); return TRUE; + case WM_TIMER: OnTimer( hwndDlg, (UINT_PTR)wParam ); return TRUE; + case WM_ML_IPC: + return OnMediaLibraryIPC( hwndDlg, wParam, lParam ); + case WM_USER + 0x200: SetWindowLongPtr( hwndDlg, DWLP_MSGRESULT, TRUE ); return TRUE; + case WM_USER + 0x201: g_rgnUpdate = (HRGN)lParam; return TRUE;// parent supports region updates. lParam is a HRGN to set HRGN coordinate mapped to parent client area (this message sent prior to WM_WINDOWPOSCHANGED. + case WM_SHOWWINDOW: + { + if ( wParam ) PostMessage( hwndDlg, WM_USER + 31, wParam, 0 ); + else OnShowWindow( hwndDlg, (BOOL)wParam ); + return TRUE; + } + case WM_USER + 30: + { + SetWindowRedraw( hwndDlg, FALSE ); + DestroyWindow( m_curview_hwnd ); + SetWindowRedraw( hwndDlg, TRUE ); + CreateCurrentView( hwndDlg ); + break; + } + case WM_USER + 31: + // double-pump this to work around slower plugins loading up + if ( !lParam ) + PostMessage( hwndDlg, WM_USER + 31, wParam, 1 ); + else + { + OnShowWindow( hwndDlg, (BOOL)wParam ); + NavItemI_EnsureVisible( NavCtrlI_GetSelection( hNavigation ) ); + } + break; + case WM_CONTEXTMENU: MSGRESULT( hwndDlg, MlView_OnContextMenu( hwndDlg, (HWND)wParam, MAKEPOINTS( lParam ) ) ); + case WM_HELP: MSGRESULT( hwndDlg, MlView_OnHelp( hwndDlg, (HELPINFO *)lParam ) ); + case WMML_SHOWCONTAINER: ShowWindow( g_ownerwnd, (INT)wParam ); return TRUE; + + } + return FALSE; +} + +void OnInitMenuPopUp( HMENU menu ) +{ + if ( main_sendtomenu || !main_sendto_hmenu || menu != main_sendto_hmenu ) + return; + + main_sendtomenu = new SendToMenu(); + main_sendtomenu->buildmenu( menu, main_sendto_mode, 0 ); +} + +void OnDisplayChange( HWND hwndDlg, UINT imgDepth, UINT hRes, UINT vRes ) +{ + ResetColors( TRUE ); // must be first + MlStockObjects_Reset(); + + ratingGlobalStyle = g_config->ReadInt( L"rating_style", RATING_DEFAULT_STYLE ); + MLRatingColumnI_Update(); + + if ( IsWindow( m_curview_hwnd ) ) + { + RECT rc; + GetClientRect( m_curview_hwnd, &rc ); + RedrawWindow( m_curview_hwnd, &rc, NULL, RDW_INVALIDATE | RDW_ERASE | RDW_ALLCHILDREN | RDW_ERASENOW/* | RDW_UPDATENOW*/ ); + MLSkinnedWnd_SkinChanged( m_curview_hwnd, TRUE, TRUE ); + SendNotifyMessageW( m_curview_hwnd, WM_DISPLAYCHANGE, imgDepth, MAKELPARAM( hRes, vRes ) ); + } + + NavCtrlI_UpdateLook( hNavigation ); + MLSkinnedWnd_SkinChanged( GetDlgItem( hwndDlg, IDC_BTN_LIB ), TRUE, TRUE ); + MLSkinnedWnd_SkinChanged( GetDlgItem( hwndDlg, IDC_VDELIM ), TRUE, TRUE ); + MLSkinnedWnd_SkinChanged( GetDlgItem( hwndDlg, IDC_NO_VIEW ), TRUE, TRUE ); + LayoutWindows( hwndDlg, divider_pos, TRUE ); +} + +#include "mldwm.h" + +int OnInitDialog( HWND hwndDlg ) +{ + firstShow = TRUE; + MlStockObjects_Init(); + + if ( S_OK == MlDwm_LoadLibrary() ) + { + DWMNCRENDERINGPOLICY ncrp = DWMNCRP_DISABLED; + BOOL allow = FALSE; + MlDwm_SetWindowAttribute( GetParent( hwndDlg ), DWMWA_NCRENDERING_POLICY, &ncrp, sizeof( ncrp ) ); + MlDwm_SetWindowAttribute( GetParent( hwndDlg ), DWMWA_ALLOW_NCPAINT, &allow, sizeof( allow ) ); + } + + MLSkinWindow2( hwndDlg, hwndDlg, SKINNEDWND_TYPE_DIALOG, SWS_USESKINFONT | SWS_USESKINCOLORS | SWS_USESKINCURSORS ); + HWND hctrl = GetDlgItem( hwndDlg, IDC_BTN_LIB ); + MLSkinWindow2( hwndDlg, hctrl, SKINNEDWND_TYPE_BUTTON, SWS_USESKINFONT | SWS_USESKINCOLORS | SWS_USESKINCURSORS ); + + hctrl = GetDlgItem( hwndDlg, IDC_VDELIM ); + MLSkinWindow2( hwndDlg, hctrl, SKINNEDWND_TYPE_DIVIDER, SWS_USESKINCOLORS | SWS_USESKINCURSORS | SWDIV_VERT ); + MLSkinnedDivider_SetCallback( hctrl, OnDividerMoved, NULL ); + + hctrl = GetDlgItem( hwndDlg, IDC_NO_VIEW ); + MLSkinWindow2( hwndDlg, hctrl, SKINNEDWND_TYPE_STATIC, SWS_USESKINCOLORS | SWS_USESKINCURSORS | SWDIV_VERT ); + + if ( !hmlifMngr ) // Initialize Image Filter manager + { + hmlifMngr = MLImageFilterI_CreateManager( 8, 4 ); + if ( hmlifMngr ) + { + RegisterImageFilters( hmlifMngr ); + SkinnedButton::RegisterImageFilter( hmlifMngr ); + } + } + + if ( !hNavigation ) + { + hNavigation = NavCtrlI_Create( hwndDlg ); + if ( hNavigation ) + { + if ( !hmlilNavigation ) hmlilNavigation = CreateNavigationImages( hmlifMngr ); + NavCtrlI_SetConfig( hNavigation, g_config ); + NavCtrlI_SetImageList( hNavigation, hmlilNavigation ); + NavCtrlI_RegisterCallback( hNavigation, OnNavCtrl_Selected, CALLBACK_ONSELECTED_I ); + NavCtrlI_RegisterCallback( hNavigation, OnNavCtrl_Click, CALLBACK_ONCLICK_I ); + NavCtrlI_RegisterCallback( hNavigation, OnNavCtrl_KeyDown, CALLBACK_ONKEYDOWN_I ); + NavCtrlI_RegisterCallback( hNavigation, OnNavCtrl_BeginDrag, CALLBACK_ONBEGINDRAG_I ); + NavCtrlI_RegisterCallback( hNavigation, OnNavCtrl_GetImageIndex, CALLBACK_ONGETIMAGEINDEX_I ); + NavCtrlI_RegisterCallback( hNavigation, OnNavCtrl_BeginTitleEdit, CALLBACK_ONBEGINTITLEEDIT_I ); + NavCtrlI_RegisterCallback( hNavigation, OnNavCtrl_EndTitleEdit, CALLBACK_ONENDTITLEEDIT_I ); + NavCtrlI_RegisterCallback( hNavigation, OnNavItem_Delete, CALLBACK_ONITEMDELETE_I ); + NavCtrlI_RegisterCallback( hNavigation, OnNavItem_CustomDraw, CALLBACK_ONITEMDRAW_I ); + NavCtrlI_RegisterCallback( hNavigation, OnNavItem_SetCursor, CALLBACK_ONSETCURSOR_I ); + NavCtrlI_RegisterCallback( hNavigation, OnNavItem_HitTest, CALLBACK_ONHITTEST_I ); + NavCtrlI_RegisterCallback( hNavigation, OnNavCtrl_Destroy, CALLBACK_ONDESTROY_I ); + hctrl = NavCtrlI_GetHWND( hNavigation ); + SetWindowLongPtr( hctrl, GWLP_ID, IDC_NAVIGATION ); + SetWindowLongPtr( hctrl, GWL_STYLE, GetWindowLongPtr( hctrl, GWL_STYLE ) | WS_GROUP ); + } + } + + if ( hNavigation ) + SetWindowPos( GetDlgItem( hwndDlg, IDC_BTN_LIB ), NavCtrlI_GetHWND( hNavigation ), 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOSIZE | SWP_NOMOVE ); + + divider_pos = g_config->ReadInt( L"ldivpos", 135 ); + + if ( !hmlilRating ) + { + hmlilRating = MLImageListI_Create( 44, 12, MLILC_COLOR24_I, 1, 1, 4, hmlifMngr ); + if ( hmlilRating ) + { + MLIMAGESOURCE_I is = { 0 }; + is.hInst = plugin.hDllInstance; + is.lpszName = MAKEINTRESOURCEW( IDB_RATING ); + is.type = SRC_TYPE_PNG_I; + MLImageListI_Add( hmlilRating, &is, MLIF_FILTER1_UID, 0 ); + } + } + + ratingGlobalStyle = g_config->ReadInt( L"rating_style", RATING_DEFAULT_STYLE ); + + MLRatingColumnI_Initialize(); + +#ifdef CLOUD + if ( !hmlilCloud ) + { + hmlilCloud = MLImageListI_Create( 16, 16, MLILC_COLOR24_I, 1, 1, 4, hmlifMngr ); + if ( hmlilCloud ) + { + MLIMAGESOURCE_I is = { 0 }; + is.hInst = plugin.hDllInstance; + is.lpszName = MAKEINTRESOURCEW( IDB_CLOUD_IS_IN ); + is.type = SRC_TYPE_PNG_I; + MLImageListI_Add( hmlilCloud, &is, MLIF_FILTER1_UID, 0 ); + + is.lpszName = MAKEINTRESOURCEW( IDB_CLOUD_PARTIAL ); + MLImageListI_Add( hmlilCloud, &is, MLIF_FILTER1_UID, 0 ); + + is.lpszName = MAKEINTRESOURCEW( IDB_CLOUD_UNAVAIL ); + MLImageListI_Add( hmlilCloud, &is, MLIF_FILTER1_UID, 0 ); + + is.lpszName = MAKEINTRESOURCEW( IDB_CLOUD_UPLOAD ); + MLImageListI_Add( hmlilCloud, &is, MLIF_FILTER1_UID, 0 ); + + is.lpszName = MAKEINTRESOURCEW( IDB_CLOUD_UPLOADING ); + MLImageListI_Add( hmlilCloud, &is, MLIF_FILTER1_UID, 0 ); + } + } + + MLCloudColumnI_Initialize(); +#endif + + OnDisplayChange( hwndDlg, 0, 0, 0 ); + + HACCEL hAccel = WASABI_API_LOADACCELERATORSW( IDR_ACCELERATOR_GLOBAL ); + if ( hAccel ) + WASABI_API_APP->app_addAccelerators( hwndDlg, &hAccel, 1, TRANSLATE_MODE_GLOBAL ); + + hAccel = WASABI_API_LOADACCELERATORSW( IDR_ACCELERATOR_MAIN ); + if ( hAccel ) + WASABI_API_APP->app_addAccelerators( hwndDlg, &hAccel, 1, TRANSLATE_MODE_CHILD ); + + return TRUE; +} + +static void OnDestroy( HWND hwndDlg ) +{ + HNAVITEM hItem = NavCtrlI_GetSelection( hNavigation ); + if ( hItem ) + { + wchar_t name[ 1024 ] = { 0 }; + if ( NavItemI_GetFullName( hItem, name, 1024 ) ) + g_config->WriteString( "last_view", AutoChar( name, CP_UTF8 ) ); + } + + g_config->WriteInt( L"ldivpos", divider_pos ); + + NavCtrlI_Destroy( hNavigation ); + hNavigation = NULL; + MLImageListI_Destroy( hmlilNavigation ); + hmlilNavigation = NULL; + MLImageFilterI_DestroyManager( hmlifMngr ); + hmlifMngr = NULL; + MLImageListI_Destroy( hmlilRating ); + hmlilRating = NULL; + MLImageListI_Destroy( hmlilCloud ); + hmlilCloud = NULL; + + RemoveProp( hwndDlg, BLOCK_ACCELTOGGLE_PROP ); + + MlStockObjects_Free(); +} + +static void OnClose( HWND hwndDlg ) +{ + toggleVisible( 1 ); +} + +static void OnShowWindow( HWND hwndDlg, BOOL fShow ) +{ + if ( firstShow && fShow ) + { + firstShow = FALSE; + + MLVisibleChanged( TRUE ); + // setting things to a more user friendly default than just the Local Media view + char *lastTitleA = g_config->ReadString( "last_view", "Local Media/Audio" ); + + HNAVITEM hDefItem = NULL; + if ( lastTitleA && *lastTitleA ) + { + wchar_t textW[ 2048 ] = { 0 }; + if ( MultiByteToWideCharSZ( CP_UTF8, 0, lastTitleA, -1, textW, 2048 ) ) + hDefItem = NavCtrlI_FindItemByFullName( hNavigation, LOCALE_INVARIANT, NICF_INVARIANT_I | NICF_DISPLAY_I | NICF_IGNORECASE_I, textW, -1, TRUE ); + } + + if ( !hDefItem ) + hDefItem = NavCtrlI_GetRoot( hNavigation ); + + NavItemI_Select( hDefItem ); + NavCtrlI_Show( hNavigation, SW_SHOWNA ); + } +} + +LRESULT OnMediaLibraryIPC( HWND hwndDlg, WPARAM wParam, LPARAM lParam ) +{ + SetWindowLongPtr( hwndDlg, DWLP_MSGRESULT, (LONGX86)(LONG_PTR)pluginHandleIpcMessage( hwndDlg, (INT)lParam, (INT_PTR)wParam ) ); + + return TRUE; +} + +void OnGetMaxMinInfo( MINMAXINFO *info ) +{ + info->ptMinTrackSize.x = 300; + info->ptMinTrackSize.y = 200; +} + +void OnBtnLibraryClick( HWND hwndBtn ) +{ + RECT r; + GetWindowRect( hwndBtn, &r ); + SendMessageW( hwndBtn, BM_SETSTATE, TRUE, 0L ); + + MediaLibrary_TrackPopup( GetSubMenu( g_context_menus, 0 ), + TPM_RIGHTBUTTON | TPM_LEFTBUTTON | TPM_BOTTOMALIGN | TPM_LEFTALIGN | TPM_VERNEGANIMATION, + r.left, r.top, + GetParent( hwndBtn ) ); + + SendMessageW( hwndBtn, BM_SETSTATE, FALSE, 0L ); + UpdateWindow( hwndBtn ); + Sleep( 100 ); + MSG msg; + while ( PeekMessageW( &msg, NULL, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE ) ); //eat return +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/wa_dlg.cpp b/Src/Plugins/General/gen_ml/wa_dlg.cpp new file mode 100644 index 00000000..5ae57253 --- /dev/null +++ b/Src/Plugins/General/gen_ml/wa_dlg.cpp @@ -0,0 +1,2 @@ +#define WA_DLG_IMPLEMENT 1 +#include "../winamp/wa_dlg.h" \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/webinfo_dlg.cpp b/Src/Plugins/General/gen_ml/webinfo_dlg.cpp new file mode 100644 index 00000000..61e780f0 --- /dev/null +++ b/Src/Plugins/General/gen_ml/webinfo_dlg.cpp @@ -0,0 +1,547 @@ +#include "main.h" +#include "api__gen_ml.h" +#include "./resource.h" +#include "./webinfo_obj.h" +#include "../nu/mtbrowser.h" +#include "./ml_ipc_0313.h" +#include "./stockobjects.h" +#include "../nu/CGlobalAtom.h" +#include + +#define BROWSER_FORCEQUITDELAY 5000 +#define BROWSER_TERMINATEDELAY 3000 + +#define BROWSER_CACHEINTERVAL 3000 + +#define TIMER_BROWSERFORCEQUIT_ID 1975 +#define TIMER_BROWSERCACHE_ID 1976 +#define TIMER_BROWSERLOADINGNOTICE_ID 1977 + +#define TIMER_BROWSERLOADINGNOTICE_DELAY 80 + +#define WEBINFOWND_CLASSW L"WAWEBINFOWND" +static CGlobalAtom BROWSERWND_PROPW(L"BWPROP"); + +#define WM_BROWSERNOTIFY (WM_USER + 0xF000) + +#define IDC_LBL_INFO 0x100 +#define IDC_BROWSER 0x1000 + + +typedef struct _BROWSERVIEW +{ + MTBROWSER browser; + BOOL bBrowserReady; + BOOL bDestroying; + UINT uMsgQuery; + LPWSTR pszCachedFileName; +} BROWSERVIEW; + + +static HWND hwndCached = NULL; +static LRESULT WINAPI WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); +static void CALLBACK Window_TimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime); + +#define GetBrowserView(_hwnd) ((BROWSERVIEW*)GetPropW(_hwnd, BROWSERWND_PROPW)) + + +// public function +HWND CreateWebInfoWindow(HWND hwndParent, UINT uMsgQuery, INT x, INT y, INT cx, INT cy, INT ctrlId) +{ + HWND hwnd; + BROWSERVIEW *pbv; + WNDCLASSW wc; + + if (hwndCached && IsWindow(hwndCached)) + { + pbv = GetBrowserView(hwndCached); + if (pbv) + { + KillTimer(hwndCached, TIMER_BROWSERCACHE_ID); + hwnd = hwndCached; + hwndCached = NULL; + + pbv->uMsgQuery = uMsgQuery; + pbv->bDestroying = FALSE; + + pbv->pszCachedFileName = NULL; + ShowWindow(hwnd, SW_HIDE); + SetParent(hwnd, hwndParent); + SetWindowPos(hwnd, HWND_TOP, x, y, cx, cy, SWP_NOACTIVATE | SWP_NOREDRAW | SWP_NOCOPYBITS); + SetWindowLongPtrW(hwnd, GWLP_ID, ctrlId); + return hwnd; + } + } + + if (!GetClassInfoW(plugin.hDllInstance, WEBINFOWND_CLASSW, &wc)) + { + ZeroMemory(&wc, sizeof(WNDCLASSW)); + wc.hInstance = plugin.hDllInstance; + wc.lpszClassName = WEBINFOWND_CLASSW; + wc.lpfnWndProc = WindowProc; + wc.style = CS_DBLCLKS; + if (!RegisterClassW(&wc)) return NULL; + } + + pbv = (BROWSERVIEW*)calloc(1, sizeof(BROWSERVIEW)); + if (!pbv) return NULL; + pbv->uMsgQuery = uMsgQuery; + + hwnd = CreateWindowExW(WS_EX_CONTROLPARENT, WEBINFOWND_CLASSW, L"", + DS_CONTROL | WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, + x, y, cx, cy, hwndParent, (HMENU)(INT_PTR)ctrlId, plugin.hDllInstance, (LPVOID)pbv); + if (!hwnd) free(pbv); + else + { + HFONT font; + HWND hwndCtrl; + + hwndCtrl = CreateWindowExW(WS_EX_NOPARENTNOTIFY, L"STATIC", L"", + WS_CHILD | SS_OWNERDRAW | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, + 0, 0, 1, 1, hwnd, (HMENU)IDC_LBL_INFO, NULL, 0); + + font = (HFONT)MlStockObjects_Get(DEFAULT_FONT); + SendMessageW(hwnd, WM_SETFONT, (WPARAM)font, FALSE); + if (hwndCtrl) SendMessageW(hwndCtrl, WM_SETFONT, (WPARAM)font, FALSE); + } + + return hwnd; +} + + +static void CALLBACK APC_NavigateToPage(HTMLContainer2 *pContainer, VARIANTARG *pArgs, INT cArgs, LPARAM *pResult) +{ + WebFileInfo *pWebInfo; + pWebInfo = reinterpret_cast(pContainer); + pWebInfo->NavigateToPage(); +} + +static void CALLBACK APC_InvokeFileInfo(HTMLContainer2 *pContainer, VARIANTARG *pArgs, INT cArgs, LPARAM *pResult) +{ + WebFileInfo *pWebInfo; + pWebInfo = reinterpret_cast(pContainer); + *pResult = (pWebInfo && 1 == cArgs) ? pWebInfo->InvokeFileInfo(pArgs[0].bstrVal) : E_INVALIDARG; +} + +static void CALLBACK APC_DisplayMessage(HTMLContainer2 *pContainer, VARIANTARG *pArgs, INT cArgs, LPARAM *pResult) +{ + WebFileInfo *pWebInfo; + pWebInfo = reinterpret_cast(pContainer); + *pResult = (pWebInfo && 1 == cArgs) ? pWebInfo->DisplayMessage(pArgs[0].bstrVal, FALSE) : E_INVALIDARG; +} + +static void CALLBACK APC_GetHostHWND(HTMLContainer2 *pContainer, VARIANTARG *pArgs, INT cArgs, LPARAM *pResult) +{ + if (1 == cArgs) + { + *((HWND*)pArgs[0].byref) = pContainer->GetHostHWND(); + *pResult = S_OK; + } + else *pResult = E_INVALIDARG; +} + +static void CALLBACK APC_RegisterCursor(HTMLContainer2 *pContainer, VARIANTARG *pArgs, INT cArgs, LPARAM *pResult) +{ + if (1 == cArgs) + { + HCURSOR hCur = (pArgs[0].byref) ? CopyCursor((HCURSOR)pArgs[0].byref) : NULL; + *pResult = pContainer->RegisterBrowserCursor(/*OCR_NORMAL*/32512, hCur); + } + else *pResult = E_INVALIDARG; +} + +static void CALLBACK APC_UpdateColors(HTMLContainer2 *pContainer, VARIANTARG *pArgs, INT cArgs, LPARAM *pResult) +{ + WebFileInfo *pWebInfo; + pWebInfo = reinterpret_cast(pContainer); + if (pWebInfo) pWebInfo->UpdateColors(); +} + +static void DisplayInfoText(HWND hwndView, LPCWSTR pszText) +{ + HWND hwndCtrl; + hwndCtrl = GetDlgItem(hwndView, IDC_LBL_INFO); + if (!hwndCtrl) return; + if (pszText) + { + SetWindowTextW(hwndCtrl, pszText); + if (!IsWindowVisible(hwndCtrl)) + { + RECT rc; + GetClientRect(hwndView, &rc); + SetWindowPos(hwndCtrl, NULL, 8, 8, rc.right - 16, rc.bottom - 16, SWP_NOACTIVATE | SWP_NOZORDER); + ShowWindow(hwndCtrl, SW_SHOWNORMAL); + } + } + else ShowWindow(hwndCtrl, SW_HIDE); +} + +static LRESULT Window_OnCreate(HWND hwnd, CREATESTRUCT *pcs) +{ + BROWSERVIEW *pbv; + + pbv =(BROWSERVIEW*)pcs->lpCreateParams; + if (pbv) + { + SetPropW(hwnd, BROWSERWND_PROPW, (HANDLE)pbv); + pbv->bBrowserReady = FALSE; + MTBrowser_Init(&pbv->browser); + } + return 0; +} + +static void Window_OnDestroy(HWND hwnd) +{ + BROWSERVIEW *pbv; + pbv = GetBrowserView(hwnd); + RemovePropW(hwnd, BROWSERWND_PROPW); + KillTimer(hwnd, TIMER_BROWSERFORCEQUIT_ID); + + if (pbv) + { + pbv->bBrowserReady = FALSE; + + MTBrowser_Kill(&pbv->browser, BROWSER_TERMINATEDELAY); + MTBrowser_Clear(&pbv->browser); + } + free(pbv); + + CoFreeUnusedLibraries(); + +} +static BOOL UpdateCursors(BROWSERVIEW *pbv) +{ + HCURSOR hCur; + HAPC hAPC; + VARIANTARG *pArgs; + + if (!pbv) return FALSE; + + hCur = (HCURSOR)SendMessageW(plugin.hwndParent, WM_WA_IPC, WACURSOR_NORMAL, IPC_GETSKINCURSORS); + + hAPC = MTBrowser_InitializeAPC(&pbv->browser, 1, 0, APC_RegisterCursor, &pArgs); + if (!hAPC) return FALSE; + + pArgs[0].vt = VT_BYREF; + V_BYREF(&pArgs[0]) = hCur; + + return MTBrowser_CallAPC(hAPC); +} + +static void Window_OnDisplayChange(HWND hwnd, INT dpi, INT resX, INT resY) +{ + HWND hwndCtrl; + BROWSERVIEW *pbv; + + pbv = GetBrowserView(hwnd); + if (pbv) + { + HAPC hAPC; + + UpdateCursors(pbv); + + hAPC = MTBrowser_InitializeAPC(&pbv->browser, 0, 0, APC_UpdateColors, NULL); + if (hAPC) MTBrowser_CallAPC(hAPC); + } + InvalidateRect(hwnd, NULL, TRUE); + hwndCtrl = GetDlgItem(hwnd, IDC_LBL_INFO); + if (hwndCtrl) InvalidateRect(hwndCtrl, NULL, TRUE); +} + +static void Window_OnShowWindow(HWND hwnd, BOOL bVisible, INT nStatus) +{ + BROWSERVIEW *pbv; + + pbv = GetBrowserView(hwnd); + if (pbv && !pbv->browser.hThread && bVisible) + { + WebFileInfo *pWebInfo; + IDispatch *pDispWA; + pDispWA = (IDispatch *)SendMessageW(plugin.hwndParent, WM_WA_IPC, 0, IPC_GET_DISPATCH_OBJECT); + if (pDispWA == (IDispatch*)1) pDispWA = NULL; + + pWebInfo = CreateWebFileInfo(hwnd, pDispWA); + if (NULL != pWebInfo) + { + SetTimer(hwnd, TIMER_BROWSERLOADINGNOTICE_ID, TIMER_BROWSERLOADINGNOTICE_DELAY, Window_TimerProc); + MTBrowser_Start(&pbv->browser, pWebInfo, WM_BROWSERNOTIFY); + } + else + { + DisplayInfoText(hwnd, WASABI_API_LNGSTRINGW(IDS_WEBINFO_NAVIGATE_ERROR)); + } + + if (NULL != pDispWA) + pDispWA->Release(); + + } + + if(bVisible && pbv && pbv->uMsgQuery) + { + HWND hwndParent; + hwndParent = GetParent(hwnd); + if (hwndParent) PostMessageW(hwndParent, pbv->uMsgQuery, 0, 0L); + } +} + +static LRESULT Window_OnEraseBkGnd(HWND hwnd, HDC hdc) +{ + if (hdc) + { + RECT rc; + GetClientRect(hwnd, &rc); + SetBkColor(hdc, WADlg_getColor(WADLG_ITEMBG)); + ExtTextOutW(hdc, 0, 0, ETO_OPAQUE, &rc, L"", 0, 0); + } + return 1; +} + +static void Window_OnShowInfo(HWND hwnd, WEBINFOSHOW *pShowParam) +{ + HAPC hAPC; + VARIANTARG *pArgs; + BROWSERVIEW *pbv = GetBrowserView(hwnd); + + if (!IsWindowVisible(hwnd) || !pbv || !pbv->bBrowserReady || !pShowParam) + { + if (pbv && pbv->pszCachedFileName) + { + free(pbv->pszCachedFileName); + pbv->pszCachedFileName = NULL; + + } + return; + } + + if (pShowParam->pszFileName) + { + if (0 == (WISF_FORCE & pShowParam->fFlags) && pbv->pszCachedFileName && + 0 == lstrcmpW(pbv->pszCachedFileName, pShowParam->pszFileName)) return; + else + { + if (pbv->pszCachedFileName) free(pbv->pszCachedFileName); + pbv->pszCachedFileName = _wcsdup(pShowParam->pszFileName); + } + } + else + { + if (0 == (WISF_FORCE & pShowParam->fFlags) && !pbv->pszCachedFileName) return; + if (pbv->pszCachedFileName) + { + free(pbv->pszCachedFileName); + pbv->pszCachedFileName = NULL; + } + } + + hAPC = MTBrowser_InitializeAPC(&pbv->browser, 1, 0, (WISF_MESSAGE & pShowParam->fFlags) ? APC_DisplayMessage : APC_InvokeFileInfo , &pArgs); + if (hAPC) + { + pArgs[0].vt = VT_BSTR; + pArgs[0].bstrVal = SysAllocString(pShowParam->pszFileName); + MTBrowser_CallAPC(hAPC); + } +} + +static void Window_OnWindowPosChanging(HWND hwnd, WINDOWPOS *pwp) +{ + if (0 == (SWP_NOSIZE & pwp->flags)) + { + BROWSERVIEW *pbv; + pbv = GetBrowserView(hwnd); + if (pbv && pbv->bBrowserReady) + { + RECT rc; + HWND hBrowser = GetDlgItem(hwnd, IDC_BROWSER); + if (NULL != hBrowser && FALSE != GetClientRect(hBrowser, &rc) && + (rc.right != pwp->cx || rc.bottom != pwp->cy)) + { + SetWindowPos(hBrowser, NULL, 0, 0, pwp->cx, pwp->cy, + SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_ASYNCWINDOWPOS | ((SWP_NOREDRAW | SWP_NOCOPYBITS) & pwp->flags)); + } + } + } +} + +static void Window_OnWindowPosChanged(HWND hwnd, WINDOWPOS *pwp) +{ + if (0 == (SWP_NOSIZE & pwp->flags)) + { + HWND hwndCtrl; + hwndCtrl = GetDlgItem(hwnd, IDC_LBL_INFO); + if (hwndCtrl && WS_VISIBLE == (WS_VISIBLE & GetWindowLongPtrW(hwnd, GWL_STYLE))) + { + SetWindowPos(hwndCtrl, NULL, 0, 0, pwp->cx - 16, pwp->cy - 16, + SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOCOPYBITS | SWP_NOREDRAW); + if (0 == (SWP_NOREDRAW & pwp->flags)) InvalidateRect(hwndCtrl, NULL, FALSE); + } + if (0 == (SWP_NOREDRAW & pwp->flags)) + { + HWND hBrowser = GetDlgItem(hwnd, IDC_BROWSER); + if (NULL == hBrowser || 0 == (WS_VISIBLE & GetWindowLongPtr(hBrowser, GWL_STYLE))) + InvalidateRect(hwnd, NULL, TRUE); + } + } +} + +static void DrawInfoLabel(DRAWITEMSTRUCT *pds) +{ + wchar_t szText[128] = {0}; + SetBkColor(pds->hDC, WADlg_getColor(WADLG_ITEMBG)); + + INT len = GetWindowTextW(pds->hwndItem, szText, sizeof(szText)/sizeof(wchar_t)); + ExtTextOutW(pds->hDC, 0, 0, ETO_OPAQUE, &pds->rcItem, L"", 0, 0); + if(len) + { + RECT rt; + INT height, x, y; + CopyRect(&rt, &pds->rcItem); + + height = DrawTextW(pds->hDC, szText, len, &rt, DT_CALCRECT | DT_CENTER | DT_WORDBREAK); + SetTextColor(pds->hDC, WADlg_getColor(WADLG_ITEMFG)); + + y = pds->rcItem.top + ((pds->rcItem.bottom - pds->rcItem.top) - height)/2; + if (y < pds->rcItem.top) y = pds->rcItem.top; + x = pds->rcItem.left + ((pds->rcItem.right - pds->rcItem.left) - (rt.right - rt.left))/2; + + SetRect(&rt, x, y, x + rt.right- rt.left, y + rt.bottom - rt.top); + + DrawTextW(pds->hDC, szText, len, &rt, DT_CENTER | DT_WORDBREAK); + } +} + +static LRESULT Window_OnDrawItem(HWND hwnd, INT ctrlID, DRAWITEMSTRUCT *pds) +{ + switch(ctrlID) + { + case IDC_LBL_INFO: DrawInfoLabel(pds); return 1; + } + return 0; +} + +static void Browser_OnDocumentComplete(HWND hwndView, BROWSERVIEW *pbv, BOOL bDocReady) +{ + if (bDocReady) + { + RECT rc; + + KillTimer(hwndView, TIMER_BROWSERLOADINGNOTICE_ID); + DisplayInfoText(hwndView, NULL); // hide + + GetClientRect(hwndView, &rc); + MTBrowser_SetLocationAPC(&pbv->browser, &rc); + } +} + +static void Browser_OnDestroyed(HWND hwndView, BROWSERVIEW *pbv) +{ + if (pbv->bDestroying) DestroyWindow(hwndView); + else {} // browser died for some reason +} + +static void Browser_OnReady(HWND hwndView, BROWSERVIEW *pbv) +{ + if (NULL == pbv) return; + pbv->bBrowserReady = TRUE; + + + HWND hBrowser = (NULL != pbv->browser.pContainer) ? pbv->browser.pContainer->GetHostHWND() : NULL; + if (NULL != hBrowser) + SetWindowLongPtr(hBrowser, GWLP_ID, IDC_BROWSER); + + UpdateCursors(pbv); + HWND hParent = GetParent(hwndView); + if (NULL != hParent && 0 != pbv->uMsgQuery) PostMessageW(hParent, pbv->uMsgQuery, 0, 0L); +} + +static void CALLBACK Window_TimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime) +{ + BROWSERVIEW *pbv; + switch(idEvent) + { + case TIMER_BROWSERFORCEQUIT_ID: + KillTimer(hwnd, TIMER_BROWSERFORCEQUIT_ID); + DestroyWindow(hwnd); + break; + case TIMER_BROWSERCACHE_ID: + KillTimer(hwnd, TIMER_BROWSERCACHE_ID); + pbv = GetBrowserView(hwnd); + + if (hwnd == hwndCached) hwndCached = NULL; + + if (pbv && pbv->bDestroying) + { + pbv->bBrowserReady = FALSE; + MTBrowser_QuitAPC(&pbv->browser); + SetTimer(hwnd, TIMER_BROWSERFORCEQUIT_ID, BROWSER_FORCEQUITDELAY, Window_TimerProc); + } + else DestroyWindow(hwnd); + break; + case TIMER_BROWSERLOADINGNOTICE_ID: + KillTimer(hwnd, TIMER_BROWSERLOADINGNOTICE_ID); + DisplayInfoText(hwnd, WASABI_API_LNGSTRINGW(IDS_LOADING_IN_PROGRESS)); + break; + } +} + +static void Window_OnRelease(HWND hwnd) +{ + BROWSERVIEW *pbv; + pbv = GetBrowserView(hwnd); + if (pbv) + { + hwndCached = hwnd; + pbv->bDestroying = TRUE; + if (pbv->pszCachedFileName) + { + free(pbv->pszCachedFileName); + pbv->pszCachedFileName = NULL; + } + SetWindowPos(hwnd, HWND_BOTTOM, -30000, -30000, 1, 1, SWP_ASYNCWINDOWPOS | SWP_HIDEWINDOW | SWP_NOACTIVATE | + SWP_NOREDRAW | SWP_DEFERERASE | SWP_NOCOPYBITS | SWP_NOSENDCHANGING); + SetParent(hwnd, plugin.hwndParent); + SetTimer(hwnd, TIMER_BROWSERCACHE_ID, BROWSER_CACHEINTERVAL, Window_TimerProc); + } + else DestroyWindow(hwnd); +} + +static void Window_OnBrowserNotify(HWND hwnd, INT nCode, LPARAM lParam) +{ + BROWSERVIEW *pbv; + pbv = GetBrowserView(hwnd); + if (!pbv) return; + switch(nCode) + { + case MTBC_READY: Browser_OnReady(hwnd, pbv); break; + case MTBC_DESTROYED: Browser_OnDestroyed(hwnd, pbv); break; + case MTBC_DOCUMENTCOMPLETE: Browser_OnDocumentComplete(hwnd, pbv, (BOOL)lParam); break; + } +} + +static LRESULT Window_OnMediaLibraryIPC(HWND hwndView, INT msg, INT_PTR param) +{ + switch(msg) + { + case ML_IPC_WEBINFO_RELEASE: Window_OnRelease(hwndView); return TRUE; + case ML_IPC_WEBINFO_SHOWINFO: Window_OnShowInfo(hwndView, (WEBINFOSHOW*)param); return TRUE; + } + return 0; +} + +static LRESULT WINAPI WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch(uMsg) + { + case WM_CREATE: return (LRESULT)Window_OnCreate(hwnd, (CREATESTRUCT*)lParam); + case WM_DESTROY: Window_OnDestroy(hwnd); break; + case WM_DISPLAYCHANGE: Window_OnDisplayChange(hwnd, (INT)wParam, LOWORD(lParam), HIWORD(lParam)); break; + case WM_SHOWWINDOW: Window_OnShowWindow(hwnd, (BOOL)wParam, (INT)lParam); break; + case WM_WINDOWPOSCHANGING: Window_OnWindowPosChanging(hwnd, (WINDOWPOS*)lParam); return 0; + case WM_WINDOWPOSCHANGED: Window_OnWindowPosChanged(hwnd, (WINDOWPOS*)lParam); return 0; + case WM_ERASEBKGND: return (LRESULT)Window_OnEraseBkGnd(hwnd, (HDC)wParam); + case WM_DRAWITEM: return Window_OnDrawItem(hwnd, (INT)wParam, (DRAWITEMSTRUCT*)lParam); + case WM_BROWSERNOTIFY: Window_OnBrowserNotify(hwnd, (INT)wParam, lParam); return 1; + case WM_ML_IPC: + return Window_OnMediaLibraryIPC(hwnd, (INT)lParam, (INT_PTR)wParam); + } + return DefWindowProcW(hwnd, uMsg, wParam, lParam); +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/webinfo_obj.cpp b/Src/Plugins/General/gen_ml/webinfo_obj.cpp new file mode 100644 index 00000000..9b01a053 --- /dev/null +++ b/Src/Plugins/General/gen_ml/webinfo_obj.cpp @@ -0,0 +1,438 @@ +#include "main.h" +#include "./webinfo_obj.h" +#include "../winamp/buildtype.h" +#include "./resource.h" +#include "api__gen_ml.h" +#include "../Winamp/buildtype.h" + +#include +#include +#include + +#include + +#define WEBINFO_URL L"http://client.winamp.com/nowplaying/mini" +#define WEBINFO_FUNCTION L"SongInfoCallback" + +#define WEBINFO_USERAGENT L"Winamp File Info" + +#define WEBINFO_DOWNLOADFLAGS ( DLCTL_DLIMAGES | \ + /*DLCTL_NO_SCRIPTS |*/ \ + /*DLCTL_NO_JAVA | */ \ + DLCTL_NO_DLACTIVEXCTLS | \ + /*DLCTL_NO_RUNACTIVEXCTLS |*/ \ + /*DLCTL_RESYNCHRONIZE |*/ \ + DLCTL_NO_BEHAVIORS | \ + 0) + +#define WEBINFO_HOSTINFODFLAGS ( DOCHOSTUIFLAG_DIALOG | \ + DOCHOSTUIFLAG_DISABLE_HELP_MENU | \ + DOCHOSTUIFLAG_NO3DBORDER | \ + DOCHOSTUIFLAG_SCROLL_NO | \ + DOCHOSTUIFLAG_DISABLE_SCRIPT_INACTIVE | \ + DOCHOSTUIFLAG_ACTIVATE_CLIENTHIT_ONLY | \ + DOCHOSTUIFLAG_ENABLE_INPLACE_NAVIGATION | \ + DOCHOSTUIFLAG_THEME | \ + DOCHOSTUIFLAG_NOPICS | \ + DOCHOSTUIFLAG_NO3DOUTERBORDER | \ + DOCHOSTUIFLAG_DISABLE_UNTRUSTEDPROTOCOL | \ + DOCHOSTUIFLAG_ENABLE_REDIRECT_NOTIFICATION | \ + DOCHOSTUIFLAG_USE_WINDOWLESS_SELECTCONTROL | \ + 0) + +#define WEBINFO_CONTAINERSTYLE (CSTYLE_NAVIGATE2_NOCLICKSOUND | CSTYLE_NOCLICKSOUND) + + + +static const wchar_t pszCSSTemplate[] = L"BODY { " + L"background-color: #%06X;" + L"color: #%06X;" + L"scrollbar-face-color: #%06X;" + L"scrollbar-track-color: #%06X;" + L"scrollbar-3dlight-color: #%06X;" + L"scrollbar-shadow-color: #%06X;" + L"scrollbar-darkshadow-color: #%06X;" + L"scrollbar-highlight-color: #%06X;" + L"scrollbar-arrow-color: #%06X" + L" }"; + +static const wchar_t pszHTMLTemplate[] = L"" + L"" + L"
" + L"%s" + L"
" + L""; + +WebFileInfo *CreateWebFileInfo(HWND hwndParent, IDispatch *pDispWA) +{ + return new WebFileInfo(hwndParent, pDispWA); +} + +static COLORREF GetHTMLColor(int nColorIndex); + +WebFileInfo::WebFileInfo(HWND hwndParent, IDispatch *pDispWA) : + HTMLContainer2(plugin.hwndParent, hwndParent), nHomePage(HOMEPAGE_NOTLOADED), + bstrMessage(NULL), bstrFileName(NULL), nDragMode(DROPEFFECT_NONE) +{ + this->pDispWA = pDispWA; + if (NULL != pDispWA) + pDispWA->AddRef(); +} + +WebFileInfo::~WebFileInfo(void) +{ + if (bstrMessage) + { + SysFreeString(bstrMessage); + bstrMessage = NULL; + } + if (bstrFileName) + { + SysFreeString(bstrFileName); + bstrFileName = NULL; + } + if (NULL != pDispWA) + pDispWA->Release(); +} + +HRESULT WebFileInfo::QueryInterface(REFIID riid, PVOID *ppvObject) +{ + if (IsEqualIID(riid, IID_IDropTarget)) + { + *ppvObject = (IDropTarget*)this; + return S_OK; + } + + return HTMLContainer2::QueryInterface(riid, ppvObject); +} + +ULONG WebFileInfo::AddRef(void) +{ + return HTMLContainer2::AddRef(); +} + +ULONG WebFileInfo::Release(void) +{ + return HTMLContainer2::Release(); +} + +HRESULT WebFileInfo::TranslateAccelerator(LPMSG lpMsg, const GUID __RPC_FAR *pguidCmdGroup, DWORD nCmdID) +{ + if ((WM_KEYDOWN == lpMsg->message || WM_KEYUP == lpMsg->message) && lpMsg->wParam >= VK_F1 && lpMsg->wParam <= VK_F24) + { + HWND hHost = GetParentHWND(); + if (hHost && IsWindow(hHost)) PostMessageW(hHost, lpMsg->message, lpMsg->wParam, lpMsg->lParam); + return S_OK; + } + return HTMLContainer2::TranslateAccelerator(lpMsg, pguidCmdGroup, nCmdID); +} + +HRESULT WebFileInfo::ShowContextMenu(DWORD dwID, POINT __RPC_FAR *ppt, IUnknown __RPC_FAR *pcmdtReserved, IDispatch __RPC_FAR *pdispReserved) +{ +#ifdef WINAMP_FINAL_BUILD + return S_OK; // block menu +#else + return E_NOTIMPL; +#endif +} + +HRESULT WebFileInfo::ShowMessage(HWND hwnd, LPOLESTR lpstrText, LPOLESTR lpstrCaption, DWORD dwType, LPOLESTR lpstrHelpFile, DWORD dwHelpContext, LRESULT *plResult) +{ + wchar_t szBuffer[256] = {0}; + lpstrCaption = WASABI_API_LNGSTRINGW_BUF(IDS_WEBINFO_MESSAGEBOX_TITLE, szBuffer, + sizeof(szBuffer)/sizeof(wchar_t)); + *plResult = MessageBoxW(hwnd, lpstrText, lpstrCaption, dwType); + return S_OK; +} + +COLORREF WebFileInfo::OnGetHostBkColor(void) +{ + return WADlg_getColor(WADLG_ITEMBG); +} + +DWORD WebFileInfo::OnGetHostInfoFlags(void) +{ + return WEBINFO_HOSTINFODFLAGS; +} + +OLECHAR *WebFileInfo::OnGetHostCSS(void) +{ + LPWSTR pszCSS; + pszCSS = (LPWSTR)CoTaskMemAlloc(sizeof(wchar_t)*4096); + if (pszCSS && S_OK != StringCchPrintfW(pszCSS, 4096, pszCSSTemplate, + GetHTMLColor(WADLG_ITEMBG), + GetHTMLColor(WADLG_ITEMFG), + GetHTMLColor(WADLG_LISTHEADER_BGCOLOR), + GetHTMLColor(WADLG_SCROLLBAR_BGCOLOR), + GetHTMLColor(WADLG_LISTHEADER_FRAME_TOPCOLOR), + GetHTMLColor(WADLG_LISTHEADER_BGCOLOR), + GetHTMLColor(WADLG_LISTHEADER_FRAME_BOTTOMCOLOR), + GetHTMLColor(WADLG_LISTHEADER_BGCOLOR), + GetHTMLColor(WADLG_BUTTONFG))) + { + CoTaskMemFree(pszCSS); + pszCSS = NULL; + } + + return pszCSS; +} + +DWORD WebFileInfo::OnGetDownlodFlags(void) +{ + + return WEBINFO_DOWNLOADFLAGS +#ifdef WINAMP_FINAL_BUILD + |DLCTL_SILENT +#endif + ; +} + +LPCWSTR WebFileInfo::OnGetUserAgent(void) +{ + return WEBINFO_USERAGENT; +} + +DWORD WebFileInfo::GetContainerStyle(void) +{ + return WEBINFO_CONTAINERSTYLE; +} +HRESULT WebFileInfo::GetExternal(IDispatch __RPC_FAR *__RPC_FAR *ppDispatch) +{ + if (NULL == ppDispatch) + return E_POINTER; + + if (NULL != pDispWA) + { + *ppDispatch = pDispWA; + pDispWA->AddRef(); + return S_OK; + } + return HTMLContainer2::GetExternal(ppDispatch); +} + +HRESULT WebFileInfo::GetDropTarget(IDropTarget __RPC_FAR *pDropTarget, IDropTarget __RPC_FAR *__RPC_FAR *ppDropTarget) +{ + if (ppDropTarget) + { + *ppDropTarget = (IDropTarget*)this; + AddRef(); + return S_OK; + } + return HTMLContainer2::GetDropTarget(pDropTarget, ppDropTarget); +} + +void WebFileInfo::OnBeforeNavigate(IDispatch *pDispatch, VARIANT *URL, VARIANT *Flags, VARIANT *TargetFrameName, VARIANT *PostData, VARIANT *Headers, VARIANT_BOOL *Cancel) +{ + HTMLContainer2::OnBeforeNavigate(pDispatch, URL, Flags, TargetFrameName, PostData, Headers, Cancel); + if (HOMEPAGE_LOADED == nHomePage) nHomePage = HOMEPAGE_NOTLOADED; + +} +void WebFileInfo::OnNewWindow3(IDispatch **ppDisp, VARIANT_BOOL *Cancel, DWORD dwFlags, BSTR bstrUrlContext, BSTR bstrUrl) +{ + HTMLContainer2::OnNewWindow3(ppDisp, Cancel, dwFlags, bstrUrlContext, bstrUrl); + if (bstrUrl) + { + HWND hwndHost; + hwndHost = GetHostHWND(); + ShellExecuteW(hwndHost, NULL, bstrUrl, NULL, L".", 0); // lets open all annoying popups in default browser + } + *ppDisp = NULL; + *Cancel = VARIANT_TRUE; +} + +void WebFileInfo::OnNavigateError(IDispatch *pDispatch, VARIANT *URL, VARIANT *TargetFrameName, VARIANT *StatusCode, VARIANT_BOOL *Cancel) +{ + HTMLContainer2::OnNavigateError(pDispatch, URL, TargetFrameName, StatusCode, Cancel); + + *Cancel = VARIANT_TRUE; + + if (bstrFileName) + { + SysFreeString(bstrFileName); + bstrFileName = NULL; + } + + IWebBrowser2 *pWeb2; + + if (pDispatch && SUCCEEDED(pDispatch->QueryInterface(IID_IWebBrowser2, (void**)&pWeb2))) + { + pWeb2->Stop(); + pWeb2->Release(); + } + + wchar_t szErrorString[128] = {0}; + WASABI_API_LNGSTRINGW_BUF(IDS_WEBINFO_NAVIGATE_ERROR, szErrorString, sizeof(szErrorString)/sizeof(wchar_t)); + DisplayMessage(szErrorString, TRUE); + nHomePage = HOMEPAGE_FAILED; +} + +void WebFileInfo::OnDocumentReady(IDispatch *pDispatch, VARIANT *URL) +{ + HTMLContainer2::OnDocumentReady(pDispatch, URL); + + if (bstrMessage) + { + if (URL && VT_BSTR == URL->vt && URL->bstrVal && 0 == lstrcmpW(URL->bstrVal, L"about:blank")) + { + wchar_t szHTML[4096] = {0}; + if (S_OK == StringCchPrintfW(szHTML, sizeof(szHTML)/sizeof(wchar_t), pszHTMLTemplate, bstrMessage)) + { + WriteHTML(szHTML); + } + } + SysFreeString(bstrMessage); + bstrMessage = NULL; + } + + if (HOMEPAGE_LOADING == nHomePage) + { + nHomePage = HOMEPAGE_LOADED; + if (bstrFileName) + { + InvokeFileInfo(bstrFileName); + SysFreeString(bstrFileName); + bstrFileName = NULL; + } + } + +} + +HRESULT WebFileInfo::InvokeFileInfo(LPCWSTR pszFileName) +{ + HRESULT hr; + DISPPARAMS dispParams; + LCID lcid; + + if (HOMEPAGE_NOTLOADED == nHomePage) + { + if (bstrFileName) + { + SysFreeString(bstrFileName); + bstrFileName = NULL; + } + bstrFileName = (pszFileName) ? SysAllocString(pszFileName) : NULL; + return NavigateToPage(); + } + + lcid = MAKELCID(MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), SORT_DEFAULT); + ZeroMemory(&dispParams, sizeof(DISPPARAMS)); + dispParams.cArgs = 1; + dispParams.rgvarg = (VARIANTARG*)calloc(dispParams.cArgs, sizeof(VARIANTARG)); + if (!dispParams.rgvarg) hr = E_OUTOFMEMORY; + else + { + VariantInit(&dispParams.rgvarg[0]); + dispParams.rgvarg[0].vt = VT_BSTR; + dispParams.rgvarg[0].bstrVal = SysAllocString(pszFileName); + wTRACE_FMT(L"WebInfo: Requesting song info for '%s'.\n", pszFileName); + hr = InvokeScriptFunction(WEBINFO_FUNCTION, lcid, &dispParams, NULL, NULL, NULL); + if (S_OK != hr) TRACE_FMT(TEXT("Error sending webinfo (0x%08X)\n"), hr); + VariantClear(&dispParams.rgvarg[0]); + free(dispParams.rgvarg); + } + return hr; +} + +HRESULT WebFileInfo::NavigateToPage(void) +{ + HRESULT hr; + nHomePage = HOMEPAGE_LOADING; + hr = NavigateToName(WEBINFO_URL, navNoHistory); + if (FAILED(hr)) nHomePage = HOMEPAGE_FAILED; + return hr; +} + +HRESULT WebFileInfo::UpdateColors(void) +{ + HRESULT hr; + IWebBrowser2 *pWeb2; + + if (HOMEPAGE_LOADED == nHomePage) return S_OK; + + hr = GetIWebBrowser2(&pWeb2); + if (SUCCEEDED(hr)) + { + hr = pWeb2->Refresh(); + pWeb2->Release(); + } + return hr; +} + +HRESULT WebFileInfo::DisplayMessage(LPCWSTR pszMessage, BOOL bPostIt) +{ + HRESULT hr; + VARIANT Flags, URL; + if (bstrMessage) + { + SysFreeString(bstrMessage); + bstrMessage = NULL; + } + bstrMessage = (pszMessage) ? SysAllocString(pszMessage) : NULL; + + VariantInit(&URL); + VariantInit(&Flags); + + Flags.vt = VT_I4; + V_I4(&Flags) = navNoHistory | navNoReadFromCache | navNoWriteToCache; + URL.vt = VT_BSTR; + V_BSTR(&URL) = SysAllocString(L"about:blank"); + + nHomePage = HOMEPAGE_NOTLOADED; + hr = (bPostIt) ? PostNavigate2(&URL, &Flags, NULL, NULL, NULL) : Navigate2(&URL, &Flags, NULL, NULL, NULL); + + VariantClear(&Flags); + VariantClear(&URL); + return hr; +} + +HRESULT WebFileInfo::DragEnter(IDataObject * pDataObject, DWORD grfKeyState, POINTL pt, DWORD * pdwEffect) +{ + HRESULT hr; + FORMATETC format = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; + + hr = pDataObject->QueryGetData(&format); + nDragMode = ((hr == S_OK) ? DROPEFFECT_COPY : DROPEFFECT_NONE); + *pdwEffect = nDragMode; + return S_OK; +} + +HRESULT WebFileInfo::DragOver(DWORD grfKeyState, POINTL pt, DWORD * pdwEffect) +{ + *pdwEffect = nDragMode; + return S_OK; +} + +HRESULT WebFileInfo::DragLeave(void) +{ + nDragMode = DROPEFFECT_NONE; + return S_OK; +} + +HRESULT WebFileInfo::Drop(IDataObject * pDataObject, DWORD grfKeyState, POINTL pt, DWORD * pdwEffect) +{ + STGMEDIUM medium; + FORMATETC format = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; + + if (nDragMode) + { + HRESULT hr = pDataObject->QueryGetData(&format); + if (S_OK == hr) + { + hr = pDataObject->GetData (&format, &medium); + if (S_OK == hr) + { + wchar_t szFileName[4096] = {0}; + HDROP hdrop = (HDROP)medium.hGlobal; + if (hdrop && DragQueryFileW(hdrop, 0, szFileName, sizeof(szFileName)/sizeof(wchar_t))) InvokeFileInfo(szFileName); + } + } + nDragMode = DROPEFFECT_NONE; + } + return S_OK; +} + +static COLORREF GetHTMLColor(int nColorIndex) +{ + COLORREF rgb = WADlg_getColor(nColorIndex); + return ((rgb >> 16)&0xff|(rgb&0xff00)|((rgb<<16)&0xff0000)); +} \ No newline at end of file diff --git a/Src/Plugins/General/gen_ml/webinfo_obj.h b/Src/Plugins/General/gen_ml/webinfo_obj.h new file mode 100644 index 00000000..19483d86 --- /dev/null +++ b/Src/Plugins/General/gen_ml/webinfo_obj.h @@ -0,0 +1,72 @@ +#ifndef NULLSOFT_MLDISC_MINIINFO_HEADER +#define NULLSOFT_MLDISC_MINIINFO_HEADER + +#if defined(_MSC_VER) && (_MSC_VER >= 1020) +#pragma once +#endif + +#include "../nu/HTMLContainer2.h" + +#define HOMEPAGE_NOTLOADED 0x00 +#define HOMEPAGE_LOADING 0x01 +#define HOMEPAGE_FAILED 0x02 +#define HOMEPAGE_LOADED 0x03 + +class WebFileInfo; + +WebFileInfo* CreateWebFileInfo(HWND hwndParent, IDispatch *pDispWA); + +class WebFileInfo : public HTMLContainer2, public IDropTarget +{ + +protected: + WebFileInfo(HWND hwndParent, IDispatch *pDispWA); + ~WebFileInfo(void); + +public: + HRESULT InvokeFileInfo(LPCWSTR pszFileName); + HRESULT NavigateToPage(void); + HRESULT DisplayMessage(LPCWSTR pszMessage, BOOL bPostIt); + HRESULT UpdateColors(void); + + // IUnknown + STDMETHOD(QueryInterface)(REFIID riid, PVOID *ppvObject); + STDMETHOD_(ULONG, AddRef)(void); + STDMETHOD_(ULONG, Release)(void); + + // IDropTarget + STDMETHOD (DragEnter)(IDataObject * pDataObject, DWORD grfKeyState, POINTL pt, DWORD * pdwEffect); + STDMETHOD (DragOver)(DWORD grfKeyState, POINTL pt, DWORD * pdwEffect); + STDMETHOD (DragLeave)(void); + STDMETHOD (Drop)(IDataObject * pDataObject, DWORD grfKeyState, POINTL pt, DWORD * pdwEffect); + STDMETHOD (GetDropTarget)(IDropTarget __RPC_FAR *pDropTarget, IDropTarget __RPC_FAR *__RPC_FAR *ppDropTarget); + +protected: + STDMETHOD (GetExternal)(IDispatch __RPC_FAR *__RPC_FAR *ppDispatch); + STDMETHOD (ShowContextMenu)(DWORD dwID, POINT __RPC_FAR *ppt, IUnknown __RPC_FAR *pcmdtReserved, IDispatch __RPC_FAR *pdispReserved); + STDMETHOD (ShowMessage)(HWND hwnd, LPOLESTR lpstrText, LPOLESTR lpstrCaption, DWORD dwType, LPOLESTR lpstrHelpFile, DWORD dwHelpContext, LRESULT *plResult); + STDMETHOD (TranslateAccelerator)(LPMSG lpMsg, const GUID __RPC_FAR *pguidCmdGroup, DWORD nCmdID); + + virtual void OnBeforeNavigate(IDispatch *pDispatch, VARIANT *URL, VARIANT *Flags, VARIANT *TargetFrameName, VARIANT *PostData, VARIANT *Headers, VARIANT_BOOL *Cancel); + virtual void OnNewWindow3(IDispatch **ppDisp, VARIANT_BOOL *Cancel, DWORD dwFlags, BSTR bstrUrlContext, BSTR bstrUrl); + virtual void OnNavigateError(IDispatch *pDispatch, VARIANT *URL, VARIANT *TargetFrameName, VARIANT *StatusCode, VARIANT_BOOL *Cancel); + virtual void OnDocumentReady(IDispatch *pDispatch, VARIANT *URL); + + virtual COLORREF OnGetHostBkColor(void); + virtual DWORD OnGetHostInfoFlags(void); + virtual OLECHAR* OnGetHostCSS(void); + virtual DWORD OnGetDownlodFlags(void); + virtual LPCWSTR OnGetUserAgent(void); + virtual DWORD GetContainerStyle(void); + +protected: + IDispatch *pDispWA; + BSTR bstrMessage; + BSTR bstrFileName; + INT nHomePage; + INT nDragMode; +private: + friend WebFileInfo *CreateWebFileInfo(HWND hwndParent, IDispatch *pDispWA); +}; + +#endif //NULLSOFT_MLDISC_MINIINFO_HEADER \ No newline at end of file diff --git a/Src/Plugins/General/gen_tray/GEN_TRAY.sln b/Src/Plugins/General/gen_tray/GEN_TRAY.sln new file mode 100644 index 00000000..51910ecb --- /dev/null +++ b/Src/Plugins/General/gen_tray/GEN_TRAY.sln @@ -0,0 +1,30 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29424.173 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gen_tray", "GEN_TRAY.vcxproj", "{DD15E699-90D2-4301-921A-7C2C48B25766}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {DD15E699-90D2-4301-921A-7C2C48B25766}.Debug|Win32.ActiveCfg = Debug|Win32 + {DD15E699-90D2-4301-921A-7C2C48B25766}.Debug|Win32.Build.0 = Debug|Win32 + {DD15E699-90D2-4301-921A-7C2C48B25766}.Debug|x64.ActiveCfg = Debug|x64 + {DD15E699-90D2-4301-921A-7C2C48B25766}.Debug|x64.Build.0 = Debug|x64 + {DD15E699-90D2-4301-921A-7C2C48B25766}.Release|Win32.ActiveCfg = Release|Win32 + {DD15E699-90D2-4301-921A-7C2C48B25766}.Release|Win32.Build.0 = Release|Win32 + {DD15E699-90D2-4301-921A-7C2C48B25766}.Release|x64.ActiveCfg = Release|x64 + {DD15E699-90D2-4301-921A-7C2C48B25766}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {9B581402-0754-4899-8AEE-E886596F3A19} + EndGlobalSection +EndGlobal diff --git a/Src/Plugins/General/gen_tray/GEN_TRAY.vcxproj b/Src/Plugins/General/gen_tray/GEN_TRAY.vcxproj new file mode 100644 index 00000000..b6287a69 --- /dev/null +++ b/Src/Plugins/General/gen_tray/GEN_TRAY.vcxproj @@ -0,0 +1,296 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + gen_tray + {DD15E699-90D2-4301-921A-7C2C48B25766} + gen_tray + 10.0.19041.0 + + + + DynamicLibrary + v142 + Unicode + + + DynamicLibrary + v142 + Unicode + + + DynamicLibrary + v142 + Unicode + + + DynamicLibrary + v142 + Unicode + + + + + + + + + + + + + + + + + + + false + $(PlatformShortName)_$(Configuration)\ + $(PlatformShortName)_$(Configuration)\ + $(IncludePath) + $(LibraryPath) + + + false + $(PlatformShortName)_$(Configuration)\ + $(PlatformShortName)_$(Configuration)\ + + + false + $(PlatformShortName)_$(Configuration)\ + $(PlatformShortName)_$(Configuration)\ + $(IncludePath) + $(LibraryPath) + true + + + false + $(PlatformShortName)_$(Configuration)\ + $(PlatformShortName)_$(Configuration)\ + + + false + + + Debug + + + Debug + + + + Disabled + .;..\..\..\Wasabi;%(AdditionalIncludeDirectories) + _WIN32_WINNT=0x0601;WINVER=0x0601;WIN32;_DEBUG;_WINDOWS;_USRDLL;GEN_TRAY_EXPORTS;UNICODE;%(PreprocessorDefinitions) + false + true + EnableFastChecks + MultiThreadedDebugDLL + $(IntDir) + $(IntDir) + $(IntDir)$(TargetName).pdb + Level3 + ProgramDatabase + 4996;%(DisableSpecificWarnings) + true + true + + + _WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions) + 0x0409 + + + shlwapi.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)$(TargetExt) + true + $(IntDir)$(TargetName).pdb + false + $(IntDir)$(TargetName).lib + MachineX86 + false + %(AdditionalLibraryDirectories) + Windows + + + xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ +xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ + Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\' + + + + + Disabled + .;../Wasabi;%(AdditionalIncludeDirectories) + _WIN32_WINNT=0x0601;WINVER=0x0601;WIN64;_DEBUG;_WINDOWS;_USRDLL;GEN_TRAY_EXPORTS;UNICODE;%(PreprocessorDefinitions) + false + true + EnableFastChecks + MultiThreadedDebugDLL + $(IntDir) + $(IntDir) + $(IntDir)$(TargetName).pdb + Level3 + ProgramDatabase + 4996;%(DisableSpecificWarnings) + true + true + + + _WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions) + 0x0409 + + + shlwapi.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)$(TargetExt) + true + $(IntDir)$(TargetName).pdb + false + $(IntDir)$(TargetName).lib + false + %(AdditionalLibraryDirectories) + Windows + + + xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ +xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ + Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\' + + + + + MinSpace + OnlyExplicitInline + Size + .;..\..\..\Wasabi;%(AdditionalIncludeDirectories) + _WIN32_WINNT=0x0601;WINVER=0x0601;WIN32;NDEBUG;_WINDOWS;_USRDLL;GEN_TRAY_EXPORTS;UNICODE;%(PreprocessorDefinitions) + true + true + MultiThreadedDLL + true + $(IntDir) + $(IntDir) + $(IntDir)$(TargetName).pdb + Level3 + None + 4996;%(DisableSpecificWarnings) + true + + + _WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions) + 0x0409 + + + shlwapi.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)$(TargetExt) + true + false + $(IntDir)$(TargetName).pdb + true + true + false + $(IntDir)$(TargetName).lib + MachineX86 + false + %(AdditionalLibraryDirectories) + Windows + + + xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ + Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\' + + + + + MinSpace + OnlyExplicitInline + Size + .;..\..\..\Wasabi;%(AdditionalIncludeDirectories) + _WIN32_WINNT=0x0601;WINVER=0x0601;WIN64;NDEBUG;_WINDOWS;_USRDLL;GEN_TRAY_EXPORTS;UNICODE;%(PreprocessorDefinitions) + true + true + MultiThreadedDLL + true + $(IntDir) + $(IntDir) + $(IntDir)$(TargetName).pdb + Level3 + None + 4996;%(DisableSpecificWarnings) + true + + + _WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions) + 0x0409 + + + shlwapi.lib;%(AdditionalDependencies) + $(OutDir)$(TargetName)$(TargetExt) + true + false + $(IntDir)$(TargetName).pdb + true + true + false + $(IntDir)$(TargetName).lib + false + %(AdditionalLibraryDirectories) + Windows + + + xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ + Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\' + + + + + + + + + + CompileAsCpp + CompileAsCpp + CompileAsCpp + CompileAsCpp + + + + + + + + + + + + + + + + + + {3e0bfa8a-b86a-42e9-a33f-ec294f823f7f} + + + + + + \ No newline at end of file diff --git a/Src/Plugins/General/gen_tray/GEN_TRAY.vcxproj.filters b/Src/Plugins/General/gen_tray/GEN_TRAY.vcxproj.filters new file mode 100644 index 00000000..40a2e389 --- /dev/null +++ b/Src/Plugins/General/gen_tray/GEN_TRAY.vcxproj.filters @@ -0,0 +1,64 @@ + + + + + Header Files + + + Header Files + + + Header Files + + + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + Image Files + + + + + {96125085-f1a0-41bb-a9be-181833e83c26} + + + {79c08881-db4b-4236-bf7d-aedeb08c65b2} + + + {642a812f-646c-4a9b-ba39-93c8405dcf96} + + + {ec91a377-7c2d-47f3-8508-27f333a2c638} + + + + + Ressource Files + + + + + Source Files + + + \ No newline at end of file diff --git a/Src/Plugins/General/gen_tray/RESOURCE.H b/Src/Plugins/General/gen_tray/RESOURCE.H new file mode 100644 index 00000000..b0425e8b --- /dev/null +++ b/Src/Plugins/General/gen_tray/RESOURCE.H @@ -0,0 +1,66 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by SCRIPT1.RC +// +#define IDS_PREVIOUS_TRACK 0 +#define IDS_PLAY_PAUSE 1 +#define IDS_STOP 2 +#define IDC_APPLY 3 +#define IDS_NEXT_TRACK 3 +#define IDS_OPEN_FILE 4 +#define IDS_COMPACT_MODE 5 +#define IDS_DECREASE_VOLUME 6 +#define IDS_INCREASE_VOLUME 7 +#define IDS_HOLD_CTRL 8 +#define IDS_HOLD_SHIFT 9 +// #define IDS_WIN2K_PLUS 10 +#define IDS_CTRL_TO_DECREASE 11 +#define IDS_CTRL_TO_INCREASE 12 +#define IDS_STOPPED_STR 13 +#define IDS_PAUSED_STR 14 +#define IDS_DEFAULT_ICONS 15 +#define IDS_CUSTOM_ICON_PACK 16 +#define IDS_CONFIG_INFO 17 +#define IDS_OFD_FILTER_STR 18 +#define IDS_OFD_TITLE_STR 19 +#define IDS_STRING22 20 +#define IDS_GET_ICON_PACKS 21 +#define IDS_DO_YOU_ALSO_WANT_TO_REMOVE_SETTINGS 22 +#define IDD_DIALOG1 101 +#define IDD_DIALOG2 102 +#define IDI_ICON1 103 +#define IDI_ICON2 104 +#define IDI_ICON3 105 +#define IDI_ICON4 106 +#define IDI_ICON5 107 +#define IDI_ICON6 108 +#define IDI_ICON7 109 +#define IDI_ICON8 110 +#define IDB_BITMAP1 111 +#define IDC_ONOFF 1000 +#define IDC_PREV 1001 +#define IDC_PREV2 1002 +#define IDC_PREV3 1003 +#define IDC_PREV4 1004 +#define IDC_PREV5 1005 +#define IDC_PREV6 1006 +#define IDC_PREV7 1007 +#define IDC_PREV8 1008 +#define IDC_COMBO1 1009 +#define IDC_EDIT1 1010 +#define IDC_BUTTON1 1011 +#define ID_INFO 1012 +#define IDC_COMBO2 1013 +#define IDC_LINK 1014 +#define IDS_NULLSOFT_TRAY_CONTROL 65534 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 117 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1015 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/Src/Plugins/General/gen_tray/TRAYCTL.C b/Src/Plugins/General/gen_tray/TRAYCTL.C new file mode 100644 index 00000000..43725e62 --- /dev/null +++ b/Src/Plugins/General/gen_tray/TRAYCTL.C @@ -0,0 +1,1526 @@ +#define PLUGIN_NAME L"Nullsoft Tray Control" +#define PLUGIN_VERSION L"2.49" + +// Winamp general purpose plug-in mini-SDK +// Copyright (C) 1997, Justin Frankel/Nullsoft +// Modifications and useability enhancements by DrO aka Darren Owen 2006-2014 +#include +#include +#include +#include "../winamp/gen.h" +#include "../winamp/wa_ipc.h" +#include "../winamp/ipc_pe.h" +#include "resource.h" +#include "winampcmd.h" +#include "api__gen_tray.h" +#include + + +#ifndef _DEBUG +BOOL WINAPI _DllMainCRTStartup(HINSTANCE hInst, ULONG ul_reason_for_call, LPVOID lpReserved) +{ + DisableThreadLibraryCalls(hInst); + return TRUE; +} +#endif + + +#define NUM_ICONS 8 +#define FOURWAY_NUM 7 +#define SYSTRAY_ICON_BASE 1024 + +// used for Win7+ usage inorder to get the direct location of the icon instead of the prior hacks (thanks MS!) +typedef HRESULT (WINAPI *SHELL_NOTIFYICONGETRECT)(const NOTIFYICONIDENTIFIER* identifier, RECT* iconLocation); +SHELL_NOTIFYICONGETRECT g_Shell_NotifyIconGetRect = 0; + +// Use a guid to uniquely identify our icon +class __declspec(uuid("B4E5FE9B-6A22-450e-9565-941EF50CFEEB")) CompactIcon; + + +// wasabi based services for localisation support +api_service *WASABI_API_SVC = 0; +api_language *WASABI_API_LNG = 0; +HINSTANCE WASABI_API_LNG_HINST = 0, WASABI_API_ORIG_HINST = 0; + +int config_enabled = 0, + xporhigher = 0, + custom_enabled = 0, + winver = 0, + flip = 0, + on = 1, + update_file = 0, + isX64 = 0, + no_uninstall = 1, + dlg_init = 0; + +UINT s_uTaskbarRestart=0; +HWND configwnd = 0; +WNDPROC lpWndProcOld = 0; +HICON Icons[NUM_ICONS] = {0}, dummyIcon = 0; +HBITMAP compact = 0; +fileinfo2W file = {0}; + +wchar_t ico_pack[MAX_PATH] = {0}, + ico_pack_base[MAX_PATH] = {0}, + ico_pack_safe[MAX_PATH] = {0}, + wa_path[MAX_PATH] = {0}, + *ini_file = 0, + szDescription[256] = {0}; + +int tips[NUM_ICONS] = { + IDS_PREVIOUS_TRACK, + IDS_PLAY_PAUSE, + IDS_STOP, + IDS_NEXT_TRACK, + IDS_OPEN_FILE, + IDS_COMPACT_MODE, + IDS_DECREASE_VOLUME, + IDS_INCREASE_VOLUME, +}; + +int tips_ex[NUM_ICONS] = { + IDS_HOLD_CTRL, + IDS_HOLD_CTRL, + IDS_HOLD_SHIFT, + -1, + -1, +// IDS_WIN2K_PLUS, + -1, + IDS_CTRL_TO_DECREASE, + IDS_CTRL_TO_INCREASE, +}; + +LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam); +BOOL CALLBACK ConfigProc(HWND hwndDlg,UINT uMsg,WPARAM wParam,LPARAM lParam); +HICON CreateInternalIcon(void); +int FileExists(char* filename); +void config(void); +void quit(void); +int init(void); +void config_write(); +void config_read(); + +extern "C" winampGeneralPurposePlugin plugin = +{ + GPPHDR_VER_U, + "nullsoft(gen_tray.dll)", + init, + config, + quit, +}; + +extern "C" __declspec(dllexport) winampGeneralPurposePlugin * winampGetGeneralPurposePlugin() { return &plugin; } + + +HWND GetPlaylistWnd(HWND winamp){ +HWND pl_wnd = 0; + + // get the playlist editor window (either v2.9x method or the older + // for compatibility incase < 2.9x are used + if(SendMessage(winamp,WM_WA_IPC,0,IPC_GETVERSION) >= 0x2900) + { + pl_wnd = (HWND)SendMessage(winamp,WM_WA_IPC,IPC_GETWND_PE,IPC_GETWND); + } + if(!pl_wnd) + { + pl_wnd = FindWindow(L"Winamp PE",0); + } + return pl_wnd; +} + +void FormCompactText(wchar_t* szTip, int szTipLength){ +int got = 0; + + // only update if we really have to (to better mimick Winamp's title behaviour, etc) + // otherwise we query in all cases which can often reflect what appears to be the wrong information since + // the current playlist entry is altered, etc on playlist modification hence an incorrect observation + if(!update_file){ + update_file = 1; + file.fileindex = (int)SendMessage(GetPlaylistWnd(plugin.hwndParent),WM_WA_IPC,IPC_PE_GETCURINDEX,0); + got = (int)SendMessage(GetPlaylistWnd(plugin.hwndParent),WM_WA_IPC,IPC_PE_GETINDEXTITLEW,(LPARAM)&file); + } + + // if it returns 0 then track information was received + if(!got && file.filetitle[0]){ + int time = (int)SendMessage(plugin.hwndParent,WM_WA_IPC,0,IPC_GETOUTPUTTIME); + wchar_t buf[MAX_PATH*2] = {0}, temp[1024] = {0}, *t = temp, *p = 0; + int over = 0, state = 0, blah = 0; + wchar_t stateStr[32] = {0}; + + switch(SendMessage(plugin.hwndParent,WM_WA_IPC,0,IPC_ISPLAYING)){ + case 0: + WASABI_API_LNGSTRINGW_BUF(IDS_STOPPED_STR,stateStr,32); + state = lstrlen(stateStr); + break; + case 3: + WASABI_API_LNGSTRINGW_BUF(IDS_PAUSED_STR,stateStr,32); + state = lstrlen(stateStr); + break; + } + + p = file.filetitle; + while(p && *p){ + *t++ = *p++; + if(*(p-1) == '&'){ + *t++ = '&'; + *t++ = '&'; + } + } + *t = 0; + + StringCchPrintf(buf,MAX_PATH*2,L"%d. %s",(file.fileindex)+1,file.filetitle); + over = lstrlen(buf); + if(over > szTipLength - 1){over = szTipLength - 1;} + lstrcpyn(szTip,buf,szTipLength); + + if(time != -1){ + time = time/1000; + + if(file.filelength[0]){ + StringCchPrintf(buf,MAX_PATH*2,L" [%02d:%02d/%s]",(time/60),time%60,file.filelength); + blah = lstrlen(buf); + } + else{ + StringCchPrintf(buf,MAX_PATH*2,L" [%02d:%02d]",(time/60),time%60); + blah = lstrlen(buf); + } + } + + if((over + blah + state) > szTipLength){ + int adj = szTipLength-blah-state-1; + szTip[adj] = 0; + szTip[adj-1] = L'.'; + szTip[adj-2] = L'.'; + szTip[adj-3] = L'.'; + } + + if(time != -1){ + StringCchCat(szTip,szTipLength,buf); + } + + if(state){ + StringCchCat(szTip,szTipLength,stateStr); + } + } + + // fall back to the Winamp version just incase + else{ + wchar_t temp[16] = {0}; + StringCchPrintf(temp,16,L"%X",SendMessage(plugin.hwndParent,WM_WA_IPC,0,IPC_GETVERSION)); + StringCchPrintf(szTip,szTipLength,L"Winamp %c.%s",temp[0],&temp[2]); + } +} + +void free_icons(void){ +int i = 0; + for (i = 0; i < NUM_ICONS; i++) + { + if( Icons[i] ) { + DestroyIcon(Icons[i]); + Icons[i] = 0; + } + } + + if(dummyIcon) { + DestroyIcon(dummyIcon); + dummyIcon = 0; + } + + if(compact) { + DeleteObject(compact); + } +} + +void do_icons(int force) +{ + static int l=0; + int i=NUM_ICONS; + + if (l == config_enabled && !force) return; + + if( force ) free_icons(); + + while (i--) + { + if (l & (1<-1:i < NUM_ICONS); (xporhigher?i --:i ++)) + { + if (config_enabled & (1<