aboutsummaryrefslogtreecommitdiff
path: root/Src/Plugins/General/gen_ml
diff options
context:
space:
mode:
Diffstat (limited to 'Src/Plugins/General/gen_ml')
-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
184 files changed, 51028 insertions, 0 deletions
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