aboutsummaryrefslogtreecommitdiff
path: root/Src/Plugins/General
diff options
context:
space:
mode:
Diffstat (limited to 'Src/Plugins/General')
-rw-r--r--Src/Plugins/General/gen_crasher/ExceptionHandler.cpp814
-rw-r--r--Src/Plugins/General/gen_crasher/ExceptionHandler.h33
-rw-r--r--Src/Plugins/General/gen_crasher/GetWinVer.cpp195
-rw-r--r--Src/Plugins/General/gen_crasher/GetWinVer.h76
-rw-r--r--Src/Plugins/General/gen_crasher/MiniVersion.cpp267
-rw-r--r--Src/Plugins/General/gen_crasher/MiniVersion.h69
-rw-r--r--Src/Plugins/General/gen_crasher/ReadMe.txt3
-rw-r--r--Src/Plugins/General/gen_crasher/api__gen_crasher.h20
-rw-r--r--Src/Plugins/General/gen_crasher/config.cpp308
-rw-r--r--Src/Plugins/General/gen_crasher/config.h62
-rw-r--r--Src/Plugins/General/gen_crasher/configDlg.cpp457
-rw-r--r--Src/Plugins/General/gen_crasher/configDlg.h15
-rw-r--r--Src/Plugins/General/gen_crasher/crashDlg.cpp101
-rw-r--r--Src/Plugins/General/gen_crasher/crashDlg.h5
-rw-r--r--Src/Plugins/General/gen_crasher/feedback/email/SendEmail.cpp364
-rw-r--r--Src/Plugins/General/gen_crasher/feedback/email/SendEmail.h38
-rw-r--r--Src/Plugins/General/gen_crasher/feedback/feedback.rc119
-rw-r--r--Src/Plugins/General/gen_crasher/feedback/feedback.sln30
-rw-r--r--Src/Plugins/General/gen_crasher/feedback/feedback.vcproj321
-rw-r--r--Src/Plugins/General/gen_crasher/feedback/feedback.vcxproj253
-rw-r--r--Src/Plugins/General/gen_crasher/feedback/feedback.vcxproj.filters79
-rw-r--r--Src/Plugins/General/gen_crasher/feedback/main.cpp79
-rw-r--r--Src/Plugins/General/gen_crasher/feedback/main.h19
-rw-r--r--Src/Plugins/General/gen_crasher/feedback/manifest.xml36
-rw-r--r--Src/Plugins/General/gen_crasher/feedback/operations.cpp163
-rw-r--r--Src/Plugins/General/gen_crasher/feedback/resource.h29
-rw-r--r--Src/Plugins/General/gen_crasher/feedback/silent.cpp134
-rw-r--r--Src/Plugins/General/gen_crasher/feedback/smtp/Base64.cpp320
-rw-r--r--Src/Plugins/General/gen_crasher/feedback/smtp/Base64.h63
-rw-r--r--Src/Plugins/General/gen_crasher/feedback/smtp/Smtp.cpp1468
-rw-r--r--Src/Plugins/General/gen_crasher/feedback/smtp/Smtp.h245
-rw-r--r--Src/Plugins/General/gen_crasher/feedback/version.rc239
-rw-r--r--Src/Plugins/General/gen_crasher/feedback/xzip/XZip.cpp2915
-rw-r--r--Src/Plugins/General/gen_crasher/feedback/xzip/XZip.h324
-rw-r--r--Src/Plugins/General/gen_crasher/gen_crasher.rc193
-rw-r--r--Src/Plugins/General/gen_crasher/gen_crasher.sln31
-rw-r--r--Src/Plugins/General/gen_crasher/gen_crasher.vcxproj290
-rw-r--r--Src/Plugins/General/gen_crasher/gen_crasher.vcxproj.filters92
-rw-r--r--Src/Plugins/General/gen_crasher/main.cpp140
-rw-r--r--Src/Plugins/General/gen_crasher/main.h24
-rw-r--r--Src/Plugins/General/gen_crasher/minidump.h29
-rw-r--r--Src/Plugins/General/gen_crasher/resource.h96
-rw-r--r--Src/Plugins/General/gen_crasher/settings.cpp251
-rw-r--r--Src/Plugins/General/gen_crasher/settings.h67
-rw-r--r--Src/Plugins/General/gen_crasher/smtpDlg.cpp127
-rw-r--r--Src/Plugins/General/gen_crasher/smtpDlg.h5
-rw-r--r--Src/Plugins/General/gen_crasher/version.rc239
-rw-r--r--Src/Plugins/General/gen_ff/AlbumArt.cpp561
-rw-r--r--Src/Plugins/General/gen_ff/AlbumArt.h115
-rw-r--r--Src/Plugins/General/gen_ff/MediaDownloader.cpp307
-rw-r--r--Src/Plugins/General/gen_ff/README.txt6
-rw-r--r--Src/Plugins/General/gen_ff/WinampConfigScriptObject.cpp276
-rw-r--r--Src/Plugins/General/gen_ff/WinampConfigScriptObject.h99
-rw-r--r--Src/Plugins/General/gen_ff/api__gen_ff.h20
-rw-r--r--Src/Plugins/General/gen_ff/bitmaps/library-hilited.pngbin0 -> 1307 bytes
-rw-r--r--Src/Plugins/General/gen_ff/bitmaps/library-selected.pngbin0 -> 1306 bytes
-rw-r--r--Src/Plugins/General/gen_ff/bitmaps/library-unselected.pngbin0 -> 1304 bytes
-rw-r--r--Src/Plugins/General/gen_ff/bitmaps/mb-hilited.pngbin0 -> 1424 bytes
-rw-r--r--Src/Plugins/General/gen_ff/bitmaps/mb-selected.pngbin0 -> 1382 bytes
-rw-r--r--Src/Plugins/General/gen_ff/bitmaps/mb-unselected.pngbin0 -> 1428 bytes
-rw-r--r--Src/Plugins/General/gen_ff/bitmaps/pledit-hover.pngbin0 -> 828 bytes
-rw-r--r--Src/Plugins/General/gen_ff/bitmaps/pledit-selected.pngbin0 -> 807 bytes
-rw-r--r--Src/Plugins/General/gen_ff/bitmaps/pledit-unselected.pngbin0 -> 826 bytes
-rw-r--r--Src/Plugins/General/gen_ff/bitmaps/video-hilited.pngbin0 -> 1441 bytes
-rw-r--r--Src/Plugins/General/gen_ff/bitmaps/video-selected.pngbin0 -> 1435 bytes
-rw-r--r--Src/Plugins/General/gen_ff/bitmaps/video-unselected.pngbin0 -> 1439 bytes
-rw-r--r--Src/Plugins/General/gen_ff/bitmaps/vis-hilited.pngbin0 -> 3241 bytes
-rw-r--r--Src/Plugins/General/gen_ff/bitmaps/vis-selected.pngbin0 -> 3509 bytes
-rw-r--r--Src/Plugins/General/gen_ff/bitmaps/vis-unselected.pngbin0 -> 3037 bytes
-rw-r--r--Src/Plugins/General/gen_ff/embedwndguid.cpp200
-rw-r--r--Src/Plugins/General/gen_ff/embedwndguid.h42
-rw-r--r--Src/Plugins/General/gen_ff/ff_ipc.cpp535
-rw-r--r--Src/Plugins/General/gen_ff/ff_ipc.h86
-rw-r--r--Src/Plugins/General/gen_ff/fsmonitor.cpp174
-rw-r--r--Src/Plugins/General/gen_ff/fsmonitor.h39
-rw-r--r--Src/Plugins/General/gen_ff/gen.h1
-rw-r--r--Src/Plugins/General/gen_ff/gen_ff.rc543
-rw-r--r--Src/Plugins/General/gen_ff/gen_ff.rc258
-rw-r--r--Src/Plugins/General/gen_ff/gen_ff.sln204
-rw-r--r--Src/Plugins/General/gen_ff/gen_ff.vcxproj988
-rw-r--r--Src/Plugins/General/gen_ff/gen_ff.vcxproj.filters2074
-rw-r--r--Src/Plugins/General/gen_ff/gen_ff_ipc.h20
-rw-r--r--Src/Plugins/General/gen_ff/main.cpp3527
-rw-r--r--Src/Plugins/General/gen_ff/main.h29
-rw-r--r--Src/Plugins/General/gen_ff/menuactions.cpp787
-rw-r--r--Src/Plugins/General/gen_ff/menuactions.h91
-rw-r--r--Src/Plugins/General/gen_ff/minibrowserCOM.cpp93
-rw-r--r--Src/Plugins/General/gen_ff/minibrowserCOM.h25
-rw-r--r--Src/Plugins/General/gen_ff/precomp__gen_ff.cpp7
-rw-r--r--Src/Plugins/General/gen_ff/precomp__gen_ff.h76
-rw-r--r--Src/Plugins/General/gen_ff/prefs.cpp136
-rw-r--r--Src/Plugins/General/gen_ff/prefs.h30
-rw-r--r--Src/Plugins/General/gen_ff/prefs_about.cpp98
-rw-r--r--Src/Plugins/General/gen_ff/prefs_alpha.cpp280
-rw-r--r--Src/Plugins/General/gen_ff/prefs_colorthemes.cpp101
-rw-r--r--Src/Plugins/General/gen_ff/prefs_font.cpp860
-rw-r--r--Src/Plugins/General/gen_ff/prefs_general.cpp273
-rw-r--r--Src/Plugins/General/gen_ff/resource.h261
-rw-r--r--Src/Plugins/General/gen_ff/servicelink.cpp72
-rw-r--r--Src/Plugins/General/gen_ff/skininfo.cpp141
-rw-r--r--Src/Plugins/General/gen_ff/skininfo.h42
-rw-r--r--Src/Plugins/General/gen_ff/wa2buckitems.cpp96
-rw-r--r--Src/Plugins/General/gen_ff/wa2buckitems.h70
-rw-r--r--Src/Plugins/General/gen_ff/wa2cfgitems.cpp306
-rw-r--r--Src/Plugins/General/gen_ff/wa2cfgitems.h137
-rw-r--r--Src/Plugins/General/gen_ff/wa2core.cpp930
-rw-r--r--Src/Plugins/General/gen_ff/wa2core.h147
-rw-r--r--Src/Plugins/General/gen_ff/wa2coreactions.cpp279
-rw-r--r--Src/Plugins/General/gen_ff/wa2coreactions.h6
-rw-r--r--Src/Plugins/General/gen_ff/wa2frontend.cpp1344
-rw-r--r--Src/Plugins/General/gen_ff/wa2frontend.h359
-rw-r--r--Src/Plugins/General/gen_ff/wa2groupdefs.cpp36
-rw-r--r--Src/Plugins/General/gen_ff/wa2groupdefs.h18
-rw-r--r--Src/Plugins/General/gen_ff/wa2playlist.cpp2
-rw-r--r--Src/Plugins/General/gen_ff/wa2playlist.h11
-rw-r--r--Src/Plugins/General/gen_ff/wa2pldirobj.cpp366
-rw-r--r--Src/Plugins/General/gen_ff/wa2pldirobj.h105
-rw-r--r--Src/Plugins/General/gen_ff/wa2pledit.cpp241
-rw-r--r--Src/Plugins/General/gen_ff/wa2pledit.h68
-rw-r--r--Src/Plugins/General/gen_ff/wa2songticker.cpp483
-rw-r--r--Src/Plugins/General/gen_ff/wa2songticker.h108
-rw-r--r--Src/Plugins/General/gen_ff/wa2wndembed.cpp946
-rw-r--r--Src/Plugins/General/gen_ff/wa2wndembed.h223
-rw-r--r--Src/Plugins/General/gen_ff/wasabi.dsp1931
-rw-r--r--Src/Plugins/General/gen_ff/wasabi.vcproj286
-rw-r--r--Src/Plugins/General/gen_ff/wasabicfg.h491
-rw-r--r--Src/Plugins/General/gen_hotkeys/Configdlg.cpp471
-rw-r--r--Src/Plugins/General/gen_hotkeys/Configdlg.h9
-rw-r--r--Src/Plugins/General/gen_hotkeys/HOTKEY.CPP56
-rw-r--r--Src/Plugins/General/gen_hotkeys/HOTKEY.H32
-rw-r--r--Src/Plugins/General/gen_hotkeys/HotKeyCtl.cpp293
-rw-r--r--Src/Plugins/General/gen_hotkeys/HotKeyCtl.h13
-rw-r--r--Src/Plugins/General/gen_hotkeys/RESOURCE.H116
-rw-r--r--Src/Plugins/General/gen_hotkeys/WINAMP.H38
-rw-r--r--Src/Plugins/General/gen_hotkeys/Wacommands.cpp366
-rw-r--r--Src/Plugins/General/gen_hotkeys/Wacommands.h50
-rw-r--r--Src/Plugins/General/gen_hotkeys/accelBlock.cpp47
-rw-r--r--Src/Plugins/General/gen_hotkeys/accelBlock.h24
-rw-r--r--Src/Plugins/General/gen_hotkeys/api__gen_hotkeys.h10
-rw-r--r--Src/Plugins/General/gen_hotkeys/gen_hotkeys.cpp519
-rw-r--r--Src/Plugins/General/gen_hotkeys/gen_hotkeys.h56
-rw-r--r--Src/Plugins/General/gen_hotkeys/gen_hotkeys.rc217
-rw-r--r--Src/Plugins/General/gen_hotkeys/gen_hotkeys.sln29
-rw-r--r--Src/Plugins/General/gen_hotkeys/gen_hotkeys.vcxproj316
-rw-r--r--Src/Plugins/General/gen_hotkeys/gen_hotkeys.vcxproj.filters79
-rw-r--r--Src/Plugins/General/gen_hotkeys/version.rc239
-rw-r--r--Src/Plugins/General/gen_hotkeys/wa_hotkeys.h91
-rw-r--r--Src/Plugins/General/gen_hotkeys/x.bmpbin0 -> 670 bytes
-rw-r--r--Src/Plugins/General/gen_ml/HeaderIconList.cpp1115
-rw-r--r--Src/Plugins/General/gen_ml/IPC.cpp1588
-rw-r--r--Src/Plugins/General/gen_ml/MediaLibraryCOM.cpp100
-rw-r--r--Src/Plugins/General/gen_ml/MediaLibraryCOM.h19
-rw-r--r--Src/Plugins/General/gen_ml/MusicID.cpp658
-rw-r--r--Src/Plugins/General/gen_ml/MusicID.h21
-rw-r--r--Src/Plugins/General/gen_ml/OnlineMediaCOM.cpp83
-rw-r--r--Src/Plugins/General/gen_ml/OnlineMediaCOM.h19
-rw-r--r--Src/Plugins/General/gen_ml/RatingsCOM.cpp119
-rw-r--r--Src/Plugins/General/gen_ml/RatingsCOM.h19
-rw-r--r--Src/Plugins/General/gen_ml/SmoothScrollList.cpp1837
-rw-r--r--Src/Plugins/General/gen_ml/api__gen_ml.h68
-rw-r--r--Src/Plugins/General/gen_ml/banner.cpp215
-rw-r--r--Src/Plugins/General/gen_ml/banner.h44
-rw-r--r--Src/Plugins/General/gen_ml/childwnd.cpp235
-rw-r--r--Src/Plugins/General/gen_ml/childwnd.h38
-rw-r--r--Src/Plugins/General/gen_ml/colors.cpp207
-rw-r--r--Src/Plugins/General/gen_ml/colors.h65
-rw-r--r--Src/Plugins/General/gen_ml/comboskin.cpp148
-rw-r--r--Src/Plugins/General/gen_ml/comboskin.h18
-rw-r--r--Src/Plugins/General/gen_ml/config.cpp128
-rw-r--r--Src/Plugins/General/gen_ml/config.h31
-rw-r--r--Src/Plugins/General/gen_ml/fileview.cpp2059
-rw-r--r--Src/Plugins/General/gen_ml/fileview.h23
-rw-r--r--Src/Plugins/General/gen_ml/fileview_columns.cpp39
-rw-r--r--Src/Plugins/General/gen_ml/fileview_compare.cpp255
-rw-r--r--Src/Plugins/General/gen_ml/fileview_filesystem.cpp289
-rw-r--r--Src/Plugins/General/gen_ml/fileview_format.cpp335
-rw-r--r--Src/Plugins/General/gen_ml/fileview_internal.h207
-rw-r--r--Src/Plugins/General/gen_ml/fileview_menu.cpp331
-rw-r--r--Src/Plugins/General/gen_ml/fileview_metadata.cpp789
-rw-r--r--Src/Plugins/General/gen_ml/fileview_toolbar.cpp338
-rw-r--r--Src/Plugins/General/gen_ml/flickerfix.cpp101
-rw-r--r--Src/Plugins/General/gen_ml/folderborwser_listbox.cpp388
-rw-r--r--Src/Plugins/General/gen_ml/folderbrowser.cpp2271
-rw-r--r--Src/Plugins/General/gen_ml/folderbrowser.h22
-rw-r--r--Src/Plugins/General/gen_ml/folderbrowser_internal.h34
-rw-r--r--Src/Plugins/General/gen_ml/gaystring.cpp182
-rw-r--r--Src/Plugins/General/gen_ml/gaystring.h44
-rw-r--r--Src/Plugins/General/gen_ml/gen_ml.rc648
-rw-r--r--Src/Plugins/General/gen_ml/gen_ml.sln92
-rw-r--r--Src/Plugins/General/gen_ml/gen_ml.vcxproj526
-rw-r--r--Src/Plugins/General/gen_ml/gen_ml.vcxproj.filters540
-rw-r--r--Src/Plugins/General/gen_ml/graphics.cpp130
-rw-r--r--Src/Plugins/General/gen_ml/graphics.h26
-rw-r--r--Src/Plugins/General/gen_ml/imagefilters.cpp349
-rw-r--r--Src/Plugins/General/gen_ml/imagefilters.h29
-rw-r--r--Src/Plugins/General/gen_ml/itemlist.cpp147
-rw-r--r--Src/Plugins/General/gen_ml/itemlist.h54
-rw-r--r--Src/Plugins/General/gen_ml/klib/khash.h528
-rw-r--r--Src/Plugins/General/gen_ml/listheader.cpp295
-rw-r--r--Src/Plugins/General/gen_ml/listskin.cpp219
-rw-r--r--Src/Plugins/General/gen_ml/listskin.h36
-rw-r--r--Src/Plugins/General/gen_ml/listview.cpp128
-rw-r--r--Src/Plugins/General/gen_ml/listview.h143
-rw-r--r--Src/Plugins/General/gen_ml/main.cpp1172
-rw-r--r--Src/Plugins/General/gen_ml/main.h142
-rw-r--r--Src/Plugins/General/gen_ml/menu.cpp20
-rw-r--r--Src/Plugins/General/gen_ml/menu.h17
-rw-r--r--Src/Plugins/General/gen_ml/menufucker.h43
-rw-r--r--Src/Plugins/General/gen_ml/ml.h915
-rw-r--r--Src/Plugins/General/gen_ml/ml_cloud.cpp66
-rw-r--r--Src/Plugins/General/gen_ml/ml_cloud.h15
-rw-r--r--Src/Plugins/General/gen_ml/ml_cloudcolumn.cpp80
-rw-r--r--Src/Plugins/General/gen_ml/ml_cloudcolumn.h34
-rw-r--r--Src/Plugins/General/gen_ml/ml_ex/ex.rc144
-rw-r--r--Src/Plugins/General/gen_ml/ml_ex/ml_ex.dsp152
-rw-r--r--Src/Plugins/General/gen_ml/ml_ex/ml_ex.dsw29
-rw-r--r--Src/Plugins/General/gen_ml/ml_ex/resource.h62
-rw-r--r--Src/Plugins/General/gen_ml/ml_ex/view_ex.cpp888
-rw-r--r--Src/Plugins/General/gen_ml/ml_imagefilter.cpp169
-rw-r--r--Src/Plugins/General/gen_ml/ml_imagefilter.h47
-rw-r--r--Src/Plugins/General/gen_ml/ml_imagelist.cpp384
-rw-r--r--Src/Plugins/General/gen_ml/ml_imagelist.h44
-rw-r--r--Src/Plugins/General/gen_ml/ml_imageloader.cpp689
-rw-r--r--Src/Plugins/General/gen_ml/ml_imageloader.h52
-rw-r--r--Src/Plugins/General/gen_ml/ml_ipc.h92
-rw-r--r--Src/Plugins/General/gen_ml/ml_ipc_0313.h1852
-rw-r--r--Src/Plugins/General/gen_ml/ml_lib.cpp586
-rw-r--r--Src/Plugins/General/gen_ml/ml_rating.cpp125
-rw-r--r--Src/Plugins/General/gen_ml/ml_rating.h39
-rw-r--r--Src/Plugins/General/gen_ml/ml_ratingcolumn.cpp874
-rw-r--r--Src/Plugins/General/gen_ml/ml_ratingcolumn.h90
-rw-r--r--Src/Plugins/General/gen_ml/mldwm.cpp46
-rw-r--r--Src/Plugins/General/gen_ml/mldwm.h47
-rw-r--r--Src/Plugins/General/gen_ml/navigation.cpp2733
-rw-r--r--Src/Plugins/General/gen_ml/navigation.h267
-rw-r--r--Src/Plugins/General/gen_ml/plugin.cpp719
-rw-r--r--Src/Plugins/General/gen_ml/png.rc57
-rw-r--r--Src/Plugins/General/gen_ml/prefs.cpp787
-rw-r--r--Src/Plugins/General/gen_ml/reflectmsg.cpp188
-rw-r--r--Src/Plugins/General/gen_ml/reflectmsg.h50
-rw-r--r--Src/Plugins/General/gen_ml/resource.h263
-rw-r--r--Src/Plugins/General/gen_ml/resources/checkmark.pngbin0 -> 302 bytes
-rw-r--r--Src/Plugins/General/gen_ml/resources/cloud_16_incloud.pngbin0 -> 206 bytes
-rw-r--r--Src/Plugins/General/gen_ml/resources/cloud_16_partial.pngbin0 -> 226 bytes
-rw-r--r--Src/Plugins/General/gen_ml/resources/cloud_16_unavail.pngbin0 -> 232 bytes
-rw-r--r--Src/Plugins/General/gen_ml/resources/cloud_16_upload.pngbin0 -> 124 bytes
-rw-r--r--Src/Plugins/General/gen_ml/resources/cloud_16_uploading.pngbin0 -> 228 bytes
-rw-r--r--Src/Plugins/General/gen_ml/resources/dragdrop.curbin0 -> 326 bytes
-rw-r--r--Src/Plugins/General/gen_ml/resources/filetype_audio_16.pngbin0 -> 198 bytes
-rw-r--r--Src/Plugins/General/gen_ml/resources/filetype_audio_32.pngbin0 -> 561 bytes
-rw-r--r--Src/Plugins/General/gen_ml/resources/filetype_playlist_16.pngbin0 -> 272 bytes
-rw-r--r--Src/Plugins/General/gen_ml/resources/filetype_playlist_32.pngbin0 -> 338 bytes
-rw-r--r--Src/Plugins/General/gen_ml/resources/filetype_unknown_16.pngbin0 -> 186 bytes
-rw-r--r--Src/Plugins/General/gen_ml/resources/filetype_unknown_32.pngbin0 -> 274 bytes
-rw-r--r--Src/Plugins/General/gen_ml/resources/filetype_video_16.pngbin0 -> 210 bytes
-rw-r--r--Src/Plugins/General/gen_ml/resources/filetype_video_32.pngbin0 -> 461 bytes
-rw-r--r--Src/Plugins/General/gen_ml/resources/menu_arrow.pngbin0 -> 122 bytes
-rw-r--r--Src/Plugins/General/gen_ml/resources/menu_check.pngbin0 -> 128 bytes
-rw-r--r--Src/Plugins/General/gen_ml/resources/menu_dot.pngbin0 -> 130 bytes
-rw-r--r--Src/Plugins/General/gen_ml/resources/menu_scrollarrow.pngbin0 -> 132 bytes
-rw-r--r--Src/Plugins/General/gen_ml/resources/menu_scrollarrow_disabled.pngbin0 -> 130 bytes
-rw-r--r--Src/Plugins/General/gen_ml/resources/rating.pngbin0 -> 328 bytes
-rw-r--r--Src/Plugins/General/gen_ml/resources/sortarrow.pngbin0 -> 182 bytes
-rw-r--r--Src/Plugins/General/gen_ml/resources/splitarrow.pngbin0 -> 120 bytes
-rw-r--r--Src/Plugins/General/gen_ml/resources/splitarrow_pressed.pngbin0 -> 120 bytes
-rw-r--r--Src/Plugins/General/gen_ml/resources/stars_invert.bmpbin0 -> 2082 bytes
-rw-r--r--Src/Plugins/General/gen_ml/resources/ti_default_16x16x16.bmpbin0 -> 568 bytes
-rw-r--r--Src/Plugins/General/gen_ml/resources/ti_labs_16x16x16.bmpbin0 -> 1078 bytes
-rw-r--r--Src/Plugins/General/gen_ml/resources/tree_closed_16x16x16.bmpbin0 -> 568 bytes
-rw-r--r--Src/Plugins/General/gen_ml/resources/tree_closed_disabled_16x16x16.bmpbin0 -> 568 bytes
-rw-r--r--Src/Plugins/General/gen_ml/resources/tree_open_16x16x16.bmpbin0 -> 568 bytes
-rw-r--r--Src/Plugins/General/gen_ml/resources/view_mode_detail.pngbin0 -> 157 bytes
-rw-r--r--Src/Plugins/General/gen_ml/resources/view_mode_icon.pngbin0 -> 179 bytes
-rw-r--r--Src/Plugins/General/gen_ml/resources/view_mode_list.pngbin0 -> 131 bytes
-rw-r--r--Src/Plugins/General/gen_ml/scrollwnd.cpp2391
-rw-r--r--Src/Plugins/General/gen_ml/scrollwnd.h220
-rw-r--r--Src/Plugins/General/gen_ml/sendto.cpp305
-rw-r--r--Src/Plugins/General/gen_ml/sendto.h31
-rw-r--r--Src/Plugins/General/gen_ml/service.cpp147
-rw-r--r--Src/Plugins/General/gen_ml/service.h49
-rw-r--r--Src/Plugins/General/gen_ml/setup.cpp39
-rw-r--r--Src/Plugins/General/gen_ml/skinexport.cpp72
-rw-r--r--Src/Plugins/General/gen_ml/skinexport.h31
-rw-r--r--Src/Plugins/General/gen_ml/skinnedbutton.cpp1343
-rw-r--r--Src/Plugins/General/gen_ml/skinnedbutton.h71
-rw-r--r--Src/Plugins/General/gen_ml/skinnedcombo.cpp444
-rw-r--r--Src/Plugins/General/gen_ml/skinnedcombo.h42
-rw-r--r--Src/Plugins/General/gen_ml/skinneddivider.cpp128
-rw-r--r--Src/Plugins/General/gen_ml/skinneddivider.h34
-rw-r--r--Src/Plugins/General/gen_ml/skinneddlg.cpp45
-rw-r--r--Src/Plugins/General/gen_ml/skinneddlg.h29
-rw-r--r--Src/Plugins/General/gen_ml/skinnededit.cpp787
-rw-r--r--Src/Plugins/General/gen_ml/skinnededit.h54
-rw-r--r--Src/Plugins/General/gen_ml/skinnedfolder.cpp61
-rw-r--r--Src/Plugins/General/gen_ml/skinnedfolder.h32
-rw-r--r--Src/Plugins/General/gen_ml/skinnedheader.cpp832
-rw-r--r--Src/Plugins/General/gen_ml/skinnedheader.h71
-rw-r--r--Src/Plugins/General/gen_ml/skinnedlistbox.cpp250
-rw-r--r--Src/Plugins/General/gen_ml/skinnedlistbox.h33
-rw-r--r--Src/Plugins/General/gen_ml/skinnedlistview.cpp526
-rw-r--r--Src/Plugins/General/gen_ml/skinnedlistview.h46
-rw-r--r--Src/Plugins/General/gen_ml/skinnedmenu.cpp81
-rw-r--r--Src/Plugins/General/gen_ml/skinnedmenu.h42
-rw-r--r--Src/Plugins/General/gen_ml/skinnedmenuthreadinfo.cpp468
-rw-r--r--Src/Plugins/General/gen_ml/skinnedmenuthreadinfo.h74
-rw-r--r--Src/Plugins/General/gen_ml/skinnedmenuwnd.cpp1538
-rw-r--r--Src/Plugins/General/gen_ml/skinnedmenuwnd.h97
-rw-r--r--Src/Plugins/General/gen_ml/skinnedprogressbar.cpp131
-rw-r--r--Src/Plugins/General/gen_ml/skinnedprogressbar.h33
-rw-r--r--Src/Plugins/General/gen_ml/skinnedscrollwnd.cpp3285
-rw-r--r--Src/Plugins/General/gen_ml/skinnedscrollwnd.h99
-rw-r--r--Src/Plugins/General/gen_ml/skinnedstatic.cpp135
-rw-r--r--Src/Plugins/General/gen_ml/skinnedstatic.h30
-rw-r--r--Src/Plugins/General/gen_ml/skinnedtooltip.cpp190
-rw-r--r--Src/Plugins/General/gen_ml/skinnedtooltip.h35
-rw-r--r--Src/Plugins/General/gen_ml/skinnedwnd.cpp569
-rw-r--r--Src/Plugins/General/gen_ml/skinnedwnd.h101
-rw-r--r--Src/Plugins/General/gen_ml/skinning.cpp151
-rw-r--r--Src/Plugins/General/gen_ml/skinning.h26
-rw-r--r--Src/Plugins/General/gen_ml/stockobjects.cpp215
-rw-r--r--Src/Plugins/General/gen_ml/stockobjects.h42
-rw-r--r--Src/Plugins/General/gen_ml/stringvector.cpp130
-rw-r--r--Src/Plugins/General/gen_ml/stringvector.h51
-rw-r--r--Src/Plugins/General/gen_ml/unused.cpp7
-rw-r--r--Src/Plugins/General/gen_ml/util.cpp84
-rw-r--r--Src/Plugins/General/gen_ml/version.rc239
-rw-r--r--Src/Plugins/General/gen_ml/view_mb.h129
-rw-r--r--Src/Plugins/General/gen_ml/view_ml.cpp1723
-rw-r--r--Src/Plugins/General/gen_ml/wa_dlg.cpp2
-rw-r--r--Src/Plugins/General/gen_ml/webinfo_dlg.cpp547
-rw-r--r--Src/Plugins/General/gen_ml/webinfo_obj.cpp438
-rw-r--r--Src/Plugins/General/gen_ml/webinfo_obj.h72
-rw-r--r--Src/Plugins/General/gen_tray/GEN_TRAY.sln30
-rw-r--r--Src/Plugins/General/gen_tray/GEN_TRAY.vcxproj296
-rw-r--r--Src/Plugins/General/gen_tray/GEN_TRAY.vcxproj.filters64
-rw-r--r--Src/Plugins/General/gen_tray/RESOURCE.H66
-rw-r--r--Src/Plugins/General/gen_tray/TRAYCTL.C1526
-rw-r--r--Src/Plugins/General/gen_tray/WINAMPCMD.H62
-rw-r--r--Src/Plugins/General/gen_tray/api__gen_tray.h8
-rw-r--r--Src/Plugins/General/gen_tray/gen_tray.rc178
-rw-r--r--Src/Plugins/General/gen_tray/icons/compact.bmpbin0 -> 126 bytes
-rw-r--r--Src/Plugins/General/gen_tray/icons/icon1.icobin0 -> 318 bytes
-rw-r--r--Src/Plugins/General/gen_tray/icons/icon2.icobin0 -> 318 bytes
-rw-r--r--Src/Plugins/General/gen_tray/icons/icon3.icobin0 -> 318 bytes
-rw-r--r--Src/Plugins/General/gen_tray/icons/icon4.icobin0 -> 318 bytes
-rw-r--r--Src/Plugins/General/gen_tray/icons/icon5.icobin0 -> 318 bytes
-rw-r--r--Src/Plugins/General/gen_tray/icons/icon7.icobin0 -> 318 bytes
-rw-r--r--Src/Plugins/General/gen_tray/icons/icon8.icobin0 -> 318 bytes
-rw-r--r--Src/Plugins/General/gen_tray/icons/icon9.icobin0 -> 318 bytes
-rw-r--r--Src/Plugins/General/gen_tray/version.rc239
350 files changed, 89281 insertions, 0 deletions
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 <tchar.h>
+#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 <strsafe.h>
+
+///////////////////////////////////////////////////////////////////////////////
+// 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 <windows.h>
+
+#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 <strsafe.h>
+
+///////////////////////////////////////////////////////////////////////////////
+// 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 <windows.h>
+#include <TCHAR.h>
+
+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 <api/service/api_service.h>
+extern api_service *serviceManager;
+#define WASABI_API_SVC serviceManager
+
+
+#include <api/application/api_application.h>
+#define WASABI_API_APP applicationApi
+
+
+#include <api/syscb/api_syscb.h>
+#define WASABI_API_SYSCB sysCallbackApi
+
+#include <api/service/waServiceFactory.h>
+
+#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 <shlwapi.h>
+#include "..\..\..\nu\ns_wc.h"
+#include "config.h"
+#include <strsafe.h>
+
+///////////////////
+///
+/// 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 <windows.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <memory.h>
+
+#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 <shlobj.h>
+#include ".\miniVersion.h"
+#include ".\getwinver.h"
+#include ".\settings.h"
+#include ".\minidump.h"
+#include ".\main.h"
+#include <strsafe.h>
+#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 <windows.h>
+
+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 <strsafe.h>
+
+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 <windows.h>
+#include <commctrl.h>
+
+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 <crtdbg.h>
+#include <io.h>
+#pragma warning(disable: 4228)
+#include <mapi.h>
+#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 <tchar.h>
+#include <windows.h>
+
+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 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="9.00"
+ Name="feedback"
+ ProjectGUID="{A845D04C-A95E-424C-BFA8-D7706DBA78BF}"
+ RootNamespace="feedback"
+ Keyword="Win32Proj"
+ TargetFrameworkVersion="131072"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="."
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_CRT_SECURE_NO_WARNINGS"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="1"
+ UsePrecompiledHeader="0"
+ PrecompiledHeaderThrough="precomp.h"
+ WarningLevel="3"
+ DebugInformationFormat="4"
+ ForcedIncludeFiles="precomp.h"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="comctl32.lib msvcrtd.lib libcpmtd.lib rpcrt4.lib"
+ OutputFile="$(ProgramFiles)\winamp\reporter.exe"
+ GenerateManifest="false"
+ IgnoreDefaultLibraryNames="libcmtd"
+ GenerateDebugInformation="true"
+ ProgramDatabaseFile="$(OutDir)/feedback.pdb"
+ SubSystem="2"
+ RandomizedBaseAddress="1"
+ DataExecutionPrevention="0"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ AdditionalManifestFiles="manifest.xml"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"
+ UseOfATL="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ FavorSizeOrSpeed="2"
+ AdditionalIncludeDirectories="."
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_CRT_SECURE_NO_WARNINGS"
+ StringPooling="true"
+ BufferSecurityCheck="false"
+ UsePrecompiledHeader="2"
+ PrecompiledHeaderThrough="precomp.h"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ ForcedIncludeFiles="precomp.h"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="comctl32.lib libcpmt.lib rpcrt4.lib"
+ OutputFile="$(ProgramFiles)\winamp\reporter.exe"
+ LinkIncremental="1"
+ GenerateManifest="false"
+ IgnoreDefaultLibraryNames=""
+ GenerateDebugInformation="true"
+ ProgramDatabaseFile="$(OutDir)/$(ProjectName).pdb"
+ SubSystem="2"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ EntryPointSymbol=""
+ RandomizedBaseAddress="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ AdditionalManifestFiles="manifest.xml"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+ >
+ <File
+ RelativePath=".\main.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\operations.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\precomp.cpp"
+ >
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="1"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath=".\silent.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+ >
+ <File
+ RelativePath=".\main.h"
+ >
+ </File>
+ <File
+ RelativePath=".\precomp.h"
+ >
+ </File>
+ <File
+ RelativePath=".\Resource.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx"
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
+ >
+ <File
+ RelativePath=".\feedback.rc"
+ >
+ </File>
+ <File
+ RelativePath=".\manifest.xml"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Settings"
+ >
+ <File
+ RelativePath="..\config.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\config.h"
+ >
+ </File>
+ <File
+ RelativePath="..\settings.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\settings.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="zip"
+ >
+ <File
+ RelativePath=".\xzip\XZip.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\xzip\XZip.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="smtp"
+ >
+ <File
+ RelativePath=".\smtp\Base64.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\smtp\Base64.h"
+ >
+ </File>
+ <File
+ RelativePath=".\smtp\Smtp.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\smtp\Smtp.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="email"
+ >
+ <File
+ RelativePath=".\email\SendEmail.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\email\SendEmail.h"
+ >
+ </File>
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
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 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{A845D04C-A95E-424C-BFA8-D7706DBA78BF}</ProjectGuid>
+ <RootNamespace>feedback</RootNamespace>
+ <WindowsTargetPlatformVersion>10.0.19041.0</WindowsTargetPlatformVersion>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <TargetName>reporter</TargetName>
+ <IncludePath>$(IncludePath)</IncludePath>
+ <LibraryPath>$(LibraryPath)</LibraryPath>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <TargetName>reporter</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <TargetName>reporter</TargetName>
+ <IncludePath>$(IncludePath)</IncludePath>
+ <LibraryPath>$(LibraryPath)</LibraryPath>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <TargetName>reporter</TargetName>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg">
+ <VcpkgEnabled>false</VcpkgEnabled>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <VcpkgConfiguration>Debug</VcpkgConfiguration>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>.;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_CRT_SECURE_NO_WARNINGS;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <DisableSpecificWarnings>4091;4244;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>comctl32.lib;rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <SubSystem>Windows</SubSystem>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <TargetMachine>MachineX86</TargetMachine>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\
+xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>.;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN64;_DEBUG;_WINDOWS;_CRT_SECURE_NO_WARNINGS;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <DisableSpecificWarnings>4091;4244;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>comctl32.lib;rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <SubSystem>Windows</SubSystem>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\
+xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <Optimization>MinSpace</Optimization>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>.;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_CRT_SECURE_NO_WARNINGS;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>None</DebugInformationFormat>
+ <DisableSpecificWarnings>4091;4244;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>comctl32.lib;rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <SubSystem>Windows</SubSystem>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <TargetMachine>MachineX86</TargetMachine>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <Optimization>MinSpace</Optimization>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>.;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN64;NDEBUG;_WINDOWS;_CRT_SECURE_NO_WARNINGS;_WINSOCK_DEPRECATED_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>None</DebugInformationFormat>
+ <DisableSpecificWarnings>4091;4244;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>comctl32.lib;rpcrt4.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <SubSystem>Windows</SubSystem>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="..\config.cpp" />
+ <ClCompile Include="..\settings.cpp" />
+ <ClCompile Include="email\SendEmail.cpp" />
+ <ClCompile Include="main.cpp" />
+ <ClCompile Include="operations.cpp" />
+ <ClCompile Include="silent.cpp" />
+ <ClCompile Include="smtp\Base64.cpp" />
+ <ClCompile Include="smtp\Smtp.cpp" />
+ <ClCompile Include="xzip\XZip.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\config.h" />
+ <ClInclude Include="..\settings.h" />
+ <ClInclude Include="email\SendEmail.h" />
+ <ClInclude Include="main.h" />
+ <ClInclude Include="Resource.h" />
+ <ClInclude Include="smtp\Base64.h" />
+ <ClInclude Include="smtp\Smtp.h" />
+ <ClInclude Include="xzip\XZip.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="feedback.rc" />
+ </ItemGroup>
+ <ItemGroup>
+ <Xml Include="manifest.xml" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ 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 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <ClCompile Include="smtp\Base64.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\config.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="main.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="operations.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="email\SendEmail.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\settings.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="silent.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="smtp\Smtp.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="xzip\XZip.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="smtp\Base64.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\config.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="main.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="Resource.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="email\SendEmail.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\settings.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="smtp\Smtp.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="xzip\XZip.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{de803cce-7a8d-46da-97a8-cb794b2ef154}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Ressource Files">
+ <UniqueIdentifier>{6cea30eb-0534-44ec-af7c-340d8047d962}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{a281ea66-f75c-47a9-90b7-61a031b4ea22}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="feedback.rc">
+ <Filter>Ressource Files</Filter>
+ </ResourceCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <Xml Include="manifest.xml">
+ <Filter>Ressource Files</Filter>
+ </Xml>
+ </ItemGroup>
+</Project> \ 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 <commctrl.h>
+#include <strsafe.h>
+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 <windows.h>
+#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 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" >
+ <assemblyIdentity
+ version="1.11.0.0"
+ processorArchitecture="X86"
+ name="Nullsoft.Error Reporter"
+ type="win32"
+ />
+ <description>Nullsoft Error Reporter</description>
+ <dependency>
+ <dependentAssembly>
+ <assemblyIdentity
+ type="win32"
+ name="Microsoft.Windows.Common-Controls"
+ version="6.0.0.0"
+ processorArchitecture="X86"
+ publicKeyToken="6595b64144ccf1df"
+ language="*"
+ />
+ </dependentAssembly>
+ </dependency>
+ <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
+ <security>
+ <requestedPrivileges>
+ <requestedExecutionLevel
+ level="asInvoker"
+ />
+ </requestedPrivileges>
+ </security>
+ </trustInfo>
+ <asmv3:application>
+ <asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
+ <dpiAware>true</dpiAware>
+ </asmv3:windowsSettings>
+ </asmv3:application>
+</assembly> \ 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 <strsafe.h>
+
+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 <commctrl.h>
+#include <shlobj.h>
+#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 <windows.h>
+
+#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 <atlbase.h> 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 <nError> value is the SMTP server's numeric error response, and the <pszErr>
+// is the descriptive error text
+//
+// <pszErr> may be NULL if the server failed to respond before the timeout!
+// <nError> 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, <nWarning> 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 <m_strUser> and <m_strPass> member
+// variables and return TRUE to retry authentication.
+//
+// <pszWarning> 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 <pszCmd> and <pszResponse> before performing
+// any actions!
+// <nResponse> 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.
+// <pvAttachments> can be either an LPTSTR containing NULL terminated strings, in which
+// case <dwAttachmentCount> should be zero, or <pvAttachments> can be an LPTSTR *
+// containing an array of LPTSTR's, in which case <dwAttachmentCount> 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
+// <pszAddrFrom> and <pszAddrTo> should be e-mail addresses with no decorations
+// Example: "foo@bar.com"
+// <pvAttachments> and <dwAttachmentCount> 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 <atlbase.h>
+#include <winsock.h>
+#include <string>
+#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<CSmtpMessageBody> Message; // An array of message bodies
+ CSimpleArray<CSmtpAddress> CC; // Carbon Copy recipients
+ CSimpleArray<CSmtpAddress> BCC; // Blind Carbon Copy recipients
+ CSimpleArray<CSmtpAttachment> Attachments; // An array of attachments
+ CSimpleMap<String,String> 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 <windows.h>
+#include <time.h>
+#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 HASH_MASK (HASH_SIZE-1)
+#define WMASK (WSIZE-1)
+// HASH_SIZE and WSIZE must be powers of two
+
+#define NIL 0
+// Tail of hash chains
+
+#define FAST 4
+#define SLOW 2
+// speed options for the general purpose bit flag
+
+#define TOO_FAR 4096
+// Matches of length 3 are discarded if their distance exceeds TOO_FAR
+
+
+
+#define EQUAL 0
+// result of memcmp for equal strings
+
+
+// ===========================================================================
+// Local data used by the "longest match" routines.
+
+#define H_SHIFT ((HASH_BITS+MIN_MATCH-1)/MIN_MATCH)
+// Number of bits by which ins_h and del_h must be shifted at each
+// input step. It must be such that after MIN_MATCH steps, the oldest
+// byte no longer takes part in the hash key, that is:
+// H_SHIFT * MIN_MATCH >= 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<<extra_lbits[code]); n++) {
+ state.ts.length_code[length++] = (uch)code;
+ }
+ }
+ Assert(state,length == 256, "ct_init: length != 256");
+ /* Note that the length 255 (match length 258) can be represented
+ * in two different ways: code 284 + 5 bits or code 285, so we
+ * overwrite length_code[255] to use the best encoding:
+ */
+ state.ts.length_code[length-1] = (uch)code;
+
+ /* Initialize the mapping dist (0..32K) -> dist code (0..29) */
+ dist = 0;
+ for (code = 0 ; code < 16; code++) {
+ state.ts.base_dist[code] = dist;
+ for (n = 0; n < (1<<extra_dbits[code]); n++) {
+ state.ts.dist_code[dist++] = (uch)code;
+ }
+ }
+ Assert(state,dist == 256, "ct_init: dist != 256");
+ dist >>= 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)<<H_SHIFT) ^ (c)) & HASH_MASK)
+
+/* ===========================================================================
+ * Insert string s in the dictionary and set match_head to the previous _head
+ * of the hash chain (the most recent string with same hash key). Return
+ * the previous length of the hash chain.
+ * IN assertion: all calls to to INSERT_STRING are made with consecutive
+ * input characters and the first MIN_MATCH bytes of s are valid
+ * (except for the last MIN_MATCH-1 bytes of the input file).
+ */
+#define INSERT_STRING(s, match_head) \
+ (UPDATE_HASH(state.ds.ins_h, state.ds.window[(s) + (MIN_MATCH-1)]), \
+ state.ds.prev[(s) & WMASK] = match_head = state.ds.head[state.ds.ins_h], \
+ state.ds.head[state.ds.ins_h] = (s))
+
+/* ===========================================================================
+ * Initialize the "longest match" routines for a new file
+ *
+ * IN assertion: window_size is > 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<MIN_MATCH-1; j++) UPDATE_HASH(state.ds.ins_h, state.ds.window[j]);
+ /* If lookahead < MIN_MATCH, ins_h is garbage, but this is
+ * not important since only literal bytes will be emitted.
+ */
+}
+
+
+/* ===========================================================================
+ * Set match_start to the longest match starting at the given string and
+ * return its length. Matches shorter or equal to prev_length are discarded,
+ * in which case the result is equal to prev_length and match_start is
+ * garbage.
+ * IN assertions: cur_match is the _head of the hash chain for the current
+ * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 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,&times,&timestamp);
+ 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 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{A029F791-2838-4D16-BC92-C8E04D677948}</ProjectGuid>
+ <RootNamespace>gen_crasher</RootNamespace>
+ <WindowsTargetPlatformVersion>10.0.19041.0</WindowsTargetPlatformVersion>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <IncludePath>$(IncludePath)</IncludePath>
+ <LibraryPath>$(LibraryPath)</LibraryPath>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <IncludePath>$(IncludePath)</IncludePath>
+ <LibraryPath>$(LibraryPath)</LibraryPath>
+ <EmbedManifest>true</EmbedManifest>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg">
+ <VcpkgEnabled>false</VcpkgEnabled>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <VcpkgConfiguration>Debug</VcpkgConfiguration>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;WIN32;_DEBUG;_WINDOWS;_USRDLL;GEN_CRASHER_EXPORTS;_WIN32_IE=0x0A00;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <TreatWChar_tAsBuiltInType>false</TreatWChar_tAsBuiltInType>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <DisableSpecificWarnings>4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>shlwapi.lib;Version.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <SubSystem>Windows</SubSystem>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <TargetMachine>MachineX86</TargetMachine>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\
+xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
+ </PostBuildEvent>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ResourceCompile>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_DEBUG;WIN64;_WINDOWS;_USRDLL;GEN_CRASHER_EXPORTS;_WIN32_IE=0x0A00;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <BrowseInformation>true</BrowseInformation>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <CompileAs>Default</CompileAs>
+ <DisableSpecificWarnings>4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>shlwapi.lib;Version.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <SubSystem>Windows</SubSystem>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\
+xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
+ </PostBuildEvent>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ResourceCompile>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <Optimization>MinSpace</Optimization>
+ <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;WIN32;NDEBUG;_WINDOWS;_USRDLL;GEN_CRASHER_EXPORTS;_WIN32_IE=0x0A00;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ <ObjectFileName>$(IntDir)</ObjectFileName>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>None</DebugInformationFormat>
+ <DisableSpecificWarnings>4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>shlwapi.lib;Version.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <SubSystem>Windows</SubSystem>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <TurnOffAssemblyGeneration>true</TurnOffAssemblyGeneration>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <TargetMachine>MachineX86</TargetMachine>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
+ </PostBuildEvent>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ResourceCompile>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <Optimization>MinSpace</Optimization>
+ <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;NDEBUG;WIN64;_WINDOWS;_USRDLL;GEN_CRASHER_EXPORTS;_WIN32_IE=0x0A00;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ <ObjectFileName>$(IntDir)</ObjectFileName>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>None</DebugInformationFormat>
+ <DisableSpecificWarnings>4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ </ClCompile>
+ <Link>
+ <AdditionalDependencies>shlwapi.lib;Version.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <TurnOffAssemblyGeneration>true</TurnOffAssemblyGeneration>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <SubSystem>Windows</SubSystem>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
+ </PostBuildEvent>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ResourceCompile>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\..\..\Wasabi\Wasabi.vcxproj">
+ <Project>{3e0bfa8a-b86a-42e9-a33f-ec294f823f7f}</Project>
+ </ProjectReference>
+ <ProjectReference Include="feedback\feedback.vcxproj">
+ <Project>{a845d04c-a95e-424c-bfa8-d7706dba78bf}</Project>
+ <CopyLocalSatelliteAssemblies>true</CopyLocalSatelliteAssemblies>
+ <ReferenceOutputAssembly>true</ReferenceOutputAssembly>
+ </ProjectReference>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\..\..\nu\ServiceWatcher.cpp" />
+ <ClCompile Include="config.cpp" />
+ <ClCompile Include="configDlg.cpp" />
+ <ClCompile Include="crashDlg.cpp" />
+ <ClCompile Include="ExceptionHandler.cpp" />
+ <ClCompile Include="GetWinVer.cpp" />
+ <ClCompile Include="main.cpp" />
+ <ClCompile Include="MiniVersion.cpp" />
+ <ClCompile Include="settings.cpp" />
+ <ClCompile Include="smtpDlg.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\..\..\Winamp\wa_ipc.h" />
+ <ClInclude Include="api__gen_crasher.h" />
+ <ClInclude Include="config.h" />
+ <ClInclude Include="configDlg.h" />
+ <ClInclude Include="crashDlg.h" />
+ <ClInclude Include="ExceptionHandler.h" />
+ <ClInclude Include="GetWinVer.h" />
+ <ClInclude Include="main.h" />
+ <ClInclude Include="minidump.h" />
+ <ClInclude Include="MiniVersion.h" />
+ <ClInclude Include="resource.h" />
+ <ClInclude Include="settings.h" />
+ <ClInclude Include="smtpDlg.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="gen_crasher.rc" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ 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 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <ClCompile Include="config.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="configDlg.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="crashDlg.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ExceptionHandler.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="GetWinVer.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="main.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="MiniVersion.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="settings.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="smtpDlg.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\nu\ServiceWatcher.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="api__gen_crasher.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="config.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="configDlg.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="crashDlg.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="ExceptionHandler.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="GetWinVer.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="main.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="minidump.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="MiniVersion.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="resource.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="settings.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="smtpDlg.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Winamp\wa_ipc.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{387133cc-523c-4c1f-8215-3de0d29784a3}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Ressource Files">
+ <UniqueIdentifier>{1515be17-59cd-45ca-abc3-a3b3e66c36b9}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{89f5adbb-9d33-4588-b2cb-942e0cc75987}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="gen_crasher.rc">
+ <Filter>Ressource Files</Filter>
+ </ResourceCompile>
+ </ItemGroup>
+</Project> \ 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<api_syscb*>(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<api_application*>(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 <windows.h>
+#include <dbghelp.h>
+#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 <dbghelp.h>
+/*
+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 <shlwapi.h>
+#include <strsafe.h>
+
+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 <strsafe.h>
+
+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 <windows.h>
+
+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 <api/core/api_core.h>
+#include "main.h"
+#include "AlbumArt.h"
+#include "wa2frontend.h"
+#include <api.h>
+#include <tataki/bitmap/bitmap.h>
+#include <api\wnd\notifmsg.h>
+#include <api/script/scriptmgr.h>
+#include <api/script/script.h>
+
+#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<AlbumArtXuiSvc> );
+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<AlbumArt *>( 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<AlbumArt *>( 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<MetadataCallbackI *>( 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<MetadataCallbackI *>( 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<AlbumArt *>( 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<AlbumArt *>( 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 <api/wnd/wndclass/bufferpaintwnd.h>
+#include <api/skin/widgets/layer.h>
+#include <api/syscb/callbacks/metacb.h>
+//#include <api/syscb/callbacks/corecbi.h>
+
+//#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<AlbumArt, albumArtXuiObjectStr, albumArtXuiSvcName> {};
+
+
+#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 <shlobj.h>
+#include <shlwapi.h>
+#include <api/script/objects/systemobj.h>
+
+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<ifc_downloadManagerCallback>
+{
+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<wchar_t *>(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<wchar_t *>(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 <DownloadsList/>
+ 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 <precomp.h>
+#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<WinampConfig *>(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<WinampConfig *>(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<WinampConfig *>(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<WinampConfigGroup *>(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<WinampConfigGroup *>(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<WinampConfigGroup *>(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<WinampConfigGroup *>(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<WinampConfigGroup *>(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 <api/script/objects/rootobj.h>
+#include <api/script/script.h>
+#include <api/script/objects/rootobject.h>
+#include <api/service/svcs/svc_scriptobji.h>
+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 <api/memmgr/api_memmgr.h>
+extern api_memmgr *memmgrApi;
+#define WASABI_API_MEMMGR memmgrApi
+
+#include <api/skin/api_colorthemes.h>
+#define WASABI_API_COLORTHEMES colorThemesApi
+
+#include <api/skin/api_palette.h>
+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
--- /dev/null
+++ b/Src/Plugins/General/gen_ff/bitmaps/library-hilited.png
Binary files 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
--- /dev/null
+++ b/Src/Plugins/General/gen_ff/bitmaps/library-selected.png
Binary files 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
--- /dev/null
+++ b/Src/Plugins/General/gen_ff/bitmaps/library-unselected.png
Binary files 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
--- /dev/null
+++ b/Src/Plugins/General/gen_ff/bitmaps/mb-hilited.png
Binary files 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
--- /dev/null
+++ b/Src/Plugins/General/gen_ff/bitmaps/mb-selected.png
Binary files 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
--- /dev/null
+++ b/Src/Plugins/General/gen_ff/bitmaps/mb-unselected.png
Binary files 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
--- /dev/null
+++ b/Src/Plugins/General/gen_ff/bitmaps/pledit-hover.png
Binary files 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
--- /dev/null
+++ b/Src/Plugins/General/gen_ff/bitmaps/pledit-selected.png
Binary files 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
--- /dev/null
+++ b/Src/Plugins/General/gen_ff/bitmaps/pledit-unselected.png
Binary files 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
--- /dev/null
+++ b/Src/Plugins/General/gen_ff/bitmaps/video-hilited.png
Binary files 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
--- /dev/null
+++ b/Src/Plugins/General/gen_ff/bitmaps/video-selected.png
Binary files 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
--- /dev/null
+++ b/Src/Plugins/General/gen_ff/bitmaps/video-unselected.png
Binary files 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
--- /dev/null
+++ b/Src/Plugins/General/gen_ff/bitmaps/vis-hilited.png
Binary files 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
--- /dev/null
+++ b/Src/Plugins/General/gen_ff/bitmaps/vis-selected.png
Binary files 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
--- /dev/null
+++ b/Src/Plugins/General/gen_ff/bitmaps/vis-unselected.png
Binary files 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 <bfc/string/stringW.h>
+
+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<EmbedWndGuid> 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 <tataki/color/skinclr.h>
+#include <api/wnd/wndclass/buttwnd.h>
+#include <api/wndmgr/skinwnd.h>
+#include <tataki/blending/blending.h>
+#include <api/skin/skinparse.h>
+#include <api/wnd/wndtrack.h>
+#include "wa2wndembed.h"
+#include "embedwndguid.h"
+#include <tataki/canvas/bltcanvas.h>
+#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<Layout *>(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<BltCanvas *>(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<BltCanvas *>(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 <bfc/ptrlist.h>
+#include <api/timer/timerclient.h>
+
+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<FSCallback> 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 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{0A75D6C7-6F14-4032-BBCC-7A234E626A56}</ProjectGuid>
+ <RootNamespace>gen_ff</RootNamespace>
+ <WindowsTargetPlatformVersion>10.0.19041.0</WindowsTargetPlatformVersion>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <IncludePath>$(IncludePath)</IncludePath>
+ <LibraryPath>$(LibraryPath)</LibraryPath>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <IncludePath>$(IncludePath)</IncludePath>
+ <LibraryPath>$(LibraryPath)</LibraryPath>
+ <EmbedManifest>true</EmbedManifest>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg">
+ <VcpkgEnableManifest>false</VcpkgEnableManifest>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <VcpkgInstalledDir>
+ </VcpkgInstalledDir>
+ <VcpkgUseStatic>false</VcpkgUseStatic>
+ <VcpkgConfiguration>Debug</VcpkgConfiguration>
+ <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <VcpkgInstalledDir>
+ </VcpkgInstalledDir>
+ <VcpkgUseStatic>false</VcpkgUseStatic>
+ <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <VcpkgInstalledDir>
+ </VcpkgInstalledDir>
+ <VcpkgUseStatic>false</VcpkgUseStatic>
+ <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
+ <VcpkgConfiguration>Debug</VcpkgConfiguration>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <VcpkgInstalledDir>..\..\..\external_dependencies\vcpkg</VcpkgInstalledDir>
+ <VcpkgUseStatic>true</VcpkgUseStatic>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>.;..;..\..\..;..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;WIN32;_WINDOWS;_USRDLL;GEN_FF_EXPORTS;NOSVCMGR;_WIN32_IE=0x0A00;_CRT_SECURE_NO_WARNINGS;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ <BrowseInformation>true</BrowseInformation>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <CompileAs>Default</CompileAs>
+ <DisableSpecificWarnings>4100;4127;4238;4244;4267;4302;4311;4312;4651;4838;4090;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <ShowIncludes>false</ShowIncludes>
+ </ClCompile>
+ <ResourceCompile>
+ <Culture>0x0409</Culture>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ResourceCompile>
+ <Link>
+ <AdditionalDependencies>shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <DelayLoadDLLs>tataki.dll;%(DelayLoadDLLs)</DelayLoadDLLs>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <TargetMachine>MachineX86</TargetMachine>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <SubSystem>Windows</SubSystem>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\
+xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>.;..;..\..\..;..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;WIN64;_DEBUG;_WINDOWS;_USRDLL;GEN_FF_EXPORTS;NOSVCMGR;_WIN32_IE=0x0A00;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ <BrowseInformation>true</BrowseInformation>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <DisableSpecificWarnings>4100;4127;4238;4244;4267;4302;4311;4312;4651;4838;4090;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <ShowIncludes>false</ShowIncludes>
+ </ClCompile>
+ <ResourceCompile>
+ <Culture>0x0409</Culture>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ResourceCompile>
+ <Link>
+ <AdditionalDependencies>shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <DelayLoadDLLs>tataki.dll;%(DelayLoadDLLs)</DelayLoadDLLs>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <SubSystem>Windows</SubSystem>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\
+xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <Optimization>MinSpace</Optimization>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>.;..;..\..\..;..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;WIN32;_WINDOWS;_USRDLL;GEN_FF_EXPORTS;NOSVCMGR;_WIN32_IE=0x0A00;_CRT_SECURE_NO_WARNINGS;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>None</DebugInformationFormat>
+ <DisableSpecificWarnings>4100;4127;4238;4244;4267;4302;4311;4312;4651;4838;4090;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ </ClCompile>
+ <ResourceCompile>
+ <Culture>0x0409</Culture>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ResourceCompile>
+ <Link>
+ <AdditionalDependencies>shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <DelayLoadDLLs>tataki.dll;%(DelayLoadDLLs)</DelayLoadDLLs>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <RandomizedBaseAddress>true</RandomizedBaseAddress>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <TargetMachine>MachineX86</TargetMachine>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <SubSystem>Windows</SubSystem>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <Optimization>MinSpace</Optimization>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>.;..;..\..\..;..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;WIN64;NDEBUG;_WINDOWS;_USRDLL;GEN_ff_EXPORTS;NOSVCMGR;_WIN32_IE=0x0A00;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>None</DebugInformationFormat>
+ <DisableSpecificWarnings>4100;4127;4238;4244;4267;4302;4311;4312;4651;4838;4090;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ </ClCompile>
+ <ResourceCompile>
+ <Culture>0x0409</Culture>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ </ResourceCompile>
+ <Link>
+ <AdditionalDependencies>shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <DelayLoadDLLs>tataki.dll;%(DelayLoadDLLs)</DelayLoadDLLs>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <RandomizedBaseAddress>true</RandomizedBaseAddress>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <SubSystem>Windows</SubSystem>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\..\..\external_dependencies\libmp4v2\libmp4v2.vcxproj">
+ <Project>{efb9b882-6a8b-463d-a8e3-a2807afc5d9f}</Project>
+ </ProjectReference>
+ <ProjectReference Include="..\..\..\tataki\tataki.vcxproj">
+ <Project>{255b68b5-7ef8-45ef-a675-2d6b88147909}</Project>
+ <CopyLocalSatelliteAssemblies>true</CopyLocalSatelliteAssemblies>
+ <ReferenceOutputAssembly>true</ReferenceOutputAssembly>
+ </ProjectReference>
+ <ProjectReference Include="..\..\..\Wasabi\bfc\bfc.vcxproj">
+ <Project>{d0ec862e-dddd-4f4f-934f-b75dc9062dc1}</Project>
+ <CopyLocalSatelliteAssemblies>true</CopyLocalSatelliteAssemblies>
+ <ReferenceOutputAssembly>true</ReferenceOutputAssembly>
+ </ProjectReference>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\gen_ml\menu.cpp" />
+ <ClCompile Include="..\..\..\nu\HTMLContainer2.cpp" />
+ <ClCompile Include="..\..\..\nu\listview.cpp" />
+ <ClCompile Include="..\..\..\nu\regexp.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\apiinit.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\application\ifc_messageprocessori.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\config\api_config.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\config\api_configi.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\config\api_configx.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\config\cfglist.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\config\cfgscriptobj.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\config\config.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\config\items\attribute.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\config\items\attrstr.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\config\items\cfgitemi.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\config\items\cfgitemx.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\config\items\intarray.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\config\options.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\config\uioptions.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\core\api_core.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\dependency\api_dependent.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\dependency\api_dependentviewer.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\filereader\api_filereader.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\filereader\filereaderapi.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\filereader\local\fileread.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\font\bitmapfont.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\font\font.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\font\fontapi.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\font\win32\truetypefont_win32.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\imgldr\api_imgldr.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\imgldr\imggen\grad.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\imgldr\imggen\osedge.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\imgldr\imggen\poly.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\imgldr\imggen\solid.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\imgldr\imgldr.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\imgldr\imgldrapi.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\locales\api_locales.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\locales\api_localesi.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\locales\api_localesx.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\locales\localesmgr.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\api_maki.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\api_makii.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\api_makix.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\debugger\api_makidebug.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\debugger\debugapi.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\debugger\debuggerui.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\debugger\debugsymbols.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\debugger\disasm.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\debugger\jitd.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\debugger\jitdbreak.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\debugger\sdebuggerui.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\debugger\sourcecodeline.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\debugger\vcpudebug.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\guru.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\objcontroller.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\objcontrollert.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\core\coreadminobj.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\core\coreobj.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\core\svc_scriptcore.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\c_browser.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\c_button.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\c_checkbox.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\c_container.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\c_dropdownlist.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\c_edit.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\c_group.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\c_guilist.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\c_guiobject.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\c_guitree.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\c_layout.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\c_menubutton.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\c_querylist.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\c_rootobj.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\c_slider.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\c_text.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\c_togglebutton.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\c_treeitem.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\h_browser.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\h_button.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\h_checkbox.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\h_container.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\h_dropdownlist.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\h_edit.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\h_group.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\h_guilist.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\h_guiobject.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\h_guitree.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\h_layout.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\h_menubutton.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\h_querylist.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\h_rootobj.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\h_slider.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\h_text.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\h_togglebutton.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\h_treeitem.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\scripthook.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\guiobj.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\guiobjectx.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\PlaylistScriptObject.cpp">
+ <ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(IntDir)$(TargetName)%(Filename)1.obj</ObjectFileName>
+ <ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(IntDir)$(TargetName)%(Filename)1.obj</ObjectFileName>
+ <ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(IntDir)$(TargetName)%(Filename)1.obj</ObjectFileName>
+ <ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(IntDir)$(TargetName)%(Filename)1.obj</ObjectFileName>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\rootobj.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\rootobjcb.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\rootobjcbi.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\rootobjcbx.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\rootobjcontroller.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\rootobject.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\rootobjecti.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\rootobjectx.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\sapplication.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\sbitlist.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\scolor.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\scolormgr.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\sfile.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\sgammagroup.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\sgammaset.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\slist.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\smap.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\spopup.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\sprivate.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\sregion.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\sxmldoc.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\systemobj.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\wacobj.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\objecttable.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\script.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\scriptmgr.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\scriptobj.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\scriptobji.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\scriptobjx.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\script\vcpu.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\service\api_service.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\service\servicei.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\service\svccache.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\service\svcenum.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\service\svcenumbyguid.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\service\svcenumt.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\service\svcs\svc_action.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\service\svcs\svc_fileread.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\service\svcs\svc_font.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\service\svcs\svc_imggen.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\service\svcs\svc_imgload.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\service\svcs\svc_minibrowser.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\service\svcs\svc_scriptobj.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\service\svcs\svc_skinfilter.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\service\svcs\svc_textfeed.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\service\svcs\svc_tooltips.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\service\svcs\svc_wndcreate.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\service\svcs\svc_xuiobject.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\service\svc_enum.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\service\waservicefactory.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\service\waservicefactorybase.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\service\waservicefactoryi.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\service\waservicefactoryt.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\service\waservicefactorytsingle.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\service\waservicefactoryx.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\api_palette.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\api_skin.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\cursormgr.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\feeds\api_textfeed.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\feeds\feedwatch.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\feeds\feedwatcherso.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\feeds\textfeed.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\gammamgr.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\groupmgr.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\groupwndcreate.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\guitree.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\nakedobject.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\objectactuator.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\regioncache.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\skin.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\skinapi.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\skinelem.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\skinfilter.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\skinfont.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\skinitem.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\skinparse.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\SkinVersion.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\animlayer.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\button.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\checkbox.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\combobox.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\compbuck2.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\customobject.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\dropdownlist.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\edit.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\fx_dmove.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\group.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\groupclickwnd.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\grouplist.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\grouptgbutton.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\grouptips.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\guiradiogroup.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\historyeditbox.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\layer.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\mb\iebrowser.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\mb\mainminibrowser.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\mb\mbsvc.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\mb\minibrowser.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\mb\minibrowserwnd.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\mb\xuibrowser.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\mouseredir.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\objdirwnd.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\pathpicker.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\pslider.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\sa.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\seqband.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\seqpreamp.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\seqvis.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\spanbar.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\sseeker.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\sstatus.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\stats\statswnd.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\stats\xuistats.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\svolbar.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\text.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\textbase.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\tgbutton.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\title.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\titlebox.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\wa2\xuiwa2slider.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\xuiaddparams.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\xuibookmarklist.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\xuicheckbox.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\xuicombobox.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\xuicustomobject.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\xuidownloadslist.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\xuidropdownlist.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\xuieditbox.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\xuiframe.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\xuigradientwnd.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\xuigrid.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\xuigroupxfade.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\xuihideobject.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\xuihistoryedit.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\xuilist.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\xuimenu.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\xuimenuso.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\xuiobjdirwnd.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\xuioswndhost.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\xuipathpicker.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\xuiprogressgrid.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\xuiradiogroup.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\xuirect.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\xuisendparams.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\xuistatus.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\xuitabsheet.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\xuithemeslist.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\xuititlebox.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\xuitree.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\xuiwndholder.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\syscb\api_syscb.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\syscb\callbacks\browsercb.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\syscb\callbacks\corecb.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\syscb\callbacks\playlistcb.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\syscb\callbacks\skincb.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\syscb\callbacks\syscb.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\syscb\callbacks\syscbi.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\syscb\callbacks\syscbx.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\timer\timerclient.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\util\selectfile.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\util\systray.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\util\varmgr.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\wac\compon.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\wndmgr\alphamgr.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\wndmgr\animate.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\wndmgr\api_wndmgr.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\wndmgr\appcmds.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\wndmgr\autopopup.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\wndmgr\container.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\wndmgr\gc.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\wndmgr\guistatuscb.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\wndmgr\layout.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\wndmgr\msgbox.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\wndmgr\resize.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\wndmgr\skinembed.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\wndmgr\skinwnd.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\wndmgr\snappnt.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\wndmgr\wndmgrapi.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\api_window.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\api_wnd.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\basewnd.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\contextmenu.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\cursor.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\deactivatemgr.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\di.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\dragitemi.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\findobjectcb.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\keyboard.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\paintset.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\popexitcb.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\popexitchecker.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\popup.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\rootwnd.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\virtualwnd.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndapi.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndclass\abstractwndhold.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndclass\appbarwnd.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndclass\blankwnd.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndclass\bufferpaintwnd.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndclass\buttbar.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndclass\buttwnd.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndclass\clickwnd.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndclass\editwnd.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndclass\editwndstring.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndclass\embeddedxui.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndclass\foreignwnd.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndclass\framewnd.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndclass\gradientwnd.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndclass\guiobjwnd.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndclass\labelwnd.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndclass\listwnd.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndclass\oswnd.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndclass\oswndhost.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndclass\qpaintwnd.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndclass\rootwndholder.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndclass\scbkgwnd.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndclass\scrollbar.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndclass\SelItemList.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndclass\sepwnd.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndclass\slider.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndclass\status.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndclass\svcwndhold.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndclass\tabsheet.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndclass\tabsheetbar.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndclass\tooltip.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndclass\treewnd.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndclass\typesheet.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndclass\wndholder.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndtrack.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\xml\LoadXML.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\xml\XMLAutoInclude.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\xml\xmlparams.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\xml\xmlparamsi.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\xml\xmlparamsx.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\xml\xmlreader.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\api\xml\xmlwrite.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\bfc\depend.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\bfc\depview.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\bfc\draw\drawpoly.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\bfc\draw\gradient.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\bfc\std_file.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\bfc\util\findopenrect.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\bfc\util\inifile.cpp" />
+ <ClCompile Include="..\..\..\Wasabi\bfc\util\timefmt.cpp" />
+ <ClCompile Include="AlbumArt.cpp" />
+ <ClCompile Include="embedwndguid.cpp">
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS</PreprocessorDefinitions>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">WIN64;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS</PreprocessorDefinitions>
+ </ClCompile>
+ <ClCompile Include="ff_ipc.cpp">
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS</PreprocessorDefinitions>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">WIN64;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS</PreprocessorDefinitions>
+ </ClCompile>
+ <ClCompile Include="fsmonitor.cpp">
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS</PreprocessorDefinitions>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">WIN64;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS</PreprocessorDefinitions>
+ </ClCompile>
+ <ClCompile Include="main.cpp">
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS</PreprocessorDefinitions>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">WIN64;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS</PreprocessorDefinitions>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">WIN32;_NDEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS</PreprocessorDefinitions>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">WIN64;_NDEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS</PreprocessorDefinitions>
+ </ClCompile>
+ <ClCompile Include="MediaDownloader.cpp" />
+ <ClCompile Include="menuactions.cpp">
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS</PreprocessorDefinitions>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">WIN64;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS</PreprocessorDefinitions>
+ </ClCompile>
+ <ClCompile Include="minibrowserCOM.cpp" />
+ <ClCompile Include="prefs.cpp">
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS</PreprocessorDefinitions>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">WIN64;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS</PreprocessorDefinitions>
+ </ClCompile>
+ <ClCompile Include="prefs_about.cpp" />
+ <ClCompile Include="prefs_alpha.cpp" />
+ <ClCompile Include="prefs_colorthemes.cpp" />
+ <ClCompile Include="prefs_font.cpp" />
+ <ClCompile Include="prefs_general.cpp" />
+ <ClCompile Include="servicelink.cpp">
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS</PreprocessorDefinitions>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">WIN64;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS</PreprocessorDefinitions>
+ </ClCompile>
+ <ClCompile Include="skininfo.cpp">
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS</PreprocessorDefinitions>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">WIN64;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS</PreprocessorDefinitions>
+ </ClCompile>
+ <ClCompile Include="wa2buckitems.cpp">
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS</PreprocessorDefinitions>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">WIN64;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS</PreprocessorDefinitions>
+ </ClCompile>
+ <ClCompile Include="wa2cfgitems.cpp">
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS</PreprocessorDefinitions>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">WIN64;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS</PreprocessorDefinitions>
+ </ClCompile>
+ <ClCompile Include="wa2core.cpp">
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS</PreprocessorDefinitions>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">WIN64;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS</PreprocessorDefinitions>
+ </ClCompile>
+ <ClCompile Include="wa2coreactions.cpp">
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS</PreprocessorDefinitions>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">WIN64;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS</PreprocessorDefinitions>
+ </ClCompile>
+ <ClCompile Include="wa2frontend.cpp">
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS</PreprocessorDefinitions>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">WIN64;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS</PreprocessorDefinitions>
+ </ClCompile>
+ <ClCompile Include="wa2groupdefs.cpp">
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS</PreprocessorDefinitions>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">WIN64;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS</PreprocessorDefinitions>
+ </ClCompile>
+ <ClCompile Include="wa2playlist.cpp">
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS</PreprocessorDefinitions>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">WIN64;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS</PreprocessorDefinitions>
+ </ClCompile>
+ <ClCompile Include="wa2pldirobj.cpp">
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS</PreprocessorDefinitions>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">WIN64;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS</PreprocessorDefinitions>
+ </ClCompile>
+ <ClCompile Include="wa2pledit.cpp">
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS</PreprocessorDefinitions>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">WIN64;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS</PreprocessorDefinitions>
+ </ClCompile>
+ <ClCompile Include="wa2songticker.cpp" />
+ <ClCompile Include="wa2wndembed.cpp">
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS</PreprocessorDefinitions>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">WIN64;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_FF_EXPORTS</PreprocessorDefinitions>
+ </ClCompile>
+ <ClCompile Include="WinampConfigScriptObject.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\gen_ml\menu.h" />
+ <CustomBuild Include="..\gen_ml\ml.h">
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
+ </CustomBuild>
+ <ClInclude Include="..\..\..\Wasabi\api\apiinit.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\application\ifc_messageprocessori.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\config\api_config.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\config\api_configi.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\config\api_configx.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\config\cfglist.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\config\config.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\config\items\attribute.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\config\items\attrstr.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\config\items\cfgitem.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\config\items\cfgitemi.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\config\items\cfgitemx.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\config\items\intarray.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\config\uioptions.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\core\api_core.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\core\coreactions.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\dependency\api_dependent.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\dependency\api_dependentviewer.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\filereader\api_filereader.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\filereader\api_readercallback.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\filereader\filereaderapi.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\filereader\local\fileread.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\filereader\svc_filereadI.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\font\api_font.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\font\bitmapfont.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\font\font.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\font\fontapi.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\font\win32\truetypefont_win32.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\imgldr\api_imgldr.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\imgldr\imggen\grad.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\imgldr\imgldr.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\imgldr\imgldrapi.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\imgldr\ImgLoaderEnum.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\locales\api_locales.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\locales\api_localesi.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\locales\api_localesx.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\locales\localesmgr.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\script\api_maki.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\script\api_makii.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\script\api_makix.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\script\debugger\debugsymbols.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\script\debugger\sdebuggerui.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\script\objects\core\coreadminobj.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\script\objects\core\coreobj.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\script\objects\c_script\h_rootobj.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\script\objects\c_script\scripthook.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\script\objects\guiobj.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\script\objects\guiobject.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\script\objects\guiobjectx.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\script\objects\PlaylistScriptObject.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\script\objects\rootobj.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\script\objects\rootobject.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\script\objects\rootobjecti.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\script\objects\rootobjectx.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\script\objects\sapplication.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\script\objects\scolor.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\script\objects\scolormgr.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\script\objects\sfile.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\script\objects\sgammagroup.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\script\objects\sgammaset.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\script\objects\sprivate.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\script\objects\svcwnd.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\script\objects\sxmldoc.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\script\objects\timer.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\script\vcpu.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\service\svcs\svc_accessibility.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\service\svcs\svc_accroleserver.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\service\svcs\svc_action.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\service\svcs\svc_droptarget.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\service\svcs\svc_fileread.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\service\svcs\svc_filesel.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\service\svcs\svc_objectdir.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\service\svcs\svc_scriptobj.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\service\svcs\svc_textfeed.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\service\svcs\svc_wndcreate.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\service\waservicefactoryt.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\skin\api_colorthemes.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\skin\api_palette.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\skin\api_skin.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\skin\cursormgr.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\skin\feeds\api_textfeed.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\skin\feeds\ezfeed.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\skin\feeds\textfeed.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\skin\gammamgr.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\skin\group.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\skin\groupmgr.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\skin\groupwndcreate.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\skin\guitree.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\skin\skin.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\skin\skinapi.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\skin\skinelem.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\skin\skinfilter.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\skin\skinfont.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\skin\skinitem.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\skin\skinparse.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\skin\SkinVersion.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\skin\widgets.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\skin\widgets\button.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\skin\widgets\combobox.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\skin\widgets\compbuck2.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\skin\widgets\customobject.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\skin\widgets\dropdownlist.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\skin\widgets\group.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\skin\widgets\grouptgbutton.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\skin\widgets\grouptips.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\skin\widgets\guiradiogroup.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\skin\widgets\historyeditbox.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\skin\widgets\layer.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\skin\widgets\mb\iebrowser.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\skin\widgets\mb\minibrowser.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\skin\widgets\mb\minibrowserwnd.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\skin\widgets\mouseredir.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\skin\widgets\objdirwnd.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\skin\widgets\pslider.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\skin\widgets\seqband.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\skin\widgets\seqpreamp.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\skin\widgets\text.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\skin\widgets\textbase.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\skin\widgets\wa2\xuiwa2slider.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\skin\widgets\xuicustomobject.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\skin\widgets\xuiframe.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\skin\widgets\xuilist.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\skin\widgets\xuimenu.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\skin\widgets\xuithemeslist.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\skin\widgets\xuitree.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\syscb\callbacks\browsercb.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\syscb\callbacks\browsercbi.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\syscb\callbacks\metacb.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\syscb\callbacks\playlistcb.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\syscb\callbacks\svccbi.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\syscb\callbacks\syscb.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\syscb\callbacks\wndcb.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\timer\api_timer.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\timer\timerclient.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\timer\timermul.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\timer\tmultiplex.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\util\selectfile.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\util\systray.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\util\varmgr.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\wac\compon.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\wndmgr\alphamgr.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\wndmgr\animate.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\wndmgr\api_wndmgr.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\wndmgr\appcmds.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\wndmgr\autopopup.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\wndmgr\container.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\wndmgr\gc.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\wndmgr\layout.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\wndmgr\msgbox.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\wndmgr\resize.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\wndmgr\skinembed.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\wndmgr\skinwnd.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\wndmgr\snappnt.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\wndmgr\wndmgrapi.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\api_window.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\api_wnd.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\basewnd.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\contextmenu.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\cursor.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\deactivatemgr.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\drag.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\dragitem.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\dragitemi.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\findobjectcb.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\keyboard.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\minibrowser.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\paintcb.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\platform\win32\bitmap.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\popexitchecker.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\popup.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\resizable.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\rootwnd.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\virtualwnd.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\wndapi.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\wndclass\abstractwndhold.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\wndclass\appbarwnd.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\wndclass\blankwnd.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\wndclass\buttbar.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\wndclass\buttwnd.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\wndclass\editwnd.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\wndclass\framewnd.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\wndclass\gradientwnd.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\wndclass\guiobjwnd.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\wndclass\labelwnd.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\wndclass\listwnd.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\wndclass\rootwndholder.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\wndclass\scbkgwnd.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\wndclass\sepwnd.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\wndclass\svcwndhold.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\wndclass\tabsheet.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\wndclass\tabsheetbar.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\wndclass\treewnd.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\wndclass\virtualhostwnd.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\wndclass\wndholder.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\wndtrack.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\xml\LoadXML.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\xml\XMLAutoInclude.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\xml\xmlparams.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\xml\xmlparamsi.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\xml\xmlparamsx.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\xml\xmlreader.h" />
+ <ClInclude Include="..\..\..\Wasabi\api\xml\xmlwrite.h" />
+ <ClInclude Include="..\..\..\Wasabi\bfc\depend.h" />
+ <ClInclude Include="..\..\..\Wasabi\bfc\depview.h" />
+ <ClInclude Include="..\..\..\Wasabi\bfc\util\findopenrect.h" />
+ <CustomBuild Include="..\..\..\Winamp\FRONTEND.H">
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
+ </CustomBuild>
+ <CustomBuild Include="..\..\..\Winamp\wa_ipc.h">
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
+ <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
+ </CustomBuild>
+ <ClInclude Include="AlbumArt.h" />
+ <ClInclude Include="api__gen_ff.h" />
+ <ClInclude Include="embedwndguid.h" />
+ <ClInclude Include="main.h" />
+ <ClInclude Include="menuactions.h" />
+ <ClInclude Include="minibrowserCOM.h" />
+ <ClInclude Include="precomp__gen_ff.h" />
+ <ClInclude Include="resource.h" />
+ <ClInclude Include="skininfo.h" />
+ <ClInclude Include="wa2buckitems.h" />
+ <ClInclude Include="wa2cfgitems.h" />
+ <ClInclude Include="wa2core.h" />
+ <ClInclude Include="wa2coreactions.h" />
+ <ClInclude Include="wa2frontend.h" />
+ <ClInclude Include="wa2groupdefs.h" />
+ <ClInclude Include="wa2playlist.h" />
+ <ClInclude Include="wa2pldirobj.h" />
+ <ClInclude Include="wa2pledit.h" />
+ <ClInclude Include="wa2songticker.h" />
+ <ClInclude Include="wa2wndembed.h" />
+ <ClInclude Include="wasabicfg.h" />
+ <ClInclude Include="WinampConfigScriptObject.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="gen_ff.rc" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ 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 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <ClCompile Include="AlbumArt.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="embedwndguid.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ff_ipc.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="fsmonitor.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="main.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="MediaDownloader.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="menuactions.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="minibrowserCOM.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="prefs.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="prefs_about.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="prefs_alpha.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="prefs_colorthemes.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="prefs_font.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="prefs_general.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="servicelink.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="skininfo.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="wa2buckitems.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="wa2cfgitems.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="wa2core.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="wa2coreactions.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="wa2frontend.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="wa2groupdefs.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="wa2playlist.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="wa2pldirobj.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="wa2pledit.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="wa2songticker.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="wa2wndembed.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="WinampConfigScriptObject.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndclass\abstractwndhold.cpp">
+ <Filter>Source Files\Wasabi\api\wnd\wndclass</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\wndmgr\alphamgr.cpp">
+ <Filter>Source Files\Wasabi\api\wndmgr</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\wndmgr\animate.cpp">
+ <Filter>Source Files\Wasabi\api\wndmgr</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\animlayer.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\config\api_config.cpp">
+ <Filter>Source Files\Wasabi\api\config</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\config\api_configi.cpp">
+ <Filter>Source Files\Wasabi\api\config</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\config\api_configx.cpp">
+ <Filter>Source Files\Wasabi\api\config</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\core\api_core.cpp">
+ <Filter>Source Files\Wasabi\api\core</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\dependency\api_dependent.cpp">
+ <Filter>Source Files\Wasabi\api\dependency</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\dependency\api_dependentviewer.cpp">
+ <Filter>Source Files\Wasabi\api\dependency</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\filereader\api_filereader.cpp">
+ <Filter>Source Files\Wasabi\api\filereader</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\imgldr\api_imgldr.cpp">
+ <Filter>Source Files\Wasabi\api\imgldr</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\locales\api_locales.cpp">
+ <Filter>Source Files\Wasabi\api\locales</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\locales\api_localesi.cpp">
+ <Filter>Source Files\Wasabi\api\locales</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\locales\api_localesx.cpp">
+ <Filter>Source Files\Wasabi\api\locales</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\api_maki.cpp">
+ <Filter>Source Files\Wasabi\api\script</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\debugger\api_makidebug.cpp">
+ <Filter>Source Files\Wasabi\api\script\debugger</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\api_makii.cpp">
+ <Filter>Source Files\Wasabi\api\script</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\api_makix.cpp">
+ <Filter>Source Files\Wasabi\api\script</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\api_palette.cpp">
+ <Filter>Source Files\Wasabi\api\skin</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\service\api_service.cpp">
+ <Filter>Source Files\Wasabi\api\service</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\api_skin.cpp">
+ <Filter>Source Files\Wasabi\api\skin</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\syscb\api_syscb.cpp">
+ <Filter>Source Files\Wasabi\api\syscb</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\feeds\api_textfeed.cpp">
+ <Filter>Source Files\Wasabi\api\skin\feeds</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\api_window.cpp">
+ <Filter>Source Files\Wasabi\api\wnd</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\api_wnd.cpp">
+ <Filter>Source Files\Wasabi\api\wnd</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\wndmgr\api_wndmgr.cpp">
+ <Filter>Source Files\Wasabi\api\wndmgr</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndclass\appbarwnd.cpp">
+ <Filter>Source Files\Wasabi\api\wnd\wndclass</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\wndmgr\appcmds.cpp">
+ <Filter>Source Files\Wasabi\api\wndmgr</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\config\items\attribute.cpp">
+ <Filter>Source Files\Wasabi\api\config\items</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\config\items\attrstr.cpp">
+ <Filter>Source Files\Wasabi\api\config\items</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\wndmgr\autopopup.cpp">
+ <Filter>Source Files\Wasabi\api\wndmgr</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\basewnd.cpp">
+ <Filter>Source Files\Wasabi\api\wnd</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\font\bitmapfont.cpp">
+ <Filter>Source Files\Wasabi\api\font</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndclass\blankwnd.cpp">
+ <Filter>Source Files\Wasabi\api\wnd\wndclass</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\syscb\callbacks\browsercb.cpp">
+ <Filter>Source Files\Wasabi\api\syscb\callbacks</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndclass\bufferpaintwnd.cpp">
+ <Filter>Source Files\Wasabi\api\wnd\wndclass</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndclass\buttbar.cpp">
+ <Filter>Source Files\Wasabi\api\wnd\wndclass</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\button.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndclass\buttwnd.cpp">
+ <Filter>Source Files\Wasabi\api\wnd\wndclass</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\c_browser.cpp">
+ <Filter>Source Files\Wasabi\api\script\objects\c_script</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\c_button.cpp">
+ <Filter>Source Files\Wasabi\api\script\objects\c_script</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\c_checkbox.cpp">
+ <Filter>Source Files\Wasabi\api\script\objects\c_script</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\c_container.cpp">
+ <Filter>Source Files\Wasabi\api\script\objects\c_script</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\c_dropdownlist.cpp">
+ <Filter>Source Files\Wasabi\api\script\objects\c_script</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\c_edit.cpp">
+ <Filter>Source Files\Wasabi\api\script\objects\c_script</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\c_group.cpp">
+ <Filter>Source Files\Wasabi\api\script\objects\c_script</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\c_guilist.cpp">
+ <Filter>Source Files\Wasabi\api\script\objects\c_script</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\c_guiobject.cpp">
+ <Filter>Source Files\Wasabi\api\script\objects\c_script</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\c_guitree.cpp">
+ <Filter>Source Files\Wasabi\api\script\objects\c_script</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\c_layout.cpp">
+ <Filter>Source Files\Wasabi\api\script\objects\c_script</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\c_menubutton.cpp">
+ <Filter>Source Files\Wasabi\api\script\objects\c_script</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\c_querylist.cpp">
+ <Filter>Source Files\Wasabi\api\script\objects\c_script</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\c_rootobj.cpp">
+ <Filter>Source Files\Wasabi\api\script\objects\c_script</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\c_slider.cpp">
+ <Filter>Source Files\Wasabi\api\script\objects\c_script</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\c_text.cpp">
+ <Filter>Source Files\Wasabi\api\script\objects\c_script</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\c_togglebutton.cpp">
+ <Filter>Source Files\Wasabi\api\script\objects\c_script</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\c_treeitem.cpp">
+ <Filter>Source Files\Wasabi\api\script\objects\c_script</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\config\items\cfgitemi.cpp">
+ <Filter>Source Files\Wasabi\api\config\items</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\config\items\cfgitemx.cpp">
+ <Filter>Source Files\Wasabi\api\config\items</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\apiinit.cpp">
+ <Filter>Source Files\Wasabi\api</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\spanbar.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\spopup.cpp">
+ <Filter>Source Files\Wasabi\api\script\objects</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\sprivate.cpp">
+ <Filter>Source Files\Wasabi\api\script\objects</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\sregion.cpp">
+ <Filter>Source Files\Wasabi\api\script\objects</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\sseeker.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\sstatus.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\stats\statswnd.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets\stats</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndclass\status.cpp">
+ <Filter>Source Files\Wasabi\api\wnd\wndclass</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\bfc\std_file.cpp">
+ <Filter>Source Files\Wasabi\bfc</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\service\svcs\svc_action.cpp">
+ <Filter>Source Files\Wasabi\api\service\svcs</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\service\svc_enum.cpp">
+ <Filter>Source Files\Wasabi\api\service</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\service\svcs\svc_fileread.cpp">
+ <Filter>Source Files\Wasabi\api\service\svcs</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\service\svcs\svc_font.cpp">
+ <Filter>Source Files\Wasabi\api\service\svcs</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\service\svcs\svc_imggen.cpp">
+ <Filter>Source Files\Wasabi\api\service\svcs</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\service\svcs\svc_imgload.cpp">
+ <Filter>Source Files\Wasabi\api\service\svcs</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\service\svcs\svc_minibrowser.cpp">
+ <Filter>Source Files\Wasabi\api\service\svcs</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\core\svc_scriptcore.cpp">
+ <Filter>Source Files\Wasabi\api\script\objects\core</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\service\svcs\svc_scriptobj.cpp">
+ <Filter>Source Files\Wasabi\api\service\svcs</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\service\svcs\svc_skinfilter.cpp">
+ <Filter>Source Files\Wasabi\api\service\svcs</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\service\svcs\svc_textfeed.cpp">
+ <Filter>Source Files\Wasabi\api\service\svcs</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\service\svcs\svc_tooltips.cpp">
+ <Filter>Source Files\Wasabi\api\service\svcs</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\service\svcs\svc_wndcreate.cpp">
+ <Filter>Source Files\Wasabi\api\service\svcs</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\service\svcs\svc_xuiobject.cpp">
+ <Filter>Source Files\Wasabi\api\service\svcs</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\service\svccache.cpp">
+ <Filter>Source Files\Wasabi\api\service</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\service\svcenum.cpp">
+ <Filter>Source Files\Wasabi\api\service</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\service\svcenumbyguid.cpp">
+ <Filter>Source Files\Wasabi\api\service</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\service\svcenumt.cpp">
+ <Filter>Source Files\Wasabi\api\service</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndclass\svcwndhold.cpp">
+ <Filter>Source Files\Wasabi\api\wnd\wndclass</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\svolbar.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\sxmldoc.cpp">
+ <Filter>Source Files\Wasabi\api\script\objects</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\syscb\callbacks\syscb.cpp">
+ <Filter>Source Files\Wasabi\api\syscb\callbacks</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\syscb\callbacks\syscbi.cpp">
+ <Filter>Source Files\Wasabi\api\syscb\callbacks</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\syscb\callbacks\syscbx.cpp">
+ <Filter>Source Files\Wasabi\api\syscb\callbacks</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\systemobj.cpp">
+ <Filter>Source Files\Wasabi\api\script\objects</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\util\systray.cpp">
+ <Filter>Source Files\Wasabi\api\util</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndclass\tabsheet.cpp">
+ <Filter>Source Files\Wasabi\api\wnd\wndclass</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndclass\tabsheetbar.cpp">
+ <Filter>Source Files\Wasabi\api\wnd\wndclass</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\text.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\textbase.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\feeds\textfeed.cpp">
+ <Filter>Source Files\Wasabi\api\skin\feeds</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\tgbutton.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\bfc\util\timefmt.cpp">
+ <Filter>Source Files\Wasabi\bfc\util</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\timer\timerclient.cpp">
+ <Filter>Source Files\Wasabi\api\timer</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\title.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\titlebox.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndclass\tooltip.cpp">
+ <Filter>Source Files\Wasabi\api\wnd\wndclass</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndclass\treewnd.cpp">
+ <Filter>Source Files\Wasabi\api\wnd\wndclass</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\font\win32\truetypefont_win32.cpp">
+ <Filter>Source Files\Wasabi\api\font\win32</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndclass\typesheet.cpp">
+ <Filter>Source Files\Wasabi\api\wnd\wndclass</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\config\uioptions.cpp">
+ <Filter>Source Files\Wasabi\api\config</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\util\varmgr.cpp">
+ <Filter>Source Files\Wasabi\api\util</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\vcpu.cpp">
+ <Filter>Source Files\Wasabi\api\script</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\debugger\vcpudebug.cpp">
+ <Filter>Source Files\Wasabi\api\script\debugger</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\virtualwnd.cpp">
+ <Filter>Source Files\Wasabi\api\wnd</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\wacobj.cpp">
+ <Filter>Source Files\Wasabi\api\script\objects</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\service\waservicefactory.cpp">
+ <Filter>Source Files\Wasabi\api\service</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\service\waservicefactorybase.cpp">
+ <Filter>Source Files\Wasabi\api\service</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\service\waservicefactoryi.cpp">
+ <Filter>Source Files\Wasabi\api\service</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\service\waservicefactoryt.cpp">
+ <Filter>Source Files\Wasabi\api\service</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\service\waservicefactorytsingle.cpp">
+ <Filter>Source Files\Wasabi\api\service</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\service\waservicefactoryx.cpp">
+ <Filter>Source Files\Wasabi\api\service</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets.cpp">
+ <Filter>Source Files\Wasabi\api\skin</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndapi.cpp">
+ <Filter>Source Files\Wasabi\api\wnd</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndclass\wndholder.cpp">
+ <Filter>Source Files\Wasabi\api\wnd\wndclass</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\wndmgr\wndmgrapi.cpp">
+ <Filter>Source Files\Wasabi\api\wndmgr</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndtrack.cpp">
+ <Filter>Source Files\Wasabi\api\wnd</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\xml\XMLAutoInclude.cpp">
+ <Filter>Source Files\Wasabi\api\xml</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\xml\xmlparams.cpp">
+ <Filter>Source Files\Wasabi\api\xml</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\xml\xmlparamsi.cpp">
+ <Filter>Source Files\Wasabi\api\xml</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\xml\xmlparamsx.cpp">
+ <Filter>Source Files\Wasabi\api\xml</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\xml\xmlreader.cpp">
+ <Filter>Source Files\Wasabi\api\xml</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\xml\xmlwrite.cpp">
+ <Filter>Source Files\Wasabi\api\xml</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\xuiaddparams.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\xuibookmarklist.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\mb\xuibrowser.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets\mb</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\xuicheckbox.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\xuicombobox.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\xuicustomobject.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\xuidownloadslist.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\xuidropdownlist.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\xuieditbox.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\xuiframe.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\xuigradientwnd.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\xuigrid.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\xuigroupxfade.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\xuihideobject.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\xuihistoryedit.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\xuilist.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\xuimenu.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\xuimenuso.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\xuiobjdirwnd.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\xuioswndhost.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\xuipathpicker.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\xuiprogressgrid.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\xuiradiogroup.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\xuirect.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\xuisendparams.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\stats\xuistats.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets\stats</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\xuistatus.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\xuitabsheet.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\xuithemeslist.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\xuititlebox.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\xuitree.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\wa2\xuiwa2slider.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets\wa2</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\xuiwndholder.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\config\cfglist.cpp">
+ <Filter>Source Files\Wasabi\api\config</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\config\cfgscriptobj.cpp">
+ <Filter>Source Files\Wasabi\api\config</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\checkbox.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndclass\clickwnd.cpp">
+ <Filter>Source Files\Wasabi\api\wnd\wndclass</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\combobox.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\compbuck2.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\wac\compon.cpp">
+ <Filter>Source Files\Wasabi\api\wac</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\config\config.cpp">
+ <Filter>Source Files\Wasabi\api\config</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\wndmgr\container.cpp">
+ <Filter>Source Files\Wasabi\api\wndmgr</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\contextmenu.cpp">
+ <Filter>Source Files\Wasabi\api\wnd</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\core\coreadminobj.cpp">
+ <Filter>Source Files\Wasabi\api\script\objects\core</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\syscb\callbacks\corecb.cpp">
+ <Filter>Source Files\Wasabi\api\syscb\callbacks</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\core\coreobj.cpp">
+ <Filter>Source Files\Wasabi\api\script\objects\core</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\cursor.cpp">
+ <Filter>Source Files\Wasabi\api\wnd</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\cursormgr.cpp">
+ <Filter>Source Files\Wasabi\api\skin</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\customobject.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\deactivatemgr.cpp">
+ <Filter>Source Files\Wasabi\api\wnd</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\debugger\debugapi.cpp">
+ <Filter>Source Files\Wasabi\api\script\debugger</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\debugger\debuggerui.cpp">
+ <Filter>Source Files\Wasabi\api\script\debugger</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\debugger\debugsymbols.cpp">
+ <Filter>Source Files\Wasabi\api\script\debugger</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\bfc\depend.cpp">
+ <Filter>Source Files\Wasabi\bfc</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\bfc\depview.cpp">
+ <Filter>Source Files\Wasabi\bfc</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\di.cpp">
+ <Filter>Source Files\Wasabi\api\wnd</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\debugger\disasm.cpp">
+ <Filter>Source Files\Wasabi\api\script\debugger</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\dragitemi.cpp">
+ <Filter>Source Files\Wasabi\api\wnd</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\bfc\draw\drawpoly.cpp">
+ <Filter>Source Files\Wasabi\bfc\draw</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\dropdownlist.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\edit.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndclass\editwnd.cpp">
+ <Filter>Source Files\Wasabi\api\wnd\wndclass</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndclass\editwndstring.cpp">
+ <Filter>Source Files\Wasabi\api\wnd\wndclass</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndclass\embeddedxui.cpp">
+ <Filter>Source Files\Wasabi\api\wnd\wndclass</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\feeds\feedwatch.cpp">
+ <Filter>Source Files\Wasabi\api\skin\feeds</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\feeds\feedwatcherso.cpp">
+ <Filter>Source Files\Wasabi\api\skin\feeds</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\filereader\local\fileread.cpp">
+ <Filter>Source Files\Wasabi\api\filereader\local</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\filereader\filereaderapi.cpp">
+ <Filter>Source Files\Wasabi\api\filereader</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\findobjectcb.cpp">
+ <Filter>Source Files\Wasabi\api\wnd</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\bfc\util\findopenrect.cpp">
+ <Filter>Source Files\Wasabi\bfc\util</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\font\font.cpp">
+ <Filter>Source Files\Wasabi\api\font</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\font\fontapi.cpp">
+ <Filter>Source Files\Wasabi\api\font</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndclass\foreignwnd.cpp">
+ <Filter>Source Files\Wasabi\api\wnd\wndclass</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndclass\framewnd.cpp">
+ <Filter>Source Files\Wasabi\api\wnd\wndclass</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\fx_dmove.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\gammamgr.cpp">
+ <Filter>Source Files\Wasabi\api\skin</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\wndmgr\gc.cpp">
+ <Filter>Source Files\Wasabi\api\wndmgr</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\imgldr\imggen\grad.cpp">
+ <Filter>Source Files\Wasabi\api\imgldr\imggen</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\bfc\draw\gradient.cpp">
+ <Filter>Source Files\Wasabi\bfc\draw</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndclass\gradientwnd.cpp">
+ <Filter>Source Files\Wasabi\api\wnd\wndclass</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\group.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\groupclickwnd.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\grouplist.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\groupmgr.cpp">
+ <Filter>Source Files\Wasabi\api\skin</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\grouptgbutton.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\grouptips.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\groupwndcreate.cpp">
+ <Filter>Source Files\Wasabi\api\skin</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\guiobj.cpp">
+ <Filter>Source Files\Wasabi\api\script\objects</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\guiobjectx.cpp">
+ <Filter>Source Files\Wasabi\api\script\objects</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndclass\guiobjwnd.cpp">
+ <Filter>Source Files\Wasabi\api\wnd\wndclass</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\guiradiogroup.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\wndmgr\guistatuscb.cpp">
+ <Filter>Source Files\Wasabi\api\wndmgr</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\guitree.cpp">
+ <Filter>Source Files\Wasabi\api\skin</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\guru.cpp">
+ <Filter>Source Files\Wasabi\api\script</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\h_browser.cpp">
+ <Filter>Source Files\Wasabi\api\script\objects\c_script</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\h_button.cpp">
+ <Filter>Source Files\Wasabi\api\script\objects\c_script</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\h_checkbox.cpp">
+ <Filter>Source Files\Wasabi\api\script\objects\c_script</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\h_container.cpp">
+ <Filter>Source Files\Wasabi\api\script\objects\c_script</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\h_dropdownlist.cpp">
+ <Filter>Source Files\Wasabi\api\script\objects\c_script</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\h_edit.cpp">
+ <Filter>Source Files\Wasabi\api\script\objects\c_script</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\h_group.cpp">
+ <Filter>Source Files\Wasabi\api\script\objects\c_script</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\h_guilist.cpp">
+ <Filter>Source Files\Wasabi\api\script\objects\c_script</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\h_guiobject.cpp">
+ <Filter>Source Files\Wasabi\api\script\objects\c_script</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\h_guitree.cpp">
+ <Filter>Source Files\Wasabi\api\script\objects\c_script</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\h_layout.cpp">
+ <Filter>Source Files\Wasabi\api\script\objects\c_script</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\h_menubutton.cpp">
+ <Filter>Source Files\Wasabi\api\script\objects\c_script</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\h_querylist.cpp">
+ <Filter>Source Files\Wasabi\api\script\objects\c_script</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\h_rootobj.cpp">
+ <Filter>Source Files\Wasabi\api\script\objects\c_script</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\h_slider.cpp">
+ <Filter>Source Files\Wasabi\api\script\objects\c_script</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\h_text.cpp">
+ <Filter>Source Files\Wasabi\api\script\objects\c_script</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\h_togglebutton.cpp">
+ <Filter>Source Files\Wasabi\api\script\objects\c_script</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\h_treeitem.cpp">
+ <Filter>Source Files\Wasabi\api\script\objects\c_script</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\historyeditbox.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\nu\HTMLContainer2.cpp">
+ <Filter>Source Files\nu</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\mb\iebrowser.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets\mb</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\application\ifc_messageprocessori.cpp">
+ <Filter>Source Files\Wasabi\api\application</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\imgldr\imgldr.cpp">
+ <Filter>Source Files\Wasabi\api\imgldr</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\imgldr\imgldrapi.cpp">
+ <Filter>Source Files\Wasabi\api\imgldr</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\bfc\util\inifile.cpp">
+ <Filter>Source Files\Wasabi\bfc\util</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\config\items\intarray.cpp">
+ <Filter>Source Files\Wasabi\api\config\items</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\debugger\jitd.cpp">
+ <Filter>Source Files\Wasabi\api\script\debugger</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\debugger\jitdbreak.cpp">
+ <Filter>Source Files\Wasabi\api\script\debugger</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\keyboard.cpp">
+ <Filter>Source Files\Wasabi\api\wnd</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndclass\labelwnd.cpp">
+ <Filter>Source Files\Wasabi\api\wnd\wndclass</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\layer.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\wndmgr\layout.cpp">
+ <Filter>Source Files\Wasabi\api\wndmgr</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\nu\listview.cpp">
+ <Filter>Source Files\nu</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndclass\listwnd.cpp">
+ <Filter>Source Files\Wasabi\api\wnd\wndclass</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\xml\LoadXML.cpp">
+ <Filter>Source Files\Wasabi\api\xml</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\mb\mainminibrowser.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets\mb</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\mb\mbsvc.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets\mb</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\mb\minibrowser.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets\mb</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\mb\minibrowserwnd.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets\mb</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\mouseredir.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\wndmgr\msgbox.cpp">
+ <Filter>Source Files\Wasabi\api\wndmgr</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\nakedobject.cpp">
+ <Filter>Source Files\Wasabi\api\skin</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objcontroller.cpp">
+ <Filter>Source Files\Wasabi\api\script</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objcontrollert.cpp">
+ <Filter>Source Files\Wasabi\api\script</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\objdirwnd.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\objectactuator.cpp">
+ <Filter>Source Files\Wasabi\api\skin</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objecttable.cpp">
+ <Filter>Source Files\Wasabi\api\script</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\config\options.cpp">
+ <Filter>Source Files\Wasabi\api\config</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\imgldr\imggen\osedge.cpp">
+ <Filter>Source Files\Wasabi\api\imgldr\imggen</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndclass\oswnd.cpp">
+ <Filter>Source Files\Wasabi\api\wnd\wndclass</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndclass\oswndhost.cpp">
+ <Filter>Source Files\Wasabi\api\wnd\wndclass</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\paintset.cpp">
+ <Filter>Source Files\Wasabi\api\wnd</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\pathpicker.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\syscb\callbacks\playlistcb.cpp">
+ <Filter>Source Files\Wasabi\api\syscb\callbacks</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\PlaylistScriptObject.cpp">
+ <Filter>Source Files\Wasabi\api\script\objects</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\imgldr\imggen\poly.cpp">
+ <Filter>Source Files\Wasabi\api\imgldr\imggen</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\popexitcb.cpp">
+ <Filter>Source Files\Wasabi\api\wnd</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\popexitchecker.cpp">
+ <Filter>Source Files\Wasabi\api\wnd</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\popup.cpp">
+ <Filter>Source Files\Wasabi\api\wnd</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\pslider.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndclass\qpaintwnd.cpp">
+ <Filter>Source Files\Wasabi\api\wnd\wndclass</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\nu\regexp.cpp">
+ <Filter>Source Files\nu</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\regioncache.cpp">
+ <Filter>Source Files\Wasabi\api\skin</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\wndmgr\resize.cpp">
+ <Filter>Source Files\Wasabi\api\wndmgr</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\rootobj.cpp">
+ <Filter>Source Files\Wasabi\api\script\objects</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\rootobjcb.cpp">
+ <Filter>Source Files\Wasabi\api\script\objects</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\rootobjcbi.cpp">
+ <Filter>Source Files\Wasabi\api\script\objects</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\rootobjcbx.cpp">
+ <Filter>Source Files\Wasabi\api\script\objects</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\rootobjcontroller.cpp">
+ <Filter>Source Files\Wasabi\api\script\objects</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\rootobject.cpp">
+ <Filter>Source Files\Wasabi\api\script\objects</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\rootobjecti.cpp">
+ <Filter>Source Files\Wasabi\api\script\objects</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\rootobjectx.cpp">
+ <Filter>Source Files\Wasabi\api\script\objects</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\rootwnd.cpp">
+ <Filter>Source Files\Wasabi\api\wnd</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndclass\rootwndholder.cpp">
+ <Filter>Source Files\Wasabi\api\wnd\wndclass</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\sa.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\sapplication.cpp">
+ <Filter>Source Files\Wasabi\api\script\objects</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\sbitlist.cpp">
+ <Filter>Source Files\Wasabi\api\script\objects</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndclass\scbkgwnd.cpp">
+ <Filter>Source Files\Wasabi\api\wnd\wndclass</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\scolor.cpp">
+ <Filter>Source Files\Wasabi\api\script\objects</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\scolormgr.cpp">
+ <Filter>Source Files\Wasabi\api\script\objects</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\locales\localesmgr.cpp">
+ <Filter>Source Files\Wasabi\api\locales</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\script.cpp">
+ <Filter>Source Files\Wasabi\api\script</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\c_script\scripthook.cpp">
+ <Filter>Source Files\Wasabi\api\script\objects\c_script</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\scriptmgr.cpp">
+ <Filter>Source Files\Wasabi\api\script</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\scriptobj.cpp">
+ <Filter>Source Files\Wasabi\api\script</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\scriptobji.cpp">
+ <Filter>Source Files\Wasabi\api\script</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\scriptobjx.cpp">
+ <Filter>Source Files\Wasabi\api\script</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndclass\scrollbar.cpp">
+ <Filter>Source Files\Wasabi\api\wnd\wndclass</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\debugger\sdebuggerui.cpp">
+ <Filter>Source Files\Wasabi\api\script\debugger</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\util\selectfile.cpp">
+ <Filter>Source Files\Wasabi\api\util</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndclass\SelItemList.cpp">
+ <Filter>Source Files\Wasabi\api\wnd\wndclass</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndclass\sepwnd.cpp">
+ <Filter>Source Files\Wasabi\api\wnd\wndclass</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\seqband.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\seqpreamp.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\widgets\seqvis.cpp">
+ <Filter>Source Files\Wasabi\api\skin\widgets</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\service\servicei.cpp">
+ <Filter>Source Files\Wasabi\api\service</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\sfile.cpp">
+ <Filter>Source Files\Wasabi\api\script\objects</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\sgammagroup.cpp">
+ <Filter>Source Files\Wasabi\api\script\objects</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\sgammaset.cpp">
+ <Filter>Source Files\Wasabi\api\script\objects</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\skin.cpp">
+ <Filter>Source Files\Wasabi\api\skin</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\skinapi.cpp">
+ <Filter>Source Files\Wasabi\api\skin</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\syscb\callbacks\skincb.cpp">
+ <Filter>Source Files\Wasabi\api\syscb\callbacks</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\skinelem.cpp">
+ <Filter>Source Files\Wasabi\api\skin</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\wndmgr\skinembed.cpp">
+ <Filter>Source Files\Wasabi\api\wndmgr</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\skinfilter.cpp">
+ <Filter>Source Files\Wasabi\api\skin</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\skinfont.cpp">
+ <Filter>Source Files\Wasabi\api\skin</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\skinitem.cpp">
+ <Filter>Source Files\Wasabi\api\skin</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\skinparse.cpp">
+ <Filter>Source Files\Wasabi\api\skin</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\skin\SkinVersion.cpp">
+ <Filter>Source Files\Wasabi\api\skin</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\wndmgr\skinwnd.cpp">
+ <Filter>Source Files\Wasabi\api\wndmgr</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\wnd\wndclass\slider.cpp">
+ <Filter>Source Files\Wasabi\api\wnd\wndclass</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\slist.cpp">
+ <Filter>Source Files\Wasabi\api\script\objects</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\objects\smap.cpp">
+ <Filter>Source Files\Wasabi\api\script\objects</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\wndmgr\snappnt.cpp">
+ <Filter>Source Files\Wasabi\api\wndmgr</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\imgldr\imggen\solid.cpp">
+ <Filter>Source Files\Wasabi\api\imgldr\imggen</Filter>
+ </ClCompile>
+ <ClCompile Include="..\gen_ml\menu.cpp">
+ <Filter>Source Files\gen_ml</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Wasabi\api\script\debugger\sourcecodeline.cpp">
+ <Filter>Source Files\Wasabi\api\script\debugger</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="AlbumArt.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="embedwndguid.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="main.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\gen_ml\menu.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="menuactions.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="minibrowserCOM.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="precomp__gen_ff.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="resource.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="skininfo.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="wa2buckitems.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="wa2cfgitems.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="wa2core.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="wa2coreactions.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="wa2frontend.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="wa2groupdefs.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="wa2playlist.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="wa2pldirobj.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="wa2pledit.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="wa2songticker.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="wa2wndembed.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="wasabicfg.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="WinampConfigScriptObject.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\wndclass\abstractwndhold.h">
+ <Filter>Header Files\Wasabi\api\wnd\wndclass</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\wndmgr\alphamgr.h">
+ <Filter>Header Files\Wasabi\api\wndmgr</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\wndmgr\animate.h">
+ <Filter>Header Files\Wasabi\api\wndmgr</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\config\api_config.h">
+ <Filter>Header Files\Wasabi\api\config</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\config\api_configi.h">
+ <Filter>Header Files\Wasabi\api\config</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\config\api_configx.h">
+ <Filter>Header Files\Wasabi\api\config</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\font\api_font.h">
+ <Filter>Header Files\Wasabi\api\font</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\filereader\api_readercallback.h">
+ <Filter>Header Files\Wasabi\api\filereader</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\skin\feeds\api_textfeed.h">
+ <Filter>Header Files\Wasabi\api\skin\feeds</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\timer\api_timer.h">
+ <Filter>Header Files\Wasabi\api\timer</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\wndmgr\api_wndmgr.h">
+ <Filter>Header Files\Wasabi\api\wndmgr</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\wndclass\appbarwnd.h">
+ <Filter>Header Files\Wasabi\api\wnd\wndclass</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\wndmgr\appcmds.h">
+ <Filter>Header Files\Wasabi\api\wndmgr</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\wndmgr\autopopup.h">
+ <Filter>Header Files\Wasabi\api\wndmgr</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\font\bitmapfont.h">
+ <Filter>Header Files\Wasabi\api\font</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\wndclass\blankwnd.h">
+ <Filter>Header Files\Wasabi\api\wnd\wndclass</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\syscb\callbacks\browsercb.h">
+ <Filter>Header Files\Wasabi\api\syscb\callbacks</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\syscb\callbacks\browsercbi.h">
+ <Filter>Header Files\Wasabi\api\syscb\callbacks</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\wndclass\buttbar.h">
+ <Filter>Header Files\Wasabi\api\wnd\wndclass</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\skin\widgets\button.h">
+ <Filter>Header Files\Wasabi\api\skin\widgets</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\wndclass\buttwnd.h">
+ <Filter>Header Files\Wasabi\api\wnd\wndclass</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\config\cfglist.h">
+ <Filter>Header Files\Wasabi\api\config</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\skin\widgets\combobox.h">
+ <Filter>Header Files\Wasabi\api\skin\widgets</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\skin\widgets\compbuck2.h">
+ <Filter>Header Files\Wasabi\api\skin\widgets</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\config\config.h">
+ <Filter>Header Files\Wasabi\api\config</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\wndmgr\container.h">
+ <Filter>Header Files\Wasabi\api\wndmgr</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\skin\widgets\customobject.h">
+ <Filter>Header Files\Wasabi\api\skin\widgets</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\skin\widgets\dropdownlist.h">
+ <Filter>Header Files\Wasabi\api\skin\widgets</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\wndclass\editwnd.h">
+ <Filter>Header Files\Wasabi\api\wnd\wndclass</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\skin\feeds\ezfeed.h">
+ <Filter>Header Files\Wasabi\api\skin\feeds</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\filereader\filereaderapi.h">
+ <Filter>Header Files\Wasabi\api\filereader</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\font\font.h">
+ <Filter>Header Files\Wasabi\api\font</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\font\fontapi.h">
+ <Filter>Header Files\Wasabi\api\font</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\wndclass\framewnd.h">
+ <Filter>Header Files\Wasabi\api\wnd\wndclass</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\wndmgr\gc.h">
+ <Filter>Header Files\Wasabi\api\wndmgr</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\wndclass\gradientwnd.h">
+ <Filter>Header Files\Wasabi\api\wnd\wndclass</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\skin\widgets\group.h">
+ <Filter>Header Files\Wasabi\api\skin\widgets</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\skin\widgets\grouptgbutton.h">
+ <Filter>Header Files\Wasabi\api\skin\widgets</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\skin\widgets\grouptips.h">
+ <Filter>Header Files\Wasabi\api\skin\widgets</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\wndclass\guiobjwnd.h">
+ <Filter>Header Files\Wasabi\api\wnd\wndclass</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\skin\widgets\guiradiogroup.h">
+ <Filter>Header Files\Wasabi\api\skin\widgets</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\script\objects\c_script\h_rootobj.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\skin\widgets\historyeditbox.h">
+ <Filter>Header Files\Wasabi\api\skin\widgets</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\application\ifc_messageprocessori.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\imgldr\imgldr.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\imgldr\imgldrapi.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\imgldr\ImgLoaderEnum.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\wndclass\labelwnd.h">
+ <Filter>Header Files\Wasabi\api\wnd\wndclass</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\skin\widgets\layer.h">
+ <Filter>Header Files\Wasabi\api\skin\widgets</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\wndmgr\layout.h">
+ <Filter>Header Files\Wasabi\api\wndmgr</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\wndclass\listwnd.h">
+ <Filter>Header Files\Wasabi\api\wnd\wndclass</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\xml\LoadXML.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\syscb\callbacks\metacb.h">
+ <Filter>Header Files\Wasabi\api\syscb\callbacks</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\skin\widgets\mb\minibrowser.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\skin\widgets\mb\minibrowserwnd.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\skin\widgets\mouseredir.h">
+ <Filter>Header Files\Wasabi\api\skin\widgets</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\wndmgr\msgbox.h">
+ <Filter>Header Files\Wasabi\api\wndmgr</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\skin\widgets\objdirwnd.h">
+ <Filter>Header Files\Wasabi\api\skin\widgets</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\syscb\callbacks\playlistcb.h">
+ <Filter>Header Files\Wasabi\api\syscb\callbacks</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\script\objects\PlaylistScriptObject.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\skin\widgets\pslider.h">
+ <Filter>Header Files\Wasabi\api\skin\widgets</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\wndmgr\resize.h">
+ <Filter>Header Files\Wasabi\api\wndmgr</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\script\objects\rootobj.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\script\objects\rootobject.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\script\objects\rootobjecti.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\script\objects\rootobjectx.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\wndclass\rootwndholder.h">
+ <Filter>Header Files\Wasabi\api\wnd\wndclass</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\script\objects\sapplication.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\wndclass\scbkgwnd.h">
+ <Filter>Header Files\Wasabi\api\wnd\wndclass</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\script\objects\scolor.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\script\objects\scolormgr.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\script\objects\c_script\scripthook.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\script\debugger\sdebuggerui.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\util\selectfile.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\wndclass\sepwnd.h">
+ <Filter>Header Files\Wasabi\api\wnd\wndclass</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\skin\widgets\seqband.h">
+ <Filter>Header Files\Wasabi\api\skin\widgets</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\skin\widgets\seqpreamp.h">
+ <Filter>Header Files\Wasabi\api\skin\widgets</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\script\objects\sfile.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\script\objects\sgammagroup.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\script\objects\sgammaset.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\skin\skin.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\skin\skinapi.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\skin\skinelem.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\wndmgr\skinembed.h">
+ <Filter>Header Files\Wasabi\api\wndmgr</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\skin\skinfilter.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\skin\skinfont.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\skin\skinitem.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\skin\skinparse.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\skin\SkinVersion.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\wndmgr\skinwnd.h">
+ <Filter>Header Files\Wasabi\api\wndmgr</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\wndmgr\snappnt.h">
+ <Filter>Header Files\Wasabi\api\wndmgr</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\script\objects\sprivate.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\service\svcs\svc_accessibility.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\service\svcs\svc_accroleserver.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\service\svcs\svc_action.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\service\svcs\svc_droptarget.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\service\svcs\svc_fileread.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\filereader\svc_filereadI.h">
+ <Filter>Header Files\Wasabi\api\filereader</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\service\svcs\svc_filesel.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\service\svcs\svc_objectdir.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\service\svcs\svc_scriptobj.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\service\svcs\svc_textfeed.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\service\svcs\svc_wndcreate.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\syscb\callbacks\svccbi.h">
+ <Filter>Header Files\Wasabi\api\syscb\callbacks</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\script\objects\svcwnd.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\wndclass\svcwndhold.h">
+ <Filter>Header Files\Wasabi\api\wnd\wndclass</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\script\objects\sxmldoc.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\syscb\callbacks\syscb.h">
+ <Filter>Header Files\Wasabi\api\syscb\callbacks</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\util\systray.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\wndclass\tabsheet.h">
+ <Filter>Header Files\Wasabi\api\wnd\wndclass</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\wndclass\tabsheetbar.h">
+ <Filter>Header Files\Wasabi\api\wnd\wndclass</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\skin\widgets\text.h">
+ <Filter>Header Files\Wasabi\api\skin\widgets</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\skin\widgets\textbase.h">
+ <Filter>Header Files\Wasabi\api\skin\widgets</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\skin\feeds\textfeed.h">
+ <Filter>Header Files\Wasabi\api\skin\feeds</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\script\objects\timer.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\timer\timerclient.h">
+ <Filter>Header Files\Wasabi\api\timer</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\timer\timermul.h">
+ <Filter>Header Files\Wasabi\api\timer</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\timer\tmultiplex.h">
+ <Filter>Header Files\Wasabi\api\timer</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\wndclass\treewnd.h">
+ <Filter>Header Files\Wasabi\api\wnd\wndclass</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\font\win32\truetypefont_win32.h">
+ <Filter>Header Files\Wasabi\api\font\win32</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\config\uioptions.h">
+ <Filter>Header Files\Wasabi\api\config</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\util\varmgr.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\script\vcpu.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\wndclass\virtualhostwnd.h">
+ <Filter>Header Files\Wasabi\api\wnd\wndclass</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\service\waservicefactoryt.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\skin\widgets.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\syscb\callbacks\wndcb.h">
+ <Filter>Header Files\Wasabi\api\syscb\callbacks</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\wndclass\wndholder.h">
+ <Filter>Header Files\Wasabi\api\wnd\wndclass</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\wndmgr\wndmgrapi.h">
+ <Filter>Header Files\Wasabi\api\wndmgr</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\xml\XMLAutoInclude.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\xml\xmlparams.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\xml\xmlparamsi.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\xml\xmlparamsx.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\xml\xmlreader.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\xml\xmlwrite.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\skin\widgets\xuicustomobject.h">
+ <Filter>Header Files\Wasabi\api\skin\widgets</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\skin\widgets\xuiframe.h">
+ <Filter>Header Files\Wasabi\api\skin\widgets</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\skin\widgets\xuilist.h">
+ <Filter>Header Files\Wasabi\api\skin\widgets</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\skin\widgets\xuimenu.h">
+ <Filter>Header Files\Wasabi\api\skin\widgets</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\skin\widgets\xuithemeslist.h">
+ <Filter>Header Files\Wasabi\api\skin\widgets</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\skin\widgets\xuitree.h">
+ <Filter>Header Files\Wasabi\api\skin\widgets</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\skin\widgets\wa2\xuiwa2slider.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\keyboard.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\minibrowser.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\paintcb.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\popexitchecker.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\popup.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\resizable.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\rootwnd.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\virtualwnd.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\wndapi.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\wndtrack.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\config\items\intarray.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\locales\localesmgr.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="api__gen_ff.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\skin\api_colorthemes.h">
+ <Filter>Header Files\Wasabi\api\skin</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\core\api_core.h">
+ <Filter>Header Files\Wasabi\api\core</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\dependency\api_dependent.h">
+ <Filter>Header Files\Wasabi\api\dependency</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\dependency\api_dependentviewer.h">
+ <Filter>Header Files\Wasabi\api\dependency</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\filereader\api_filereader.h">
+ <Filter>Header Files\Wasabi\api\filereader</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\imgldr\api_imgldr.h">
+ <Filter>Header Files\Wasabi\api\imgldr</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\locales\api_locales.h">
+ <Filter>Header Files\Wasabi\api\locales</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\locales\api_localesx.h">
+ <Filter>Header Files\Wasabi\api\locales</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\locales\api_localesi.h">
+ <Filter>Header Files\Wasabi\api\locales</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\script\api_maki.h">
+ <Filter>Header Files\Wasabi\api\script</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\script\api_makii.h">
+ <Filter>Header Files\Wasabi\api\script</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\script\api_makix.h">
+ <Filter>Header Files\Wasabi\api\script</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\skin\api_palette.h">
+ <Filter>Header Files\Wasabi\api\skin</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\skin\api_skin.h">
+ <Filter>Header Files\Wasabi\api\skin</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\api_window.h">
+ <Filter>Header Files\Wasabi\api\wnd</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\api_wnd.h">
+ <Filter>Header Files\Wasabi\api\wnd</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\apiinit.h">
+ <Filter>Header Files\Wasabi\api</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\config\items\attribute.h">
+ <Filter>Header Files\Wasabi\api\config\items</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\config\items\attrstr.h">
+ <Filter>Header Files\Wasabi\api\config\items</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\basewnd.h">
+ <Filter>Header Files\Wasabi\api\wnd</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\platform\win32\bitmap.h">
+ <Filter>Header Files\Wasabi\api\wnd\platform\win32</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\config\items\cfgitem.h">
+ <Filter>Header Files\Wasabi\api\config\items</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\config\items\cfgitemi.h">
+ <Filter>Header Files\Wasabi\api\config\items</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\config\items\cfgitemx.h">
+ <Filter>Header Files\Wasabi\api\config\items</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\wac\compon.h">
+ <Filter>Header Files\Wasabi\api\wac</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\contextmenu.h">
+ <Filter>Header Files\Wasabi\api\wnd</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\core\coreactions.h">
+ <Filter>Header Files\Wasabi\api\core</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\script\objects\core\coreadminobj.h">
+ <Filter>Header Files\Wasabi\api\script\objects\core</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\script\objects\core\coreobj.h">
+ <Filter>Header Files\Wasabi\api\script\objects\core</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\cursor.h">
+ <Filter>Header Files\Wasabi\api\wnd</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\skin\cursormgr.h">
+ <Filter>Header Files\Wasabi\api\skin</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\deactivatemgr.h">
+ <Filter>Header Files\Wasabi\api\wnd</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\script\debugger\debugsymbols.h">
+ <Filter>Header Files\Wasabi\api\script\debugger</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\bfc\depend.h">
+ <Filter>Header Files\Wasabi\bfc</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\bfc\depview.h">
+ <Filter>Header Files\Wasabi\bfc</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\drag.h">
+ <Filter>Header Files\Wasabi\api\wnd</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\dragitemi.h">
+ <Filter>Header Files\Wasabi\api\wnd</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\dragitem.h">
+ <Filter>Header Files\Wasabi\api\wnd</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\filereader\local\fileread.h">
+ <Filter>Header Files\Wasabi\api\filereader\local</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\wnd\findobjectcb.h">
+ <Filter>Header Files\Wasabi\api\wnd</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\bfc\util\findopenrect.h">
+ <Filter>Header Files\Wasabi\bfc\util</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\skin\gammamgr.h">
+ <Filter>Header Files\Wasabi\api\skin</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\imgldr\imggen\grad.h">
+ <Filter>Header Files\Wasabi\api\imgldr\imggen</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\skin\group.h">
+ <Filter>Header Files\Wasabi\api\skin</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\skin\groupmgr.h">
+ <Filter>Header Files\Wasabi\api\skin</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\skin\groupwndcreate.h">
+ <Filter>Header Files\Wasabi\api\skin</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\script\objects\guiobj.h">
+ <Filter>Header Files\Wasabi\api\script\objects</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\script\objects\guiobjectx.h">
+ <Filter>Header Files\Wasabi\api\script\objects</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\script\objects\guiobject.h">
+ <Filter>Header Files\Wasabi\api\script\objects</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\skin\guitree.h">
+ <Filter>Header Files\Wasabi\api\skin</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Wasabi\api\skin\widgets\mb\iebrowser.h">
+ <Filter>Header Files\Wasabi\api\skin\widgets\mb</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <CustomBuild Include="..\gen_ml\ml.h">
+ <Filter>Header Files</Filter>
+ </CustomBuild>
+ <CustomBuild Include="..\..\..\Winamp\wa_ipc.h">
+ <Filter>Header Files</Filter>
+ </CustomBuild>
+ <CustomBuild Include="..\..\..\Winamp\FRONTEND.H">
+ <Filter>Header Files\Winamp</Filter>
+ </CustomBuild>
+ </ItemGroup>
+ <ItemGroup>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{bf65d618-5e45-4f2a-8125-af87835711e2}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Ressource Files">
+ <UniqueIdentifier>{bbaa79ab-c1cc-4e73-b671-e9532c2313cf}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{076c0d6d-9764-48af-83ce-e1e298abadcc}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\Wasabi">
+ <UniqueIdentifier>{3ddc66e2-c10a-44eb-846f-dde2fd579af9}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\Wasabi\api">
+ <UniqueIdentifier>{ed9da2cf-84b0-4883-8183-5756443f1d45}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\Wasabi\api\config">
+ <UniqueIdentifier>{428efb3d-499b-4538-b225-59995d0a4ad8}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\Wasabi\api\skin">
+ <UniqueIdentifier>{711418c8-1bf4-487d-8e5e-55bc545352a3}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\Wasabi\api\skin\widgets">
+ <UniqueIdentifier>{a935b145-a8e6-4fb1-b6d3-2a7d252c9a51}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\Wasabi\api\wnd">
+ <UniqueIdentifier>{71c5c868-2d6d-405c-8cb7-76423e1d7c52}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\Wasabi\api\wnd\wndclass">
+ <UniqueIdentifier>{430dcbf9-48d9-4b4a-ab86-d7d4d6a6d60b}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\Wasabi\api\wac">
+ <UniqueIdentifier>{6d228362-30a9-4c00-a03e-2a09d669b992}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\Wasabi\api\script">
+ <UniqueIdentifier>{83e1c33a-cf4b-4207-ac1c-87b8755d4ad7}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\Wasabi\api\script\objects">
+ <UniqueIdentifier>{8520b576-ce2c-469c-86fa-14d0f9763eca}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\Wasabi\api\script\objects\core">
+ <UniqueIdentifier>{f2411e13-0f08-4b12-a167-2f4aafb392a3}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\Wasabi\api\syscb">
+ <UniqueIdentifier>{cf9bb71c-7c6e-4425-b0a4-0f5a534db77f}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\Wasabi\api\syscb\callbacks">
+ <UniqueIdentifier>{d148307d-f103-4179-b419-39c6d223d3b1}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\Wasabi\api\script\debugger">
+ <UniqueIdentifier>{a47af283-0ae4-4855-9719-db7403be60f8}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\Wasabi\bfc">
+ <UniqueIdentifier>{c2af4389-c666-45af-9218-86f2adccdcdb}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\Wasabi\bfc\draw">
+ <UniqueIdentifier>{117e60d7-5011-40d3-b3ec-abdceaa4c72e}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\Wasabi\api\skin\feeds">
+ <UniqueIdentifier>{ebddb0e8-052a-41d9-8506-f61752e88cfa}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\Wasabi\api\filereader">
+ <UniqueIdentifier>{034c19ed-376c-4107-93e5-ba0861c07235}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\Wasabi\api\filereader\local">
+ <UniqueIdentifier>{55d69045-d7bf-4a69-b84a-7538dfe6baed}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\Wasabi\bfc\util">
+ <UniqueIdentifier>{f448e746-ba3c-42bb-a0c1-f7e93508834f}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\Wasabi\api\font">
+ <UniqueIdentifier>{2372ff4c-f72f-43de-a65d-ba9f537b2913}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\Wasabi\api\wndmgr">
+ <UniqueIdentifier>{a657e19a-8af2-4f0b-955c-9ccbae2e77d8}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\Wasabi\api\imgldr">
+ <UniqueIdentifier>{1f90661a-bc0a-441b-b342-2d6eedf9fb94}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\Wasabi\api\imgldr\imggen">
+ <UniqueIdentifier>{0ae62a5f-d0c5-4ff6-89ca-ab170103c2bc}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\Wasabi\api\script\objects\c_script">
+ <UniqueIdentifier>{5ab5bf42-9891-4d5f-a9ff-9ece8a4270c2}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\nu">
+ <UniqueIdentifier>{3d2e3e56-48d8-430f-94de-9b8a57dfe4c9}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\Wasabi\api\skin\widgets\mb">
+ <UniqueIdentifier>{93fe5f86-d0f9-4c43-8e81-cddae4227244}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\Wasabi\api\application">
+ <UniqueIdentifier>{584eed9a-47e5-4c37-8bca-a95d75c29f62}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\Wasabi\api\config\items">
+ <UniqueIdentifier>{bffcb066-b578-490f-aaf9-fbf8aef6d4c4}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\Wasabi\api\xml">
+ <UniqueIdentifier>{dd59272c-3349-40ef-b44b-7347bbf4392d}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\Wasabi\api\locales">
+ <UniqueIdentifier>{15a4a353-d120-484d-a13a-6ba08eb013b8}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\Wasabi\api\util">
+ <UniqueIdentifier>{c56b1f21-e4b6-4135-b990-c5530dd83d03}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\Wasabi\api\service">
+ <UniqueIdentifier>{b9f8ab37-1686-49b1-b338-ba9dd90c6886}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\gen_ml">
+ <UniqueIdentifier>{339b1b9e-0d18-4483-a452-2d7dac4e9774}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\Wasabi\api\skin\widgets\stats">
+ <UniqueIdentifier>{c9c62206-3924-43e8-b900-a52671601a0e}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\Wasabi\api\service\svcs">
+ <UniqueIdentifier>{f7c32081-1c3e-4652-8fdc-999024d3abab}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\Wasabi\api\timer">
+ <UniqueIdentifier>{92bac19f-25cb-4864-ad12-9454ca805370}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\Wasabi\api\font\win32">
+ <UniqueIdentifier>{bfa00cac-2671-4338-a525-dc2a7a3cb835}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\Wasabi\api\skin\widgets\wa2">
+ <UniqueIdentifier>{e4c9b2bb-dd6a-4bc2-ad09-ca5df8a0f6b8}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\Wasabi\api\core">
+ <UniqueIdentifier>{5b8e1124-eb86-4fdb-80b1-e62a9038d935}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files\Wasabi\api\dependency">
+ <UniqueIdentifier>{d3a01f56-464b-4976-813a-80569bcdac9b}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\Wasabi">
+ <UniqueIdentifier>{e8d5e893-f323-4825-991f-51c02ed78ecd}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\Wasabi\api">
+ <UniqueIdentifier>{e0ab39aa-1910-4819-a602-0fdd17d66456}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\Wasabi\api\wnd">
+ <UniqueIdentifier>{6ef901fe-7701-4453-b9c4-d7472c0de173}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\Wasabi\api\skin">
+ <UniqueIdentifier>{ff1b723c-3fc3-46c9-8cef-2cc3425816c7}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\Wasabi\api\skin\widgets">
+ <UniqueIdentifier>{a415d0c3-a60e-464c-81cc-706cb07d84d9}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\Wasabi\api\skin\widgets\wa2">
+ <UniqueIdentifier>{2a37a25a-a795-41d6-b223-ab458cea036f}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\Wasabi\api\xml">
+ <UniqueIdentifier>{29e11ac1-6f08-4aaa-a395-e3848eb70331}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\Wasabi\api\wnd\wndclass">
+ <UniqueIdentifier>{5e435b28-0443-4721-8b76-88aa15f8328f}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\Wasabi\api\syscb">
+ <UniqueIdentifier>{1e142ae9-c0c9-48cc-9c21-ff3aa423aa04}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\Wasabi\api\syscb\callbacks">
+ <UniqueIdentifier>{39f17d69-60a7-4065-b4a4-7c29811564e7}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\Wasabi\api\service">
+ <UniqueIdentifier>{8d268021-ee46-4bee-9f81-7c6abfc875a4}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\Wasabi\api\script">
+ <UniqueIdentifier>{e86e6bdd-0733-456f-9887-0255316e7467}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\Wasabi\api\util">
+ <UniqueIdentifier>{781a8fb4-cac7-4473-a69c-1756a64cfd49}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\Wasabi\api\config">
+ <UniqueIdentifier>{c861b738-eb90-48e4-968a-772d679f3cc6}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\Wasabi\api\font">
+ <UniqueIdentifier>{b0ee89d7-6347-4058-8480-871ff274503e}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\Wasabi\api\font\win32">
+ <UniqueIdentifier>{aee2537a-d56d-463e-9f3d-0cb76daf7c94}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\Wasabi\api\timer">
+ <UniqueIdentifier>{763bb103-bdcd-4a05-be99-8424a41baedc}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\Wasabi\api\skin\feeds">
+ <UniqueIdentifier>{590fffe6-e7cb-4003-881e-cb8e3de02a0e}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\Wasabi\api\script\objects">
+ <UniqueIdentifier>{94de1687-3b46-432c-b0db-2f3d91b37c79}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\Wasabi\api\service\svcs">
+ <UniqueIdentifier>{24e3748f-b780-4f9b-b819-c3d885bd2794}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\Wasabi\api\filereader">
+ <UniqueIdentifier>{aec2a760-6241-4542-9d9d-fd2c87cb86bf}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\Wasabi\api\wndmgr">
+ <UniqueIdentifier>{76ae14a7-78a1-4352-873a-7ec4c1673336}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\Wasabi\api\script\debugger">
+ <UniqueIdentifier>{13b72f8c-f0f6-4cdc-a8ae-a9dfee4b3b84}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\Wasabi\api\script\objects\c_script">
+ <UniqueIdentifier>{8d9f0dc3-353d-4ea1-999b-d0c58e5d8237}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\Wasabi\api\core">
+ <UniqueIdentifier>{44c64431-48ef-4126-865d-e7eedbe2d502}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\Wasabi\api\dependency">
+ <UniqueIdentifier>{7ecf8a6f-c16e-4ab5-a085-15df6e952981}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\Wasabi\api\imgldr">
+ <UniqueIdentifier>{94eb27c8-6d80-4b08-8f0e-36c68aa47651}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\Wasabi\api\locales">
+ <UniqueIdentifier>{6bbae227-6fd3-40d9-8e81-64146c137948}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\Wasabi\api\config\items">
+ <UniqueIdentifier>{56124eba-f9ec-4c7b-ab97-d0d84b0e32ed}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\Wasabi\api\wnd\platform">
+ <UniqueIdentifier>{6f852919-472d-458b-8ac5-5f8d41657372}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\Wasabi\api\wnd\platform\win32">
+ <UniqueIdentifier>{40ebbca9-2cf3-41fe-bf4b-7265a5d488ca}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\Wasabi\api\wac">
+ <UniqueIdentifier>{6b36cef2-2356-41af-b817-f508f96d78d5}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\Wasabi\api\script\objects\core">
+ <UniqueIdentifier>{0291d685-9a9b-485b-9aae-744d4afbde70}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\Wasabi\bfc">
+ <UniqueIdentifier>{1edcd4ab-1ef7-48fb-a49c-35b3a9a3be5c}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\Wasabi\api\filereader\local">
+ <UniqueIdentifier>{a80e512f-2eda-4ae3-978f-9030b568f8da}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\Wasabi\bfc\util">
+ <UniqueIdentifier>{19b8ce8d-6a1f-4eca-8e27-4e84a9be5014}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\Winamp">
+ <UniqueIdentifier>{fb9b1379-7d7c-4066-a819-80a2e55dd0d6}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\Wasabi\api\imgldr\imggen">
+ <UniqueIdentifier>{a9dc66ed-6c31-4fae-a36c-9743ba4a33a3}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files\Wasabi\api\skin\widgets\mb">
+ <UniqueIdentifier>{70d07248-75c9-493f-b244-fe2e839114d8}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="gen_ff.rc">
+ <Filter>Ressource Files</Filter>
+ </ResourceCompile>
+ </ItemGroup>
+</Project> \ 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 <windows.h>
+#include <commctrl.h>
+
+#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 <api/wnd/wndclass/foreignwnd.h>
+#include "../winamp/wa_ipc.h"
+#include "../gen_hotkeys/wa_hotkeys.h"
+#include <api/script/objects/c_script/c_group.h>
+#include <api/script/objects/c_script/c_text.h>
+
+#include <api/apiinit.h>
+#include <api/wnd/rootwnd.h>
+#include <api/script/objects/guiobject.h>
+#include <api/core/coreactions.h>
+#include "menuactions.h"
+#include <api/wnd/wndclass/oswndhost.h>
+#include <api/script/scriptobj.h>
+#include <api/service/svcs/svc_wndcreate.h>
+#include <bfc/reentryfilter.h>
+#include <api/skin/skinparse.h>
+#include <api/wndmgr/skinembed.h>
+#include <api/skin/widgets/xuiwndholder.h>
+#include <api/skin/widgets/text.h>
+#include <api/script/scriptmgr.h>
+#include <api/wac/compon.h>
+#include <api/application/version.h>
+#include <bfc/parse/pathparse.h>
+#include <tataki/blending/blending.h>
+#include "skininfo.h"
+#include <api/skin/guitree.h>
+
+#include <bfc/wasabi_std_wnd.h>
+
+#include "wa2core.h"
+#include <api/locales/xlatstr.h>
+
+#include <api/wndmgr/gc.h>
+#include <api/syscb/callbacks/gccb.h>
+#include <api/script/vcpu.h>
+#include <tataki/canvas/bltcanvas.h>
+
+#include "../nu/AutoWide.h"
+#include <shlwapi.h>
+#include <windowsx.h>
+//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<HWND> 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<Layout *>(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<ifc_window*>(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<Text*>(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;c<SkinParser::getNumContainers();c++) {
+ Container *cont = SkinParser::enumContainer(c);
+ if (cont && wantContainerInMenu(cont)) {
+ if (cont->getName() == NULL) continue;
+ if (id == 0) {
+ if (cont)
+ cont->toggle();
+ return 1;
+ }
+ id--;
+ }
+ }
+ int n = WASABI_API_WNDMGR->autopopup_getNumGuids();
+ for (c=0;c<n;c++) {
+ GUID guid = WASABI_API_WNDMGR->autopopup_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;c<n;c++) {
+ const char *gid = WASABI_API_WNDMGR->autopopup_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 <class api_T>
+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<api_T *>( factory->getInterface() );
+ }
+}
+
+template <class api_T>
+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;i<maincontainer->getNumLayouts();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<api_language*>(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<Layout*>(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<StringW, StringWComparator> 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<wchar_t *>(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<wchar_t *>(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<wchar_t *>(groupdesc);
+ // allow localisation of the color editor menu item
+ i.dwTypeData = const_cast<wchar_t *>(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<Layout*>(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<Layout*>(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<Layout*>(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<Layout *>(rootparent);
+ if (l != NULL)
+ oldalpha = static_cast<double>(l->getAlpha());
+ }
+ else
+ {
+ oldalpha = static_cast<double>(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<Layout*>(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<Layout*>(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 <api/skin/skinparse.h>
+#include "wa2cfgitems.h"
+#include "main.h"
+#include "resource.h"
+#include <api/locales/xlatstr.h>
+#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<HMENU> menulist;
+TList<HMENU> 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<ColorThemeSlot, ColorThemeSlotSort> 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<wchar_t *>(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 <api/service/svcs/svc_action.h>
+
+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 <ocidl.h>
+#include <api/skin/widgets/mb/iebrowser.h>
+
+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 <wasabicfg.h>
+//#include <api.h>
+#include <api__gen_ff.h>
+#include <bfc/platform/platform.h>
+#include <bfc/assert.h>
+#include <bfc/common.h>
+#include <bfc/wasabi_std.h>
+#include <bfc/ptrlist.h>
+#include <bfc/stack.h>
+#include <bfc/tlist.h>
+#include <bfc/string/bfcstring.h>
+#include <bfc/string/StringW.h>
+#include <bfc/wasabi_std_wnd.h>
+#include <bfc/std_string.h>
+#include <bfc/dispatch.h>
+#include <bfc/nsGUID.h>
+#include <api/service/servicei.h>
+
+#include <api/service/waservicefactory.h>
+
+#include <api/config/cfgscriptobj.h>
+
+#include <api/wnd/rootwnd.h>
+#include <api/wnd/basewnd.h>
+#include <api/wnd/wndclass/guiobjwnd.h>
+
+#include <api/wnd/wndclass/guiobjwnd.h>
+#include <api/script/objects/guiobject.h>
+
+#include <api/script/scriptobj.h>
+#include <api/script/objcontroller.h>
+#include <api/script/scriptvar.h>
+
+#include "wa2core.h"
+#include "wa2frontend.h"
+#include "wa2wndembed.h"
+
+#include <tataki/canvas/canvas.h>
+#include <tataki/region/region.h>
+#include <tataki/bitmap/bitmap.h>
+
+#include <api/skin/skin.h>
+#include <api/skin/skinparse.h>
+#include <api/skin/widgets.h>
+
+#include <api/timer/timerclient.h>
+
+#include <api/skin/widgets.h>
+
+
+
+#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 <commctrl.h>
+#include <windowsx.h>
+
+void turnonoff(HWND wnd, int *t, int n, int v) {
+ for (int i=0;i<n;i++) {
+ EnableWindow(GetDlgItem(wnd, t[i]), v);
+ }
+}
+
+extern void initFFApi();
+extern Wa2CfgItems *cfgitems;
+extern HINSTANCE hInstance;
+
+_int last_page(L"Last Page", 0);
+Wa2FFOptions *ffoptions = NULL;
+HWND subWnd = NULL, tabwnd = NULL;
+int subWndId = -1;
+extern int m_are_we_loaded;
+int toggle_from_wa2 = 0;
+
+void _dosetsel(HWND hwndDlg)
+{
+ tabwnd = GetDlgItem(hwndDlg,IDC_TAB1);
+ int sel=TabCtrl_GetCurSel(tabwnd);
+
+ if (sel >= 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 <api/config/items/cfgitemi.h>
+
+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<HMENU> 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 <bfc/wasabi_std_wnd.h>
+#include "../Agave/Language/api_language.h"
+#include <api/skin/skinparse.h>
+#include <commctrl.h>
+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 <windowsx.h>
+
+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 <commctrl.h>
+#include <windows.h>
+#include <windowsx.h>
+#include "resource.h"
+#include <api/font/font.h>
+#include <bfc/parse/paramparser.h>
+#include "../nu/ListView.h"
+#include "prefs.h"
+#include "gen.h"
+#include "wa2cfgitems.h"
+#include <api/font/win32/truetypefont_win32.h>
+#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<font_entry, font_entry_comparator1> fontlist;
+PtrListQuickSorted<font_entry, font_entry_comparator2> fontlist_byfilename;
+
+PtrListQuickSorted<font_entry, font_entry_comparator1> skin_fontlist;
+PtrListQuickSorted<font_entry, font_entry_comparator2> 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<StringW, StringWComparator> global_list;
+ PtrListQuickSorted<StringW, StringWComparator> 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 <commctrl.h>
+#include "wa2cfgitems.h"
+#include "gen.h"
+#include "prefs.h"
+#include "../Agave/Language/api_language.h"
+#include <api/skin/skinparse.h>
+
+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 <precomp.h>
+
+// 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 <precomp.h>
+#include "skininfo.h"
+#include "../xml/obj_xml.h"
+#include <api/xml/XMLAutoInclude.h>
+#include <api/xml/LoadXML.h>
+#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 <bfc/string/StringW.h>
+
+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 <precomp.h>
+#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<ButtonWnd> (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 <api/wnd/bucketitem.h>
+
+#define WA2BUCKETITEM_PARENT Wa2BucketItem
+
+//-------------------------------------------------------------------------------------------
+class Wa2BucketItem : public BucketItemT<ButtonWnd> {
+ 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 <precomp.h>
+#include "wa2cfgitems.h"
+#include "wa2wndembed.h"
+#include "wa2core.h"
+#include <api/config/options.h>
+#include "wa2frontend.h"
+#include "../../Plugins/Output/out_ds/ds_ipc.h"
+#include <bfc/parse/pathparse.h>
+#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 <api/config/items/cfgitemi.h>
+#include <api/config/items/attribs.h>
+#include <api/config/items/attrcb.h>
+
+//---------------------------------------------------------
+// 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 <precomp.h>
+#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 <shlwapi.h>
+
+
+
+// {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 <bfc/string/StringW.h>
+//#include "../studio/bfc/timerclient.h"
+#include <api/core/buttons.h>
+#include <api/syscb/callbacks/corecbi.h>
+#include <api/core/api_core.h>
+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<CoreCallback> 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 <precomp.h>
+#include "wa2frontend.h"
+#include "wa2core.h"
+#include "wa2coreactions.h"
+#include "main.h"
+#include <api/core/api_core.h>
+#include <api/locales/xlatstr.h>
+
+//-----------------------------------------------------------------------------------------------
+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 <api/core/coreactions.h>
+
+#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 <precomp.h>
+/*------------------------------------------------------------------------------------------------
+ Winamp 2.9/5 frontend class
+------------------------------------------------------------------------------------------------*/
+#include <stdio.h>
+#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<intptr_t>(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 <windows.h>
+#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 <precomp.h>
+#include "wa2groupdefs.h"
+#include <bfc/string/StringW.h>
+
+//-----------------------------------------------------------------------------------------------
+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"<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"yes\"?>"
+ L"<WinampAbstractionLayer version=\"0.8\">"
+
+ L"<groupdef id=\"library.content.group\">"
+ L" <windowholder hold=\"{6B0EDF80-C9A5-11d3-9F26-00C04F39FFC6}\" fitparent=\"1\" />"
+ L"</groupdef>";
+
+ // footer
+
+ s += L"</WinampAbstractionLayer>";
+
+ 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 <api/syscb/callbacks/skincb.h>
+
+//-----------------------------------------------------------------------------------------------
+
+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 <precomp.h>
+#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 <precomp.h>
+#include "main.h"
+#include "wa2pldirobj.h"
+#include "wa2frontend.h"
+
+#include <bfc/util/timefmt.h>
+#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<PlDirXuiSvc>);
+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<PlDirObject *>(this));
+ getScriptObject()->vcpu_setClassName(L"PlDir");
+ getScriptObject()->vcpu_setController(pldirController);
+
+ _pldirController.mylist.addItem(this);
+}
+
+PlDirObject::~PlDirObject()
+{
+ int numItems = getItemCount();
+
+ for (int i=0;i<numItems;i++)
+ {
+ GUID *playlist_guid = (GUID *)getItemData(i);
+ if (playlist_guid)
+ delete playlist_guid;
+ }
+
+ if (WASABI_API_SYSCB) WASABI_API_SYSCB->syscb_deregisterCallback(static_cast<PlaylistCallbackI *>(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;i<numItems;i++)
+ {
+ GUID *playlist_guid = (GUID *)getItemData(i);
+ if (playlist_guid)
+ delete playlist_guid;
+ }
+
+ deleteAllItems();
+ if (AGAVE_API_PLAYLISTS)
+ {
+ AGAVE_API_PLAYLISTS->Lock();
+ 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<PlaylistCallbackI *>(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<Wa2PlaylistEditor *>(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<PlDirObject *>(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 <api/script/objects/rootobj.h>
+#include <api/script/objcontroller.h>
+#include <api/script/scriptobj.h>
+#include <bfc/depend.h>
+#include <api/service/svcs/svc_scriptobji.h>
+#include <api/wnd/wndclass/listwnd.h>
+#include "wa2playlist.h"
+#include <api/syscb/callbacks/playlistcb.h>
+#include <api/service/svcs/svc_scriptobji.h>
+
+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<Wa2Playlist> 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<PlDirObject> mylist;
+
+ private:
+ static function_descriptor_struct exportedFunction[];
+};
+
+extern const wchar_t plDirXuiObjectStr[];
+extern char plDirXuiSvcName[];
+class PlDirXuiSvc : public XuiObjectSvc<PlDirObject, plDirXuiObjectStr, plDirXuiSvcName> {};
+
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 <precomp.h>
+#include "wa2pledit.h"
+#include <api/script/objects/PlaylistScriptObject.h>
+#include <tataki/color/skinclr.h>
+#include "wa2frontend.h"
+#include <tataki/canvas/bltcanvas.h>
+#include "../nu/AutoWide.h"
+#ifndef _WASABIRUNTIME
+
+BEGIN_SERVICES(Wa2Pledit_Svc);
+DECLARE_SERVICE(XuiObjectCreator<Wa2PleditXuiSvc>);
+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> 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;i<n;i++)
+ {
+ fileinfo2 *fi = wa2.PE_getFileTitle(i);
+ addItem(StringPrintfW(L"%d.", i+1), i+1);
+ setSubItem(i, 1, AutoWide(fi->filetitle));
+ 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 <api/wnd/wndclass/listwnd.h>
+#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<Wa2PlaylistEditor> editors;
+ int cur_index;
+};
+
+// -----------------------------------------------------------------------
+extern const wchar_t Wa2PleditXuiObjectStr[];
+extern char Wa2PleditXuiSvcName[];
+
+class Wa2PleditXuiSvc : public XuiObjectSvc<Wa2PlaylistEditor, Wa2PleditXuiObjectStr, Wa2PleditXuiSvcName> {};
+
+
+#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 <precomp.h>
+#include "wa2songticker.h"
+#include <api.h>
+#include <tataki/color/skinclr.h>
+#include <api/core/api_core.h>
+#include <api/application/api_application.h>
+#include <wasabicfg.h>
+#include "wa2frontend.h"
+#include <api/skin/skinelem.h>
+#include <api/skin/skinparse.h>
+#include <api/config/items/attribs.h>
+#include <api/config/api_config.h>
+
+// {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<SongTickerXuiSvc>);
+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 <api/wnd/wndclass/bufferpaintwnd.h>
+#include <api/skin/widgets/textbase.h>
+#include <api/syscb/callbacks/corecbi.h>
+#include <tataki/color/skinclr.h>
+#include <bfc/depend.h>
+
+#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<SongTicker, songtickerXuiObjectStr, songtickerXuiSvcName> {};
+
+#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 <precomp.h>
+#include "wa2wndembed.h"
+#include "wa2frontend.h"
+#include "wa2buckitems.h"
+#include "embedwndguid.h"
+#include "main.h"
+#include <api/wnd/bucketitem.h>
+#include "resource.h"
+#include <api/wnd/wndclass/wndholder.h>
+#include <api/wndmgr/layout.h>
+#include "wa2cfgitems.h"
+#include "gen.h"
+#include "../Agave/Language/api_language.h"
+
+extern TList<HWND> 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<WindowHolder*>(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<WindowHolder*>(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<WndCallbackI*>(this));
+}
+
+//-----------------------------------------------------------------------------------------------
+Wa2WndEmbed::~Wa2WndEmbed()
+{
+ WASABI_API_SYSCB->syscb_deregisterCallback(static_cast<WndCallbackI*>(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<Layout *>(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<WindowHolder *>(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<Layout *>(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<WindowHolder *>(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<WindowHolder *>(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<Layout*>(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<WndStatus> 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 <api/service/svcs/svc_wndcreate.h>
+#include <bfc/depview.h>
+#include <bfc/reentryfilter.h>
+#include <api/wndmgr/appcmds.h>
+#include <api/skin/widgets/xuioswndhost.h>
+#include <api/script/objects/c_script/h_layout.h>
+#include <api/syscb/callbacks/wndcb.h>
+#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 <container primarycontent="guid:main"/>
+
+
+//-----------------------------------------------------------------------------------------------
+// {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<ifc_window>, 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<Wa2BucketItem> bucketitems;
+ PtrList<EmbedEntry> wndhosts;
+ PtrList<PlDirObject> pldirs;
+ static PtrList<WndStatus> 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 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="7.10"
+ Name="wasabi"
+ ProjectGUID="{CE8C15F0-959F-43F8-A195-F24B79144D40}"
+ SccProjectName=""
+ SccLocalPath="">
+ <Platforms>
+ <Platform
+ Name="Win32"/>
+ </Platforms>
+ <Configurations>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="4"
+ UseOfMFC="0"
+ UseOfATL="0"
+ ATLMinimizesCRunTimeLibraryUsage="FALSE"
+ CharacterSet="2"
+ WholeProgramOptimization="FALSE">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ GlobalOptimizations="TRUE"
+ InlineFunctionExpansion="0"
+ EnableIntrinsicFunctions="FALSE"
+ FavorSizeOrSpeed="2"
+ OmitFramePointers="TRUE"
+ AdditionalIncludeDirectories="../gen_ff;../Wasabi;../Wasabi/api/font/freetype/freetype2/include"
+ PreprocessorDefinitions="WIN32;NDEBUG;_LIB;WINVER=0x400;NOSVCMGR;NOCBMGR"
+ StringPooling="TRUE"
+ ExceptionHandling="FALSE"
+ RuntimeLibrary="2"
+ BufferSecurityCheck="FALSE"
+ EnableFunctionLevelLinking="TRUE"
+ UsePrecompiledHeader="3"
+ PrecompiledHeaderThrough="precomp.h"
+ PrecompiledHeaderFile=".\_build/Release/wasabi.pch"
+ AssemblerListingLocation=".\_build/Release/"
+ ObjectFile=".\_build/Release/"
+ ProgramDataBaseFileName=".\_build/Release/"
+ WarningLevel="3"
+ SuppressStartupBanner="TRUE"
+ DebugInformationFormat="3"
+ CompileAs="0"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile="$(OutDir)/wasabi.lib"
+ SuppressStartupBanner="TRUE"/>
+ <Tool
+ Name="VCMIDLTool"/>
+ <Tool
+ Name="VCPostBuildEventTool"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="1033"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCXMLDataGeneratorTool"/>
+ <Tool
+ Name="VCManagedWrapperGeneratorTool"/>
+ <Tool
+ Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+ </Configuration>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory=".\_build/Debug"
+ IntermediateDirectory=".\_build/Debug"
+ ConfigurationType="4"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="FALSE"
+ CharacterSet="2">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="../gen_ff,../Wasabi,../Wasabi/api/font/freetype/freetype2/include"
+ PreprocessorDefinitions="WIN32;_DEBUG;_LIB;NOSVCMGR;NOCBMGR"
+ MinimalRebuild="TRUE"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="5"
+ BufferSecurityCheck="TRUE"
+ EnableFunctionLevelLinking="TRUE"
+ UsePrecompiledHeader="3"
+ PrecompiledHeaderThrough="precomp.h"
+ PrecompiledHeaderFile=".\_build/Debug/wasabi.pch"
+ AssemblerListingLocation=".\_build/Debug/"
+ ObjectFile=".\_build/Debug/"
+ ProgramDataBaseFileName=".\_build/Debug/"
+ WarningLevel="4"
+ SuppressStartupBanner="TRUE"
+ DebugInformationFormat="4"
+ CompileAs="0"
+ DisableSpecificWarnings="4100"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile=".\_build/Debug\wasabi.lib"
+ SuppressStartupBanner="TRUE"/>
+ <Tool
+ Name="VCMIDLTool"/>
+ <Tool
+ Name="VCPostBuildEventTool"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="_DEBUG"
+ Culture="1033"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCXMLDataGeneratorTool"/>
+ <Tool
+ Name="VCManagedWrapperGeneratorTool"/>
+ <Tool
+ Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+ </Configuration>
+ <Configuration
+ Name="Profiling|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="4"
+ UseOfMFC="0"
+ UseOfATL="0"
+ ATLMinimizesCRunTimeLibraryUsage="FALSE"
+ CharacterSet="2"
+ WholeProgramOptimization="FALSE">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ GlobalOptimizations="TRUE"
+ InlineFunctionExpansion="0"
+ EnableIntrinsicFunctions="FALSE"
+ FavorSizeOrSpeed="2"
+ OmitFramePointers="TRUE"
+ AdditionalIncludeDirectories="../gen_ff;../Wasabi;../Wasabi/api/font/freetype/freetype2/include"
+ PreprocessorDefinitions="WIN32;NDEBUG;_LIB;WINVER=0x400;NOSVCMGR;NOCBMGR"
+ StringPooling="TRUE"
+ ExceptionHandling="FALSE"
+ RuntimeLibrary="2"
+ BufferSecurityCheck="FALSE"
+ EnableFunctionLevelLinking="TRUE"
+ UsePrecompiledHeader="3"
+ PrecompiledHeaderThrough="precomp.h"
+ PrecompiledHeaderFile=".\_build/Release/wasabi.pch"
+ AssemblerListingLocation=""
+ ObjectFile="$(IntDir)/"
+ ProgramDataBaseFileName=".\_build/Release/"
+ WarningLevel="3"
+ SuppressStartupBanner="TRUE"
+ DebugInformationFormat="3"
+ CompileAs="0"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLibrarianTool"
+ OutputFile=".\_build/Release\wasabi.lib"
+ SuppressStartupBanner="TRUE"/>
+ <Tool
+ Name="VCMIDLTool"/>
+ <Tool
+ Name="VCPostBuildEventTool"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="1033"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCXMLDataGeneratorTool"/>
+ <Tool
+ Name="VCManagedWrapperGeneratorTool"/>
+ <Tool
+ Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat">
+ <File
+ RelativePath="precomp.cpp">
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UsePrecompiledHeader="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ BasicRuntimeChecks="3"
+ UsePrecompiledHeader="1"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Profiling|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions=""
+ UsePrecompiledHeader="1"/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\Wasabi\bfc\platform\types.h">
+ </File>
+ <File
+ RelativePath="wasabicfg.h">
+ </File>
+ <Filter
+ Name="debug"
+ Filter="">
+ </Filter>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl">
+ <File
+ RelativePath="..\Wasabi\api\config\cfgscriptobj.h">
+ </File>
+ <File
+ RelativePath="..\Wasabi\api\syscb\callbacks\corecbi.h">
+ </File>
+ <File
+ RelativePath="..\Wasabi\api\script\objects\c_script\h_rootobj.h">
+ </File>
+ <File
+ RelativePath="..\Wasabi\bfc\loadlib.h">
+ </File>
+ <File
+ RelativePath="..\Wasabi\api\skin\widgets\mb\mbsvc.h">
+ </File>
+ <File
+ RelativePath="..\Wasabi\api\wnd\paintset.h">
+ </File>
+ <File
+ RelativePath="..\Wasabi\api\syscb\callbacks\skincb.h">
+ </File>
+ <File
+ RelativePath="StdAfx.h">
+ </File>
+ <File
+ RelativePath="..\Wasabi\api\script\objects\core\svc_scriptcore.h">
+ </File>
+ <File
+ RelativePath="..\Wasabi\bfc\file\tmpnamestr.h">
+ </File>
+ <File
+ RelativePath="..\Wasabi\bfc\file\wildcharsenum.h">
+ </File>
+ </Filter>
+ <File
+ RelativePath="Readme.txt">
+ </File>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
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 <api/application/api_application.h>
+#include <api/service/api_service.h>
+#include <api/syscb/api_syscbi.h>
+
+#ifdef WASABI_COMPILE_MEMMGR
+# include <api/memmgr/api_memmgr.h>
+#endif
+
+#ifdef WASABI_COMPILE_SCRIPT
+# include <api/script/api_maki.h>
+#endif
+
+#ifdef WASABI_COMPILE_FONTS
+# include <api/font/api_font.h>
+#endif
+
+#ifdef WASABI_COMPILE_WND
+# include <api/wnd/api_wnd.h>
+#endif
+
+#ifdef WASABI_COMPILE_IMGLDR
+# include <api/imgldr/api_imgldr.h>
+#endif
+
+#ifdef WASABI_COMPILE_FILEREADER
+# include <api/filereader/api_filereader.h>
+#endif
+
+#ifdef WASABI_COMPILE_TIMERS
+# include <api/timer/api_timer.h>
+#endif
+
+#ifdef WASABI_COMPILE_WNDMGR
+# include <api/wndmgr/api_wndmgr.h>
+#endif
+
+#ifdef WASABI_COMPILE_LOCALES
+# include <api/locales/api_locales.h>
+#endif
+
+#ifdef WASABI_COMPILE_CONFIG
+# include <api/config/api_config.h>
+#endif
+
+# include "../xml/obj_xml.h"
+
+#ifdef WASABI_COMPILE_SKIN
+# include <api/skin/api_skin.h>
+#endif
+
+#ifdef WASABI_COMPILE_MAKIDEBUG
+# include <api/script/debugger/api_makidebug.h>
+#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 <windows.h>
+#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 <windows.h>
+
+#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 <wtypes.h>
+#include <api\application\ifc_messageprocessor.h>
+
+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 <api/application/api_application.h>
+#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 <api/service/waServiceFactory.h>
+
+///////////////////////////////////////////////////////////
+// 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<api_application*>(sf->getInterface());
+
+ sf = WASABI_API_SVC->service_getServiceByGuid(languageApiGUID);
+ if (sf) WASABI_API_LNG = reinterpret_cast<api_language*>(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 <windows.h>
+#include <commctrl.h>
+#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 <strsafe.h>
+
+///////////////////////////////////////////////////////////
+// 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 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{A0E56406-B1C9-4FC8-9589-A640D71A7364}</ProjectGuid>
+ <RootNamespace>gen_hotkeys</RootNamespace>
+ <WindowsTargetPlatformVersion>10.0.19041.0</WindowsTargetPlatformVersion>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC71.props" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ <Import Project="$(VCTargetsPath)Microsoft.CPP.UpgradeFromVC71.props" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <IncludePath>$(IncludePath)</IncludePath>
+ <LibraryPath>$(LibraryPath)</LibraryPath>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <IncludePath>$(IncludePath)</IncludePath>
+ <LibraryPath>$(LibraryPath)</LibraryPath>
+ <EmbedManifest>true</EmbedManifest>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg">
+ <VcpkgEnabled>false</VcpkgEnabled>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <VcpkgConfiguration>Debug</VcpkgConfiguration>
+ <VcpkgTriplet>
+ </VcpkgTriplet>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <VcpkgConfiguration>Debug</VcpkgConfiguration>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>.;..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;WIN32;_DEBUG;_WINDOWS;_USRDLL;HOTAMP3_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <Culture>0x0409</Culture>
+ </ResourceCompile>
+ <Link>
+ <AdditionalDependencies>odbc32.lib;odbccp32.lib;comctl32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <TargetMachine>MachineX86</TargetMachine>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <SubSystem>Windows</SubSystem>
+ <MapFileName>$(IntDir)$(TargetName).map</MapFileName>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\
+xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>.;..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;WIN64;_DEBUG;_WINDOWS;_USRDLL;HOTAMP3_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <Culture>0x0409</Culture>
+ </ResourceCompile>
+ <Link>
+ <AdditionalDependencies>odbc32.lib;odbccp32.lib;comctl32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <SubSystem>Windows</SubSystem>
+ <MapFileName>$(IntDir)$(TargetName).map</MapFileName>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\
+xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <Optimization>MinSpace</Optimization>
+ <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>.;..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;WIN32;NDEBUG;_WINDOWS;_USRDLL;HOTAMP3_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>None</DebugInformationFormat>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <Culture>0x0409</Culture>
+ </ResourceCompile>
+ <Link>
+ <AdditionalDependencies>odbc32.lib;odbccp32.lib;comctl32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <MapFileName>$(IntDir)$(TargetName).map</MapFileName>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <TargetMachine>MachineX86</TargetMachine>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <SubSystem>Windows</SubSystem>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <Optimization>MinSpace</Optimization>
+ <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>.;..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;WIN64;NDEBUG;_WINDOWS;_USRDLL;HOTAMP3_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>None</DebugInformationFormat>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <Culture>0x0409</Culture>
+ </ResourceCompile>
+ <Link>
+ <AdditionalDependencies>odbc32.lib;odbccp32.lib;comctl32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <MapFileName>$(IntDir)$(TargetName).map</MapFileName>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <SubSystem>Windows</SubSystem>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="accelBlock.cpp" />
+ <ClCompile Include="ConfigDlg.cpp">
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;HOTAMP3_EXPORTS</PreprocessorDefinitions>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;HOTAMP3_EXPORTS</PreprocessorDefinitions>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">WIN64;_DEBUG;_WINDOWS;_MBCS;_USRDLL;HOTAMP3_EXPORTS</PreprocessorDefinitions>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">WIN64;NDEBUG;_WINDOWS;_MBCS;_USRDLL;HOTAMP3_EXPORTS</PreprocessorDefinitions>
+ </ClCompile>
+ <ClCompile Include="gen_hotkeys.cpp">
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;HOTAMP3_EXPORTS</PreprocessorDefinitions>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;HOTAMP3_EXPORTS</PreprocessorDefinitions>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">WIN64;_DEBUG;_WINDOWS;_MBCS;_USRDLL;HOTAMP3_EXPORTS</PreprocessorDefinitions>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">WIN64;NDEBUG;_WINDOWS;_MBCS;_USRDLL;HOTAMP3_EXPORTS</PreprocessorDefinitions>
+ </ClCompile>
+ <ClCompile Include="HotKey.cpp">
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;HOTAMP3_EXPORTS</PreprocessorDefinitions>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;HOTAMP3_EXPORTS</PreprocessorDefinitions>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">WIN64;_DEBUG;_WINDOWS;_MBCS;_USRDLL;HOTAMP3_EXPORTS</PreprocessorDefinitions>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">WIN64;NDEBUG;_WINDOWS;_MBCS;_USRDLL;HOTAMP3_EXPORTS</PreprocessorDefinitions>
+ </ClCompile>
+ <ClCompile Include="HotKeyCtl.cpp">
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;HOTAMP3_EXPORTS</PreprocessorDefinitions>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;HOTAMP3_EXPORTS</PreprocessorDefinitions>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">WIN64;_DEBUG;_WINDOWS;_MBCS;_USRDLL;HOTAMP3_EXPORTS</PreprocessorDefinitions>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">WIN64;NDEBUG;_WINDOWS;_MBCS;_USRDLL;HOTAMP3_EXPORTS</PreprocessorDefinitions>
+ </ClCompile>
+ <ClCompile Include="WACommands.cpp">
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;HOTAMP3_EXPORTS</PreprocessorDefinitions>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;HOTAMP3_EXPORTS</PreprocessorDefinitions>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">WIN64;_DEBUG;_WINDOWS;_MBCS;_USRDLL;HOTAMP3_EXPORTS</PreprocessorDefinitions>
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Release|x64'">WIN64;NDEBUG;_WINDOWS;_MBCS;_USRDLL;HOTAMP3_EXPORTS</PreprocessorDefinitions>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="accelBlock.h" />
+ <ClInclude Include="api__gen_hotkeys.h" />
+ <ClInclude Include="ConfigDlg.h" />
+ <ClInclude Include="gen_hotkeys.h" />
+ <ClInclude Include="HotKey.h" />
+ <ClInclude Include="HotKeyCtl.h" />
+ <ClInclude Include="resource.h" />
+ <ClInclude Include="WACommands.h" />
+ <ClInclude Include="wa_hotkeys.h" />
+ <ClInclude Include="winamp.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="gen_hotkeys.rc" />
+ </ItemGroup>
+ <ItemGroup>
+ <Image Include="x.bmp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\..\..\Wasabi\Wasabi.vcxproj">
+ <Project>{3e0bfa8a-b86a-42e9-a33f-ec294f823f7f}</Project>
+ </ProjectReference>
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ 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 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <ClCompile Include="WACommands.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="HotKeyCtl.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="HotKey.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="gen_hotkeys.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ConfigDlg.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="accelBlock.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="winamp.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="WACommands.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="wa_hotkeys.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="resource.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="HotKey.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="gen_hotkeys.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="ConfigDlg.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="api__gen_hotkeys.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="accelBlock.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="HotKeyCtl.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{b429b35f-cbbb-427f-b618-1e7c2ef4fa72}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Ressource Files">
+ <UniqueIdentifier>{d06654b1-494e-468a-9e54-d70525ca2531}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{b03b7dae-2c85-4929-8e46-eb6b06b87635}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Image Files">
+ <UniqueIdentifier>{36cdd73d-755e-40a7-945a-f6cad3b9379c}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <Image Include="x.bmp">
+ <Filter>Image Files</Filter>
+ </Image>
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="gen_hotkeys.rc">
+ <Filter>Ressource Files</Filter>
+ </ResourceCompile>
+ </ItemGroup>
+</Project> \ 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
--- /dev/null
+++ b/Src/Plugins/General/gen_hotkeys/x.bmp
Binary files 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 <time.h>
+#include <winuser.h>
+#include <assert.h>
+#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<ComboSkin*>(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 <ocidl.h>
+
+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 <vector>
+#include "../nu/AutoLock.h"
+
+#include <atlbase.h>
+#include <assert.h>
+
+#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, &params, 0, 0, &ret);
+ else
+ blah->callback->Invoke(0, GUID_NULL, 0, DISPATCH_METHOD, &params, 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, &params, 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, &params, 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, &params, 0, 0, &ret);
+ else
+ blah->callback->Invoke(0, GUID_NULL, 0, DISPATCH_METHOD, &params, 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, &params, 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, &params, 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<MyEventHandler*> 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<IDispatch *>(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 <ocidl.h>
+
+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 <ocidl.h>
+
+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 <ocidl.h>
+
+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 = &rect;
+ 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 <api/wnd/api_wnd.h>
+#include <api/skin/api_skin.h>
+#include <api/skin/api_palette.h>
+#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 <api/application/api_application.h>
+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 <omBrowser\obj_ombrowser.h>
+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 <api/skin/api_skin.h>
+extern api_skin *skinApi;
+#define WASABI_API_SKIN skinApi
+
+#include "../Agave/Config/api_config.h"
+
+#include <api/skin/api_palette.h>
+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 <windows.h>
+
+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 <tataki/color/skinclr.h>
+#include <shlwapi.h>
+#include <math.h>
+
+#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 <windows.h>
+
+//******************************* 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 <windows.h>
+
+//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 <windows.h>
+#include <strsafe.h>
+
+#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 <shlwapi.h>
+#include <strsafe.h>
+
+#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 <windows.h>
+
+#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 <windowsx.h>
+#include <strsafe.h>
+
+#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 <strsafe.h>
+
+
+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 <vector>
+#include "../playlist/svc_playlisthandler.h"
+#include "../playlist/api_playlistmanager.h"
+#include <api/service/waservicefactorybase.h>
+#include <api/service/services.h>
+
+#include <shlwapi.h>
+#include <strsafe.h>
+#include <algorithm>
+
+#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<FILETYPEREC> 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<svc_playlisthandler *>(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 <strsafe.h>
+
+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 <windows.h>
+#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 <windowsx.h>
+#include <strsafe.h>
+
+#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 <vector>
+#include <deque>
+#include "../nu/threadname.h"
+#include <api/service/waServiceFactory.h>
+
+#include "../playlist/api_playlistmanager.h"
+#include "../playlist/ifc_playlistloadercallback.h"
+#include "../Agave/Metadata/api_metadata.h"
+
+#include <shlwapi.h>
+#include <strsafe.h>
+#include <algorithm>
+
+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<PATHID> PATHLIST;
+typedef std::vector<METAINFO*> 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<DISCOVERJOB*> 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 <windowsx.h>
+#include <strsafe.h>
+
+#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 <windows.h>
+#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 <vector>
+#include "../Winamp/wa_dlg.h"
+#include "./skinnedlistbox.h"
+#include "./colors.h"
+
+#include <windowsx.h>
+#include <strsafe.h>
+
+
+
+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<FBCOLUMN> *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<FBCOLUMN>();
+ 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 <windows.h>
+
+#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 <windows.h>
+#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 <windows.h>
+
+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 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{9B212232-4908-49D2-8D6D-96555B1F701E}</ProjectGuid>
+ <RootNamespace>gen_ml</RootNamespace>
+ <WindowsTargetPlatformVersion>10.0.19041.0</WindowsTargetPlatformVersion>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <IncludePath>$(IncludePath)</IncludePath>
+ <LibraryPath>$(LibraryPath)</LibraryPath>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <IncludePath>$(IncludePath)</IncludePath>
+ <LibraryPath>$(LibraryPath)</LibraryPath>
+ <EmbedManifest>true</EmbedManifest>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg">
+ <VcpkgEnableManifest>false</VcpkgEnableManifest>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <VcpkgInstalledDir>
+ </VcpkgInstalledDir>
+ <VcpkgUseStatic>false</VcpkgUseStatic>
+ <VcpkgConfiguration>Debug</VcpkgConfiguration>
+ <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <VcpkgInstalledDir>
+ </VcpkgInstalledDir>
+ <VcpkgUseStatic>false</VcpkgUseStatic>
+ <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <VcpkgInstalledDir>
+ </VcpkgInstalledDir>
+ <VcpkgUseStatic>true</VcpkgUseStatic>
+ <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
+ <VcpkgConfiguration>Debug</VcpkgConfiguration>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <VcpkgInstalledDir>
+ </VcpkgInstalledDir>
+ <VcpkgUseStatic>true</VcpkgUseStatic>
+ <VcpkgTriplet>x86-windows-static-md</VcpkgTriplet>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>.;..;..\..\..;..\..\..\WAT;..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_WINDOWS;_USRDLL;GEN_ML_EXPORTS;_WIN32_IE=0x0A00;IGNORE_API_GRACENOTE;WIN32;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <BasicRuntimeChecks>Default</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
+ <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ <BrowseInformation>true</BrowseInformation>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <DisableSpecificWarnings>4302;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ <StringPooling>false</StringPooling>
+ <FunctionLevelLinking>false</FunctionLevelLinking>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>%(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\"</PreprocessorDefinitions>
+ <Culture>0x0409</Culture>
+ <AdditionalIncludeDirectories>..\..\..\external_dependencies\CEF;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ <Link>
+ <AdditionalDependencies>comctl32.lib;shlwapi.lib;rpcrt4.lib;fmtd.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <DelayLoadDLLs>tataki.dll;%(DelayLoadDLLs)</DelayLoadDLLs>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <SupportUnloadOfDelayLoadedDLL>
+ </SupportUnloadOfDelayLoadedDLL>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <TargetMachine>MachineX86</TargetMachine>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>..\..\..\external_dependencies\vcpkg\buildtrees\fmt\$(PlatformShortName)-windows-dbg;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <SubSystem>Windows</SubSystem>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <IgnoreAllDefaultLibraries>
+ </IgnoreAllDefaultLibraries>
+ <IgnoreSpecificDefaultLibraries>
+ </IgnoreSpecificDefaultLibraries>
+ <StackReserveSize>0x8000</StackReserveSize>
+ <LargeAddressAware>true</LargeAddressAware>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\
+xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>.;..;..\..\..;..\..\..\WAT;..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_WINDOWS;_USRDLL;GEN_ML_EXPORTS;_WIN32_IE=0x0A00;IGNORE_API_GRACENOTE;WIN64;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <BasicRuntimeChecks>Default</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
+ <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ <BrowseInformation>true</BrowseInformation>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <DisableSpecificWarnings>4244;4267;4302;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <StringPooling>true</StringPooling>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <Culture>0x0409</Culture>
+ </ResourceCompile>
+ <Link>
+ <AdditionalDependencies>comctl32.lib;shlwapi.lib;rpcrt4.lib;fmtd.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <DelayLoadDLLs>tataki.dll;%(DelayLoadDLLs)</DelayLoadDLLs>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <SupportUnloadOfDelayLoadedDLL>
+ </SupportUnloadOfDelayLoadedDLL>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>..\..\..\external_dependencies\vcpkg\buildtrees\fmt\$(PlatformShortName)-windows-dbg;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <SubSystem>Windows</SubSystem>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <IgnoreAllDefaultLibraries>
+ </IgnoreAllDefaultLibraries>
+ <IgnoreSpecificDefaultLibraries>
+ </IgnoreSpecificDefaultLibraries>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\
+xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <Optimization>MinSpace</Optimization>
+ <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>.;..;..\..\..;..\..\..\WAT;..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_WINDOWS;_USRDLL;GEN_ML_EXPORTS;_WIN32_IE=0x0A00;IGNORE_API_GRACENOTE;WIN32;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <DisableSpecificWarnings>4244;4267;4302;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>%(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\"</PreprocessorDefinitions>
+ <Culture>0x0409</Culture>
+ <AdditionalIncludeDirectories>..\..\..\external_dependencies\CEF;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ <Link>
+ <AdditionalDependencies>comctl32.lib;shlwapi.lib;rpcrt4.lib;fmt.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <DelayLoadDLLs>tataki.dll;%(DelayLoadDLLs)</DelayLoadDLLs>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <TargetMachine>MachineX86</TargetMachine>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>..\..\..\external_dependencies\vcpkg\buildtrees\fmt\$(PlatformShortName)-windows-rel;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <SubSystem>Windows</SubSystem>
+ <StackReserveSize>0x8000</StackReserveSize>
+ <LargeAddressAware>true</LargeAddressAware>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <Optimization>MinSpace</Optimization>
+ <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>.;..;..\..\..;..\..\..\WAT;..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_WINDOWS;_USRDLL;GEN_ML_EXPORTS;_WIN32_IE=0x0A00;IGNORE_API_GRACENOTE;WIN64;NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>None</DebugInformationFormat>
+ <DisableSpecificWarnings>4244;4267;4302;4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <Culture>0x0409</Culture>
+ </ResourceCompile>
+ <Link>
+ <AdditionalDependencies>comctl32.lib;shlwapi.lib;rpcrt4.lib;fmt.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <DelayLoadDLLs>tataki.dll;%(DelayLoadDLLs)</DelayLoadDLLs>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>..\..\..\external_dependencies\vcpkg\buildtrees\fmt\$(PlatformShortName)-windows-rel;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <SubSystem>Windows</SubSystem>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\..\..\tataki\tataki.vcxproj">
+ <Project>{255b68b5-7ef8-45ef-a675-2d6b88147909}</Project>
+ <CopyLocalSatelliteAssemblies>true</CopyLocalSatelliteAssemblies>
+ <ReferenceOutputAssembly>true</ReferenceOutputAssembly>
+ </ProjectReference>
+ <ProjectReference Include="..\..\..\Wasabi\Wasabi.vcxproj">
+ <Project>{3e0bfa8a-b86a-42e9-a33f-ec294f823f7f}</Project>
+ </ProjectReference>
+ <ProjectReference Include="..\..\..\WAT\WAT.vcxproj">
+ <Project>{c5714908-a71f-4644-bd95-aad8ee7914da}</Project>
+ </ProjectReference>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="..\..\..\nu\CCVersion.cpp" />
+ <ClCompile Include="..\..\..\nu\HTMLContainer2.cpp" />
+ <ClCompile Include="..\..\..\nu\menushortcuts.cpp" />
+ <ClCompile Include="..\..\..\nu\mtbrowser.cpp" />
+ <ClCompile Include="..\..\..\nu\ServiceWatcher.cpp" />
+ <ClCompile Include="..\..\..\nu\trace.cpp" />
+ <ClCompile Include="..\..\..\Winamp\strutil.cpp" />
+ <ClCompile Include="childwnd.cpp">
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_ML_EXPORTS</PreprocessorDefinitions>
+ </ClCompile>
+ <ClCompile Include="colors.cpp" />
+ <ClCompile Include="comboskin.cpp">
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_ML_EXPORTS</PreprocessorDefinitions>
+ </ClCompile>
+ <ClCompile Include="config.cpp">
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_ML_EXPORTS</PreprocessorDefinitions>
+ </ClCompile>
+ <ClCompile Include="fileview.cpp" />
+ <ClCompile Include="fileview_columns.cpp" />
+ <ClCompile Include="fileview_compare.cpp" />
+ <ClCompile Include="fileview_filesystem.cpp" />
+ <ClCompile Include="fileview_format.cpp" />
+ <ClCompile Include="fileview_menu.cpp" />
+ <ClCompile Include="fileview_metadata.cpp" />
+ <ClCompile Include="fileview_toolbar.cpp" />
+ <ClCompile Include="flickerfix.cpp" />
+ <ClCompile Include="folderborwser_listbox.cpp" />
+ <ClCompile Include="folderbrowser.cpp" />
+ <ClCompile Include="gaystring.cpp">
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_ML_EXPORTS</PreprocessorDefinitions>
+ </ClCompile>
+ <ClCompile Include="graphics.cpp" />
+ <ClCompile Include="HeaderIconList.cpp" />
+ <ClCompile Include="imagefilters.cpp" />
+ <ClCompile Include="IPC.cpp" />
+ <ClCompile Include="itemlist.cpp" />
+ <ClCompile Include="main.cpp" />
+ <ClCompile Include="MediaLibraryCOM.cpp">
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_ML_EXPORTS</PreprocessorDefinitions>
+ </ClCompile>
+ <ClCompile Include="mldwm.cpp" />
+ <ClCompile Include="ml_cloud.cpp" />
+ <ClCompile Include="ml_cloudcolumn.cpp" />
+ <ClCompile Include="ml_imagefilter.cpp" />
+ <ClCompile Include="ml_imagelist.cpp" />
+ <ClCompile Include="ml_imageloader.cpp" />
+ <ClCompile Include="ml_lib.cpp">
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_ML_EXPORTS</PreprocessorDefinitions>
+ </ClCompile>
+ <ClCompile Include="ml_rating.cpp" />
+ <ClCompile Include="ml_ratingcolumn.cpp" />
+ <ClCompile Include="MusicID.cpp" />
+ <ClCompile Include="navigation.cpp" />
+ <ClCompile Include="OnlineMediaCOM.cpp">
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_ML_EXPORTS</PreprocessorDefinitions>
+ </ClCompile>
+ <ClCompile Include="plugin.cpp">
+ <BasicRuntimeChecks Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">EnableFastChecks</BasicRuntimeChecks>
+ </ClCompile>
+ <ClCompile Include="prefs.cpp" />
+ <ClCompile Include="RatingsCOM.cpp">
+ <PreprocessorDefinitions Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GEN_ML_EXPORTS</PreprocessorDefinitions>
+ </ClCompile>
+ <ClCompile Include="reflectmsg.cpp" />
+ <ClCompile Include="sendto.cpp" />
+ <ClCompile Include="service.cpp" />
+ <ClCompile Include="setup.cpp" />
+ <ClCompile Include="skinexport.cpp" />
+ <ClCompile Include="skinnedbutton.cpp" />
+ <ClCompile Include="skinnedcombo.cpp" />
+ <ClCompile Include="skinneddivider.cpp" />
+ <ClCompile Include="skinneddlg.cpp" />
+ <ClCompile Include="skinnededit.cpp" />
+ <ClCompile Include="skinnedfolder.cpp" />
+ <ClCompile Include="skinnedheader.cpp" />
+ <ClCompile Include="skinnedlistbox.cpp" />
+ <ClCompile Include="skinnedlistview.cpp" />
+ <ClCompile Include="skinnedmenu.cpp" />
+ <ClCompile Include="skinnedmenuthreadinfo.cpp" />
+ <ClCompile Include="skinnedmenuwnd.cpp" />
+ <ClCompile Include="skinnedprogressbar.cpp" />
+ <ClCompile Include="skinnedscrollwnd.cpp" />
+ <ClCompile Include="skinnedstatic.cpp" />
+ <ClCompile Include="skinnedtooltip.cpp" />
+ <ClCompile Include="skinnedwnd.cpp" />
+ <ClCompile Include="skinning.cpp" />
+ <ClCompile Include="SmoothScrollList.cpp" />
+ <ClCompile Include="stockobjects.cpp" />
+ <ClCompile Include="stringvector.cpp" />
+ <ClCompile Include="util.cpp" />
+ <ClCompile Include="view_ml.cpp" />
+ <ClCompile Include="wa_dlg.cpp" />
+ <ClCompile Include="webinfo_dlg.cpp" />
+ <ClCompile Include="webinfo_obj.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="..\..\..\nu\HTMLContainer2.h" />
+ <ClInclude Include="..\..\..\nu\mtbrowser.h" />
+ <ClInclude Include="..\..\..\Winamp\strutil.h" />
+ <ClInclude Include="..\..\..\Winamp\wa_dlg.h" />
+ <ClInclude Include="..\..\..\Winamp\wa_ipc.h" />
+ <ClInclude Include="api__gen_ml.h" />
+ <ClInclude Include="childwnd.h" />
+ <ClInclude Include="colors.h" />
+ <ClInclude Include="comboskin.h" />
+ <ClInclude Include="config.h" />
+ <ClInclude Include="fileview.h" />
+ <ClInclude Include="fileview_internal.h" />
+ <ClInclude Include="folderbrowser.h" />
+ <ClInclude Include="folderbrowser_internal.h" />
+ <ClInclude Include="gaystring.h" />
+ <ClInclude Include="graphics.h" />
+ <ClInclude Include="imagefilters.h" />
+ <ClInclude Include="itemlist.h" />
+ <ClInclude Include="main.h" />
+ <ClInclude Include="MediaLibraryCOM.h" />
+ <ClInclude Include="menufucker.h" />
+ <ClInclude Include="ml.h" />
+ <ClInclude Include="mldwm.h" />
+ <ClInclude Include="ml_cloud.h" />
+ <ClInclude Include="ml_cloudcolumn.h" />
+ <ClInclude Include="ml_imagefilter.h" />
+ <ClInclude Include="ml_imagelist.h" />
+ <ClInclude Include="ml_imageloader.h" />
+ <ClInclude Include="ml_ipc.h" />
+ <ClInclude Include="ml_ipc_0313.h" />
+ <ClInclude Include="ml_rating.h" />
+ <ClInclude Include="ml_ratingcolumn.h" />
+ <ClInclude Include="MusicID.h" />
+ <ClInclude Include="navigation.h" />
+ <ClInclude Include="OnlineMediaCOM.h" />
+ <ClInclude Include="RatingsCOM.h" />
+ <ClInclude Include="reflectmsg.h" />
+ <ClInclude Include="resource.h" />
+ <ClInclude Include="sendto.h" />
+ <ClInclude Include="service.h" />
+ <ClInclude Include="skinexport.h" />
+ <ClInclude Include="skinnedbutton.h" />
+ <ClInclude Include="skinnedcombo.h" />
+ <ClInclude Include="skinneddivider.h" />
+ <ClInclude Include="skinneddlg.h" />
+ <ClInclude Include="skinnededit.h" />
+ <ClInclude Include="skinnedfolder.h" />
+ <ClInclude Include="skinnedheader.h" />
+ <ClInclude Include="skinnedlistbox.h" />
+ <ClInclude Include="skinnedlistview.h" />
+ <ClInclude Include="skinnedmenu.h" />
+ <ClInclude Include="skinnedmenuthreadinfo.h" />
+ <ClInclude Include="skinnedmenuwnd.h" />
+ <ClInclude Include="skinnedprogressbar.h" />
+ <ClInclude Include="skinnedscrollwnd.h" />
+ <ClInclude Include="skinnedstatic.h" />
+ <ClInclude Include="skinnedtooltip.h" />
+ <ClInclude Include="skinnedwnd.h" />
+ <ClInclude Include="skinning.h" />
+ <ClInclude Include="stockobjects.h" />
+ <ClInclude Include="stringvector.h" />
+ <ClInclude Include="webinfo_obj.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <Image Include="resources\cloud_16_incloud.png" />
+ <Image Include="resources\cloud_16_partial.png" />
+ <Image Include="resources\cloud_16_unavail.png" />
+ <Image Include="resources\filetype_audio_16.png" />
+ <Image Include="resources\filetype_audio_32.png" />
+ <Image Include="resources\filetype_playlist_16.png" />
+ <Image Include="resources\filetype_playlist_32.png" />
+ <Image Include="resources\filetype_unknown_16.png" />
+ <Image Include="resources\filetype_unknown_32.png" />
+ <Image Include="resources\filetype_video_16.png" />
+ <Image Include="resources\filetype_video_32.png" />
+ <Image Include="resources\menu_arrow.png" />
+ <Image Include="resources\menu_check.png" />
+ <Image Include="resources\menu_dot.png" />
+ <Image Include="resources\menu_scrollarrow.png" />
+ <Image Include="resources\menu_scrollarrow_disabled.png" />
+ <Image Include="resources\rating.png" />
+ <Image Include="resources\sortarrow.png" />
+ <Image Include="resources\splitarrow.png" />
+ <Image Include="resources\splitarrow_pressed.png" />
+ <Image Include="resources\ti_cdrom_16x16x16.bmp" />
+ <Image Include="resources\ti_default_16x16x16.bmp" />
+ <Image Include="resources\ti_labs_16x16x16.bmp" />
+ <Image Include="resources\ti_playlist_16x16x16.bmp" />
+ <Image Include="resources\tree_closed_16x16x16.bmp" />
+ <Image Include="resources\tree_closed_disabled_16x16x16.bmp" />
+ <Image Include="resources\tree_open_16x16x16.bmp" />
+ <Image Include="resources\view_mode_detail.png" />
+ <Image Include="resources\view_mode_icon.png" />
+ <Image Include="resources\view_mode_list.png" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="resources\dragdrop.cur" />
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="gen_ml.rc" />
+ <ResourceCompile Include="png.rc" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ 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 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <ClCompile Include="webinfo_obj.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="childwnd.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="colors.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="comboskin.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="config.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="fileview.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="fileview_columns.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="fileview_compare.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="fileview_filesystem.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="fileview_format.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="fileview_menu.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="fileview_metadata.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="fileview_toolbar.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="flickerfix.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="folderborwser_listbox.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="folderbrowser.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="gaystring.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="graphics.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="HeaderIconList.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="imagefilters.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="IPC.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="itemlist.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="main.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="MediaLibraryCOM.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ml_cloud.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ml_cloudcolumn.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ml_imagefilter.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ml_imagelist.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ml_imageloader.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ml_lib.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ml_rating.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ml_ratingcolumn.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="mldwm.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="MusicID.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="navigation.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="OnlineMediaCOM.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="plugin.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="prefs.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="RatingsCOM.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="reflectmsg.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="sendto.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="service.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="setup.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="skinexport.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="skinnedbutton.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="skinnedcombo.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="skinneddivider.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="skinneddlg.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="skinnededit.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="skinnedfolder.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="skinnedheader.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="skinnedlistbox.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="skinnedlistview.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="skinnedmenu.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="skinnedmenuthreadinfo.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="skinnedmenuwnd.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="skinnedprogressbar.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="skinnedscrollwnd.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="skinnedstatic.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="skinnedtooltip.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="skinnedwnd.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="skinning.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="SmoothScrollList.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="stockobjects.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="stringvector.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="util.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="view_ml.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="wa_dlg.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="webinfo_dlg.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\nu\CCVersion.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\nu\HTMLContainer2.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\nu\menushortcuts.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\nu\mtbrowser.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\nu\ServiceWatcher.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\Winamp\strutil.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="..\..\..\nu\trace.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="webinfo_obj.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="stringvector.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="stockobjects.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="skinning.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="skinnedwnd.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="skinnedtooltip.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="skinnedstatic.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="skinnedscrollwnd.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="skinnedprogressbar.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="skinnedmenuwnd.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="skinnedmenuthreadinfo.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="skinnedmenu.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="skinnedlistview.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="skinnedlistbox.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="skinnedheader.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="skinnedfolder.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="skinnededit.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="skinneddlg.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="skinneddivider.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="skinnedcombo.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="skinnedbutton.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="skinexport.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="service.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="sendto.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="resource.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="reflectmsg.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="RatingsCOM.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="OnlineMediaCOM.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="navigation.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="MusicID.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="mldwm.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="ml_ratingcolumn.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="ml_rating.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="ml_ipc_0313.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="ml_ipc.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="ml_imageloader.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="ml_imagelist.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="ml_imagefilter.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="ml_cloudcolumn.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="ml_cloud.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="ml.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="menufucker.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="MediaLibraryCOM.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="main.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="itemlist.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="imagefilters.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="graphics.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="gaystring.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="folderbrowser_internal.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="folderbrowser.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="fileview_internal.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="fileview.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="config.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="comboskin.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="colors.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="childwnd.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="api__gen_ml.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\nu\HTMLContainer2.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\nu\mtbrowser.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Winamp\strutil.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Winamp\wa_dlg.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="..\..\..\Winamp\wa_ipc.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="gen_ml.rc">
+ <Filter>Ressource Files</Filter>
+ </ResourceCompile>
+ <ResourceCompile Include="png.rc">
+ <Filter>Ressource Files</Filter>
+ </ResourceCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <Image Include="resources\filetype_video_32.png">
+ <Filter>Image Files</Filter>
+ </Image>
+ <Image Include="resources\filetype_audio_16.png">
+ <Filter>Image Files</Filter>
+ </Image>
+ <Image Include="resources\filetype_audio_32.png">
+ <Filter>Image Files</Filter>
+ </Image>
+ <Image Include="resources\filetype_playlist_16.png">
+ <Filter>Image Files</Filter>
+ </Image>
+ <Image Include="resources\filetype_playlist_32.png">
+ <Filter>Image Files</Filter>
+ </Image>
+ <Image Include="resources\filetype_unknown_16.png">
+ <Filter>Image Files</Filter>
+ </Image>
+ <Image Include="resources\filetype_unknown_32.png">
+ <Filter>Image Files</Filter>
+ </Image>
+ <Image Include="resources\filetype_video_16.png">
+ <Filter>Image Files</Filter>
+ </Image>
+ <Image Include="resources\cloud_16_unavail.png">
+ <Filter>Image Files</Filter>
+ </Image>
+ <Image Include="resources\cloud_16_partial.png">
+ <Filter>Image Files</Filter>
+ </Image>
+ <Image Include="resources\cloud_16_incloud.png">
+ <Filter>Image Files</Filter>
+ </Image>
+ <Image Include="resources\menu_arrow.png">
+ <Filter>Image Files</Filter>
+ </Image>
+ <Image Include="resources\menu_check.png">
+ <Filter>Image Files</Filter>
+ </Image>
+ <Image Include="resources\menu_dot.png">
+ <Filter>Image Files</Filter>
+ </Image>
+ <Image Include="resources\menu_scrollarrow.png">
+ <Filter>Image Files</Filter>
+ </Image>
+ <Image Include="resources\menu_scrollarrow_disabled.png">
+ <Filter>Image Files</Filter>
+ </Image>
+ <Image Include="resources\rating.png">
+ <Filter>Image Files</Filter>
+ </Image>
+ <Image Include="resources\sortarrow.png">
+ <Filter>Image Files</Filter>
+ </Image>
+ <Image Include="resources\splitarrow.png">
+ <Filter>Image Files</Filter>
+ </Image>
+ <Image Include="resources\splitarrow_pressed.png">
+ <Filter>Image Files</Filter>
+ </Image>
+ <Image Include="resources\ti_cdrom_16x16x16.bmp">
+ <Filter>Image Files</Filter>
+ </Image>
+ <Image Include="resources\ti_default_16x16x16.bmp">
+ <Filter>Image Files</Filter>
+ </Image>
+ <Image Include="resources\ti_labs_16x16x16.bmp">
+ <Filter>Image Files</Filter>
+ </Image>
+ <Image Include="resources\ti_playlist_16x16x16.bmp">
+ <Filter>Image Files</Filter>
+ </Image>
+ <Image Include="resources\tree_closed_16x16x16.bmp">
+ <Filter>Image Files</Filter>
+ </Image>
+ <Image Include="resources\tree_closed_disabled_16x16x16.bmp">
+ <Filter>Image Files</Filter>
+ </Image>
+ <Image Include="resources\tree_open_16x16x16.bmp">
+ <Filter>Image Files</Filter>
+ </Image>
+ <Image Include="resources\view_mode_detail.png">
+ <Filter>Image Files</Filter>
+ </Image>
+ <Image Include="resources\view_mode_icon.png">
+ <Filter>Image Files</Filter>
+ </Image>
+ <Image Include="resources\view_mode_list.png">
+ <Filter>Image Files</Filter>
+ </Image>
+ </ItemGroup>
+ <ItemGroup>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{0a05bd80-ec56-40ac-a633-3a3c9aa13bf6}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Ressource Files">
+ <UniqueIdentifier>{5216efe6-f772-4af6-b4c9-3b7b25b0153c}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{2807c32e-739b-42a2-85e4-a5b2b4fc36be}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Image Files">
+ <UniqueIdentifier>{c5319216-3757-4121-90c8-052366765304}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="resources\dragdrop.cur">
+ <Filter>Image Files</Filter>
+ </None>
+ </ItemGroup>
+</Project> \ 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 <math.h>
+
+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 <windows.h>
+
+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 <windows.h>
+#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 <attractor@live.co.uk>
+
+ 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 <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+/* 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 <windowsx.h>
+#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;i<l;i++) {
+ HD_ITEM hi;
+ hi.mask = HDI_WIDTH;
+ Header_GetItem(hwnd, i, &hi);
+ m_origwidth+=hi.cxy;
+ }
+ for(i=item;i<l;i++) {
+ if(i==-1) continue;
+ HD_ITEM hi;
+ hi.mask = HDI_WIDTH;
+ Header_GetItem(hwnd, i, &hi);
+ m_origsizes[i]=hi.cxy;
+ if(m_origwidth==0)
+ m_origperc[i]=0;
+ else
+ m_origperc[i]=(float)hi.cxy/(float)m_origwidth;
+ }
+}
+
+static void columnAutoResizeProp(HWND hwnd, int item)
+{
+ SendMessage(GetParent(hwnd),WM_SETREDRAW,FALSE,0);
+ int l=Header_GetItemCount(hwnd);
+ int width=0;
+ int i;
+
+ HD_ITEM hi;
+ hi.mask = HDI_WIDTH;
+ Header_GetItem(hwnd, item, &hi);
+ width=m_origwidth-(hi.cxy-m_origsizes[item]);
+ float rest=0;
+ for(i=item+1;i<l;i++) {
+ HD_ITEM hi;
+ hi.mask = HDI_WIDTH;
+ float l=m_origperc[i]*(float)width;
+ l+=rest;
+ rest=0;
+ int l2=(int)l;
+ rest+=l-(float)l2;
+ hi.cxy=l2;
+ Header_SetItem(hwnd, i, &hi);
+ }
+ SendMessage(GetParent(hwnd),WM_SETREDRAW,TRUE,0);
+}
+
+static void columnAutoResizeItem(HWND hwnd, int item)
+{
+ HD_ITEM hi;
+ hi.mask = HDI_WIDTH;
+ Header_GetItem(hwnd, item, &hi);
+ int diff=hi.cxy-m_origsizes[item];
+
+ hi.mask = HDI_WIDTH;
+ if(Header_GetItem(hwnd, item+1, &hi)) {
+ SendMessage(GetParent(hwnd),WM_SETREDRAW,FALSE,0);
+ hi.cxy-=diff;
+ Header_SetItem(hwnd,item+1,&hi);
+ SendMessage(GetParent(hwnd),WM_SETREDRAW,TRUE,0);
+ }
+ m_origsizes[item]+=diff;
+}
+
+static void size_autoResizeStart(HWND h)
+{
+ RECT r;
+ GetClientRect(h,&r);
+ char tmp[128];
+ wsprintf(tmp,"start:%i\n",r.right-r.left);
+ OutputDebugString(tmp);
+}
+
+static void size_autoResizeProp(HWND h)
+{
+ RECT r;
+ GetClientRect(h,&r);
+ char tmp[128];
+ wsprintf(tmp,"prop:%i\n",r.right-r.left);
+ OutputDebugString(tmp);
+}
+
+INT_PTR handleListViewHeaderMsgs(HWND hwndDlg,
+ HWND headerWnd,
+ HWND listWnd,
+ UINT uMsg,
+ WPARAM wParam,
+ LPARAM lParam,
+ BOOL sortShow,
+ BOOL sortAscending,
+ int sortIndex)
+{
+ if (uMsg == WM_NOTIFY) {
+ LPNMCUSTOMDRAW lpnmcd = (NMCUSTOMDRAW *)lParam;
+ if(lpnmcd->hdr.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 <windows.h>
+
+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 <windows.h>
+#include <commctrl.h>
+#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 <windows.h>
+#include <windowsx.h>
+#include <commctrl.h>
+
+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<l;i++)
+ {
+ RECT r;
+ GetItemRect(i, &r);
+ if (r.left<=x && r.right>=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 <windowsx.h>
+#include <time.h>
+#include <rpc.h>
+#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 <shlwapi.h>
+
+#include "api__gen_ml.h"
+#include <api/service/waServiceFactory.h>
+#include "./navigation.h"
+//#include "./skinnedwnd.h"
+#include "./skinning.h"
+#include "../nu/ServiceWatcher.h"
+#include "MusicID.h"
+#include <tataki/export.h>
+#include <strsafe.h>
+#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 <class api_T>
+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<api_T *>( factory->getInterface() );
+ }
+}
+
+template <class api_T>
+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<wchar_t *>(-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<wchar_t *>(-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 <windows.h>
+#include <windowsx.h>
+#include <commctrl.h>
+
+#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 <wtypes.h>
+#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 <windows.h>
+#include <commctrl.h>
+#include <stddef.h>
+#include <api/service/api_service.h>
+
+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 <stddef.h> 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://<drive letter>,<track index>. 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 <time.h>
+
+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 <commctrl.h>
+
+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 <windows.h>
+#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 <commctrl.h>
+#include <strsafe.h>
+
+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 <windows.h>
+
+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 <windows.h>
+#include <windowsx.h>
+#include <stdio.h>
+#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;i<m_songs.GetSize();i++)
+ {
+ Song *s=(Song *)m_songs.Get(i);
+ totallen+=s->songlen;
+ 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 <strsafe.h>
+
+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 <windows.h>
+
+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 <windows.h>
+#include <commctrl.h>
+#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 <api/service/waServiceFactory.h>
+#include <api/service/svcs/svc_imgload.h>
+#include <api/memmgr/api_memmgr.h>
+
+#include <commctrl.h>
+#include <shlwapi.h>
+
+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<api_memmgr*>(sf->getInterface());
+ }
+ if (wasabiMemMgr && !wasabiPNGLoader)
+ {
+ sf = WASABI_API_SVC->service_getServiceByGuid(pngGUID);
+ if (sf) wasabiPNGLoader = reinterpret_cast<svc_imageLoader*>(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 <windows.h>
+
+
+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 <windows.h>
+#include <stddef.h>
+
+//# 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 <wtypes.h>
+
+////////////////////////////////////////////////////////////////////
+// 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 <windows.h>
+#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 <commctrl.h>
+
+#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 <windows.h>
+#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 <commctrl.h>
+#include <strsafe.h>
+
+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)&param);
+ 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 <windows.h>
+
+// 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 <windows.h>
+
+#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 <windowsx.h>
+#include <strsafe.h>
+
+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"<<empty>>"
+#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 <windows.h>
+#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 <time.h>
+#include <shlwapi.h>
+#include <strsafe.h>
+
+#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 <shlwapi.h>
+#include <strsafe.h>
+
+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, &centerRect))
+ 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 <windows.h>
+
+// 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
--- /dev/null
+++ b/Src/Plugins/General/gen_ml/resources/checkmark.png
Binary files 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
--- /dev/null
+++ b/Src/Plugins/General/gen_ml/resources/cloud_16_incloud.png
Binary files 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
--- /dev/null
+++ b/Src/Plugins/General/gen_ml/resources/cloud_16_partial.png
Binary files 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
--- /dev/null
+++ b/Src/Plugins/General/gen_ml/resources/cloud_16_unavail.png
Binary files 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
--- /dev/null
+++ b/Src/Plugins/General/gen_ml/resources/cloud_16_upload.png
Binary files 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
--- /dev/null
+++ b/Src/Plugins/General/gen_ml/resources/cloud_16_uploading.png
Binary files 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
--- /dev/null
+++ b/Src/Plugins/General/gen_ml/resources/dragdrop.cur
Binary files 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
--- /dev/null
+++ b/Src/Plugins/General/gen_ml/resources/filetype_audio_16.png
Binary files 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
--- /dev/null
+++ b/Src/Plugins/General/gen_ml/resources/filetype_audio_32.png
Binary files 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
--- /dev/null
+++ b/Src/Plugins/General/gen_ml/resources/filetype_playlist_16.png
Binary files 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
--- /dev/null
+++ b/Src/Plugins/General/gen_ml/resources/filetype_playlist_32.png
Binary files 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
--- /dev/null
+++ b/Src/Plugins/General/gen_ml/resources/filetype_unknown_16.png
Binary files 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
--- /dev/null
+++ b/Src/Plugins/General/gen_ml/resources/filetype_unknown_32.png
Binary files 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
--- /dev/null
+++ b/Src/Plugins/General/gen_ml/resources/filetype_video_16.png
Binary files 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
--- /dev/null
+++ b/Src/Plugins/General/gen_ml/resources/filetype_video_32.png
Binary files 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
--- /dev/null
+++ b/Src/Plugins/General/gen_ml/resources/menu_arrow.png
Binary files 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
--- /dev/null
+++ b/Src/Plugins/General/gen_ml/resources/menu_check.png
Binary files 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
--- /dev/null
+++ b/Src/Plugins/General/gen_ml/resources/menu_dot.png
Binary files 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
--- /dev/null
+++ b/Src/Plugins/General/gen_ml/resources/menu_scrollarrow.png
Binary files 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
--- /dev/null
+++ b/Src/Plugins/General/gen_ml/resources/menu_scrollarrow_disabled.png
Binary files 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
--- /dev/null
+++ b/Src/Plugins/General/gen_ml/resources/rating.png
Binary files 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
--- /dev/null
+++ b/Src/Plugins/General/gen_ml/resources/sortarrow.png
Binary files 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
--- /dev/null
+++ b/Src/Plugins/General/gen_ml/resources/splitarrow.png
Binary files 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
--- /dev/null
+++ b/Src/Plugins/General/gen_ml/resources/splitarrow_pressed.png
Binary files 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
--- /dev/null
+++ b/Src/Plugins/General/gen_ml/resources/stars_invert.bmp
Binary files 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
--- /dev/null
+++ b/Src/Plugins/General/gen_ml/resources/ti_default_16x16x16.bmp
Binary files 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
--- /dev/null
+++ b/Src/Plugins/General/gen_ml/resources/ti_labs_16x16x16.bmp
Binary files 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
--- /dev/null
+++ b/Src/Plugins/General/gen_ml/resources/tree_closed_16x16x16.bmp
Binary files 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
--- /dev/null
+++ b/Src/Plugins/General/gen_ml/resources/tree_closed_disabled_16x16x16.bmp
Binary files 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
--- /dev/null
+++ b/Src/Plugins/General/gen_ml/resources/tree_open_16x16x16.bmp
Binary files 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
--- /dev/null
+++ b/Src/Plugins/General/gen_ml/resources/view_mode_detail.png
Binary files 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
--- /dev/null
+++ b/Src/Plugins/General/gen_ml/resources/view_mode_icon.png
Binary files 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
--- /dev/null
+++ b/Src/Plugins/General/gen_ml/resources/view_mode_list.png
Binary files 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 <windowsx.h>
+#include <tchar.h>
+#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 <windows.h>
+
+// 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 <windowsx.h>
+#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<INT_PTR>(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<INT_PTR>(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 <strsafe.h>
+
+#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<ifc_omservice*>(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 <wtypes.h>
+#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 <shlwapi.h>
+
+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 <wtypes.h>
+
+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 <math.h>
+#include <strsafe.h>
+
+#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, &param->pbps->rcPaint);
+ CopyRect(&param->pbps->rcPaint, param->prcImage);
+
+ PushButton_DrawBackground(param->pbps);
+
+ param->pbps->hdc = hdcSrc;
+ CopyRect(&param->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 <windowsx.h>
+#include <strsafe.h>
+
+#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 <commctrl.h>
+
+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 <windowsx.h>
+#include <strsafe.h>
+
+
+#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 <commctrl.h>
+
+
+#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 <strsafe.h>
+
+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 <commctrl.h>
+
+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 <wtypes.h>
+#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 <new>
+#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 <wtypes.h>
+#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 <strsafe.h>
+
+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 <commctrl.h>
+#include <windowsx.h>
+#include "../winamp/wa_dlg.h"
+#include "api__gen_ml.h"
+#include "./colors.h"
+#include <tataki/bitmap/bitmap.h>
+#include <tataki/bitmap/autobitmap.h>
+#include <tataki/canvas/canvas.h>
+#include <api/wnd/api_window.h>
+#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 = &rect;
+ 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 = &wp;
+ 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 <strsafe.h>
+
+#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 <strsafe.h>
+
+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 <windows.h>
+#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 <wtypes.h>
+
+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 <windows.h>
+
+#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 <strsafe.h>
+
+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 <windows.h>
+
+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 <string.h>
+#include <shlobj.h>
+#include "./config.h"
+#include "./resource.h"
+
+#include "../nu/AutoChar.h"
+#include "../nu/AutoWide.h"
+#include "api__gen_ml.h"
+#include <strsafe.h>
+#include <api/syscb/callbacks/browsercb.h>
+
+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<intptr_t>(loc), reinterpret_cast<intptr_t>(&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 <shlobj.h>
+#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 <windowsx.h>
+#include <time.h>
+#include <rpc.h>
+#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 <shlwapi.h>
+#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 <strsafe.h>
+
+#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<WebFileInfo*>(pContainer);
+ pWebInfo->NavigateToPage();
+}
+
+static void CALLBACK APC_InvokeFileInfo(HTMLContainer2 *pContainer, VARIANTARG *pArgs, INT cArgs, LPARAM *pResult)
+{
+ WebFileInfo *pWebInfo;
+ pWebInfo = reinterpret_cast<WebFileInfo*>(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<WebFileInfo*>(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<WebFileInfo*>(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 <exdisp.h>
+#include <mshtmdid.h>
+#include <mshtml.h>
+
+#include <strsafe.h>
+
+#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"<HTML><HEAD></HEAD><BODY>"
+ L"<table height=\"100%%\" width=\"100%%\">"
+ L"<tr><td align=center valign=middle height=\"96%%\" width=\"96%%\">"
+ L"<font size=-1 face=\"Arial\">%s</font>"
+ L"</td></tr></table>"
+ L"</BODY></HTML>";
+
+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 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectName>gen_tray</ProjectName>
+ <ProjectGuid>{DD15E699-90D2-4301-921A-7C2C48B25766}</ProjectGuid>
+ <RootNamespace>gen_tray</RootNamespace>
+ <WindowsTargetPlatformVersion>10.0.19041.0</WindowsTargetPlatformVersion>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <PlatformToolset>v142</PlatformToolset>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <IncludePath>$(IncludePath)</IncludePath>
+ <LibraryPath>$(LibraryPath)</LibraryPath>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ <IncludePath>$(IncludePath)</IncludePath>
+ <LibraryPath>$(LibraryPath)</LibraryPath>
+ <EmbedManifest>true</EmbedManifest>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
+ <IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg">
+ <VcpkgEnabled>false</VcpkgEnabled>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <VcpkgConfiguration>Debug</VcpkgConfiguration>
+ </PropertyGroup>
+ <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <VcpkgConfiguration>Debug</VcpkgConfiguration>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>.;..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;WIN32;_DEBUG;_WINDOWS;_USRDLL;GEN_TRAY_EXPORTS;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <DisableSpecificWarnings>4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <Culture>0x0409</Culture>
+ </ResourceCompile>
+ <Link>
+ <AdditionalDependencies>shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <TargetMachine>MachineX86</TargetMachine>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <SubSystem>Windows</SubSystem>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\
+xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>.;../Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;WIN64;_DEBUG;_WINDOWS;_USRDLL;GEN_TRAY_EXPORTS;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>false</MinimalRebuild>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <DisableSpecificWarnings>4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <Culture>0x0409</Culture>
+ </ResourceCompile>
+ <Link>
+ <AdditionalDependencies>shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <SubSystem>Windows</SubSystem>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\
+xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <Optimization>MinSpace</Optimization>
+ <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>.;..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;WIN32;NDEBUG;_WINDOWS;_USRDLL;GEN_TRAY_EXPORTS;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>None</DebugInformationFormat>
+ <DisableSpecificWarnings>4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <Culture>0x0409</Culture>
+ </ResourceCompile>
+ <Link>
+ <AdditionalDependencies>shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <TargetMachine>MachineX86</TargetMachine>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <SubSystem>Windows</SubSystem>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <Optimization>MinSpace</Optimization>
+ <InlineFunctionExpansion>OnlyExplicitInline</InlineFunctionExpansion>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <AdditionalIncludeDirectories>.;..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;WIN64;NDEBUG;_WINDOWS;_USRDLL;GEN_TRAY_EXPORTS;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <MultiProcessorCompilation>true</MultiProcessorCompilation>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <BufferSecurityCheck>true</BufferSecurityCheck>
+ <AssemblerListingLocation>$(IntDir)</AssemblerListingLocation>
+ <ObjectFileName>$(IntDir)</ObjectFileName>
+ <ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
+ <WarningLevel>Level3</WarningLevel>
+ <DebugInformationFormat>None</DebugInformationFormat>
+ <DisableSpecificWarnings>4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_WIN32_WINNT=0x0601;WINVER=0x0601;_UNICODE;UNICODE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <Culture>0x0409</Culture>
+ </ResourceCompile>
+ <Link>
+ <AdditionalDependencies>shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <GenerateDebugInformation>false</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
+ <SubSystem>Windows</SubSystem>
+ </Link>
+ <PostBuildEvent>
+ <Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
+ <Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
+ </PostBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClInclude Include="api__gen_tray.h" />
+ <ClInclude Include="RESOURCE.H" />
+ <ClInclude Include="WINAMPCMD.H" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="TRAYCTL.C">
+ <CompileAs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">CompileAsCpp</CompileAs>
+ <CompileAs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">CompileAsCpp</CompileAs>
+ <CompileAs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">CompileAsCpp</CompileAs>
+ <CompileAs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">CompileAsCpp</CompileAs>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <Image Include="icons\compact.bmp" />
+ <Image Include="icons\icon1.ico" />
+ <Image Include="icons\icon2.ico" />
+ <Image Include="icons\icon3.ico" />
+ <Image Include="icons\icon4.ico" />
+ <Image Include="icons\icon5.ico" />
+ <Image Include="icons\icon7.ico" />
+ <Image Include="icons\icon8.ico" />
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="gen_tray.rc" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\..\..\Wasabi\Wasabi.vcxproj">
+ <Project>{3e0bfa8a-b86a-42e9-a33f-ec294f823f7f}</Project>
+ </ProjectReference>
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ 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 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <ClInclude Include="api__gen_tray.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="RESOURCE.H">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="WINAMPCMD.H">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <Image Include="icons\icon1.ico">
+ <Filter>Image Files</Filter>
+ </Image>
+ <Image Include="icons\icon2.ico">
+ <Filter>Image Files</Filter>
+ </Image>
+ <Image Include="icons\icon3.ico">
+ <Filter>Image Files</Filter>
+ </Image>
+ <Image Include="icons\icon4.ico">
+ <Filter>Image Files</Filter>
+ </Image>
+ <Image Include="icons\icon5.ico">
+ <Filter>Image Files</Filter>
+ </Image>
+ <Image Include="icons\icon7.ico">
+ <Filter>Image Files</Filter>
+ </Image>
+ <Image Include="icons\icon8.ico">
+ <Filter>Image Files</Filter>
+ </Image>
+ <Image Include="icons\compact.bmp">
+ <Filter>Image Files</Filter>
+ </Image>
+ </ItemGroup>
+ <ItemGroup>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{96125085-f1a0-41bb-a9be-181833e83c26}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Ressource Files">
+ <UniqueIdentifier>{79c08881-db4b-4236-bf7d-aedeb08c65b2}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{642a812f-646c-4a9b-ba39-93c8405dcf96}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Image Files">
+ <UniqueIdentifier>{ec91a377-7c2d-47f3-8508-27f333a2c638}</UniqueIdentifier>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="gen_tray.rc">
+ <Filter>Ressource Files</Filter>
+ </ResourceCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="TRAYCTL.C">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+</Project> \ 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 <windows.h>
+#include <commctrl.h>
+#include <shlwapi.h>
+#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 <strsafe.h>
+
+
+#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<<i))
+ {
+ NOTIFYICONDATAW tnid={0};
+ tnid.cbSize=sizeof(NOTIFYICONDATAW);
+ tnid.hWnd=plugin.hwndParent;
+ tnid.uID=i+SYSTRAY_ICON_BASE;
+ Shell_NotifyIcon(NIM_DELETE, &tnid);
+ }
+ }
+
+ l=config_enabled;
+
+ if(!on) {return;}
+
+ // have to do XP+ specific changes here in order for the icon addition order to appear as expected and not back to front!
+ for (i = (xporhigher?(NUM_ICONS-1):0); (xporhigher?i>-1:i < NUM_ICONS); (xporhigher?i --:i ++))
+ {
+ if (config_enabled & (1<<i))
+ {
+ // check if an icon pack has not been set or it's not a valid file that's being passed
+ // if so then need to use the default icons
+ if (!ico_pack[0] || !PathFileExists(ico_pack) )
+ {
+ if (!Icons[i]) Icons[i] = LoadIcon(plugin.hDllInstance,MAKEINTRESOURCE(IDI_ICON1+i));
+ if (i == 5) {
+ compact = (HBITMAP)LoadImage(plugin.hDllInstance,MAKEINTRESOURCE(IDB_BITMAP1),IMAGE_BITMAP,0,0,LR_SHARED);
+ Icons[i] = CreateInternalIcon();
+ }
+ }
+ else
+ {
+ wchar_t* icpb = 0;
+ lstrcpyn(ico_pack_base,ico_pack,ARRAYSIZE(ico_pack_base));
+ icpb = ico_pack_base + lstrlen(ico_pack_base) - 1;
+ while(icpb && *icpb && *icpb != L'\\'){icpb = CharPrev(ico_pack,icpb);}
+ if (icpb) *icpb = 0;
+
+ if (!Icons[i]){
+ wchar_t entry[MAX_PATH] = {0}, buf[MAX_PATH] = {0};
+ int compact_loaded = 0;
+
+ StringCchPrintf(entry,MAX_PATH,L"ico%d",i+1);
+ GetPrivateProfileString(L"tray icon pack",entry,buf,buf,ARRAYSIZE(buf),ico_pack);
+ StringCchPrintf(entry,MAX_PATH,L"%s\\%s",ico_pack_base,buf);
+
+ Icons[i] = (HICON)LoadImage(0,entry,(i != 5?IMAGE_ICON:IMAGE_BITMAP),0,0,LR_LOADFROMFILE|LR_SHARED);
+ if (i == 5) {
+ compact = (HBITMAP)Icons[i];
+ if(compact) {
+ compact_loaded = 1;
+ }
+ }
+ // if this fails then we use the built-in versions
+ if (!Icons[i]) Icons[i] = LoadIcon(plugin.hDllInstance,MAKEINTRESOURCE(IDI_ICON1+i));
+ if (i == 5) {
+ if (!compact_loaded) {
+ compact = (HBITMAP)LoadImage(plugin.hDllInstance,MAKEINTRESOURCE(IDB_BITMAP1),IMAGE_BITMAP,0,0,LR_SHARED);
+ }
+ Icons[i] = CreateInternalIcon();
+ }
+ }
+ }
+
+ {
+ NOTIFYICONDATAW tnid={0};
+ tnid.cbSize=sizeof(NOTIFYICONDATAW);
+ tnid.hWnd=plugin.hwndParent;
+ tnid.uID=i+SYSTRAY_ICON_BASE;
+ tnid.uFlags=NIF_ICON | NIF_TIP | NIF_MESSAGE | NIF_GUID;
+ tnid.uCallbackMessage=WM_USER + 2707;
+ tnid.hIcon=Icons[i];
+ if(i != 5)
+ StringCchPrintf(tnid.szTip,sizeof(tnid.szTip)/sizeof(wchar_t),L"%s - Winamp",WASABI_API_LNGSTRINGW(tips[i]));
+ else
+ FormCompactText(tnid.szTip,(sizeof(tnid.szTip)/sizeof(wchar_t)));
+
+ Shell_NotifyIcon(NIM_ADD, &tnid);
+ }
+ }
+ }
+}
+
+void config(void)
+{
+ if(!IsWindow(configwnd))
+ WASABI_API_DIALOGBOXW(IDD_DIALOG1,0,ConfigProc);
+ else
+ SetActiveWindow(configwnd);
+}
+
+void quit(void)
+{
+ config_enabled=0;
+ do_icons(0);
+ free_icons();
+}
+
+BOOL CALLBACK FindTrayWnd(HWND hwnd, LPARAM lParam)
+{
+ wchar_t szClassName[256] = {0};
+ GetClassName(hwnd, szClassName, 255); // Did we find the Main System Tray? If so, then get its size and quit
+ if (!lstrcmpi(szClassName, L"TrayNotifyWnd"))
+ {
+ HWND* pWnd = (HWND*)lParam;
+ *pWnd = hwnd;
+ return FALSE;
+ }
+
+ //Original code I found on Internet were seeking here for system clock and it was assumming that clock is on the right side of tray.
+ //After that calculated size of tray was adjusted by removing space occupied by clock.
+ //This is not a good idea - some clocks are ABOVE or somewhere else on the screen. I found that is far safer to just ignore clock space.
+ return TRUE;
+}
+
+BOOL CALLBACK FindToolBarInTrayWnd(HWND hwnd, LPARAM lParam)
+{
+ wchar_t szClassName[256] = {0};
+ GetClassName(hwnd, szClassName, 255); // Did we find the Main System Tray? If so, then get its size and quit
+ if (!lstrcmpi(szClassName, L"ToolbarWindow32"))
+ {
+ HWND* pWnd = (HWND*)lParam;
+ *pWnd = hwnd;
+ return FALSE;
+ }
+ return TRUE;
+}
+
+HWND GetTrayNotifyWnd(BOOL a_bSeekForEmbedToolbar)
+{
+ HWND hWndTrayNotifyWnd = 0, hWndShellTrayWnd = FindWindow(L"Shell_TrayWnd", 0);
+
+ if (hWndShellTrayWnd)
+ {
+ EnumChildWindows(hWndShellTrayWnd, FindTrayWnd, (LPARAM)&hWndTrayNotifyWnd);
+
+ if(hWndTrayNotifyWnd && IsWindow(hWndTrayNotifyWnd))
+ {
+ HWND hWndToolBarWnd = 0;
+ EnumChildWindows(hWndTrayNotifyWnd, FindToolBarInTrayWnd, (LPARAM)&hWndToolBarWnd);
+ if(hWndToolBarWnd)
+ {
+ return hWndToolBarWnd;
+ }
+ }
+
+ return hWndTrayNotifyWnd;
+ }
+
+ return hWndShellTrayWnd;
+}
+
+typedef BOOL (WINAPI *ISWOW64PROCESS)(HANDLE hProcess,PBOOL Wow64Process);
+BOOL IsRunningX64(void){
+ ISWOW64PROCESS iswow64process = (ISWOW64PROCESS)GetProcAddress(GetModuleHandle(L"kernel32"),"IsWow64Process");
+ if (iswow64process) {
+ BOOL Wow64Process = 0;
+ if(iswow64process(GetCurrentProcess(),&Wow64Process)){
+ return Wow64Process;
+ }
+ }
+ return FALSE;
+}
+
+struct TRAYDATA
+{
+ HWND hwnd;
+ UINT uID;
+ UINT uCallbackMessage;
+ DWORD Reserved[2];
+ HICON hIcon;
+};
+
+typedef struct _TBBUTTON64 {
+ int iBitmap;
+ int idCommand;
+ BYTE fsState;
+ BYTE fsStyle;
+ BYTE bReserved[6]; // padding for alignment
+ DWORD_PTR dwData;
+ INT_PTR iString;
+} TBBUTTON64, NEAR* PTBBUTTON64, *LPTBBUTTON64;
+typedef const TBBUTTON64 *LPCTBBUTTON64;
+
+// this is used on Win7+ installs where the OS has a direct api to allow for the querying of the icon position
+// most likely added natively due to the notification area fly out
+BOOL NotifyIconGetRect(LPRECT a_rcIcon){
+ if(g_Shell_NotifyIconGetRect){
+ NOTIFYICONIDENTIFIER niid = {sizeof(NOTIFYICONIDENTIFIER),plugin.hwndParent,SYSTRAY_ICON_BASE+5,0};
+ return SUCCEEDED(g_Shell_NotifyIconGetRect(&niid,a_rcIcon));
+ }
+ return FALSE;
+}
+
+//First tracking method: attaches to Tray process and reads data directly, is fast and reliable but will fail if user uses non standard tray software
+//It was suggested by Neal Andrews with VB example: http://www.codeproject.com/shell/ctrayiconposition.asp?select=999036&forumid=14631&df=100#xx999036xx
+//Ported to C++ by Ireneusz Zielinski
+//Made vaguely 64-bit compatible in v2.2 of this plugin
+BOOL FindOutPositionOfIconDirectly(HWND a_hWndOwner, const int a_iButtonID, LPRECT a_rcIcon)
+{
+ if(!NotifyIconGetRect(a_rcIcon))
+ {
+ DWORD dwTrayProcessID = -1, tbSize = (isX64?sizeof(TBBUTTON64):sizeof(TBBUTTON));
+ HANDLE hTrayProc = NULL;
+ int iButtonsCount = 0, iButton = 0;
+ LPVOID lpData = 0, lpData2 = 0;
+ BOOL bIconFound = FALSE;
+
+ //first of all let's find a Tool bar control embed in Tray window
+ HWND hWndTray = GetTrayNotifyWnd(TRUE);
+
+ if (hWndTray == NULL)
+ {
+ return FALSE;
+ }
+
+ //now we have to get an ID of the parent process for system tray
+ GetWindowThreadProcessId(hWndTray, &dwTrayProcessID);
+ if(!dwTrayProcessID)
+ {
+ return FALSE;
+ }
+
+ // need to use the older PROCESS_ALL_ACCESS define as it otherwise causes
+ // this to fail on all XP machines but will still work ok on Vista / Win7
+ #define PROCESS_ALL_ACCESS_XP (STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xFFF)
+ hTrayProc = OpenProcess(PROCESS_ALL_ACCESS_XP/*PROCESS_ALL_ACCESS*/, 0, dwTrayProcessID);
+ if(hTrayProc == NULL)
+ {
+ return FALSE;
+ }
+
+ //now we check how many buttons is there - should be more than 0
+ iButtonsCount = (int)SendMessage(hWndTray, TB_BUTTONCOUNT, 0, 0);
+
+ //We want to get data from another process - it's not possible to just send messages like TB_GETBUTTON with a localy
+ //allocated buffer for return data. Pointer to localy allocated data has no usefull meaning in a context of another
+ //process (since Win95) - so we need to allocate some memory inside Tray process.
+ //We allocate sizeof(TBBUTTON) bytes of memory - because TBBUTTON is the biggest structure we will fetch. But this buffer
+ //will be also used to get smaller pieces of data like RECT structures.
+ lpData = VirtualAllocEx(hTrayProc, NULL, tbSize, MEM_COMMIT, PAGE_READWRITE);
+ if(lpData == NULL || iButtonsCount < 1)
+ {
+ CloseHandle(hTrayProc);
+ return FALSE;
+ }
+
+ for(iButton = 0; iButton < iButtonsCount; iButton++)
+ {
+ HWND hWndOfIconOwner = 0;
+ int iIconId = 0;
+
+ //first let's read TBUTTON information about each button in a task bar of tray
+ SIZE_T dwBytesRead = -1;
+ TBBUTTON64 buttonData64 = {0};
+ TBBUTTON buttonData = {0};
+ TRAYDATA traydata = {0};
+
+ SendMessage(hWndTray, TB_GETBUTTON, iButton, (LPARAM)lpData);
+ ReadProcessMemory(hTrayProc, lpData, (isX64?(LPVOID)&buttonData64:&buttonData), tbSize, &dwBytesRead);
+
+ if(dwBytesRead < tbSize)
+ {
+ continue;
+ }
+
+ // now let's read extra data associated with each button: there will be a HWND of the window that created an icon and icon ID
+ ReadProcessMemory(hTrayProc, (LPCVOID)(isX64?buttonData64.dwData:buttonData.dwData), (LPVOID)&traydata, sizeof(TRAYDATA), &dwBytesRead);
+ if(dwBytesRead < sizeof(TRAYDATA))
+ {
+ continue;
+ }
+
+ // will get the hwnd and icon id of the 'button' being checked factoring for x86 and x64 structures
+ if(!isX64)
+ {
+ hWndOfIconOwner = traydata.hwnd;
+ iIconId = traydata.uID;
+ }
+ else
+ {
+ LPARAM *tb = (LPARAM*)&traydata;
+ hWndOfIconOwner = (HWND)tb[0];
+ iIconId = (int)tb[2];
+ }
+
+ if(hWndOfIconOwner != a_hWndOwner || iIconId != a_iButtonID)
+ {
+ continue;
+ }
+
+ //we found our icon - in WinXP/Vista+ it could be hidden - let's check it:
+ if(buttonData.fsState & TBSTATE_HIDDEN)
+ {
+ break;
+ }
+
+ //now just ask a tool bar of rectangle of our icon
+ SendMessage(hWndTray, TB_GETITEMRECT, iButton, (LPARAM)lpData);
+ ReadProcessMemory(hTrayProc, lpData, a_rcIcon, sizeof(RECT), &dwBytesRead);
+
+ if(dwBytesRead < sizeof(RECT))
+ {
+ continue;
+ }
+
+ MapWindowPoints(hWndTray, NULL, (LPPOINT)a_rcIcon, 2);
+ bIconFound = TRUE;
+ break;
+ }
+
+ VirtualFreeEx(hTrayProc, lpData, 0, MEM_RELEASE);
+ VirtualFreeEx(hTrayProc, lpData2, 0, MEM_RELEASE);
+ CloseHandle(hTrayProc);
+
+ return bIconFound;
+ }
+ return TRUE;
+}
+
+HICON CreateInternalIcon(void)
+{
+ HICON hGrayIcon = 0;
+ HDC hMainDC = 0, hMemDC1 = 0, hMemDC2 = 0;
+ BITMAP bmp = {0};
+ ICONINFO csII = {0}, csGrayII = {0};
+
+ // destroy the old version of the icon where possible otherwise we'll get a resource leak
+ // which can have a nasty effect if allowed to grow too large
+ if(Icons[5]){
+ DestroyIcon(Icons[5]);
+ }
+
+ // create a dummy base icon with which to work on (saves having to bundle a blank on in the dll)
+ if(!dummyIcon){dummyIcon = CreateIcon(plugin.hDllInstance,32,32,1,32,0,0);}
+
+ if(!GetIconInfo(dummyIcon,&csII)){return 0;}
+
+ if(!(hMainDC = GetDC(plugin.hwndParent)) || !(hMemDC1 = CreateCompatibleDC(hMainDC)) || !(hMemDC2 = CreateCompatibleDC(hMainDC))){
+ return 0;
+ }
+
+ if(GetObject(csII.hbmColor,sizeof(BITMAP),&bmp))
+ {
+ int width = 0, height = 0;
+ csGrayII.hbmColor = CreateBitmap((width = csII.xHotspot*2),(height = csII.yHotspot*2),bmp.bmPlanes,bmp.bmBitsPixel,0);
+ if(csGrayII.hbmColor){
+ int is_playing = (int)SendMessage(plugin.hwndParent,WM_WA_IPC,0,IPC_ISPLAYING), dwLoopY = 0, dwLoopX = 0;
+ // this is used for the temporary bitmap where the mask is created for the transparency (magic pink fun)
+ HBITMAP hAndMask = CreateCompatibleBitmap(hMemDC2,width,height);
+ HDC hAndMaskDC = CreateCompatibleDC(hMemDC2);
+ SelectObject(hAndMaskDC,hAndMask);
+
+ HBITMAP hOldBmp1 = (HBITMAP)SelectObject(hMemDC1,csII.hbmColor);
+ HBITMAP hOldBmp2 = (HBITMAP)SelectObject(hMemDC2,csGrayII.hbmColor);
+
+ SetStretchBltMode(hMemDC2,COLORONCOLOR);
+
+ // play or pause or 'blank' image would go here
+ SelectObject(hMemDC1,compact);
+ // not the most elegant code but it'll correctly select the play/pause icon as needed based on
+ // the current playback state and it's flip play/pause state as appropriately
+ StretchBlt(hMemDC2,0,0,16,16,hMemDC1,(!is_playing?0:(is_playing!=1?(flip?32:0):32)),0,8,8,SRCCOPY);
+
+ // open or stop image would go here
+ StretchBlt(hMemDC2,16,0,16,16,hMemDC1,(!is_playing?8:40),0,8,8,SRCCOPY);
+
+ // previous track image
+ StretchBlt(hMemDC2,0,16,16,16,hMemDC1,16,0,8,8,SRCCOPY);
+
+ // next track image
+ StretchBlt(hMemDC2,16,16,16,16,hMemDC1,24,0,8,8,SRCCOPY);
+
+ // process the image now that we've generated it
+ for(dwLoopX=0;dwLoopX<width;++dwLoopX)
+ {
+ for(dwLoopY=0;dwLoopY<height;++dwLoopY)
+ {
+ COLORREF MainBitPixel = GetPixel(hMemDC2,dwLoopX,dwLoopY);
+ // checks for magic pink and then will remove it and clear/set the relevant areas in the image mask
+ if(MainBitPixel == 0xff00ff)
+ {
+ SetPixel(hAndMaskDC,dwLoopX,dwLoopY,RGB(255,255,255));
+ SetPixel(hMemDC2,dwLoopX,dwLoopY,RGB(0,0,0));
+ }
+ else
+ {
+ SetPixel(hAndMaskDC,dwLoopX,dwLoopY,RGB(0,0,0));
+ SetPixel(hMemDC2,dwLoopX,dwLoopY,MainBitPixel);
+ }
+ }
+ }
+
+ // set the mask for the transparent areas, etc
+ csGrayII.hbmMask = hAndMask;
+
+ DeleteDC(hAndMaskDC);
+
+ SelectObject(hMemDC1,hOldBmp1);
+ SelectObject(hMemDC2,hOldBmp2);
+
+ csGrayII.fIcon = 1;
+ hGrayIcon = CreateIconIndirect(&csGrayII);
+ DeleteObject(hAndMask);
+ }
+
+ DeleteObject(csGrayII.hbmColor);
+ DeleteObject(csGrayII.hbmMask);
+ }
+
+ DeleteObject(csII.hbmColor);
+ DeleteObject(csII.hbmMask);
+ DeleteDC(hMemDC1);
+ DeleteDC(hMemDC2);
+ ReleaseDC(plugin.hwndParent,hMainDC);
+ return hGrayIcon;
+}
+
+LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ // this will detect the start of playback and allow us to query for the new compact mode text
+ // done in 2.1+ to resolve issues with the compact mode text not updating correctly in all cases
+ if(lParam == IPC_PLAYING_FILE){
+ update_file=0;
+ }
+
+ if((lParam == IPC_CB_MISC && wParam == IPC_CB_MISC_STATUS)){
+ if (config_enabled & (1<<5)) {
+ NOTIFYICONDATAW tnid={0};
+ tnid.cbSize=sizeof(NOTIFYICONDATAW);
+ tnid.hWnd=plugin.hwndParent;
+ tnid.uID=1029;
+ tnid.uFlags=NIF_ICON|NIF_TIP;
+ tnid.hIcon=(Icons[5] = CreateInternalIcon());
+
+ // force an update if stopping and the playlist is clear
+ if(!SendMessage(hwnd,WM_WA_IPC,0,IPC_ISPLAYING)){
+ if(!SendMessage(hwnd,WM_WA_IPC,0,IPC_GETLISTLENGTH)){
+ file.filetitle[0] = 0;
+ update_file=0;
+ }
+ }
+
+ FormCompactText(tnid.szTip,(sizeof(tnid.szTip)/sizeof(wchar_t)));
+ Shell_NotifyIcon(NIM_MODIFY,&tnid);
+ }
+ }
+
+ if(message == WM_TIMER && wParam == 64){
+ if (config_enabled & (1<<5)) {
+ if(SendMessage(hwnd,WM_WA_IPC,0,IPC_ISPLAYING) == 3){
+ NOTIFYICONDATAW tnid={0};
+ tnid.cbSize=sizeof(NOTIFYICONDATAW);
+ flip = !flip;
+ tnid.hWnd=plugin.hwndParent;
+ tnid.uID=1029;
+ tnid.uFlags=NIF_ICON|NIF_TIP;
+ tnid.hIcon=(Icons[5] = CreateInternalIcon());
+ FormCompactText(tnid.szTip,(sizeof(tnid.szTip)/sizeof(wchar_t)));
+ Shell_NotifyIcon(NIM_MODIFY,&tnid);
+ }
+ else
+ {
+ NOTIFYICONDATAW tnid={0};
+ // this resets the play/pause flashing so it's in a known state when not paused
+ tnid.cbSize=sizeof(NOTIFYICONDATAW);
+ tnid.hWnd=plugin.hwndParent;
+ tnid.uID=1029;
+ tnid.uFlags=(flip?NIF_ICON:0)|NIF_TIP;
+ // only re-create the icon when it's needed to be done otherwise, just update the tooltip
+ if(flip)
+ {
+ tnid.hIcon=(Icons[5] = CreateInternalIcon());
+ }
+ flip = 0;
+ FormCompactText(tnid.szTip,sizeof(tnid.szTip)/sizeof(wchar_t));
+ Shell_NotifyIcon(NIM_MODIFY,&tnid);
+ }
+ }
+ }
+
+ if (message == WM_USER+2707)
+ {
+ switch (LOWORD(lParam))
+ {
+ case WM_LBUTTONDOWN:
+ if (config_enabled) switch (LOWORD(wParam))
+ {
+ // previous icon
+ case 1024:
+ {
+ int a;
+ if ((a= (int)SendMessage(hwnd,WM_USER,0,IPC_ISPLAYING)) == 0) // not playing, let's hit prev
+ {
+ SendMessage(hwnd,WM_COMMAND,WINAMP_BUTTON1,0);
+ }
+ else if (a != 3) // restart or full previous action
+ {
+ if ((GetKeyState(VK_CONTROL)&0x1000) && SendMessage(hwnd,WM_USER,0,IPC_GETOUTPUTTIME) > 2000 )
+ {
+ SendMessage(hwnd,WM_COMMAND,WINAMP_BUTTON2,0); // restart (only on a ctrl+click)
+ }
+ else
+ {
+ SendMessage(hwnd,WM_COMMAND,WINAMP_BUTTON1,0); // move to the previous track and then start
+ }
+ }
+ else
+ { // prev
+ SendMessage(hwnd,WM_COMMAND,WINAMP_BUTTON1,0);
+ }
+ }
+ return 0;
+
+ // play/pause icon
+ case 1025:
+ if ((GetKeyState(VK_CONTROL)&0x1000) ) // restart the current track
+ {
+ SendMessage(hwnd,WM_COMMAND,WINAMP_BUTTON4,0);
+ SendMessage(hwnd,WM_COMMAND,WINAMP_BUTTON2,0);
+ }
+ else
+ {
+ // do play/pause switching to maintain current usability of the plugin
+ SendMessage(hwnd,WM_COMMAND,WINAMP_BUTTON2+(SendMessage(hwnd,WM_USER,0,IPC_ISPLAYING) == 1),0);
+ }
+ return 0;
+
+ // stop icon
+ case 1026:
+ SendMessage(hwnd,WM_COMMAND,WINAMP_BUTTON4 + ((GetKeyState(VK_SHIFT) & 0x1000)?100:0) ,0);
+ return 0;
+
+ // next icon
+ case 1027:
+ SendMessage(hwnd,WM_COMMAND,WINAMP_BUTTON5,0);
+ return 0;
+
+ // open file(s) icon
+ case 1028:
+ SetForegroundWindow(hwnd);
+ if (GetKeyState(VK_CONTROL) & (1<<15))
+ SendMessage(hwnd,WM_COMMAND,WINAMP_FILE_LOC,0);
+ else if (GetKeyState(VK_SHIFT) & (1<<15))
+ SendMessage(hwnd,WM_COMMAND,WINAMP_FILE_DIR,0);
+ else
+ SendMessage(hwnd,WM_COMMAND,WINAMP_FILE_PLAY,0);
+ return 0;
+
+ // 4way mode handling, etc
+ case 1029:
+ {
+ RECT rc = {0}, r = {0};
+
+ // Note: this isn't compatible with hidden icons on Win7 (still to be fixed for v2.4)
+ if(FindOutPositionOfIconDirectly(hwnd,1029,&rc))
+ {
+ int i = 0,
+ // on at least Win7 (possibly earlier) the icon size is different than a fixed size
+ // so for v2.3 we're just going to split the icon based on the reported size rather
+ // than assuming it is a 16x16 (really 18x18 icon) as Win7's taskbar is different!
+ height = (rc.bottom-rc.top)/2,
+ width = (rc.right-rc.left)/2,
+ x[4] = {0,width,0,width},
+ y[4] = {0,0,height,height};
+ POINT pt = {0};
+ GetCursorPos(&pt);
+ for(i = 0; i < 4; i++)
+ {
+ CopyRect(&r,&rc);
+ r.right = r.left+x[i]+width;
+ r.bottom = r.top+y[i]+height;
+
+ if(PtInRect(&r,pt))
+ {
+ switch(i)
+ {
+ // play/pause icon
+ case 0:
+ if ((GetKeyState(VK_CONTROL)&0x1000) ) // restart the current track
+ {
+ SendMessage(hwnd,WM_COMMAND,WINAMP_BUTTON4,0);
+ SendMessage(hwnd,WM_COMMAND,WINAMP_BUTTON2,0);
+ }
+ else
+ {
+ // do play/pause switching to maintain current usability of the plugin
+ SendMessage(hwnd,WM_COMMAND,WINAMP_BUTTON2+(SendMessage(hwnd,WM_USER,0,IPC_ISPLAYING) == 1),0);
+ }
+ break;
+
+ // open file(s) / stop icon
+ case 1:
+ if(!SendMessage(hwnd,WM_USER,0,IPC_ISPLAYING)){
+ SetForegroundWindow(hwnd);
+ if (GetKeyState(VK_CONTROL) & (1<<15))
+ SendMessage(hwnd,WM_COMMAND,WINAMP_FILE_LOC,0);
+ else if (GetKeyState(VK_SHIFT) & (1<<15))
+ SendMessage(hwnd,WM_COMMAND,WINAMP_FILE_DIR,0);
+ else
+ SendMessage(hwnd,WM_COMMAND,WINAMP_FILE_PLAY,0);
+ }
+ else{
+ SendMessage(hwnd,WM_COMMAND,WINAMP_BUTTON4 + ((GetKeyState(VK_SHIFT) & 0x1000)?100:0) ,0);
+ }
+ break;
+
+ // previous icon
+ case 2:
+ {
+ int a;
+ if ((a= (int)SendMessage(hwnd,WM_USER,0,IPC_ISPLAYING)) == 0) // not playing, let's hit prev
+ {
+ SendMessage(hwnd,WM_COMMAND,WINAMP_BUTTON1,0);
+ }
+ else if (a != 3) // restart or full previous action
+ {
+ if ((GetKeyState(VK_CONTROL)&0x1000) && SendMessage(hwnd,WM_USER,0,IPC_GETOUTPUTTIME) > 2000 )
+ {
+ SendMessage(hwnd,WM_COMMAND,WINAMP_BUTTON2,0); // restart (only on a ctrl+click)
+ }
+ else
+ {
+ SendMessage(hwnd,WM_COMMAND,WINAMP_BUTTON1,0); // move to the previous track and then start
+ }
+ }
+ else
+ { // prev
+ SendMessage(hwnd,WM_COMMAND,WINAMP_BUTTON1,0);
+ }
+ }
+ break;
+
+ // next icon
+ case 3:
+ SendMessage(hwnd,WM_COMMAND,WINAMP_BUTTON5,0);
+ break;
+ }
+ break;
+ }
+ }
+ }
+ }
+ return 0;
+
+ // vol down
+ case 1030:
+ {
+ int curvol = (int)SendMessage(hwnd,WM_WA_IPC,-666,IPC_SETVOLUME)-((GetKeyState(VK_CONTROL)&0x1000)?30:10);
+ if(curvol<0){curvol = 0;}
+ SendMessage(hwnd,WM_WA_IPC,curvol,IPC_SETVOLUME);
+ }
+ return 0;
+
+ // vol up
+ case 1031:
+ {
+ int curvol = (int)SendMessage(hwnd,WM_WA_IPC,-666,IPC_SETVOLUME)+((GetKeyState(VK_CONTROL)&0x1000)?30:10);
+ if(curvol>255){curvol = 255;}
+ SendMessage(hwnd,WM_WA_IPC,curvol,IPC_SETVOLUME);
+ }
+ return 0;
+ }
+ break;
+
+ case WM_RBUTTONDOWN:
+ if (config_enabled) switch (LOWORD(wParam))
+ {
+ // previousicon
+ case 1024:
+ {
+ SendMessage(hwnd,WM_COMMAND,WINAMP_BUTTON5,0);
+ }
+ break;
+
+ // next icon
+ case 1027:
+ {
+ int a;
+ if ((a= (int)SendMessage(hwnd,WM_USER,0,IPC_ISPLAYING)) == 0) // not playing, let's hit prev
+ {
+ SendMessage(hwnd,WM_COMMAND,WINAMP_BUTTON1,0);
+ }
+ else if (a != 3) // restart or full previous action
+ {
+ if ((GetKeyState(VK_CONTROL)&0x1000) && SendMessage(hwnd,WM_USER,0,IPC_GETOUTPUTTIME) > 2000 )
+ {
+ SendMessage(hwnd,WM_COMMAND,WINAMP_BUTTON2,0); // restart (only on a ctrl+click)
+ }
+ else
+ {
+ SendMessage(hwnd,WM_COMMAND,WINAMP_BUTTON1,0); // move to the previous track and then start
+ SendMessage(hwnd,WM_COMMAND,WINAMP_BUTTON2,0);
+ }
+ }
+ else
+ { // prev
+ SendMessage(hwnd,WM_COMMAND,WINAMP_BUTTON1,0);
+ }
+ }
+ break;
+
+ // vol down
+ case 1031:
+ {
+ int curvol = (int)SendMessage(hwnd,WM_WA_IPC,-666,IPC_SETVOLUME)-((GetKeyState(VK_CONTROL)&0x1000)?30:10);
+ if(curvol<0){curvol = 0;}
+ SendMessage(hwnd,WM_WA_IPC,curvol,IPC_SETVOLUME);
+ }
+ return 0;
+
+ // vol up
+ case 1030:
+ {
+ int curvol = (int)SendMessage(hwnd,WM_WA_IPC,-666,IPC_SETVOLUME)+((GetKeyState(VK_CONTROL)&0x1000)?30:10);
+ if(curvol>255){curvol = 255;}
+ SendMessage(hwnd,WM_WA_IPC,curvol,IPC_SETVOLUME);
+ }
+ return 0;
+ }
+ break;
+ }
+ }
+
+ {
+ int ret = (int)CallWindowProc(lpWndProcOld,hwnd,message,wParam,lParam);
+
+ // do this after passing the main batch of messages onto Winamp/rest of the subclass chain so
+ // that Winamp will restore its tray icon first and then we do ours (otherwise it looks silly)
+ if(message == s_uTaskbarRestart)
+ {
+ // have to force the icons to be displayed since there are none in the tray at this point
+ do_icons(1);
+ }
+
+ return ret;
+ }
+}
+
+// GetWindowsVersionRunningOnCompact(...)
+//
+// Function to get the version of windows being run on
+//
+// Optionally a 'short version[2]' can be passed into the
+// function as 'GetWindowsVersionRunningOnCompact(version)'
+// which allows the OS version to be returned for the user to
+// be able to make use of
+//
+int GetWindowsVersionRunningOnCompact(DWORD* version)
+{
+ OSVERSIONINFO osvi = {sizeof(OSVERSIONINFO),0};
+ int ver_detect = -1;
+
+ // Win9x detection
+ //
+ // Windows 95 - Major 4 & Minor 0 ver_detect = 1
+ // Windows 98 - Major 4 & Minor 10 ver_detect = 2
+ // Windows ME - Major 4 & Minor 90 ver_detect = 3
+ //
+ // Win NT detection
+ //
+ // Windows NT 3.51 - Major 3 & Minor 51 ver_detect = 4
+ // Windows NT 4 - Major 4 & Minor 0 ver_detect = 5
+ // Windows 2000 - Major 5 & Minor 0 ver_detect = 6
+ // Windows XP - Major 5 & Minor 1 ver_detect = 7
+ // Windows Server 2003 - Major 5 & Minor 2 ver_detect = 8
+ // Windows Vista - Major 6 & Minor 0 ver_detect = 9
+ // Windows 7 - Major 6 & Minor 1 ver_detect = 9
+ // Windows 8 - Major 6 & Minor 2 ver_detect = 9
+ // Windows 8.1 - Major 6 & Minor 3 ver_detect = 9
+ // Windows 10 - Major 10 & Minor 0 ver_detect = 10
+ // Windows 11 - Major 11 & Minor 0 ver_detect = 11
+ //
+ // Unknown OS version ver_detect = -1
+
+ GetVersionEx(&osvi);
+
+ // is it a Win9x platform that we are running on?
+ if(osvi.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
+ {
+ // Windows 98 (4.10)
+ if(osvi.dwMinorVersion == 10){
+ ver_detect = 2;
+ }
+
+ // Windows ME (4.90)
+ else if(osvi.dwMinorVersion == 90){
+ ver_detect = 3;
+ }
+
+ // Windows 95 (4.0)
+ else {
+ ver_detect = 1;
+ }
+ }
+
+ // is it a WinNT platform that we are running on?
+ else if(osvi.dwPlatformId == VER_PLATFORM_WIN32_NT)
+ {
+ // Windows NT 4 (4.0)
+ if(osvi.dwMajorVersion == 4 && osvi.dwMinorVersion == 0)
+ {
+ ver_detect = 5;
+ }
+
+ else if(osvi.dwMajorVersion == 5)
+ {
+ // Windows XP (5.1)
+ if(osvi.dwMinorVersion == 1)
+ {
+ ver_detect = 7;
+ }
+
+ // Windows Server 2003 (5.2)
+ else if(osvi.dwMinorVersion == 2)
+ {
+ ver_detect = 8;
+ }
+
+ // Windows 2000 (5.0)
+ else
+ {
+ ver_detect = 6;
+ }
+ }
+
+ // Windows Vista/7/8/8.1 (6.0)
+ else if(osvi.dwMajorVersion == 6)
+ {
+ ver_detect = 9;
+ }
+
+ // Windows 10 (10.0)
+ else if(osvi.dwMajorVersion == 10)
+ {
+ ver_detect = 10;
+ }
+
+ // Windows 11 (11.0)
+ else if(osvi.dwMajorVersion == 11)
+ {
+ ver_detect = 11;
+ }
+
+ // Windows NT 3.51 (3.51)
+ else
+ {
+ ver_detect = 4;
+ }
+ }
+ else
+ {
+ ver_detect = -1;
+ }
+
+ // copies the value into the structure
+ if(version)
+ {
+ *version = MAKELONG(osvi.dwMinorVersion,osvi.dwMajorVersion);
+ }
+
+ return ver_detect;
+}
+
+void GetWinampPath(void)
+{
+ wchar_t* p = wa_path;
+ p += GetModuleFileName(0,wa_path,ARRAYSIZE(wa_path)) - 1;
+ while(p && *p && *p != L'\\'){p = CharPrev(wa_path,p);}
+ if (p) *p = 0;
+}
+
+int init(void)
+{
+ // 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 == (api_service*)1) WASABI_API_SVC = NULL;
+ if (!WASABI_API_SVC || WASABI_API_SVC == (api_service *)1)
+ return GEN_INIT_FAILURE;
+
+ xporhigher = ((winver=GetWindowsVersionRunningOnCompact(0))>6);
+ isX64 = IsRunningX64();
+ s_uTaskbarRestart = RegisterWindowMessage(TEXT("TaskbarCreated"));
+ g_Shell_NotifyIconGetRect = (SHELL_NOTIFYICONGETRECT)GetProcAddress(GetModuleHandle(L"SHELL32"),"Shell_NotifyIconGetRect");
+
+ waServiceFactory *sf = WASABI_API_SVC->service_getServiceByGuid(languageApiGUID);
+ if (sf) WASABI_API_LNG = reinterpret_cast<api_language*>(sf->getInterface());
+
+ // need to have this initialised before we try to do anything with localisation features
+ WASABI_API_START_LANG(plugin.hDllInstance,GenTrayLangGUID);
+
+ StringCchPrintf(szDescription, ARRAYSIZE(szDescription),
+ WASABI_API_LNGSTRINGW(IDS_NULLSOFT_TRAY_CONTROL), PLUGIN_VERSION);
+ plugin.description = (char*)szDescription;
+
+ GetWinampPath();
+ config_read();
+
+ if (IsWindowUnicode(plugin.hwndParent))
+ lpWndProcOld = (WNDPROC)SetWindowLongPtrW(plugin.hwndParent,GWLP_WNDPROC,(LPARAM)WndProc);
+ else
+ lpWndProcOld = (WNDPROC)SetWindowLongPtrA(plugin.hwndParent,GWLP_WNDPROC,(LPARAM)WndProc);
+
+ do_icons(0);
+
+ return 0;
+}
+
+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 CALLBACK ConfigProc(HWND hwndDlg,UINT uMsg,WPARAM wParam,LPARAM lParam)
+{
+ switch (uMsg)
+ {
+ case WM_INITDIALOG:
+ {
+ int count = CB_ERR;
+ configwnd = hwndDlg;
+ dlg_init = 1;
+ link_startsubclass(hwndDlg,IDC_LINK);
+
+ CheckDlgButton(hwndDlg,IDC_ONOFF,on?BST_CHECKED:BST_UNCHECKED);
+
+ for (int i = 0; i < NUM_ICONS; i++)
+ {
+ wchar_t str[512] = {0}, tmp[256] = {0}, tmp2[256] = {0};
+ CheckDlgButton(hwndDlg,IDC_PREV+i,(config_enabled&(1<<i))?BST_CHECKED:BST_UNCHECKED);
+ StringCchPrintf(str,512,L"%s %s",
+ WASABI_API_LNGSTRINGW_BUF(tips[i],tmp2,256),
+ (tips_ex[i]!=-1?WASABI_API_LNGSTRINGW_BUF(tips_ex[i],tmp,256):L""));
+ SetDlgItemText(hwndDlg,IDC_PREV+i,str);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_PREV+i),on);
+ }
+
+ EnableWindow(GetDlgItem(hwndDlg,IDC_COMBO2),on);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_BUTTON1),custom_enabled && on);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_EDIT1),custom_enabled && on);
+
+ SetDlgItemText(hwndDlg,IDC_EDIT1,ico_pack_safe);
+
+ dlg_init = 0;
+
+ WIN32_FIND_DATA findfile = {0};
+ wchar_t use[MAX_PATH] = {0};
+
+ StringCchPrintf(use,MAX_PATH,L"%s\\Plugins\\Tray_Control\\*.*",wa_path);
+ HANDLE hFind = FindFirstFile(use, &findfile);
+
+ while(hFind && hFind != INVALID_HANDLE_VALUE)
+ {
+ if(findfile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY){
+ WIN32_FIND_DATA subfile = {0};
+ StringCchPrintf(use,MAX_PATH,L"%s\\Plugins\\Tray_Control\\%s\\*.icp",
+ wa_path,findfile.cFileName,findfile.cFileName);
+
+ HANDLE hsubFind = FindFirstFile(use,&subfile);
+
+ while(hsubFind && hsubFind != INVALID_HANDLE_VALUE){
+ wchar_t icpname[MAX_PATH] = {0};
+ StringCchPrintf(icpname,MAX_PATH,
+ L"%s\\Plugins\\Tray_Control\\%s\\%s",
+ wa_path,findfile.cFileName,subfile.cFileName);
+
+ if(PathFileExists(icpname))
+ {
+ // need to ideally make this one work with CharPrev(..)
+ wchar_t* p = subfile.cFileName + lstrlen(subfile.cFileName) - 1;
+ while(p && *p && *p != L'.'){p = CharPrevW(subfile.cFileName, p);}
+ if (p) *p = 0;
+ if(!lstrcmpiW(findfile.cFileName,subfile.cFileName))
+ {
+ SendDlgItemMessageW(hwndDlg,IDC_COMBO2,CB_ADDSTRING,0,(LPARAM)subfile.cFileName);
+ }
+ else
+ {
+ wchar_t str[MAX_PATH] = {0};
+ int insertpos = 0;
+ StringCchPrintf(str,MAX_PATH,L"%s\\%s",findfile.cFileName,subfile.cFileName);
+ insertpos = (int)SendDlgItemMessage(hwndDlg,IDC_COMBO2,CB_ADDSTRING,0,(LPARAM)str);
+ if(insertpos != CB_ERR)
+ {
+ SendDlgItemMessage(hwndDlg,IDC_COMBO2,CB_SETITEMDATA,insertpos,1);
+ }
+ }
+ }
+
+ // if there are no more files then stop the search
+ if(hsubFind && !FindNextFile(hsubFind, &subfile))
+ {
+ FindClose(hsubFind);
+ hsubFind = 0;
+ }
+ }
+ }
+
+ // if there are no more files then stop the search
+ if(hFind && !FindNextFileW(hFind, &findfile))
+ {
+ FindClose(hFind);
+ hFind = 0;
+ }
+ }
+
+ EnableWindow(GetDlgItem(hwndDlg,IDC_EDIT1),custom_enabled);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_BUTTON1),custom_enabled);
+
+ EnableWindow(GetDlgItem(hwndDlg,IDC_PREV6),winver>=6 && on);
+
+ SendDlgItemMessage(hwndDlg,IDC_COMBO2,CB_INSERTSTRING,0,
+ (LPARAM)WASABI_API_LNGSTRINGW(IDS_DEFAULT_ICONS));
+ SendDlgItemMessage(hwndDlg,IDC_COMBO2,CB_INSERTSTRING,
+ (count = (int)SendDlgItemMessage(hwndDlg,IDC_COMBO2,CB_GETCOUNT,0,0)),
+ (LPARAM)WASABI_API_LNGSTRINGW(IDS_CUSTOM_ICON_PACK));
+
+ if (!custom_enabled)
+ {
+ if(ico_pack[0])
+ {
+ wchar_t base[MAX_PATH] = {0}, *pack = 0, use[MAX_PATH] = {0};
+ StringCchPrintf(use,MAX_PATH,L"%s\\Plugins\\Tray_Control\\",wa_path);
+ lstrcpyn(base,ico_pack,ARRAYSIZE(base));
+
+ if(StrStrI(base,use))
+ {
+ pack = PathFindFileName(base);
+ if (pack && *pack)
+ {
+ PathRemoveExtension(pack);
+ }
+ }
+
+ count = (int)SendDlgItemMessage(hwndDlg,IDC_COMBO2,CB_FINDSTRINGEXACT,0,(LPARAM)pack);
+ if(count == CB_ERR)
+ {
+ count = 0;
+ }
+ }
+ else
+ {
+ count = 0;
+ }
+ }
+
+ SendDlgItemMessage(hwndDlg,IDC_COMBO2,CB_SETCURSEL,count,0);
+ SetFocus(GetDlgItem(hwndDlg,IDC_COMBO2));
+ }
+ break;
+
+ // mimicks the get... links in the winamp preferences
+ case WM_DRAWITEM:
+ {
+ DRAWITEMSTRUCT* lpdis = (DRAWITEMSTRUCT*)lParam;
+ if(lpdis->CtlID == IDC_LINK)
+ {
+ HFONT OldFont = 0;
+ LOGFONT lf = {0};
+ RECT rc = lpdis->rcItem, r = {0};
+ POINT pt = {0};
+ int in = 0;
+ wchar_t gicpStr[128] = {0};
+
+ WASABI_API_LNGSTRINGW_BUF(IDS_GET_ICON_PACKS,gicpStr,128);
+ GetObject(GetCurrentObject(lpdis->hDC,OBJ_FONT),sizeof(lf),&lf);
+ lf.lfUnderline = TRUE;
+ OldFont = (HFONT)SelectObject(lpdis->hDC,CreateFontIndirect(&lf));
+
+ // Calculate needed size of the control
+ DrawText(lpdis->hDC,gicpStr,-1,&rc,DT_VCENTER|DT_SINGLELINE|DT_CALCRECT);
+
+ // Make some more room so the focus rect won't cut letters off
+ rc.right = min(rc.right + 2, lpdis->rcItem.right);
+
+ GetWindowRect(lpdis->hwndItem,&r);
+ GetCursorPos(&pt);
+ in = PtInRect(&r,pt);
+ SetTextColor(lpdis->hDC,(COLORREF)RGB((in?255:0),0,(!in?255:0)));
+
+ // Draw the text
+ DrawText(lpdis->hDC,gicpStr,-1,&rc,DT_CENTER|DT_VCENTER|DT_SINGLELINE);
+
+ DeleteObject(SelectObject(lpdis->hDC, OldFont));
+ }
+ }
+ break;
+
+ case WM_COMMAND:
+ if (LOWORD(wParam) >= IDC_PREV && LOWORD(wParam) <= IDC_PREV+NUM_ICONS)
+ {
+ config_enabled=0;
+ for (int i = 0; i < NUM_ICONS; i++)
+ if (IsDlgButtonChecked(hwndDlg,IDC_PREV+i))
+ config_enabled |= 1<<i;
+ do_icons(0);
+ }
+
+ else if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
+ {
+ GetDlgItemText(hwndDlg,IDC_EDIT1,ico_pack_safe,ARRAYSIZE(ico_pack_safe));
+ config_write();
+ EndDialog(hwndDlg,0);
+ configwnd = 0;
+ }
+
+ else if (LOWORD(wParam) == IDC_ONOFF)
+ {
+ on = (IsDlgButtonChecked(hwndDlg,LOWORD(wParam))==BST_CHECKED);
+ for (int i = 0; i < NUM_ICONS; i++)
+ {
+ EnableWindow(GetDlgItem(hwndDlg,IDC_PREV+i),on);
+ }
+ EnableWindow(GetDlgItem(hwndDlg,IDC_PREV6),winver>=6 && on);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_COMBO2),on);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_BUTTON1),custom_enabled && on);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_EDIT1),custom_enabled && on);
+
+ do_icons(1);
+ }
+
+ else if (LOWORD(wParam) == ID_INFO)
+ {
+ wchar_t str[2560] = {0}, str1[2048] = {0};
+ StringCchPrintf(str, 2560, WASABI_API_LNGSTRINGW_BUF(IDS_CONFIG_INFO, str1, 2048), wa_path);
+ MessageBoxW(hwndDlg,str,szDescription,0);
+ }
+
+ else if (LOWORD(wParam) == IDC_LINK && HIWORD(wParam) == BN_CLICKED)
+ {
+ SendMessage(plugin.hwndParent,WM_WA_IPC,(WPARAM)"https://winampheritage.com/plugin/nullsoft-tray-control-plug-in-icon-pack/222396",IPC_OPEN_URL);
+ }
+
+ else if (LOWORD(wParam) == IDC_BUTTON1)
+ {
+ OPENFILENAME of = {sizeof(OPENFILENAME),0};
+ wchar_t titleStr[128], filterStr[128] = {0};
+
+ of.hwndOwner = hwndDlg;
+ of.hInstance = plugin.hDllInstance;
+ of.lpstrFilter = WASABI_API_LNGSTRINGW_BUF(IDS_OFD_FILTER_STR,filterStr,128);
+ StringCchCat(filterStr+lstrlen(filterStr)+1,128,L"*.icp");
+ of.nMaxCustFilter = 64;
+ of.lpstrFile = ico_pack;
+ of.nMaxFile = ARRAYSIZE(ico_pack);
+ of.lpstrTitle = WASABI_API_LNGSTRINGW_BUF(IDS_OFD_TITLE_STR,titleStr,128);
+ of.nMaxFileTitle = lstrlen(of.lpstrTitle);
+ of.Flags = OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT|OFN_EXPLORER|OFN_PATHMUSTEXIST|OFN_CREATEPROMPT|OFN_ENABLESIZING|OFN_ALLOWMULTISELECT;
+ of.lpstrDefExt = L"icp";
+
+ if(GetOpenFileName(&of))
+ {
+ SetDlgItemText(hwndDlg,IDC_EDIT1,ico_pack);
+ do_icons(1);
+ }
+ }
+
+ else if (LOWORD(wParam) == IDC_EDIT1 && HIWORD(wParam)==EN_CHANGE)
+ {
+ if(!dlg_init)
+ {
+ GetDlgItemText(hwndDlg,IDC_EDIT1,ico_pack,ARRAYSIZE(ico_pack));
+ do_icons(1);
+ }
+ }
+
+ else if (LOWORD(wParam) == IDC_COMBO2 && HIWORD(wParam)== CBN_SELCHANGE)
+ {
+ wchar_t buf[MAX_PATH] = {0};
+ int cursel = (int)SendDlgItemMessage(hwndDlg,IDC_COMBO2,CB_GETCURSEL,0,0);
+ SendDlgItemMessage(hwndDlg,IDC_COMBO2,CB_GETLBTEXT,cursel,(LPARAM)buf);
+
+ custom_enabled=0;
+
+ if(cursel && cursel != CB_ERR)
+ {
+ if(cursel != (SendDlgItemMessage(hwndDlg,IDC_COMBO2,CB_GETCOUNT,0,0)-1))
+ {
+ // this will detect if it was a multi-entry style icon pack and form as required
+ if(!SendDlgItemMessage(hwndDlg,IDC_COMBO2,CB_GETITEMDATA,cursel,0))
+ {
+ StringCchPrintf(ico_pack,MAX_PATH,L"%s\\Plugins\\Tray_Control\\%s\\%s.icp",wa_path,buf,buf);
+ }
+ else
+ {
+ StringCchPrintf(ico_pack,MAX_PATH,L"%s\\Plugins\\Tray_Control\\%s.icp",wa_path,buf);
+ }
+ }
+ else
+ {
+ custom_enabled=1;
+ GetDlgItemText(hwndDlg,IDC_EDIT1,ico_pack,ARRAYSIZE(ico_pack));
+ }
+ }
+ else
+ {
+ ico_pack[0] = 0;
+ }
+
+ EnableWindow(GetDlgItem(hwndDlg,IDC_EDIT1),custom_enabled);
+ EnableWindow(GetDlgItem(hwndDlg,IDC_BUTTON1),custom_enabled);
+
+ do_icons(1);
+ }
+ break;
+ }
+ return FALSE;
+}
+
+void config_read(void)
+{
+ // this will only correctly work with Winamp 2.9+/5.x+
+ // see IPC_GETINIFILE for a way to query the location of Winamp.ini correctly
+ // whatever version of Winamp is being run on
+ // as of v2.41 this now uses IPC_GETINIFILEW though it's trivial to use
+ // IPC_GETINIFILE as a fallback on older clients (built for 5.58+)
+ ini_file=(wchar_t*)SendMessage(plugin.hwndParent,WM_USER,0,IPC_GETINIFILEW);
+ config_enabled = GetPrivateProfileInt(PLUGIN_NAME,L"BEN",config_enabled,ini_file);
+ custom_enabled = GetPrivateProfileInt(PLUGIN_NAME,L"custom",custom_enabled,ini_file);
+ on = GetPrivateProfileInt(PLUGIN_NAME,L"on",on,ini_file);
+ GetPrivateProfileString(PLUGIN_NAME,L"ico_pack",ico_pack,ico_pack,ARRAYSIZE(ico_pack),ini_file);
+ GetPrivateProfileString(PLUGIN_NAME,L"ico_pack_safe",ico_pack_safe,ico_pack_safe,ARRAYSIZE(ico_pack_safe),ini_file);
+}
+
+void config_write(void)
+{
+ if(!no_uninstall) return;
+
+ wchar_t string[32] = {0};
+ StringCchPrintf(string,32,L"%d",config_enabled);
+ WritePrivateProfileString(PLUGIN_NAME,L"BEN",string,ini_file);
+ WritePrivateProfileString(PLUGIN_NAME,L"ico_pack",ico_pack,ini_file);
+ WritePrivateProfileString(PLUGIN_NAME,L"ico_pack_safe",ico_pack_safe,ini_file);
+ StringCchPrintf(string,32,L"%d",custom_enabled);
+ WritePrivateProfileString(PLUGIN_NAME,L"custom",string,ini_file);
+ StringCchPrintf(string,32,L"%d",on);
+ WritePrivateProfileString(PLUGIN_NAME,L"on",string,ini_file);
+}
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ __declspec(dllexport) int winampUninstallPlugin(HINSTANCE hDllInst, HWND hwndDlg, int param){
+ // prompt to remove our settings with default as no (just incase)
+ if(MessageBoxW(hwndDlg,WASABI_API_LNGSTRINGW(IDS_DO_YOU_ALSO_WANT_TO_REMOVE_SETTINGS),
+ szDescription,MB_YESNO|MB_DEFBUTTON2) == IDYES)
+ {
+ WritePrivateProfileString(PLUGIN_NAME,0,0,ini_file);
+ no_uninstall = 0;
+ }
+
+ // as we're doing too much in subclasses, etc we cannot allow for on-the-fly removal so need to do a normal reboot
+ return GEN_PLUGIN_UNINSTALL_REBOOT;
+ }
+
+#ifdef __cplusplus
+}
+#endif \ No newline at end of file
diff --git a/Src/Plugins/General/gen_tray/WINAMPCMD.H b/Src/Plugins/General/gen_tray/WINAMPCMD.H
new file mode 100644
index 00000000..068b55ce
--- /dev/null
+++ b/Src/Plugins/General/gen_tray/WINAMPCMD.H
@@ -0,0 +1,62 @@
+#define WINAMP_FILE_QUIT 40001
+#define WINAMP_OPTIONS_PREFS 40012
+#define WINAMP_OPTIONS_AOT 40019
+#define WINAMP_FILE_REPEAT 40022
+#define WINAMP_FILE_SHUFFLE 40023
+#define WINAMP_HIGH_PRIORITY 40025
+#define WINAMP_FILE_PLAY 40029
+#define WINAMP_OPTIONS_EQ 40036
+#define WINAMP_OPTIONS_ELAPSED 40037
+#define WINAMP_OPTIONS_REMAINING 40038
+#define WINAMP_OPTIONS_PLEDIT 40040
+#define WINAMP_HELP_ABOUT 40041
+#define WINAMP_MAINMENU 40043
+#define WINAMP_BUTTON1 40044
+#define WINAMP_BUTTON2 40045
+#define WINAMP_BUTTON3 40046
+#define WINAMP_BUTTON4 40047
+#define WINAMP_BUTTON5 40048
+#define WINAMP_VOLUMEUP 40058
+#define WINAMP_VOLUMEDOWN 40059
+#define WINAMP_FFWD5S 40060
+#define WINAMP_REW5S 40061
+#define WINAMP_NEXT_WINDOW 40063
+#define WINAMP_OPTIONS_WINDOWSHADE 40064
+#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_OPTIONS_DSIZE 40165
+#define IDC_SORT_FILENAME 40166
+#define IDC_SORT_FILETITLE 40167
+#define IDC_SORT_ENTIREFILENAME 40168
+#define IDC_SELECTALL 40169
+#define IDC_SELECTNONE 40170
+#define IDC_SELECTINV 40171
+#define IDM_EQ_LOADPRE 40172
+#define IDM_EQ_LOADMP3 40173
+#define IDM_EQ_LOADDEFAULT 40174
+#define IDM_EQ_SAVEPRE 40175
+#define IDM_EQ_SAVEMP3 40176
+#define IDM_EQ_SAVEDEFAULT 40177
+#define IDM_EQ_DELPRE 40178
+#define IDM_EQ_DELMP3 40180
+#define IDC_PLAYLIST_PLAY 40184
+#define WINAMP_FILE_LOC 40185
+#define WINAMP_OPTIONS_EASYMOVE 40186
+#define WINAMP_FILE_DIR 40187
+#define WINAMP_EDIT_ID3 40188
+#define WINAMP_TOGGLE_AUTOSCROLL 40189
+#define WINAMP_VISSETUP 40190
+#define WINAMP_PLGSETUP 40191
+#define WINAMP_VISPLUGIN 40192
+#define WINAMP_JUMP 40193
+#define WINAMP_JUMPFILE 40194
+#define WINAMP_JUMP10FWD 40195
+#define WINAMP_JUMP10BACK 40197
diff --git a/Src/Plugins/General/gen_tray/api__gen_tray.h b/Src/Plugins/General/gen_tray/api__gen_tray.h
new file mode 100644
index 00000000..d50e2c43
--- /dev/null
+++ b/Src/Plugins/General/gen_tray/api__gen_tray.h
@@ -0,0 +1,8 @@
+#ifndef NULLSOFT_GEN_TRAY_API_H
+#define NULLSOFT_GEN_TRAY_API_H
+
+#include "api/service/api_service.h"
+#include "api/service/waServiceFactory.h"
+#include "../Agave/Language/api_language.h"
+
+#endif // !NULLSOFT_GEN_TRAY_API_H \ No newline at end of file
diff --git a/Src/Plugins/General/gen_tray/gen_tray.rc b/Src/Plugins/General/gen_tray/gen_tray.rc
new file mode 100644
index 00000000..f0c2e76c
--- /dev/null
+++ b/Src/Plugins/General/gen_tray/gen_tray.rc
@@ -0,0 +1,178 @@
+// 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, 215, 212
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Nullsoft Notification Area Control Preferences"
+FONT 8, "MS Shell Dlg", 0, 0, 0x0
+BEGIN
+ CONTROL "Enable Nullsoft Notification Area Control",IDC_ONOFF,
+ "Button",BS_AUTOCHECKBOX | BS_MULTILINE | WS_TABSTOP,4,4,207,10
+ CONTROL "",IDC_PREV,"Button",BS_AUTOCHECKBOX | BS_MULTILINE | WS_TABSTOP,12,18,199,10
+ CONTROL "",IDC_PREV2,"Button",BS_AUTOCHECKBOX | BS_MULTILINE | WS_TABSTOP,12,32,199,10
+ CONTROL "",IDC_PREV3,"Button",BS_AUTOCHECKBOX | BS_MULTILINE | WS_TABSTOP,12,45,199,10
+ CONTROL "",IDC_PREV4,"Button",BS_AUTOCHECKBOX | BS_MULTILINE | WS_TABSTOP,12,58,199,10
+ CONTROL "",IDC_PREV5,"Button",BS_AUTOCHECKBOX | BS_MULTILINE | WS_TABSTOP,12,72,199,10
+ CONTROL "",IDC_PREV6,"Button",BS_AUTOCHECKBOX | BS_MULTILINE | WS_TABSTOP,12,85,199,10
+ CONTROL "",IDC_PREV7,"Button",BS_AUTOCHECKBOX | BS_MULTILINE | WS_TABSTOP,12,98,199,10
+ CONTROL "",IDC_PREV8,"Button",BS_AUTOCHECKBOX | BS_MULTILINE | WS_TABSTOP,12,111,199,10
+ GROUPBOX "Icon Pack Selection",IDC_STATIC,4,124,207,67
+ LTEXT "Select the icon pack to use instead of the default ones or select a custom option to manually specify the pack location",IDC_STATIC,10,136,195,18
+ COMBOBOX IDC_COMBO2,10,157,178,58,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP
+ PUSHBUTTON "?",ID_INFO,192,157,14,12
+ EDITTEXT IDC_EDIT1,10,173,158,12,ES_AUTOHSCROLL
+ PUSHBUTTON "...",IDC_BUTTON1,172,173,16,12
+ CONTROL "",IDC_LINK,"Button",BS_OWNERDRAW | BS_NOTIFY | WS_TABSTOP,4,198,53,10
+ DEFPUSHBUTTON "Close",IDOK,162,195,49,13
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO
+BEGIN
+ IDD_DIALOG1, DIALOG
+ BEGIN
+ LEFTMARGIN, 4
+ RIGHTMARGIN, 211
+ TOPMARGIN, 4
+ BOTTOMMARGIN, 208
+ 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
+ "#include ""version.rc2""\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_ICON1 ICON "icons/icon1.ico"
+IDI_ICON2 ICON "icons/icon2.ico"
+IDI_ICON3 ICON "icons/icon3.ico"
+IDI_ICON4 ICON "icons/icon4.ico"
+IDI_ICON5 ICON "icons/icon5.ico"
+IDI_ICON7 ICON "icons/icon7.ico"
+IDI_ICON8 ICON "icons/icon8.ico"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Bitmap
+//
+
+IDB_BITMAP1 BITMAP "icons\\compact.bmp"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE
+BEGIN
+ IDS_NULLSOFT_TRAY_CONTROL "Nullsoft Notification Area Control v%s"
+ 65535 "{25B50046-5B31-418b-B77E-1B0D140D64ED}"
+END
+
+STRINGTABLE
+BEGIN
+ IDS_PREVIOUS_TRACK "Previous Track"
+ IDS_PLAY_PAUSE "Play/Pause"
+ IDS_STOP "Stop"
+ IDS_NEXT_TRACK "Next Track"
+ IDS_OPEN_FILE "Open File"
+ IDS_COMPACT_MODE "Compact Mode"
+ IDS_DECREASE_VOLUME "Decrease Volume"
+ IDS_INCREASE_VOLUME "Increase Volume"
+ IDS_HOLD_CTRL "(hold Ctrl to restart current song)"
+ IDS_HOLD_SHIFT "(hold Shift for 'stop with fadeout')"
+// IDS_WIN2K_PLUS "(only available on WinXP and up)"
+ IDS_CTRL_TO_DECREASE "(hold Ctrl to decrease faster)"
+ IDS_CTRL_TO_INCREASE "(hold Ctrl to increase faster)"
+ IDS_STOPPED_STR " [Stopped]"
+ IDS_PAUSED_STR " [Paused]"
+ IDS_DEFAULT_ICONS "Default Icons"
+END
+
+STRINGTABLE
+BEGIN
+ IDS_CUSTOM_ICON_PACK "Custom Icon Pack..."
+ IDS_CONFIG_INFO "Notification area icon packs consist of upto 9 files (1 *.icp, 7 *.ico and 1 *.bmp) \n& all of the required files need to be in the same folder to be found.\n\nThe format of the icp file is:\n\n[tray icon pack]\nico1=1.ico\nico2=2.ico\nico3=3.ico\nico4=4.ico\nico5=5.ico\nico6=my.bmp\nico7=7.ico\nico8=8.ico\n\nOrder is: Previous, Play/Pause, Stop, Next, Open, Compact Mode\n\tVolume Down and Volume Up\n\nEach line requires just the image filename to be specified.\nThe plug-in then looks for them in the same folder as the icp file.\n\nThe 'ico6' entry provides the image for the compact mode icon and is\nmade up of 6 8x8 pixel blocks which are merged together as needed\nto form the final 16x16 icon. For transparent areas you will need to\nset the required areas in your bitmap to pink (RGB 255,0,255).\n\nThe default icon pack location is:\n%s\\Plugins\\Tray_Control"
+ IDS_OFD_FILTER_STR "Notification area icon pack (*.icp)"
+ IDS_OFD_TITLE_STR "Choose a notification area icon pack to use..."
+ IDS_GET_ICON_PACKS "Get Icon Packs"
+ IDS_DO_YOU_ALSO_WANT_TO_REMOVE_SETTINGS
+ "Do you also want to remove the saved settings for this plug-in?"
+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_tray/icons/compact.bmp b/Src/Plugins/General/gen_tray/icons/compact.bmp
new file mode 100644
index 00000000..be7da86f
--- /dev/null
+++ b/Src/Plugins/General/gen_tray/icons/compact.bmp
Binary files differ
diff --git a/Src/Plugins/General/gen_tray/icons/icon1.ico b/Src/Plugins/General/gen_tray/icons/icon1.ico
new file mode 100644
index 00000000..37231154
--- /dev/null
+++ b/Src/Plugins/General/gen_tray/icons/icon1.ico
Binary files differ
diff --git a/Src/Plugins/General/gen_tray/icons/icon2.ico b/Src/Plugins/General/gen_tray/icons/icon2.ico
new file mode 100644
index 00000000..9dd35686
--- /dev/null
+++ b/Src/Plugins/General/gen_tray/icons/icon2.ico
Binary files differ
diff --git a/Src/Plugins/General/gen_tray/icons/icon3.ico b/Src/Plugins/General/gen_tray/icons/icon3.ico
new file mode 100644
index 00000000..61f36238
--- /dev/null
+++ b/Src/Plugins/General/gen_tray/icons/icon3.ico
Binary files differ
diff --git a/Src/Plugins/General/gen_tray/icons/icon4.ico b/Src/Plugins/General/gen_tray/icons/icon4.ico
new file mode 100644
index 00000000..e1a15db9
--- /dev/null
+++ b/Src/Plugins/General/gen_tray/icons/icon4.ico
Binary files differ
diff --git a/Src/Plugins/General/gen_tray/icons/icon5.ico b/Src/Plugins/General/gen_tray/icons/icon5.ico
new file mode 100644
index 00000000..f249ad0c
--- /dev/null
+++ b/Src/Plugins/General/gen_tray/icons/icon5.ico
Binary files differ
diff --git a/Src/Plugins/General/gen_tray/icons/icon7.ico b/Src/Plugins/General/gen_tray/icons/icon7.ico
new file mode 100644
index 00000000..0e2fe7ba
--- /dev/null
+++ b/Src/Plugins/General/gen_tray/icons/icon7.ico
Binary files differ
diff --git a/Src/Plugins/General/gen_tray/icons/icon8.ico b/Src/Plugins/General/gen_tray/icons/icon8.ico
new file mode 100644
index 00000000..c18da079
--- /dev/null
+++ b/Src/Plugins/General/gen_tray/icons/icon8.ico
Binary files differ
diff --git a/Src/Plugins/General/gen_tray/icons/icon9.ico b/Src/Plugins/General/gen_tray/icons/icon9.ico
new file mode 100644
index 00000000..7845f62f
--- /dev/null
+++ b/Src/Plugins/General/gen_tray/icons/icon9.ico
Binary files differ
diff --git a/Src/Plugins/General/gen_tray/version.rc2 b/Src/Plugins/General/gen_tray/version.rc2
new file mode 100644
index 00000000..5672adb0
--- /dev/null
+++ b/Src/Plugins/General/gen_tray/version.rc2
@@ -0,0 +1,39 @@
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+#include "..\..\..\Winamp/buildType.h"
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 2,49,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", "2,49,0,0"
+ VALUE "InternalName", "Nullsoft Notification Area Control"
+ VALUE "LegalCopyright", "Copyright © 1997-2023 Winamp SA"
+ VALUE "LegalTrademarks", "Nullsoft and Winamp are trademarks of Winamp SA"
+ VALUE "OriginalFilename", "gen_tray.dll"
+ VALUE "ProductName", "Winamp"
+ VALUE "ProductVersion", STR_WINAMP_PRODUCTVER
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END